Merge branch 'counterStringType' into 'master'

CounterType: use String inside CounterType to handle KeywordCounter

See merge request core-developers/forge!2798
This commit is contained in:
Sol
2020-05-27 00:17:28 +00:00
180 changed files with 1995 additions and 1619 deletions

View File

@@ -464,7 +464,7 @@ public class AiAttackController {
final CardCollectionView beastions = ai.getCardsIn(ZoneType.Battlefield, "Beastmaster Ascension");
int minCreatures = 7;
for (final Card beastion : beastions) {
final int counters = beastion.getCounters(CounterType.QUEST);
final int counters = beastion.getCounters(CounterEnumType.QUEST);
minCreatures = Math.min(minCreatures, 7 - counters);
}
if (this.attackers.size() >= minCreatures) {
@@ -1065,7 +1065,7 @@ public class AiAttackController {
}
}
// if enough damage: switch to next planeswalker or player
if (damage >= pw.getCounters(CounterType.LOYALTY)) {
if (damage >= pw.getCounters(CounterEnumType.LOYALTY)) {
List<Card> pwDefending = combat.getDefendingPlaneswalkers();
boolean found = false;
// look for next planeswalker
@@ -1192,7 +1192,7 @@ public class AiAttackController {
if (isWorthLessThanAllKillers || canKillAllDangerous || numberOfPossibleBlockers < 2) {
numberOfPossibleBlockers += 1;
if (isWorthLessThanAllKillers && ComputerUtilCombat.canDestroyAttacker(ai, attacker, defender, combat, false)
&& !(attacker.hasKeyword(Keyword.UNDYING) && attacker.getCounters(CounterType.P1P1) == 0)) {
&& !(attacker.hasKeyword(Keyword.UNDYING) && attacker.getCounters(CounterEnumType.P1P1) == 0)) {
canBeKilledByOne = true; // there is a single creature on the battlefield that can kill the creature
// see if the defending creature is of higher or lower
// value. We don't want to attack only to lose value

View File

@@ -228,9 +228,9 @@ public class AiBlockController {
// 3.Blockers that can destroy the attacker and have an upside when dying
killingBlockers = getKillingBlockers(combat, attacker, blockers);
for (Card b : killingBlockers) {
if ((b.hasKeyword(Keyword.UNDYING) && b.getCounters(CounterType.P1P1) == 0) || b.hasSVar("SacMe")
|| (b.hasKeyword(Keyword.VANISHING) && b.getCounters(CounterType.TIME) == 1)
|| (b.hasKeyword(Keyword.FADING) && b.getCounters(CounterType.FADE) == 0)
if ((b.hasKeyword(Keyword.UNDYING) && b.getCounters(CounterEnumType.P1P1) == 0) || b.hasSVar("SacMe")
|| (b.hasKeyword(Keyword.VANISHING) && b.getCounters(CounterEnumType.TIME) == 1)
|| (b.hasKeyword(Keyword.FADING) && b.getCounters(CounterEnumType.FADE) == 0)
|| b.hasSVar("EndOfTurnLeavePlay")) {
blocker = b;
break;
@@ -299,8 +299,8 @@ public class AiBlockController {
final List<Card> blockers = getPossibleBlockers(combat, attacker, blockersLeft, true);
for (Card b : blockers) {
if ((b.hasKeyword(Keyword.VANISHING) && b.getCounters(CounterType.TIME) == 1)
|| (b.hasKeyword(Keyword.FADING) && b.getCounters(CounterType.FADE) == 0)
if ((b.hasKeyword(Keyword.VANISHING) && b.getCounters(CounterEnumType.TIME) == 1)
|| (b.hasKeyword(Keyword.FADING) && b.getCounters(CounterEnumType.FADE) == 0)
|| b.hasSVar("EndOfTurnLeavePlay")) {
blocker = b;
if (!ComputerUtilCombat.canDestroyAttacker(ai, attacker, blocker, combat, false)) {
@@ -851,7 +851,7 @@ public class AiBlockController {
damageToPW += ComputerUtilCombat.predictDamageTo((Card) def, pwatkr.getNetCombatDamage(), pwatkr, true);
}
}
if ((!onlyIfLethal && damageToPW > 0) || damageToPW >= def.getCounters(CounterType.LOYALTY)) {
if ((!onlyIfLethal && damageToPW > 0) || damageToPW >= def.getCounters(CounterEnumType.LOYALTY)) {
threatenedPWs.add((Card) def);
}
}
@@ -909,7 +909,7 @@ public class AiBlockController {
damageToPW += ComputerUtilCombat.predictDamageTo(pw, pwAtk.getNetCombatDamage(), pwAtk, true);
}
}
if (!isFullyBlocked && damageToPW >= pw.getCounters(CounterType.LOYALTY)) {
if (!isFullyBlocked && damageToPW >= pw.getCounters(CounterEnumType.LOYALTY)) {
for (Card chump : pwDefenders) {
if (chosenChumpBlockers.contains(chump)) {
combat.removeFromCombat(chump);

View File

@@ -177,7 +177,7 @@ public class AiController {
&& CardFactoryUtil.isCounterable(host)) {
return true;
} else if ("ChaliceOfTheVoid".equals(curse) && sa.isSpell() && CardFactoryUtil.isCounterable(host)
&& host.getCMC() == c.getCounters(CounterType.CHARGE)) {
&& host.getCMC() == c.getCounters(CounterEnumType.CHARGE)) {
return true;
} else if ("BazaarOfWonders".equals(curse) && sa.isSpell() && CardFactoryUtil.isCounterable(host)) {
String hostName = host.getName();
@@ -1801,7 +1801,7 @@ public class AiController {
throw new UnsupportedOperationException("AI is not supposed to reach this code at the moment");
}
public CardCollection chooseCardsForEffect(CardCollectionView pool, SpellAbility sa, int min, int max, boolean isOptional) {
public CardCollection chooseCardsForEffect(CardCollectionView pool, SpellAbility sa, int min, int max, boolean isOptional, Map<String, Object> params) {
if (sa == null || sa.getApi() == null) {
throw new UnsupportedOperationException();
}
@@ -1834,7 +1834,7 @@ public class AiController {
default:
CardCollection editablePool = new CardCollection(pool);
for (int i = 0; i < max; i++) {
Card c = player.getController().chooseSingleEntityForEffect(editablePool, sa, null, isOptional);
Card c = player.getController().chooseSingleEntityForEffect(editablePool, sa, null, isOptional, params);
if (c != null) {
result.add(c);
editablePool.remove(c);

View File

@@ -16,6 +16,7 @@ import forge.game.card.CardCollectionView;
import forge.game.card.CardLists;
import forge.game.card.CardPredicates;
import forge.game.card.CardPredicates.Presets;
import forge.game.card.CounterEnumType;
import forge.game.card.CounterType;
import forge.game.cost.*;
import forge.game.player.Player;
@@ -627,41 +628,41 @@ public class AiCostDecision extends CostDecisionMakerBase {
// the first things are benefit from removing counters
// try to remove +1/+1 counter from undying creature
List<Card> prefs = CardLists.filter(typeList, CardPredicates.hasCounter(CounterType.P1P1, c),
List<Card> prefs = CardLists.filter(typeList, CardPredicates.hasCounter(CounterEnumType.P1P1, c),
CardPredicates.hasKeyword("Undying"));
if (!prefs.isEmpty()) {
Collections.sort(prefs, CardPredicates.compareByCounterType(CounterType.P1P1));
Collections.sort(prefs, CardPredicates.compareByCounterType(CounterEnumType.P1P1));
PaymentDecision result = PaymentDecision.card(prefs);
result.ct = CounterType.P1P1;
result.ct = CounterType.get(CounterEnumType.P1P1);
return result;
}
// try to remove -1/-1 counter from persist creature
prefs = CardLists.filter(typeList, CardPredicates.hasCounter(CounterType.M1M1, c),
prefs = CardLists.filter(typeList, CardPredicates.hasCounter(CounterEnumType.M1M1, c),
CardPredicates.hasKeyword("Persist"));
if (!prefs.isEmpty()) {
Collections.sort(prefs, CardPredicates.compareByCounterType(CounterType.M1M1));
Collections.sort(prefs, CardPredicates.compareByCounterType(CounterEnumType.M1M1));
PaymentDecision result = PaymentDecision.card(prefs);
result.ct = CounterType.M1M1;
result.ct = CounterType.get(CounterEnumType.M1M1);
return result;
}
// try to remove Time counter from Chronozoa, it will generate more
prefs = CardLists.filter(typeList, CardPredicates.hasCounter(CounterType.TIME, c),
prefs = CardLists.filter(typeList, CardPredicates.hasCounter(CounterEnumType.TIME, c),
CardPredicates.nameEquals("Chronozoa"));
if (!prefs.isEmpty()) {
Collections.sort(prefs, CardPredicates.compareByCounterType(CounterType.TIME));
Collections.sort(prefs, CardPredicates.compareByCounterType(CounterEnumType.TIME));
PaymentDecision result = PaymentDecision.card(prefs);
result.ct = CounterType.TIME;
result.ct = CounterType.get(CounterEnumType.TIME);
return result;
}
// try to remove Quest counter on something with enough counters for the
// effect to continue
prefs = CardLists.filter(typeList, CardPredicates.hasCounter(CounterType.QUEST, c));
prefs = CardLists.filter(typeList, CardPredicates.hasCounter(CounterEnumType.QUEST, c));
if (!prefs.isEmpty()) {
prefs = CardLists.filter(prefs, new Predicate<Card>() {
@@ -673,12 +674,12 @@ public class AiCostDecision extends CostDecisionMakerBase {
if (crd.hasSVar("MaxQuestEffect")) {
e = Integer.parseInt(crd.getSVar("MaxQuestEffect"));
}
return crd.getCounters(CounterType.QUEST) >= e + c;
return crd.getCounters(CounterEnumType.QUEST) >= e + c;
}
});
Collections.sort(prefs, Collections.reverseOrder(CardPredicates.compareByCounterType(CounterType.QUEST)));
Collections.sort(prefs, Collections.reverseOrder(CardPredicates.compareByCounterType(CounterEnumType.QUEST)));
PaymentDecision result = PaymentDecision.card(prefs);
result.ct = CounterType.QUEST;
result.ct = CounterType.get(CounterEnumType.QUEST);
return result;
}
@@ -775,7 +776,7 @@ public class AiCostDecision extends CostDecisionMakerBase {
@Override
public boolean apply(final Card crd) {
for (Map.Entry<CounterType, Integer> e : crd.getCounters().entrySet()) {
if (e.getValue() >= c && (ctr.equals("ANY") || e.getKey() == CounterType.valueOf(ctr))) {
if (e.getValue() >= c && (ctr.equals("ANY") || e.getKey().equals(CounterType.getType(ctr)))) {
return true;
}
}
@@ -787,7 +788,7 @@ public class AiCostDecision extends CostDecisionMakerBase {
PaymentDecision result = PaymentDecision.card(card);
for (Map.Entry<CounterType, Integer> e : card.getCounters().entrySet()) {
if (e.getValue() >= c && (ctr.equals("ANY") || e.getKey() == CounterType.valueOf(ctr))) {
if (e.getValue() >= c && (ctr.equals("ANY") || e.getKey().equals(CounterType.getType(ctr)))) {
result.ct = e.getKey();
break;
}

View File

@@ -6,12 +6,12 @@
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
@@ -65,7 +65,7 @@ import java.util.*;
* <p>
* ComputerUtil class.
* </p>
*
*
* @author Forge
* @version $Id$
*/
@@ -213,7 +213,7 @@ public class ComputerUtil {
sa.setActivatingPlayer(ai);
if (!ComputerUtilCost.canPayCost(sa, ai))
return false;
final Card source = sa.getHostCard();
if (sa.isSpell() && !source.isCopiedSpell()) {
sa.setHostCard(game.getAction().moveToStack(source, sa));
@@ -349,8 +349,8 @@ public class ComputerUtil {
for (int ip = 0; ip < 6; ip++) {
final int priority = 6 - ip;
if (priority == 2 && ai.isCardInPlay("Crucible of Worlds")) {
CardCollection landsInPlay = CardLists.getType(typeList, "Land");
if (!landsInPlay.isEmpty()) {
CardCollection landsInPlay = CardLists.getType(typeList, "Land");
if (!landsInPlay.isEmpty()) {
// Don't need more land.
return ComputerUtilCard.getWorstLand(landsInPlay);
}
@@ -379,16 +379,16 @@ public class ComputerUtil {
return ComputerUtilCard.getWorstLand(landsInPlay);
}
}
// try everything when about to die
if (game.getPhaseHandler().getPhase().equals(PhaseType.COMBAT_DECLARE_BLOCKERS)
&& ComputerUtilCombat.lifeInSeriousDanger(ai, game.getCombat())) {
final CardCollection nonCreatures = CardLists.getNotType(typeList, "Creature");
if (!nonCreatures.isEmpty()) {
return ComputerUtilCard.getWorstAI(nonCreatures);
} else if (!typeList.isEmpty()) {
return ComputerUtilCard.getWorstAI(typeList);
}
if (game.getPhaseHandler().getPhase().equals(PhaseType.COMBAT_DECLARE_BLOCKERS)
&& ComputerUtilCombat.lifeInSeriousDanger(ai, game.getCombat())) {
final CardCollection nonCreatures = CardLists.getNotType(typeList, "Creature");
if (!nonCreatures.isEmpty()) {
return ComputerUtilCard.getWorstAI(nonCreatures);
} else if (!typeList.isEmpty()) {
return ComputerUtilCard.getWorstAI(typeList);
}
}
}
else if (pref.contains("DiscardCost")) { // search for permanents with DiscardMe
@@ -450,14 +450,14 @@ public class ComputerUtil {
return ComputerUtilCard.getWorstLand(landsInHand);
}
}
// try everything when about to die
if (activate != null && "Reality Smasher".equals(activate.getName()) ||
game.getPhaseHandler().getPhase().equals(PhaseType.COMBAT_DECLARE_BLOCKERS)
&& ComputerUtilCombat.lifeInSeriousDanger(ai, game.getCombat())) {
if (!typeList.isEmpty()) {
return ComputerUtilCard.getWorstAI(typeList);
}
game.getPhaseHandler().getPhase().equals(PhaseType.COMBAT_DECLARE_BLOCKERS)
&& ComputerUtilCombat.lifeInSeriousDanger(ai, game.getCombat())) {
if (!typeList.isEmpty()) {
return ComputerUtilCard.getWorstAI(typeList);
}
}
} else if (pref.contains("DonateMe")) {
// search for permanents with DonateMe. priority 1 is the lowest, priority 5 the highest
@@ -540,7 +540,7 @@ public class ComputerUtil {
public static CardCollection chooseExileFrom(final Player ai, final ZoneType zone, final String type, final Card activate,
final Card target, final int amount) {
CardCollection typeList = CardLists.getValidCards(ai.getCardsIn(zone), type.split(";"), activate.getController(), activate, null);
if ((target != null) && target.getController() == ai) {
typeList.remove(target); // don't exile the card we're pumping
}
@@ -561,7 +561,7 @@ public class ComputerUtil {
public static CardCollection choosePutToLibraryFrom(final Player ai, final ZoneType zone, final String type, final Card activate,
final Card target, final int amount) {
CardCollection typeList = CardLists.getValidCards(ai.getCardsIn(zone), type.split(";"), activate.getController(), activate, null);
if ((target != null) && target.getController() == ai) {
typeList.remove(target); // don't move the card we're pumping
}
@@ -572,11 +572,11 @@ public class ComputerUtil {
CardLists.sortByPowerAsc(typeList);
final CardCollection list = new CardCollection();
if (zone != ZoneType.Hand) {
Collections.reverse(typeList);
}
for (int i = 0; i < amount; i++) {
list.add(typeList.get(i));
}
@@ -636,7 +636,7 @@ public class ComputerUtil {
}
ComputerUtilCard.sortByEvaluateCreature(typeList);
Collections.reverse(typeList);
final CardCollection tapList = new CardCollection();
// Accumulate from "worst" creature
@@ -709,7 +709,7 @@ public class ComputerUtil {
return returnList;
}
public static CardCollection choosePermanentsToSacrifice(final Player ai, final CardCollectionView cardlist, final int amount, final SpellAbility source,
public static CardCollection choosePermanentsToSacrifice(final Player ai, final CardCollectionView cardlist, final int amount, final SpellAbility source,
final boolean destroy, final boolean isOptional) {
CardCollection remaining = new CardCollection(cardlist);
final CardCollection sacrificed = new CardCollection();
@@ -718,9 +718,9 @@ public class ComputerUtil {
final int considerSacThreshold = getAIPreferenceParameter(host, "CreatureEvalThreshold");
if ("OpponentOnly".equals(source.getParam("AILogic"))) {
if(!source.getActivatingPlayer().isOpponentOf(ai)) {
return sacrificed; // sacrifice none
}
if(!source.getActivatingPlayer().isOpponentOf(ai)) {
return sacrificed; // sacrifice none
}
} else if ("DesecrationDemon".equals(source.getParam("AILogic"))) {
if (!SpecialCardAi.DesecrationDemon.considerSacrificingCreature(ai, source)) {
return sacrificed; // don't sacrifice unless in special conditions specified by DesecrationDemon AI
@@ -738,27 +738,27 @@ public class ComputerUtil {
boolean removedSelf = false;
if (isOptional && source.hasParam("Devour") || source.hasParam("Exploit") || considerSacLogic) {
if (source.hasParam("Exploit")) {
for (Trigger t : host.getTriggers()) {
if (t.getMode() == TriggerType.Exploited) {
final String execute = t.getParam("Execute");
if (execute == null) {
continue;
}
final SpellAbility exSA = AbilityFactory.getAbility(host.getSVar(execute), host);
if (source.hasParam("Exploit")) {
for (Trigger t : host.getTriggers()) {
if (t.getMode() == TriggerType.Exploited) {
final String execute = t.getParam("Execute");
if (execute == null) {
continue;
}
final SpellAbility exSA = AbilityFactory.getAbility(host.getSVar(execute), host);
exSA.setActivatingPlayer(ai);
exSA.setTrigger(true);
exSA.setActivatingPlayer(ai);
exSA.setTrigger(true);
// Run non-mandatory trigger.
// These checks only work if the Executing SpellAbility is an Ability_Sub.
if ((exSA instanceof AbilitySub) && !SpellApiToAi.Converter.get(exSA.getApi()).doTriggerAI(ai, exSA, false)) {
// AI would not run this trigger if given the chance
return sacrificed;
}
}
}
}
// Run non-mandatory trigger.
// These checks only work if the Executing SpellAbility is an Ability_Sub.
if ((exSA instanceof AbilitySub) && !SpellApiToAi.Converter.get(exSA.getApi()).doTriggerAI(ai, exSA, false)) {
// AI would not run this trigger if given the chance
return sacrificed;
}
}
}
}
remaining = CardLists.filter(remaining, new Predicate<Card>() {
@Override
public boolean apply(final Card c) {
@@ -819,7 +819,7 @@ public class ComputerUtil {
if (ai.isOpponentOf(c.getController()))
return c;
}
if (destroy) {
final CardCollection indestructibles = CardLists.getKeyword(remaining, Keyword.INDESTRUCTIBLE);
if (!indestructibles.isEmpty()) {
@@ -909,7 +909,7 @@ public class ComputerUtil {
} catch (final Exception ex) {
throw new RuntimeException(TextUtil.concatNoSpace("There is an error in the card code for ", c.getName(), ":", ex.getMessage()), ex);
}
}
}
}
@@ -958,16 +958,16 @@ public class ComputerUtil {
final Card card = sa.getHostCard();
if (card.hasSVar("PlayMain1")) {
if (card.getSVar("PlayMain1").equals("ALWAYS") || sa.getPayCosts().hasNoManaCost()) {
return true;
} else if (card.getSVar("PlayMain1").equals("OPPONENTCREATURES")) {
//Only play these main1 when the opponent has creatures (stealing and giving them haste)
if (!ai.getOpponents().getCreaturesInPlay().isEmpty()) {
return true;
}
} else if (!card.getController().getCreaturesInPlay().isEmpty()) {
return true;
}
if (card.getSVar("PlayMain1").equals("ALWAYS") || sa.getPayCosts().hasNoManaCost()) {
return true;
} else if (card.getSVar("PlayMain1").equals("OPPONENTCREATURES")) {
//Only play these main1 when the opponent has creatures (stealing and giving them haste)
if (!ai.getOpponents().getCreaturesInPlay().isEmpty()) {
return true;
}
} else if (!card.getController().getCreaturesInPlay().isEmpty()) {
return true;
}
}
// try not to cast Raid creatures in main 1 if an attack is likely
@@ -980,7 +980,7 @@ public class ComputerUtil {
}
if (card.getManaCost().isZero()) {
return true;
return true;
}
if (card.hasKeyword(Keyword.RIOT) && ChooseGenericEffectAi.preferHasteForRiot(sa, ai)) {
@@ -1008,9 +1008,9 @@ public class ComputerUtil {
&& (card.hasKeyword(Keyword.HASTE) || ComputerUtil.hasACardGivingHaste(ai, true) || sa.isDash())) {
return true;
}
if (card.hasKeyword(Keyword.EXALTED)) {
return true;
return true;
}
//cast equipments in Main1 when there are creatures to equip and no other unequipped equipment
@@ -1140,7 +1140,7 @@ public class ComputerUtil {
if (discard.hasSVar("DiscardMe")) {
return true;
}
final Game game = ai.getGame();
final CardCollection landsInPlay = CardLists.filter(ai.getCardsIn(ZoneType.Battlefield), CardPredicates.Presets.LANDS);
final CardCollection landsInHand = CardLists.filter(ai.getCardsIn(ZoneType.Hand), CardPredicates.Presets.LANDS);
@@ -1240,11 +1240,11 @@ public class ComputerUtil {
}
}
} // AntiBuffedBy
if (sub != null) {
if (sub != null) {
return castSpellInMain1(ai, sub);
}
return false;
}
@@ -1253,7 +1253,7 @@ public class ComputerUtil {
int activations = sa.getActivationsThisTurn();
if (!sa.isIntrinsic()) {
return MyRandom.getRandom().nextFloat() >= .95; // Abilities created by static abilities have no memory
return MyRandom.getRandom().nextFloat() >= .95; // Abilities created by static abilities have no memory
}
if (activations < 10) { //10 activations per turn should still be acceptable
@@ -1270,27 +1270,27 @@ public class ComputerUtil {
return false;
}
if (abCost.hasTapCost() && source.hasSVar("AITapDown")) {
return true;
return true;
} else if (sa.hasParam("Planeswalker") && ai.getGame().getPhaseHandler().is(PhaseType.MAIN2)) {
for (final CostPart part : abCost.getCostParts()) {
if (part instanceof CostPutCounter) {
return true;
}
}
for (final CostPart part : abCost.getCostParts()) {
if (part instanceof CostPutCounter) {
return true;
}
}
}
for (final CostPart part : abCost.getCostParts()) {
if (part instanceof CostSacrifice) {
final CostSacrifice sac = (CostSacrifice) part;
final String type = sac.getType();
if (type.equals("CARDNAME")) {
if (source.getSVar("SacMe").equals("6")) {
return true;
}
continue;
}
final CardCollection typeList =
CardLists.getValidCards(ai.getCardsIn(ZoneType.Battlefield), type.split(","), source.getController(), source, sa);
for (Card c : typeList) {
@@ -1325,14 +1325,14 @@ public class ComputerUtil {
Map<String, String> params = stAb.getMapParams();
if ("Continuous".equals(params.get("Mode")) && params.containsKey("AddKeyword")
&& params.get("AddKeyword").contains("Haste")) {
if (c.isEquipment() && c.getEquipping() == null) {
return true;
}
final String affected = params.get("Affected");
if (affected.contains("Creature.YouCtrl")
|| affected.contains("Other+YouCtrl")) {
|| affected.contains("Other+YouCtrl")) {
return true;
} else if (affected.contains("Creature.PairedWith") && !c.isPaired()) {
return true;
@@ -1341,10 +1341,10 @@ public class ComputerUtil {
}
for (Trigger t : c.getTriggers()) {
Map<String, String> params = t.getMapParams();
Map<String, String> params = t.getMapParams();
if (!"ChangesZone".equals(params.get("Mode"))
|| !"Battlefield".equals(params.get("Destination"))
|| !params.containsKey("ValidCard")) {
|| !"Battlefield".equals(params.get("Destination"))
|| !params.containsKey("ValidCard")) {
continue;
}
@@ -1360,10 +1360,10 @@ public class ComputerUtil {
}
}
}
all.addAll(ai.getCardsActivableInExternalZones(true));
all.addAll(ai.getCardsIn(ZoneType.Hand));
for (final Card c : all) {
for (final SpellAbility sa : c.getSpellAbilities()) {
if (sa.getApi() == ApiType.Pump && sa.hasParam("KW") && sa.getParam("KW").contains("Haste")) {
@@ -1398,10 +1398,10 @@ public class ComputerUtil {
public static boolean hasAFogEffect(final Player ai) {
final CardCollection all = new CardCollection(ai.getCardsIn(ZoneType.Battlefield));
all.addAll(ai.getCardsActivableInExternalZones(true));
all.addAll(ai.getCardsIn(ZoneType.Hand));
for (final Card c : all) {
for (final SpellAbility sa : c.getSpellAbilities()) {
if (sa.getApi() != ApiType.Fog) {
@@ -1431,7 +1431,7 @@ public class ComputerUtil {
final CardCollection all = new CardCollection(ai.getCardsIn(ZoneType.Battlefield));
all.addAll(ai.getCardsActivableInExternalZones(true));
all.addAll(CardLists.filter(ai.getCardsIn(ZoneType.Hand), Predicates.not(Presets.PERMANENTS)));
for (final Card c : all) {
for (final SpellAbility sa : c.getSpellAbilities()) {
if (sa.getApi() != ApiType.DealDamage) {
@@ -1490,7 +1490,7 @@ public class ComputerUtil {
/**
* Returns list of objects threatened by effects on the stack
*
*
* @param ai
* calling player
* @param sa
@@ -1505,7 +1505,7 @@ public class ComputerUtil {
if (game.getStack().isEmpty()) {
return objects;
}
// check stack for something that will kill this
for (SpellAbilityStackInstance si : game.getStack()) {
// iterate from top of stack to find SpellAbility, including sub-abilities,
@@ -1523,8 +1523,8 @@ public class ComputerUtil {
if (top) {
break; // only evaluate top-stack
}
}
}
return objects;
}
@@ -1536,14 +1536,14 @@ public class ComputerUtil {
int toughness = 0;
boolean grantIndestructible = false;
boolean grantShroud = false;
if (topStack == null) {
return objects;
}
final Card source = topStack.getHostCard();
final ApiType threatApi = topStack.getApi();
// Can only Predict things from AFs
if (threatApi == null) {
return threatened;
@@ -1557,7 +1557,7 @@ public class ComputerUtil {
CardCollectionView battleField = aiPlayer.getCardsIn(ZoneType.Battlefield);
objects = CardLists.getValidCards(battleField, topStack.getParam("ValidCards").split(","), source.getController(), source, topStack);
} else {
return threatened;
return threatened;
}
} else {
objects = topStack.getTargets().getTargets();
@@ -1571,7 +1571,7 @@ public class ComputerUtil {
}
}
if (canBeTargeted.isEmpty()) {
return threatened;
return threatened;
}
objects = canBeTargeted;
}
@@ -1640,7 +1640,7 @@ public class ComputerUtil {
}
// don't use it on creatures that can't be regenerated
if ((saviourApi == ApiType.Regenerate || saviourApi == ApiType.RegenerateAll) &&
if ((saviourApi == ApiType.Regenerate || saviourApi == ApiType.RegenerateAll) &&
(!c.canBeShielded() || noRegen)) {
continue;
}
@@ -1652,14 +1652,14 @@ public class ComputerUtil {
continue;
}
}
if (saviourApi == ApiType.PutCounter || saviourApi == ApiType.PutCounterAll) {
boolean canSave = ComputerUtilCombat.predictDamageTo(c, dmg - toughness, source, false) < ComputerUtilCombat.getDamageToKill(c);
if (!canSave) {
continue;
}
}
// cannot protect against source
if (saviourApi == ApiType.Protection && (ProtectAi.toProtectFrom(source, saviour) == null)) {
continue;
@@ -1670,7 +1670,7 @@ public class ComputerUtil {
if (saviourApi == ApiType.ChangeZone && (c.getOwner().isOpponentOf(aiPlayer) || c.isToken())) {
continue;
}
if (ComputerUtilCombat.predictDamageTo(c, dmg, source, false) >= ComputerUtilCombat.getDamageToKill(c)) {
threatened.add(c);
}
@@ -1689,7 +1689,7 @@ public class ComputerUtil {
}
// -Toughness Curse
else if ((threatApi == ApiType.Pump || threatApi == ApiType.PumpAll && topStack.isCurse())
&& (saviourApi == ApiType.ChangeZone || saviourApi == ApiType.Pump || saviourApi == ApiType.PumpAll
&& (saviourApi == ApiType.ChangeZone || saviourApi == ApiType.Pump || saviourApi == ApiType.PumpAll
|| saviourApi == ApiType.Protection || saviourApi == ApiType.PutCounter || saviourApi == ApiType.PutCounterAll
|| saviourApi == null)) {
final int dmg = -AbilityUtils.calculateAmount(topStack.getHostCard(),
@@ -1702,7 +1702,7 @@ public class ComputerUtil {
if (!canRemove) {
continue;
}
if (saviourApi == ApiType.Pump || saviourApi == ApiType.PumpAll) {
final boolean cantSave = c.getNetToughness() + toughness <= dmg
|| (!c.hasKeyword(Keyword.INDESTRUCTIBLE) && c.getShieldCount() == 0 && !grantIndestructible
@@ -1711,14 +1711,14 @@ public class ComputerUtil {
continue;
}
}
if (saviourApi == ApiType.PutCounter || saviourApi == ApiType.PutCounterAll) {
boolean canSave = c.getNetToughness() + toughness > dmg;
if (!canSave) {
continue;
}
}
if (saviourApi == ApiType.Protection) {
if (tgt == null || (ProtectAi.toProtectFrom(source, saviour) == null)) {
continue;
@@ -1812,9 +1812,9 @@ public class ComputerUtil {
}
}
//GainControl
else if ((threatApi == ApiType.GainControl
|| (threatApi == ApiType.Attach && topStack.hasParam("AILogic") && topStack.getParam("AILogic").equals("GainControl") ))
&& (saviourApi == ApiType.ChangeZone || saviourApi == ApiType.Pump || saviourApi == ApiType.PumpAll
else if ((threatApi == ApiType.GainControl
|| (threatApi == ApiType.Attach && topStack.hasParam("AILogic") && topStack.getParam("AILogic").equals("GainControl") ))
&& (saviourApi == ApiType.ChangeZone || saviourApi == ApiType.Pump || saviourApi == ApiType.PumpAll
|| saviourApi == ApiType.Protection || saviourApi == null)) {
for (final Object o : objects) {
if (o instanceof Card) {
@@ -1931,7 +1931,7 @@ public class ComputerUtil {
public static int scoreHand(CardCollectionView handList, Player ai, int cardsToReturn) {
// TODO Improve hand scoring in relation to cards to return.
// If final hand size is 5, score a hand based on what that 5 would be.
// Or if this is really really fast, determine what the 5 would be based on scoring
// Or if this is really really fast, determine what the 5 would be based on scoring
// All of the possibilities
final AiController aic = ((PlayerControllerAi)ai.getController()).getAi();
@@ -2014,16 +2014,16 @@ public class ComputerUtil {
final CardCollectionView handList = ai.getCardsIn(ZoneType.Hand);
return scoreHand(handList, ai, cardsToReturn) <= 0;
}
public static CardCollection getPartialParisCandidates(Player ai) {
// Commander no longer uses partial paris.
final CardCollection candidates = new CardCollection();
final CardCollectionView handList = ai.getCardsIn(ZoneType.Hand);
final CardCollection lands = CardLists.getValidCards(handList, "Card.Land", ai, null);
final CardCollection nonLands = CardLists.getValidCards(handList, "Card.nonLand", ai, null);
CardLists.sortByCmcDesc(nonLands);
if (lands.size() >= 3 && lands.size() <= 4) {
return candidates;
}
@@ -2031,7 +2031,7 @@ public class ComputerUtil {
//Not enough lands!
int tgtCandidates = Math.max(Math.abs(lands.size()-nonLands.size()), 3);
System.out.println("Partial Paris: " + ai.getName() + " lacks lands, aiming to exile " + tgtCandidates + " cards.");
for (int i=0;i<tgtCandidates;i++) {
candidates.add(nonLands.get(i));
}
@@ -2053,7 +2053,7 @@ public class ComputerUtil {
numProducers.get(col).add(c);
}
}
}
}
}
}
@@ -2062,7 +2062,7 @@ public class ComputerUtil {
System.out.print(c.toString() + ", ");
}
System.out.println();
if (candidates.size() < 2) {
candidates.clear();
}
@@ -2102,7 +2102,7 @@ public class ComputerUtil {
CardCollection landsInHand = CardLists.filter(cardsInHand, CardPredicates.Presets.LANDS_PRODUCING_MANA);
// valuable mana-producing artifacts that may be equated to a land
List<String> manaArts = Arrays.asList("Mox Pearl", "Mox Sapphire", "Mox Jet", "Mox Ruby", "Mox Emerald");
// evaluate creatures available in deck
CardCollectionView allCreatures = CardLists.filter(allCards, Predicates.and(CardPredicates.Presets.CREATURES, CardPredicates.isOwner(player)));
int numCards = allCreatures.size();
@@ -2185,7 +2185,7 @@ public class ComputerUtil {
}
Collections.sort(goodChoices, CardLists.TextLenComparator);
CardLists.sortByCmcDesc(goodChoices);
dChoices.add(goodChoices.get(0));
@@ -2196,7 +2196,7 @@ public class ComputerUtil {
if (p == aiChooser) { // ask that ai player what he would like to discard
final AiController aic = ((PlayerControllerAi)p.getController()).getAi();
return aic.getCardsToDiscard(min, max, validCards, sa);
}
}
// no special options for human or remote friends
return getCardsToDiscardFromOpponent(aiChooser, p, sa, validCards, min, max);
}
@@ -2245,7 +2245,7 @@ public class ComputerUtil {
chosen = ComputerUtilCard.getMostProminentType(ai.getCardsIn(ZoneType.Battlefield), valid);
}
else if (logic.equals("MostProminentOppControls")) {
CardCollection list = CardLists.filterControlledBy(game.getCardsIn(ZoneType.Battlefield), ai.getOpponents());
CardCollection list = CardLists.filterControlledBy(game.getCardsIn(ZoneType.Battlefield), ai.getOpponents());
chosen = ComputerUtilCard.getMostProminentType(list, valid);
if (!CardType.isACreatureType(chosen) || invalidTypes.contains(chosen)) {
list = CardLists.filterControlledBy(game.getCardsInGame(), ai.getOpponents());
@@ -2272,11 +2272,11 @@ public class ComputerUtil {
chosen = ComputerUtilCard.getMostProminentType(list, valid);
} else if (logic.equals("MostNeededType")) {
// Choose a type that is in the deck, but not in hand or on the battlefield
// Choose a type that is in the deck, but not in hand or on the battlefield
final List<String> basics = new ArrayList<>(CardType.Constant.BASIC_TYPES);
CardCollectionView presentCards = CardCollection.combine(ai.getCardsIn(ZoneType.Battlefield), ai.getCardsIn(ZoneType.Hand));
CardCollectionView possibleCards = ai.getAllCards();
for (String b : basics) {
if (!Iterables.any(presentCards, CardPredicates.isType(b)) && Iterables.any(possibleCards, CardPredicates.isType(b))) {
chosen = b;
@@ -2333,6 +2333,8 @@ public class ComputerUtil {
boolean opponent = controller.isOpponentOf(ai);
final CounterType p1p1Type = CounterType.get(CounterEnumType.P1P1);
if (!sa.hasParam("AILogic")) {
return Aggregates.random(options);
}
@@ -2386,7 +2388,7 @@ public class ComputerUtil {
}
}
// is it can't receive counters, choose +1/+1 ones
if (!source.canReceiveCounters(CounterType.P1P1)) {
if (!source.canReceiveCounters(p1p1Type)) {
return opponent ? "Feather" : "Quill";
}
// if source is not on the battlefield anymore, choose +1/+1
@@ -2418,7 +2420,7 @@ public class ComputerUtil {
Card token = TokenAi.spawnToken(controller, saToken);
// is it can't receive counters, choose +1/+1 ones
if (!source.canReceiveCounters(CounterType.P1P1)) {
if (!source.canReceiveCounters(p1p1Type)) {
return opponent ? "Strength" : "Numbers";
}
@@ -2441,11 +2443,11 @@ public class ComputerUtil {
Card sourceNumbers = CardUtil.getLKICopy(source);
Card sourceStrength = CardUtil.getLKICopy(source);
sourceNumbers.setCounters(CounterType.P1P1, sourceNumbers.getCounters(CounterType.P1P1) + numStrength);
sourceNumbers.setCounters(p1p1Type, sourceNumbers.getCounters(p1p1Type) + numStrength);
sourceNumbers.setZone(source.getZone());
sourceStrength.setCounters(CounterType.P1P1,
sourceStrength.getCounters(CounterType.P1P1) + numStrength + 1);
sourceStrength.setCounters(p1p1Type,
sourceStrength.getCounters(p1p1Type) + numStrength + 1);
sourceStrength.setZone(source.getZone());
int scoreStrength = ComputerUtilCard.evaluateCreature(sourceStrength) + tokenScore * numNumbers;
@@ -2467,7 +2469,7 @@ public class ComputerUtil {
}
// is it can't receive counters, choose +1/+1 ones
if (!source.canReceiveCounters(CounterType.P1P1)) {
if (!source.canReceiveCounters(p1p1Type)) {
return opponent ? "Sprout" : "Harvest";
}
@@ -2544,11 +2546,11 @@ public class ComputerUtil {
});
return ComputerUtilCard.getBestCreatureAI(killables);
}
public static int predictDamageFromSpell(final SpellAbility sa, final Player targetPlayer) {
int damage = -1; // returns -1 if the spell does not deal damage
final Card card = sa.getHostCard();
SpellAbility ab = sa;
while (ab != null) {
if (ab.getApi() == ApiType.DealDamage) {
@@ -2567,12 +2569,12 @@ public class ComputerUtil {
}
ab = ab.getSubAbility();
}
return damage;
}
public static int getDamageForPlaying(final Player player, final SpellAbility sa) {
// check for bad spell cast triggers
int damage = 0;
final Game game = player.getGame();
@@ -2602,7 +2604,7 @@ public class ComputerUtil {
continue;
}
}
if (trigParams.containsKey("ValidActivatingPlayer")) {
if (!player.isValid(trigParams.get("ValidActivatingPlayer"), source.getController(), source, sa)) {
continue;
@@ -2662,7 +2664,7 @@ public class ComputerUtil {
}
}
}
return damage;
}
@@ -2685,7 +2687,7 @@ public class ComputerUtil {
if (!trigger.requirementsCheck(game)) {
continue;
}
if (trigParams.containsKey("CheckOnTriggeredCard")
if (trigParams.containsKey("CheckOnTriggeredCard")
&& AbilityUtils.getDefinedCards(permanent, source.getSVar(trigParams.get("CheckOnTriggeredCard").split(" ")[0]), null).isEmpty()) {
continue;
}
@@ -2759,27 +2761,27 @@ public class ComputerUtil {
}
public static boolean isNegativeCounter(CounterType type, Card c) {
return type == CounterType.AGE || type == CounterType.BRIBERY || type == CounterType.DOOM
|| type == CounterType.M1M1 || type == CounterType.M0M2 || type == CounterType.M0M1
|| type == CounterType.M1M0 || type == CounterType.M2M1 || type == CounterType.M2M2
return type.is(CounterEnumType.AGE) || type.is(CounterEnumType.BRIBERY) || type.is(CounterEnumType.DOOM)
|| type.is(CounterEnumType.M1M1) || type.is(CounterEnumType.M0M2) || type.is(CounterEnumType.M0M1)
|| type.is(CounterEnumType.M1M0) || type.is(CounterEnumType.M2M1) || type.is(CounterEnumType.M2M2)
// Blaze only hurts Lands
|| (type == CounterType.BLAZE && c.isLand())
|| (type.is(CounterEnumType.BLAZE) && c.isLand())
// Iceberg does use Ice as Storage
|| (type == CounterType.ICE && !"Iceberg".equals(c.getName()))
|| (type.is(CounterEnumType.ICE) && !"Iceberg".equals(c.getName()))
// some lands does use Depletion as Storage Counter
|| (type == CounterType.DEPLETION && c.hasKeyword("CARDNAME doesn't untap during your untap step."))
|| (type.is(CounterEnumType.DEPLETION) && c.hasKeyword("CARDNAME doesn't untap during your untap step."))
// treat Time Counters on suspended Cards as Bad,
// and also on Chronozoa
|| (type == CounterType.TIME && (!c.isInPlay() || "Chronozoa".equals(c.getName())))
|| type == CounterType.GOLD || type == CounterType.MUSIC || type == CounterType.PUPA
|| type == CounterType.PARALYZATION || type == CounterType.SHELL || type == CounterType.SLEEP
|| type == CounterType.SLUMBER || type == CounterType.SLEIGHT || type == CounterType.WAGE;
|| (type.is(CounterEnumType.TIME) && (!c.isInPlay() || "Chronozoa".equals(c.getName())))
|| type.is(CounterEnumType.GOLD) || type.is(CounterEnumType.MUSIC) || type.is(CounterEnumType.PUPA)
|| type.is(CounterEnumType.PARALYZATION) || type.is(CounterEnumType.SHELL) || type.is(CounterEnumType.SLEEP)
|| type.is(CounterEnumType.SLUMBER) || type.is(CounterEnumType.SLEIGHT) || type.is(CounterEnumType.WAGE);
}
// this countertypes has no effect
public static boolean isUselessCounter(CounterType type) {
return type == CounterType.AWAKENING || type == CounterType.MANIFESTATION || type == CounterType.PETRIFICATION
|| type == CounterType.TRAINING;
return type.is(CounterEnumType.AWAKENING) || type.is(CounterEnumType.MANIFESTATION) || type.is(CounterEnumType.PETRIFICATION)
|| type.is(CounterEnumType.TRAINING);
}
public static Player evaluateBoardPosition(final List<Player> listToEvaluate) {
@@ -2891,7 +2893,7 @@ public class ComputerUtil {
return false;
}
public static boolean targetPlayableSpellCard(final Player ai, CardCollection options, final SpellAbility sa, final boolean withoutPayingManaCost) {
// determine and target a card with a SA that the AI can afford and will play
AiController aic = ((PlayerControllerAi) ai.getController()).getAi();

View File

@@ -1422,8 +1422,8 @@ public class ComputerUtilCard {
if (combat.isAttacking(c) && opp.getLife() > 0) {
int dmg = ComputerUtilCombat.damageIfUnblocked(c, opp, combat, true);
int pumpedDmg = ComputerUtilCombat.damageIfUnblocked(pumped, opp, pumpedCombat, true);
int poisonOrig = opp.canReceiveCounters(CounterType.POISON) ? ComputerUtilCombat.poisonIfUnblocked(c, ai) : 0;
int poisonPumped = opp.canReceiveCounters(CounterType.POISON) ? ComputerUtilCombat.poisonIfUnblocked(pumped, ai) : 0;
int poisonOrig = opp.canReceiveCounters(CounterEnumType.POISON) ? ComputerUtilCombat.poisonIfUnblocked(c, ai) : 0;
int poisonPumped = opp.canReceiveCounters(CounterEnumType.POISON) ? ComputerUtilCombat.poisonIfUnblocked(pumped, ai) : 0;
// predict Infect
if (pumpedDmg == 0 && c.hasKeyword(Keyword.INFECT)) {
@@ -1446,7 +1446,7 @@ public class ComputerUtilCard {
}
if (pumpedDmg > dmg) {
if ((!c.hasKeyword(Keyword.INFECT) && pumpedDmg >= opp.getLife())
|| (c.hasKeyword(Keyword.INFECT) && opp.canReceiveCounters(CounterType.POISON) && pumpedDmg >= opp.getPoisonCounters())
|| (c.hasKeyword(Keyword.INFECT) && opp.canReceiveCounters(CounterEnumType.POISON) && pumpedDmg >= opp.getPoisonCounters())
|| ("PumpForTrample".equals(sa.getParam("AILogic")))) {
return true;
}
@@ -1765,10 +1765,10 @@ public class ComputerUtilCard {
}
public static boolean hasActiveUndyingOrPersist(final Card c) {
if (c.hasKeyword(Keyword.UNDYING) && c.getCounters(CounterType.P1P1) == 0) {
if (c.hasKeyword(Keyword.UNDYING) && c.getCounters(CounterEnumType.P1P1) == 0) {
return true;
}
if (c.hasKeyword(Keyword.PERSIST) && c.getCounters(CounterType.M1M1) == 0) {
if (c.hasKeyword(Keyword.PERSIST) && c.getCounters(CounterEnumType.M1M1) == 0) {
return true;
}
return false;

View File

@@ -328,7 +328,7 @@ public class ComputerUtilCombat {
public static int resultingPoison(final Player ai, final Combat combat) {
// ai can't get poision counters, so the value can't change
if (!ai.canReceiveCounters(CounterType.POISON)) {
if (!ai.canReceiveCounters(CounterEnumType.POISON)) {
return ai.getPoisonCounters();
}
@@ -931,7 +931,7 @@ public class ComputerUtilCombat {
if (dealsFirstStrikeDamage(attacker, withoutAbilities, null)
&& (attacker.hasKeyword(Keyword.WITHER) || attacker.hasKeyword(Keyword.INFECT))
&& !dealsFirstStrikeDamage(blocker, withoutAbilities, null)
&& !blocker.canReceiveCounters(CounterType.M1M1)) {
&& !blocker.canReceiveCounters(CounterEnumType.M1M1)) {
power -= attacker.getNetCombatDamage();
}
@@ -1058,7 +1058,7 @@ public class ComputerUtilCombat {
continue;
}
if (ability.hasParam("Adapt") && blocker.getCounters(CounterType.P1P1) > 0) {
if (ability.hasParam("Adapt") && blocker.getCounters(CounterEnumType.P1P1) > 0) {
continue;
}
@@ -1234,7 +1234,7 @@ public class ComputerUtilCombat {
continue;
}
if (ability.hasParam("Adapt") && blocker.getCounters(CounterType.P1P1) > 0) {
if (ability.hasParam("Adapt") && blocker.getCounters(CounterEnumType.P1P1) > 0) {
continue;
}
@@ -1296,7 +1296,7 @@ public class ComputerUtilCombat {
if (ComputerUtilCombat.dealsFirstStrikeDamage(blocker, withoutAbilities, combat)
&& (blocker.hasKeyword(Keyword.WITHER) || blocker.hasKeyword(Keyword.INFECT))
&& !ComputerUtilCombat.dealsFirstStrikeDamage(attacker, withoutAbilities, combat)
&& !attacker.canReceiveCounters(CounterType.M1M1)) {
&& !attacker.canReceiveCounters(CounterEnumType.M1M1)) {
power -= blocker.getNetCombatDamage();
}
theTriggers.addAll(blocker.getTriggers());
@@ -1456,7 +1456,7 @@ public class ComputerUtilCombat {
continue;
}
if (ability.hasParam("Adapt") && attacker.getCounters(CounterType.P1P1) > 0) {
if (ability.hasParam("Adapt") && attacker.getCounters(CounterEnumType.P1P1) > 0) {
continue;
}
@@ -1693,7 +1693,7 @@ public class ComputerUtilCombat {
continue;
}
if (ability.hasParam("Adapt") && attacker.getCounters(CounterType.P1P1) > 0) {
if (ability.hasParam("Adapt") && attacker.getCounters(CounterEnumType.P1P1) > 0) {
continue;
}
@@ -1848,10 +1848,10 @@ public class ComputerUtilCombat {
if (((attacker.hasKeyword(Keyword.INDESTRUCTIBLE) || (ComputerUtil.canRegenerate(ai, attacker) && !withoutAbilities))
&& !(blocker.hasKeyword(Keyword.WITHER) || blocker.hasKeyword(Keyword.INFECT)))
|| (attacker.hasKeyword(Keyword.PERSIST) && !attacker.canReceiveCounters(CounterType.M1M1) && (attacker
.getCounters(CounterType.M1M1) == 0))
|| (attacker.hasKeyword(Keyword.UNDYING) && !attacker.canReceiveCounters(CounterType.P1P1) && (attacker
.getCounters(CounterType.P1P1) == 0))) {
|| (attacker.hasKeyword(Keyword.PERSIST) && !attacker.canReceiveCounters(CounterEnumType.M1M1) && (attacker
.getCounters(CounterEnumType.M1M1) == 0))
|| (attacker.hasKeyword(Keyword.UNDYING) && !attacker.canReceiveCounters(CounterEnumType.P1P1) && (attacker
.getCounters(CounterEnumType.P1P1) == 0))) {
return false;
}
@@ -2080,10 +2080,10 @@ public class ComputerUtilCombat {
if (((blocker.hasKeyword(Keyword.INDESTRUCTIBLE) || (ComputerUtil.canRegenerate(ai, blocker) && !withoutAbilities)) && !(attacker
.hasKeyword(Keyword.WITHER) || attacker.hasKeyword(Keyword.INFECT)))
|| (blocker.hasKeyword(Keyword.PERSIST) && !blocker.canReceiveCounters(CounterType.M1M1) && (blocker
.getCounters(CounterType.M1M1) == 0))
|| (blocker.hasKeyword(Keyword.UNDYING) && !blocker.canReceiveCounters(CounterType.P1P1) && (blocker
.getCounters(CounterType.P1P1) == 0))) {
|| (blocker.hasKeyword(Keyword.PERSIST) && !blocker.canReceiveCounters(CounterEnumType.M1M1) && (blocker
.getCounters(CounterEnumType.M1M1) == 0))
|| (blocker.hasKeyword(Keyword.UNDYING) && !blocker.canReceiveCounters(CounterEnumType.P1P1) && (blocker
.getCounters(CounterEnumType.P1P1) == 0))) {
return false;
}

View File

@@ -45,7 +45,7 @@ public class ComputerUtilCost {
final CostPutCounter addCounter = (CostPutCounter) part;
final CounterType type = addCounter.getCounter();
if (type.equals(CounterType.M1M1)) {
if (type.equals(CounterEnumType.M1M1)) {
return false;
}
}
@@ -75,7 +75,7 @@ public class ComputerUtilCost {
final CounterType type = remCounter.counter;
if (!part.payCostFromSource()) {
if (CounterType.P1P1.equals(type)) {
if (CounterEnumType.P1P1.equals(type)) {
return false;
}
continue;
@@ -97,7 +97,7 @@ public class ComputerUtilCost {
// check the sa what the PaymentDecision is.
// ignore Loyality abilities with Zero as Cost
if (sa != null && !CounterType.LOYALTY.equals(type)) {
if (sa != null && !CounterEnumType.LOYALTY.equals(type)) {
final AiCostDecision decision = new AiCostDecision(sa.getActivatingPlayer(), sa);
PaymentDecision pay = decision.visit(remCounter);
if (pay == null || pay.c <= 0) {
@@ -106,7 +106,7 @@ public class ComputerUtilCost {
}
//don't kill the creature
if (CounterType.P1P1.equals(type) && source.getLethalDamage() <= 1
if (CounterEnumType.P1P1.equals(type) && source.getLethalDamage() <= 1
&& !source.hasKeyword(Keyword.UNDYING)) {
return false;
}

View File

@@ -371,7 +371,7 @@ public class ComputerUtilMana {
adjustManaCostToAvoidNegEffects(cost, sa.getHostCard(), ai);
List<Mana> manaSpentToPay = test ? new ArrayList<>() : sa.getPayingMana();
boolean purePhyrexian = cost.containsOnlyPhyrexianMana();
int testEnergyPool = ai.getCounters(CounterType.ENERGY);
int testEnergyPool = ai.getCounters(CounterEnumType.ENERGY);
List<SpellAbility> paymentList = Lists.newArrayList();

View File

@@ -5,7 +5,7 @@ import com.google.common.base.Function;
import forge.game.ability.AbilityUtils;
import forge.game.ability.ApiType;
import forge.game.card.Card;
import forge.game.card.CounterType;
import forge.game.card.CounterEnumType;
import forge.game.cost.CostPayEnergy;
import forge.game.keyword.Keyword;
import forge.game.keyword.KeywordInterface;
@@ -246,7 +246,7 @@ public class CreatureEvaluator implements Function<Card, Integer> {
// Electrostatic Pummeler, can be expanded for similar cards
int initPower = getEffectivePower(sa.getHostCard());
int pumpedPower = initPower;
int energy = sa.getHostCard().getController().getCounters(CounterType.ENERGY);
int energy = sa.getHostCard().getController().getCounters(CounterEnumType.ENERGY);
if (energy > 0) {
int numActivations = energy / 3;
for (int i = 0; i < numActivations; i++) {

View File

@@ -1091,11 +1091,11 @@ public abstract class GameState {
}
private void applyCountersToGameEntity(GameEntity entity, String counterString) {
entity.setCounters(Maps.newEnumMap(CounterType.class));
entity.setCounters(Maps.newHashMap());
String[] allCounterStrings = counterString.split(",");
for (final String counterPair : allCounterStrings) {
String[] pair = counterPair.split("=", 2);
entity.addCounter(CounterType.valueOf(pair[0]), Integer.parseInt(pair[1]), null, false, false, null);
entity.addCounter(CounterType.getType(pair[0]), Integer.parseInt(pair[1]), null, false, false, null);
}
}
@@ -1137,7 +1137,7 @@ public abstract class GameState {
Map<CounterType, Integer> counters = c.getCounters();
// Note: Not clearCounters() since we want to keep the counters
// var as-is.
c.setCounters(Maps.newEnumMap(CounterType.class));
c.setCounters(Maps.newHashMap());
if (c.isAura()) {
// dummy "enchanting" to indicate that the card will be force-attached elsewhere
// (will be overridden later, so the actual value shouldn't matter)

View File

@@ -145,12 +145,12 @@ public class PlayerControllerAi extends PlayerController {
}
@Override
public CardCollectionView chooseCardsForEffect(CardCollectionView sourceList, SpellAbility sa, String title, int min, int max, boolean isOptional) {
return brains.chooseCardsForEffect(sourceList, sa, min, max, isOptional);
public CardCollectionView chooseCardsForEffect(CardCollectionView sourceList, SpellAbility sa, String title, int min, int max, boolean isOptional, Map<String, Object> params) {
return brains.chooseCardsForEffect(sourceList, sa, min, max, isOptional, params);
}
@Override
public <T extends GameEntity> T chooseSingleEntityForEffect(FCollectionView<T> optionList, DelayedReveal delayedReveal, SpellAbility sa, String title, boolean isOptional, Player targetedPlayer) {
public <T extends GameEntity> T chooseSingleEntityForEffect(FCollectionView<T> optionList, DelayedReveal delayedReveal, SpellAbility sa, String title, boolean isOptional, Player targetedPlayer, Map<String, Object> params) {
if (delayedReveal != null) {
reveal(delayedReveal.getCards(), delayedReveal.getZone(), delayedReveal.getOwner(), delayedReveal.getMessagePrefix());
}
@@ -158,13 +158,13 @@ public class PlayerControllerAi extends PlayerController {
if (null == api) {
throw new InvalidParameterException("SA is not api-based, this is not supported yet");
}
return SpellApiToAi.Converter.get(api).chooseSingleEntity(player, sa, (FCollection<T>)optionList, isOptional, targetedPlayer);
return SpellApiToAi.Converter.get(api).chooseSingleEntity(player, sa, (FCollection<T>)optionList, isOptional, targetedPlayer, params);
}
@Override
public <T extends GameEntity> List<T> chooseEntitiesForEffect(
FCollectionView<T> optionList, int min, int max, DelayedReveal delayedReveal, SpellAbility sa, String title,
Player targetedPlayer) {
Player targetedPlayer, Map<String, Object> params) {
if (delayedReveal != null) {
reveal(delayedReveal.getCards(), delayedReveal.getZone(), delayedReveal.getOwner(), delayedReveal.getMessagePrefix());
}
@@ -172,7 +172,7 @@ public class PlayerControllerAi extends PlayerController {
List<T> selecteds = new ArrayList<>();
T selected;
do {
selected = chooseSingleEntityForEffect(remaining, null, sa, title, selecteds.size()>=min, targetedPlayer);
selected = chooseSingleEntityForEffect(remaining, null, sa, title, selecteds.size()>=min, targetedPlayer, params);
if ( selected != null ) {
remaining.remove(selected);
selecteds.add(selected);
@@ -182,7 +182,23 @@ public class PlayerControllerAi extends PlayerController {
}
@Override
public SpellAbility chooseSingleSpellForEffect(java.util.List<SpellAbility> spells, SpellAbility sa, String title,
public List<SpellAbility> chooseSpellAbilitiesForEffect(List<SpellAbility> spells, SpellAbility sa, String title,
int num, Map<String, Object> params) {
List<SpellAbility> remaining = Lists.newArrayList(spells);
List<SpellAbility> selecteds = Lists.newArrayList();
SpellAbility selected;
do {
selected = chooseSingleSpellForEffect(remaining, sa, title, params);
if ( selected != null ) {
remaining.remove(selected);
selecteds.add(selected);
}
} while ( (selected != null ) && (selecteds.size() < num) );
return selecteds;
}
@Override
public SpellAbility chooseSingleSpellForEffect(List<SpellAbility> spells, SpellAbility sa, String title,
Map<String, Object> params) {
ApiType api = sa.getApi();
if (null == api) {

View File

@@ -327,7 +327,7 @@ public class SpecialCardAi {
boolean canTrample = source.hasKeyword(Keyword.TRAMPLE);
if (!isBlocking && combat.getDefenderByAttacker(source) instanceof Card) {
int loyalty = combat.getDefenderByAttacker(source).getCounters(CounterType.LOYALTY);
int loyalty = combat.getDefenderByAttacker(source).getCounters(CounterEnumType.LOYALTY);
int totalDamageToPW = 0;
for (Card atk : (combat.getAttackersOf(combat.getDefenderByAttacker(source)))) {
if (combat.isUnblocked(atk)) {
@@ -407,7 +407,7 @@ public class SpecialCardAi {
}
public static Pair<Integer, Integer> getPumpedPT(Player ai, int power, int toughness) {
int energy = ai.getCounters(CounterType.ENERGY);
int energy = ai.getCounters(CounterEnumType.ENERGY);
if (energy > 0) {
int numActivations = energy / 3;
for (int i = 0; i < numActivations; i++) {
@@ -708,7 +708,7 @@ public class SpecialCardAi {
// if there's another reanimator card currently suspended, don't cast a new one until the previous
// one resolves, otherwise the reanimation attempt will be ruined (e.g. Living End)
for (Card ex : ai.getCardsIn(ZoneType.Exile)) {
if (ex.hasSVar("IsReanimatorCard") && ex.getCounters(CounterType.TIME) > 0) {
if (ex.hasSVar("IsReanimatorCard") && ex.getCounters(CounterEnumType.TIME) > 0) {
return false;
}
}
@@ -767,7 +767,7 @@ public class SpecialCardAi {
Player controller = c.getController();
boolean wasCaged = false;
for (Card caged : CardLists.filter(controller.getCardsIn(ZoneType.Exile),
CardPredicates.hasCounter(CounterType.CAGE))) {
CardPredicates.hasCounter(CounterEnumType.CAGE))) {
if (c.getName().equals(caged.getName())) {
wasCaged = true;
break;
@@ -1073,7 +1073,7 @@ public class SpecialCardAi {
// Sarkhan the Mad
public static class SarkhanTheMad {
public static boolean considerDig(final Player ai, final SpellAbility sa) {
return sa.getHostCard().getCounters(CounterType.LOYALTY) == 1;
return sa.getHostCard().getCounters(CounterEnumType.LOYALTY) == 1;
}
public static boolean considerMakeDragon(final Player ai, final SpellAbility sa) {
@@ -1109,7 +1109,7 @@ public class SpecialCardAi {
// Sorin, Vengeful Bloodlord
public static class SorinVengefulBloodlord {
public static boolean consider(final Player ai, final SpellAbility sa) {
int loyalty = sa.getHostCard().getCounters(CounterType.LOYALTY);
int loyalty = sa.getHostCard().getCounters(CounterEnumType.LOYALTY);
CardCollection creaturesToGet = CardLists.filter(ai.getCardsIn(ZoneType.Graveyard),
Predicates.and(CardPredicates.Presets.CREATURES, CardPredicates.lessCMC(loyalty - 1), new Predicate<Card>() {
@Override
@@ -1365,7 +1365,7 @@ public class SpecialCardAi {
Card source = sa.getHostCard();
Game game = source.getGame();
final int loyalty = source.getCounters(CounterType.LOYALTY);
final int loyalty = source.getCounters(CounterEnumType.LOYALTY);
int x = -1, best = 0;
Card single = null;
for (int i = 0; i < loyalty; i++) {

View File

@@ -307,7 +307,7 @@ public abstract class SpellAbilityAi {
}
@SuppressWarnings("unchecked")
public <T extends GameEntity> T chooseSingleEntity(Player ai, SpellAbility sa, Collection<T> options, boolean isOptional, Player targetedPlayer) {
public <T extends GameEntity> T chooseSingleEntity(Player ai, SpellAbility sa, Collection<T> options, boolean isOptional, Player targetedPlayer, Map<String, Object> params) {
boolean hasPlayer = false;
boolean hasCard = false;
boolean hasPlaneswalker = false;
@@ -324,11 +324,11 @@ public abstract class SpellAbilityAi {
}
if (hasPlayer && hasPlaneswalker) {
return (T) chooseSinglePlayerOrPlaneswalker(ai, sa, (Collection<GameEntity>) options);
return (T) chooseSinglePlayerOrPlaneswalker(ai, sa, (Collection<GameEntity>) options, params);
} else if (hasCard) {
return (T) chooseSingleCard(ai, sa, (Collection<Card>) options, isOptional, targetedPlayer);
return (T) chooseSingleCard(ai, sa, (Collection<Card>) options, isOptional, targetedPlayer, params);
} else if (hasPlayer) {
return (T) chooseSinglePlayer(ai, sa, (Collection<Player>) options);
return (T) chooseSinglePlayer(ai, sa, (Collection<Player>) options, params);
}
return null;
@@ -339,17 +339,17 @@ public abstract class SpellAbilityAi {
return spells.get(0);
}
protected Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer) {
protected Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer, Map<String, Object> params) {
System.err.println("Warning: default (ie. inherited from base class) implementation of chooseSingleCard is used by " + sa.getHostCard().getName() + " for " + this.getClass().getName() + ". Consider declaring an overloaded method");
return Iterables.getFirst(options, null);
}
protected Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable<Player> options) {
protected Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable<Player> options, Map<String, Object> params) {
System.err.println("Warning: default (ie. inherited from base class) implementation of chooseSinglePlayer is used by " + sa.getHostCard().getName() + " for " + this.getClass().getName() + ". Consider declaring an overloaded method");
return Iterables.getFirst(options, null);
}
protected GameEntity chooseSinglePlayerOrPlaneswalker(Player ai, SpellAbility sa, Iterable<GameEntity> options) {
protected GameEntity chooseSinglePlayerOrPlaneswalker(Player ai, SpellAbility sa, Iterable<GameEntity> options, Map<String, Object> params) {
System.err.println("Warning: default (ie. inherited from base class) implementation of chooseSinglePlayerOrPlaneswalker is used for " + this.getClass().getName() + ". Consider declaring an overloaded method");
return Iterables.getFirst(options, null);
}

View File

@@ -1,5 +1,7 @@
package forge.ai.ability;
import java.util.Map;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
@@ -23,7 +25,7 @@ public class AmassAi extends SpellAbilityAi {
final Game game = ai.getGame();
if (!aiArmies.isEmpty()) {
return CardLists.count(aiArmies, CardPredicates.canReceiveCounters(CounterType.P1P1)) > 0;
return CardLists.count(aiArmies, CardPredicates.canReceiveCounters(CounterEnumType.P1P1)) > 0;
} else {
final String tokenScript = "b_0_0_zombie_army";
final int amount = AbilityUtils.calculateAmount(host, sa.getParamOrDefault("Num", "1"), sa);
@@ -44,8 +46,8 @@ public class AmassAi extends SpellAbilityAi {
CardCollection preList = new CardCollection(token);
game.getAction().checkStaticAbilities(false, Sets.newHashSet(token), preList);
if (token.canReceiveCounters(CounterType.P1P1)) {
token.setCounters(CounterType.P1P1, amount);
if (token.canReceiveCounters(CounterEnumType.P1P1)) {
token.setCounters(CounterEnumType.P1P1, amount);
}
if (token.isCreature() && token.getNetToughness() < 1) {
@@ -86,8 +88,8 @@ public class AmassAi extends SpellAbilityAi {
}
@Override
protected Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer) {
Iterable<Card> better = CardLists.filter(options, CardPredicates.canReceiveCounters(CounterType.P1P1));
protected Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer, Map<String, Object> params) {
Iterable<Card> better = CardLists.filter(options, CardPredicates.canReceiveCounters(CounterEnumType.P1P1));
if (Iterables.isEmpty(better)) {
better = options;
}

View File

@@ -1704,12 +1704,12 @@ public class AttachAi extends SpellAbilityAi {
}
@Override
protected Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer) {
protected Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer, Map<String, Object> params) {
return attachToCardAIPreferences(ai, sa, true);
}
@Override
protected Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable<Player> options) {
protected Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable<Player> options, Map<String, Object> params) {
return attachToPlayerAIPreferences(ai, sa, true);
}
}

View File

@@ -17,6 +17,8 @@
*/
package forge.ai.ability;
import java.util.Map;
import forge.ai.ComputerUtilCard;
import forge.ai.SpellAbilityAi;
import forge.game.card.Card;
@@ -50,7 +52,7 @@ public final class BondAi extends SpellAbilityAi {
@Override
protected Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer) {
protected Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer, Map<String, Object> params) {
return ComputerUtilCard.getBestCreatureAI(options);
}

View File

@@ -8,6 +8,7 @@ import forge.game.player.PlayerPredicates;
import forge.game.spellability.SpellAbility;
import java.util.Collection;
import java.util.Map;
public class ChangeCombatantsAi extends SpellAbilityAi {
/* (non-Javadoc)
@@ -45,7 +46,7 @@ public class ChangeCombatantsAi extends SpellAbilityAi {
}
@Override
public <T extends GameEntity> T chooseSingleEntity(Player ai, SpellAbility sa, Collection<T> options, boolean isOptional, Player targetedPlayer) {
public <T extends GameEntity> T chooseSingleEntity(Player ai, SpellAbility sa, Collection<T> options, boolean isOptional, Player targetedPlayer, Map<String, Object> params) {
PlayerCollection targetableOpps = new PlayerCollection();
for (GameEntity p : options) {
if (p instanceof Player && !p.equals(sa.getHostCard().getController())) {

View File

@@ -142,7 +142,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
* </p>
* @param sa
* a {@link forge.game.spellability.SpellAbility} object.
*
*
* @return a boolean.
*/
@Override
@@ -170,7 +170,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
* a {@link forge.game.spellability.SpellAbility} object.
* @param mandatory
* a boolean.
*
*
* @return a boolean.
*/
@Override
@@ -370,10 +370,10 @@ public class ChangeZoneAi extends SpellAbilityAi {
if (!activateForCost && list.isEmpty()) {
return false;
}
if ("Atarka's Command".equals(sourceName)
&& (list.size() < 2 || ai.getLandsPlayedThisTurn() < 1)) {
// be strict on playing lands off charms
return false;
if ("Atarka's Command".equals(sourceName)
&& (list.size() < 2 || ai.getLandsPlayedThisTurn() < 1)) {
// be strict on playing lands off charms
return false;
}
String num = sa.getParam("ChangeNum");
@@ -385,7 +385,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
source.setSVar("PayX", Integer.toString(xPay));
}
}
if (sourceName.equals("Temur Sabertooth")) {
// activated bounce + pump
if (ComputerUtilCard.shouldPumpCard(ai, sa.getSubAbility(), source, 0, 0, Arrays.asList("Indestructible")) ||
@@ -400,9 +400,9 @@ public class ChangeZoneAi extends SpellAbilityAi {
}
}
if (ComputerUtil.playImmediately(ai, sa)) {
return true;
return true;
}
// don't use fetching to top of library/graveyard before main2
@@ -418,9 +418,9 @@ public class ChangeZoneAi extends SpellAbilityAi {
}
if (ComputerUtil.waitForBlocking(sa)) {
return false;
return false;
}
final AbilitySub subAb = sa.getSubAbility();
return subAb == null || SpellApiToAi.Converter.get(subAb.getApi()).chkDrawbackWithSubs(ai, subAb);
}
@@ -551,7 +551,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
* basicManaFixing.
* </p>
* @param ai
*
*
* @param list
* a List<Card> object.
* @return a {@link forge.game.card.Card} object.
@@ -584,7 +584,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
if (minType != null) {
result = CardLists.getType(list, minType);
}
// pick dual lands if available
if (Iterables.any(result, Predicates.not(CardPredicates.Presets.BASIC_LANDS))) {
result = CardLists.filter(result, Predicates.not(CardPredicates.Presets.BASIC_LANDS));
@@ -597,7 +597,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
* <p>
* areAllBasics.
* </p>
*
*
* @param types
* a {@link java.lang.String} object.
* @return a boolean.
@@ -617,8 +617,8 @@ public class ChangeZoneAi extends SpellAbilityAi {
* @return Card
*/
private static Card chooseCreature(final Player ai, CardCollection list) {
// Creating a new combat for testing purposes.
final Player opponent = ai.getWeakestOpponent();
// Creating a new combat for testing purposes.
final Player opponent = ai.getWeakestOpponent();
Combat combat = new Combat(opponent);
for (Card att : opponent.getCreaturesInPlay()) {
combat.addAttacker(att, ai);
@@ -742,7 +742,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
/*
* (non-Javadoc)
*
*
* @see
* forge.ai.SpellAbilityAi#checkPhaseRestrictions(forge.game.player.Player,
* forge.game.spellability.SpellAbility, forge.game.phase.PhaseHandler)
@@ -781,7 +781,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
return false;
}
}
//don't unearth after attacking is possible
if (sa.hasParam("Unearth") && ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS)) {
return false;
@@ -895,7 +895,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
if (list.size() < tgt.getMinTargets(sa.getHostCard(), sa)) {
return false;
}
immediately |= ComputerUtil.playImmediately(ai, sa);
// Narrow down the list:
@@ -926,7 +926,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
CardCollection blockers = currCombat.getBlockers(attacker);
// Save my attacker by bouncing a blocker
if (attacker.getController().equals(ai) && attacker.getShieldCount() == 0
&& ComputerUtilCombat.attackerWouldBeDestroyed(ai, attacker, currCombat)
&& ComputerUtilCombat.attackerWouldBeDestroyed(ai, attacker, currCombat)
&& !currCombat.getBlockers(attacker).isEmpty()) {
ComputerUtilCard.sortByEvaluateCreature(blockers);
Combat combat = new Combat(ai);
@@ -970,9 +970,9 @@ public class ChangeZoneAi extends SpellAbilityAi {
sa.getTargets().add(tobounce);
boolean saheeliFelidarCombo = sa.getHostCard().getName().equals("Felidar Guardian")
boolean saheeliFelidarCombo = sa.getHostCard().getName().equals("Felidar Guardian")
&& tobounce.getName().equals("Saheeli Rai")
&& CardLists.filter(ai.getCardsIn(ZoneType.Battlefield), CardPredicates.nameEquals("Felidar Guardian")).size() <
&& CardLists.filter(ai.getCardsIn(ZoneType.Battlefield), CardPredicates.nameEquals("Felidar Guardian")).size() <
CardLists.filter(ai.getOpponents().getCardsIn(ZoneType.Battlefield), CardPredicates.isType("Creature")).size() + ai.getOpponentsGreatestLifeTotal() + 10;
// remember that the card was bounced already unless it's a special combo case
@@ -985,20 +985,20 @@ public class ChangeZoneAi extends SpellAbilityAi {
// bounce opponent's stuff
list = CardLists.filterControlledBy(list, ai.getOpponents());
if (!CardLists.getNotType(list, "Land").isEmpty()) {
// When bouncing opponents stuff other than lands, don't bounce cards with CMC 0
list = CardLists.filter(list, new Predicate<Card>() {
@Override
public boolean apply(final Card c) {
for (Card aura : c.getEnchantedBy()) {
// When bouncing opponents stuff other than lands, don't bounce cards with CMC 0
list = CardLists.filter(list, new Predicate<Card>() {
@Override
public boolean apply(final Card c) {
for (Card aura : c.getEnchantedBy()) {
return aura.getController().isOpponentOf(ai);
}
if (blink) {
return c.isToken();
} else {
return c.isToken() || c.getCMC() > 0;
}
}
});
}
if (blink) {
return c.isToken();
} else {
return c.isToken() || c.getCMC() > 0;
}
}
});
}
// TODO: Blink permanents with ETB triggers
/*else if (!sa.isTrigger() && SpellAbilityAi.playReusable(ai, sa)) {
@@ -1023,7 +1023,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
}
} else if (origin.contains(ZoneType.Graveyard)) {
if (destination.equals(ZoneType.Exile) || destination.equals(ZoneType.Library)) {
if (destination.equals(ZoneType.Exile) || destination.equals(ZoneType.Library)) {
// Don't use these abilities before main 2 if possible
if (!immediately && game.getPhaseHandler().getPhase().isBefore(PhaseType.MAIN2)
&& !sa.hasParam("ActivationPhases") && !ComputerUtil.castSpellInMain1(ai, sa)) {
@@ -1035,7 +1035,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
&& !ComputerUtil.activateForCost(sa, ai)) {
return false;
}
} else if (destination.equals(ZoneType.Hand)) {
} else if (destination.equals(ZoneType.Hand)) {
// only retrieve cards from computer graveyard
list = CardLists.filterControlledBy(list, ai);
} else if (sa.hasParam("AttachedTo")) {
@@ -1096,10 +1096,10 @@ public class ChangeZoneAi extends SpellAbilityAi {
// Only care about combatants during combat
if (game.getPhaseHandler().inCombat() && origin.contains(ZoneType.Battlefield)) {
CardCollection newList = CardLists.getValidCards(list, "Card.attacking,Card.blocking", null, null);
if (!newList.isEmpty() || !sa.isTrigger()) {
list = newList;
}
CardCollection newList = CardLists.getValidCards(list, "Card.attacking,Card.blocking", null, null);
if (!newList.isEmpty() || !sa.isTrigger()) {
list = newList;
}
}
boolean doWithoutTarget = sa.hasParam("Planeswalker") && sa.usesTargeting()
@@ -1251,7 +1251,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
return true;
}
/**
* Checks if a permanent threatened by a stack ability or in combat can
* be saved by bouncing.
@@ -1324,11 +1324,11 @@ public class ChangeZoneAi extends SpellAbilityAi {
Collections.sort(aiPlaneswalkers, new Comparator<Card>() {
@Override
public int compare(final Card a, final Card b) {
return a.getCounters(CounterType.LOYALTY) - b.getCounters(CounterType.LOYALTY);
return a.getCounters(CounterEnumType.LOYALTY) - b.getCounters(CounterEnumType.LOYALTY);
}
});
for (Card pw : aiPlaneswalkers) {
int curLoyalty = pw.getCounters(CounterType.LOYALTY);
int curLoyalty = pw.getCounters(CounterEnumType.LOYALTY);
int freshLoyalty = Integer.valueOf(pw.getCurrentState().getBaseLoyalty());
if (freshLoyalty - curLoyalty >= loyaltyDiff && curLoyalty <= maxLoyaltyToConsider) {
return pw;
@@ -1506,10 +1506,10 @@ public class ChangeZoneAi extends SpellAbilityAi {
if (type == null) {
type = "Card";
}
Card c = null;
final Player activator = sa.getActivatingPlayer();
CardLists.shuffle(fetchList);
// Save a card as a default, in case we can't find anything suitable.
Card first = fetchList.get(0);
@@ -1614,19 +1614,19 @@ public class ChangeZoneAi extends SpellAbilityAi {
// AI was never asked
return true;
}
@Override
public Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer) {
public Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer, Map<String, Object> params) {
// Called when looking for creature to attach aura or equipment
return ComputerUtilCard.getBestAI(options);
}
/* (non-Javadoc)
* @see forge.card.ability.SpellAbilityAi#chooseSinglePlayer(forge.game.player.Player, forge.card.spellability.SpellAbility, java.util.List)
*/
@Override
public Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable<Player> options) {
public Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable<Player> options, Map<String, Object> params) {
// Currently only used by Curse of Misfortunes, so this branch should never get hit
// But just in case it does, just select the first option
return Iterables.getFirst(options, null);
@@ -1801,7 +1801,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
if (causeSa != null && (causeSub = causeSa.getSubAbility()) != null) {
ApiType subApi = causeSub.getApi();
if (subApi == ApiType.ChangeZone && "Exile".equals(causeSub.getParam("Origin"))
&& "Battlefield".equals(causeSub.getParam("Destination"))) {
// A blink effect implemented using ChangeZone API
@@ -1817,7 +1817,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
} else return causeSa.getHostCard() == null || !causeSa.getHostCard().equals(sa.getReplacingObject(AbilityKey.Card))
|| !causeSa.getActivatingPlayer().equals(aiPlayer);
}
// Normally we want the commander back in Command zone to recast him later
return true;
}

View File

@@ -11,6 +11,7 @@ import forge.util.MyRandom;
import forge.util.collect.FCollection;
import java.util.List;
import java.util.Map;
public class CharmAi extends SpellAbilityAi {
@Override
@@ -232,7 +233,7 @@ public class CharmAi extends SpellAbilityAi {
}
@Override
public Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable<Player> opponents) {
public Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable<Player> opponents, Map<String, Object> params) {
return Aggregates.random(opponents);
}
}

View File

@@ -2,6 +2,7 @@ package forge.ai.ability;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
@@ -20,7 +21,7 @@ import forge.game.card.CardCollectionView;
import forge.game.card.CardLists;
import forge.game.card.CardPredicates;
import forge.game.card.CardPredicates.Presets;
import forge.game.card.CounterType;
import forge.game.card.CounterEnumType;
import forge.game.combat.Combat;
import forge.game.keyword.Keyword;
import forge.game.phase.PhaseType;
@@ -99,7 +100,7 @@ public class ChooseCardAi extends SpellAbilityAi {
});
return !choices.isEmpty();
} else if (aiLogic.equals("Ashiok")) {
final int loyalty = host.getCounters(CounterType.LOYALTY) - 1;
final int loyalty = host.getCounters(CounterEnumType.LOYALTY) - 1;
for (int i = loyalty; i >= 0; i--) {
host.setSVar("ChosenX", "Number$" + i);
choices = ai.getGame().getCardsIn(choiceZone);
@@ -140,12 +141,12 @@ public class ChooseCardAi extends SpellAbilityAi {
}
return checkApiLogic(ai, sa);
}
/* (non-Javadoc)
* @see forge.card.ability.SpellAbilityAi#chooseSingleCard(forge.card.spellability.SpellAbility, java.util.List, boolean)
*/
@Override
public Card chooseSingleCard(final Player ai, final SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer) {
public Card chooseSingleCard(final Player ai, final SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer, Map<String, Object> params) {
final Card host = sa.getHostCard();
final Player ctrl = host.getController();
final String logic = sa.getParam("AILogic");
@@ -191,7 +192,7 @@ public class ChooseCardAi extends SpellAbilityAi {
if (combat == null || !combat.isAttacking(c, ai) || !combat.isUnblocked(c)) {
return false;
}
int ref = ComputerUtilAbility.getAbilitySourceName(sa).equals("Forcefield") ? 1 : 0;
int ref = ComputerUtilAbility.getAbilitySourceName(sa).equals("Forcefield") ? 1 : 0;
return ComputerUtilCombat.damageIfUnblocked(c, ai, combat, true) > ref;
}
});

View File

@@ -1,6 +1,7 @@
package forge.ai.ability;
import java.util.List;
import java.util.Map;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
@@ -23,7 +24,6 @@ public class ChooseCardNameAi extends SpellAbilityAi {
@Override
protected boolean canPlayAI(Player ai, SpellAbility sa) {
Card source = sa.getHostCard();
if (sa.hasParam("AILogic")) {
// Don't tap creatures that may be able to block
if (ComputerUtil.waitForBlocking(sa)) {
@@ -60,7 +60,7 @@ public class ChooseCardNameAi extends SpellAbilityAi {
* @see forge.card.ability.SpellAbilityAi#chooseSingleCard(forge.card.spellability.SpellAbility, java.util.List, boolean)
*/
@Override
public Card chooseSingleCard(final Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer) {
public Card chooseSingleCard(final Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer, Map<String, Object> params) {
return ComputerUtilCard.getBestAI(options);
}
@@ -86,7 +86,7 @@ public class ChooseCardNameAi extends SpellAbilityAi {
if (rules.getSplitType() == CardSplitType.Split) {
Card copy = CardUtil.getLKICopy(card);
// for calcing i need only one split side
// for calcing i need only one split side
if (isOther) {
copy.getCurrentState().copyFrom(card.getState(CardStateName.RightSplit), true);
} else {

View File

@@ -8,6 +8,7 @@ import forge.game.spellability.SpellAbility;
import java.util.Collections;
import java.util.List;
import java.util.Map;
public class ChooseCompanionAi extends SpellAbilityAi {
@@ -15,7 +16,7 @@ public class ChooseCompanionAi extends SpellAbilityAi {
* @see forge.card.ability.SpellAbilityAi#chooseSingleCard(forge.card.spellability.SpellAbility, java.util.List, boolean)
*/
@Override
public Card chooseSingleCard(final Player ai, final SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer) {
public Card chooseSingleCard(final Player ai, final SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer, Map<String, Object> params) {
List<Card> cards = Lists.newArrayList(options);
if (cards.isEmpty()) {
return null;

View File

@@ -180,25 +180,25 @@ public class ChooseGenericEffectAi extends SpellAbilityAi {
Card imprinted = host.getImprintedCards().getFirst();
int dmg = imprinted.getCMC();
Player owner = imprinted.getOwner();
//useless cards in hand
if (imprinted.getName().equals("Bridge from Below") ||
imprinted.getName().equals("Haakon, Stromgald Scourge")) {
return allow;
}
//bad cards when are thrown from the library to the graveyard, but Yixlid can prevent that
if (!player.getGame().isCardInPlay("Yixlid Jailer") && (
imprinted.getName().equals("Gaea's Blessing") ||
imprinted.getName().equals("Narcomoeba"))) {
return allow;
}
// milling against Tamiyo is pointless
if (owner.isCardInCommand("Emblem - Tamiyo, the Moon Sage")) {
return allow;
}
// milling a land against Gitrog result in card draw
if (imprinted.isLand() && owner.isCardInPlay("The Gitrog Monster")) {
// try to mill owner
@@ -207,19 +207,19 @@ public class ChooseGenericEffectAi extends SpellAbilityAi {
}
return allow;
}
// milling a creature against Sidisi result in more creatures
if (imprinted.isCreature() && owner.isCardInPlay("Sidisi, Brood Tyrant")) {
return allow;
}
//if Iona does prevent from casting, allow it to draw
//if Iona does prevent from casting, allow it to draw
for (final Card io : player.getCardsIn(ZoneType.Battlefield, "Iona, Shield of Emeria")) {
if (CardUtil.getColors(imprinted).hasAnyColor(MagicColor.fromName(io.getChosenColor()))) {
return allow;
}
}
if (dmg == 0) {
// If CMC = 0, mill it!
return deny;
@@ -244,7 +244,7 @@ public class ChooseGenericEffectAi extends SpellAbilityAi {
SpellAbility counterSA = spells.get(0), tokenSA = spells.get(1);
// check for something which might prevent the counters to be placed on host
if (!host.canReceiveCounters(CounterType.P1P1)) {
if (!host.canReceiveCounters(CounterEnumType.P1P1)) {
return tokenSA;
}
@@ -256,7 +256,7 @@ public class ChooseGenericEffectAi extends SpellAbilityAi {
// need a copy for one with extra +1/+1 counter boost,
// without causing triggers to run
final Card copy = CardUtil.getLKICopy(host);
copy.setCounters(CounterType.P1P1, copy.getCounters(CounterType.P1P1) + n);
copy.setCounters(CounterEnumType.P1P1, copy.getCounters(CounterEnumType.P1P1) + n);
copy.setZone(host.getZone());
// if host would put into the battlefield attacking
@@ -283,7 +283,7 @@ public class ChooseGenericEffectAi extends SpellAbilityAi {
// in this cases Token might be prefered even if they would not survive
final Card tokenCard = TokenAi.spawnToken(player, tokenSA);
// Token would not survive
// Token would not survive
if (!tokenCard.isCreature() || tokenCard.getNetToughness() < 1) {
return counterSA;
}
@@ -336,7 +336,7 @@ public class ChooseGenericEffectAi extends SpellAbilityAi {
filtered.add(sp);
}
}
// TODO find better way to check
if (!filtered.isEmpty()) {
return filtered.get(0);
@@ -362,7 +362,7 @@ public class ChooseGenericEffectAi extends SpellAbilityAi {
game.getAction().checkStaticAbilities(false);
// can't gain counters, use Haste
if (!copy.canReceiveCounters(CounterType.P1P1)) {
if (!copy.canReceiveCounters(CounterEnumType.P1P1)) {
return true;
}

View File

@@ -9,6 +9,7 @@ import forge.game.spellability.SpellAbility;
import forge.game.zone.ZoneType;
import java.util.List;
import java.util.Map;
public class ChoosePlayerAi extends SpellAbilityAi {
@Override
@@ -27,7 +28,7 @@ public class ChoosePlayerAi extends SpellAbilityAi {
}
@Override
public Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable<Player> choices) {
public Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable<Player> choices, Map<String, Object> params) {
Player chosen = null;
if ("Curse".equals(sa.getParam("AILogic"))) {
for (Player pc : choices) {

View File

@@ -1,6 +1,7 @@
package forge.ai.ability;
import java.util.List;
import java.util.Map;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
@@ -126,7 +127,7 @@ public class ChooseSourceAi extends SpellAbilityAi {
@Override
public Card chooseSingleCard(final Player aiChoser, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer) {
public Card chooseSingleCard(final Player aiChoser, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer, Map<String, Object> params) {
if ("NeedsPrevention".equals(sa.getParam("AILogic"))) {
final Player ai = sa.getActivatingPlayer();
final Game game = ai.getGame();

View File

@@ -1,6 +1,8 @@
package forge.ai.ability;
import java.util.Map;
import com.google.common.collect.Iterables;
import forge.ai.ComputerUtilCard;
@@ -56,7 +58,7 @@ public class ClashAi extends SpellAbilityAi {
* forge.game.spellability.SpellAbility, java.lang.Iterable)
*/
@Override
protected Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable<Player> options) {
protected Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable<Player> options, Map<String, Object> params) {
for (Player p : options) {
if (p.getCardsIn(ZoneType.Library).size() == 0)
return p;
@@ -82,7 +84,7 @@ public class ClashAi extends SpellAbilityAi {
PlayerCollection players = ai.getOpponents().filter(PlayerPredicates.isTargetableBy(sa));
// use chooseSinglePlayer function to the select player
Player chosen = chooseSinglePlayer(ai, sa, players);
Player chosen = chooseSinglePlayer(ai, sa, players, null);
if (chosen != null) {
sa.resetTargets();
sa.getTargets().add(chosen);

View File

@@ -15,6 +15,7 @@ import forge.game.spellability.SpellAbility;
import forge.game.zone.ZoneType;
import java.util.List;
import java.util.Map;
public class CloneAi extends SpellAbilityAi {
@@ -169,7 +170,7 @@ public class CloneAi extends SpellAbilityAi {
*/
@Override
protected Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional,
Player targetedPlayer) {
Player targetedPlayer, Map<String, Object> params) {
final Card host = sa.getHostCard();
final Player ctrl = host.getController();

View File

@@ -18,6 +18,7 @@
package forge.ai.ability;
import java.util.List;
import java.util.Map;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
@@ -302,7 +303,7 @@ public class ControlGainAi extends SpellAbilityAi {
} // pumpDrawbackAI()
@Override
protected Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable<Player> options) {
protected Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable<Player> options, Map<String, Object> params) {
final List<Card> cards = Lists.newArrayList();
for (Player p : options) {
cards.addAll(p.getCreaturesInPlay());

View File

@@ -19,6 +19,7 @@ import forge.game.zone.ZoneType;
import java.util.Collection;
import java.util.List;
import java.util.Map;
public class CopyPermanentAi extends SpellAbilityAi {
@Override
@@ -204,7 +205,7 @@ public class CopyPermanentAi extends SpellAbilityAi {
* @see forge.card.ability.SpellAbilityAi#chooseSingleCard(forge.game.player.Player, forge.card.spellability.SpellAbility, java.util.List, boolean)
*/
@Override
public Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer) {
public Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer, Map<String, Object> params) {
// Select a card to attach to
CardCollection betterOptions = getBetterOptions(ai, sa, options, isOptional);
if (!betterOptions.isEmpty()) {
@@ -223,7 +224,7 @@ public class CopyPermanentAi extends SpellAbilityAi {
}
@Override
protected Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable<Player> options) {
protected Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable<Player> options, Map<String, Object> params) {
final List<Card> cards = new PlayerCollection(options).getCreaturesInPlay();
Card chosen = ComputerUtilCard.getBestCreatureAI(cards);
return chosen != null ? chosen.getController() : Iterables.getFirst(options, null);

View File

@@ -6,12 +6,12 @@
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
@@ -26,7 +26,7 @@ import forge.game.card.Card;
import forge.game.card.CardCollection;
import forge.game.card.CardCollectionView;
import forge.game.card.CardLists;
import forge.game.card.CounterType;
import forge.game.card.CounterEnumType;
import forge.game.keyword.Keyword;
import forge.util.Aggregates;
@@ -35,7 +35,7 @@ import forge.util.Aggregates;
* <p>
* AbilityFactory_Counters class.
* </p>
*
*
* @author Forge
* @version $Id$
*/
@@ -46,7 +46,7 @@ public abstract class CountersAi {
* <p>
* chooseCursedTarget.
* </p>
*
*
* @param list
* a {@link forge.CardList} object.
* @param type
@@ -77,7 +77,7 @@ public abstract class CountersAi {
* <p>
* chooseBoonTarget.
* </p>
*
*
* @param list
* a {@link forge.CardList} object.
* @param type
@@ -97,7 +97,7 @@ public abstract class CountersAi {
final CardCollection boon = CardLists.filter(list, new Predicate<Card>() {
@Override
public boolean apply(final Card c) {
return c.getCounters(CounterType.DIVINITY) == 0;
return c.getCounters(CounterEnumType.DIVINITY) == 0;
}
});
choice = ComputerUtilCard.getMostExpensivePermanentAI(boon, null, false);

View File

@@ -42,14 +42,14 @@ public class CountersMoveAi extends SpellAbilityAi {
protected boolean checkPhaseRestrictions(final Player ai, final SpellAbility sa, final PhaseHandler ph) {
final Card host = sa.getHostCard();
final String type = sa.getParam("CounterType");
final CounterType cType = "Any".equals(type) ? null : CounterType.valueOf(type);
final CounterType cType = "Any".equals(type) ? null : CounterType.getType(type);
// Don't tap creatures that may be able to block
if (ComputerUtil.waitForBlocking(sa)) {
return false;
}
if (CounterType.P1P1.equals(cType) && sa.hasParam("Source")) {
if (CounterEnumType.P1P1.equals(cType) && sa.hasParam("Source")) {
int amount = calcAmount(sa, cType);
final List<Card> srcCards = AbilityUtils.getDefinedCards(host, sa.getParam("Source"), sa);
if (ph.getPlayerTurn().isOpponentOf(ai)) {
@@ -92,7 +92,7 @@ public class CountersMoveAi extends SpellAbilityAi {
// for Simic Fluxmage and other
return ph.getNextTurn().equals(ai) && !ph.getPhase().isBefore(PhaseType.END_OF_TURN);
} else if (CounterType.P1P1.equals(cType) && sa.hasParam("Defined")) {
} else if (CounterEnumType.P1P1.equals(cType) && sa.hasParam("Defined")) {
// something like Cyptoplast Root-kin
if (ph.getPlayerTurn().isOpponentOf(ai)) {
if (ph.inCombat() && ph.getPhase().isBefore(PhaseType.COMBAT_DECLARE_BLOCKERS)) {
@@ -115,6 +115,7 @@ public class CountersMoveAi extends SpellAbilityAi {
protected boolean doTriggerAINoCost(final Player ai, SpellAbility sa, boolean mandatory) {
if (sa.usesTargeting()) {
sa.resetTargets();
if (!moveTgtAI(ai, sa) && !mandatory) {
return false;
@@ -142,7 +143,7 @@ public class CountersMoveAi extends SpellAbilityAi {
final Card host = sa.getHostCard();
final String type = sa.getParam("CounterType");
final CounterType cType = "Any".equals(type) ? null : CounterType.valueOf(type);
final CounterType cType = "Any".equals(type) ? null : CounterType.getType(type);
final List<Card> srcCards = AbilityUtils.getDefinedCards(host, sa.getParam("Source"), sa);
final List<Card> destCards = AbilityUtils.getDefinedCards(host, sa.getParam("Defined"), sa);
@@ -189,7 +190,7 @@ public class CountersMoveAi extends SpellAbilityAi {
// check for some specific AI preferences
if ("DontMoveCounterIfLethal".equals(sa.getParam("AILogic"))) {
return cType != CounterType.P1P1 || src.getNetToughness() - src.getTempToughnessBoost() - 1 > 0;
return !cType.is(CounterEnumType.P1P1) || src.getNetToughness() - src.getTempToughnessBoost() - 1 > 0;
}
}
// no target
@@ -234,7 +235,7 @@ public class CountersMoveAi extends SpellAbilityAi {
final Card host = sa.getHostCard();
final Game game = ai.getGame();
final String type = sa.getParam("CounterType");
final CounterType cType = "Any".equals(type) ? null : CounterType.valueOf(type);
final CounterType cType = "Any".equals(type) || "All".equals(type) ? null : CounterType.getType(type);
List<Card> tgtCards = CardLists.getTargetableCards(game.getCardsIn(ZoneType.Battlefield), sa);
@@ -278,7 +279,7 @@ public class CountersMoveAi extends SpellAbilityAi {
// do not steal a P1P1 from Undying if it would die
// this way
if (CounterType.P1P1.equals(cType) && srcCardCpy.getNetToughness() <= 0) {
if (CounterEnumType.P1P1.equals(cType) && srcCardCpy.getNetToughness() <= 0) {
return srcCardCpy.getCounters(cType) > 0 || !card.hasKeyword(Keyword.UNDYING) || card.isToken();
}
return true;
@@ -321,13 +322,13 @@ public class CountersMoveAi extends SpellAbilityAi {
}
// try to remove P1P1 from undying or evolve
if (CounterType.P1P1.equals(cType)) {
if (CounterEnumType.P1P1.equals(cType)) {
if (card.hasKeyword(Keyword.UNDYING) || card.hasKeyword(Keyword.EVOLVE)
|| card.hasKeyword(Keyword.ADAPT)) {
return true;
}
}
if (CounterType.M1M1.equals(cType) && card.hasKeyword(Keyword.PERSIST)) {
if (CounterEnumType.M1M1.equals(cType) && card.hasKeyword(Keyword.PERSIST)) {
return true;
}
@@ -382,10 +383,10 @@ public class CountersMoveAi extends SpellAbilityAi {
}
if (cType != null) {
if (CounterType.P1P1.equals(cType) && card.hasKeyword(Keyword.UNDYING)) {
if (CounterEnumType.P1P1.equals(cType) && card.hasKeyword(Keyword.UNDYING)) {
return false;
}
if (CounterType.M1M1.equals(cType) && card.hasKeyword(Keyword.PERSIST)) {
if (CounterEnumType.M1M1.equals(cType) && card.hasKeyword(Keyword.PERSIST)) {
return false;
}
@@ -393,7 +394,7 @@ public class CountersMoveAi extends SpellAbilityAi {
return false;
}
}
return false;
return true;
}
});
@@ -452,7 +453,7 @@ public class CountersMoveAi extends SpellAbilityAi {
// or for source -> multiple defined
@Override
protected Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional,
Player targetedPlayer) {
Player targetedPlayer, Map<String, Object> params) {
if (sa.hasParam("AiLogic")) {
String logic = sa.getParam("AiLogic");

View File

@@ -16,6 +16,7 @@ import forge.game.card.Card;
import forge.game.card.CardCollection;
import forge.game.card.CardLists;
import forge.game.card.CardPredicates;
import forge.game.card.CounterEnumType;
import forge.game.card.CounterType;
import forge.game.phase.PhaseHandler;
import forge.game.phase.PhaseType;
@@ -77,7 +78,7 @@ public class CountersMultiplyAi extends SpellAbilityAi {
protected boolean checkPhaseRestrictions(final Player ai, final SpellAbility sa, final PhaseHandler ph) {
final CounterType counterType = getCounterType(sa);
if (!CounterType.P1P1.equals(counterType) && counterType != null) {
if (!CounterEnumType.P1P1.equals(counterType) && counterType != null) {
if (!sa.hasParam("ActivationPhases")) {
// Don't use non P1P1/M1M1 counters before main 2 if possible
if (ph.getPhase().isBefore(PhaseType.MAIN2) && !ComputerUtil.castSpellInMain1(ai, sa)) {
@@ -147,15 +148,15 @@ public class CountersMultiplyAi extends SpellAbilityAi {
if (!aiList.isEmpty()) {
// counter type list to check
// first loyalty, then P1P!, then Charge Counter
List<CounterType> typeList = Lists.newArrayList(CounterType.LOYALTY, CounterType.P1P1, CounterType.CHARGE);
for (CounterType type : typeList) {
List<CounterEnumType> typeList = Lists.newArrayList(CounterEnumType.LOYALTY, CounterEnumType.P1P1, CounterEnumType.CHARGE);
for (CounterEnumType type : typeList) {
// enough targets
if (!sa.canAddMoreTarget()) {
break;
}
if (counterType == null || counterType == type) {
addTargetsByCounterType(ai, sa, aiList, type);
if (counterType == null || counterType.is(type)) {
addTargetsByCounterType(ai, sa, aiList, CounterType.get(type));
}
}
}
@@ -164,7 +165,7 @@ public class CountersMultiplyAi extends SpellAbilityAi {
if (!oppList.isEmpty()) {
// not enough targets
if (sa.canAddMoreTarget()) {
final CounterType type = CounterType.M1M1;
final CounterType type = CounterType.get(CounterEnumType.M1M1);
if (counterType == null || counterType == type) {
addTargetsByCounterType(ai, sa, oppList, type);
}

View File

@@ -15,6 +15,7 @@ import forge.game.GameEntity;
import forge.game.card.Card;
import forge.game.card.CardLists;
import forge.game.card.CardUtil;
import forge.game.card.CounterEnumType;
import forge.game.card.CounterType;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
@@ -32,7 +33,7 @@ public class CountersProliferateAi extends SpellAbilityAi {
for (final Player p : allies) {
// player has experience or energy counter
if (p.getCounters(CounterType.EXPERIENCE) + p.getCounters(CounterType.ENERGY) >= 1) {
if (p.getCounters(CounterEnumType.EXPERIENCE) + p.getCounters(CounterEnumType.ENERGY) >= 1) {
allyExpOrEnergy = true;
}
cperms.addAll(CardLists.filter(p.getCardsIn(ZoneType.Battlefield), new Predicate<Card>() {
@@ -115,17 +116,19 @@ public class CountersProliferateAi extends SpellAbilityAi {
*/
@SuppressWarnings("unchecked")
@Override
public <T extends GameEntity> T chooseSingleEntity(Player ai, SpellAbility sa, Collection<T> options, boolean isOptional, Player targetedPlayer) {
public <T extends GameEntity> T chooseSingleEntity(Player ai, SpellAbility sa, Collection<T> options, boolean isOptional, Player targetedPlayer, Map<String, Object> params) {
// Proliferate is always optional for all, no need to select best
final CounterType poison = CounterType.get(CounterEnumType.POISON);
// because countertype can't be chosen anymore, only look for posion counters
for (final Player p : Iterables.filter(options, Player.class)) {
if (p.isOpponentOf(ai)) {
if (p.getCounters(CounterType.POISON) > 0 && p.canReceiveCounters(CounterType.POISON)) {
if (p.getCounters(poison) > 0 && p.canReceiveCounters(poison)) {
return (T)p;
}
} else {
if (p.getCounters(CounterType.POISON) <= 5 || p.canReceiveCounters(CounterType.POISON)) {
if (p.getCounters(poison) <= 5 || p.canReceiveCounters(poison)) {
return (T)p;
}
}

View File

@@ -35,7 +35,7 @@ public class CountersPutAi extends SpellAbilityAi {
/*
* (non-Javadoc)
*
*
* @see forge.ai.SpellAbilityAi#willPayCosts(forge.game.player.Player,
* forge.game.spellability.SpellAbility, forge.game.cost.Cost,
* forge.game.card.Card)
@@ -56,17 +56,17 @@ public class CountersPutAi extends SpellAbilityAi {
if (part instanceof CostRemoveCounter) {
final CostRemoveCounter remCounter = (CostRemoveCounter) part;
final CounterType counterType = remCounter.counter;
if (counterType.name().equals(type) && !aiLogic.startsWith("MoveCounter")) {
if (counterType.getName().equals(type) && !aiLogic.startsWith("MoveCounter")) {
return false;
}
if (!part.payCostFromSource()) {
if (counterType.equals(CounterType.P1P1)) {
if (counterType.is(CounterEnumType.P1P1)) {
return false;
}
continue;
}
// don't kill the creature
if (counterType.equals(CounterType.P1P1) && source.getLethalDamage() <= 1) {
if (counterType.is(CounterEnumType.P1P1) && source.getLethalDamage() <= 1) {
return false;
}
}
@@ -77,7 +77,7 @@ public class CountersPutAi extends SpellAbilityAi {
/*
* (non-Javadoc)
*
*
* @see
* forge.ai.SpellAbilityAi#checkPhaseRestrictions(forge.game.player.Player,
* forge.game.spellability.SpellAbility, forge.game.phase.PhaseHandler)
@@ -109,7 +109,7 @@ public class CountersPutAi extends SpellAbilityAi {
}
}
int maxLevel = Integer.parseInt(sa.getParam("MaxLevel"));
return source.getCounters(CounterType.LEVEL) < maxLevel;
return source.getCounters(CounterEnumType.LEVEL) < maxLevel;
}
return super.checkPhaseRestrictions(ai, sa, ph);
@@ -146,7 +146,7 @@ public class CountersPutAi extends SpellAbilityAi {
if (abTgt.canTgtPlayer()) {
// try to kill opponent with Poison
PlayerCollection oppList = ai.getOpponents().filter(PlayerPredicates.isTargetableBy(sa));
PlayerCollection poisonList = oppList.filter(PlayerPredicates.hasCounter(CounterType.POISON, 9));
PlayerCollection poisonList = oppList.filter(PlayerPredicates.hasCounter(CounterEnumType.POISON, 9));
if (!poisonList.isEmpty()) {
sa.getTargets().add(poisonList.max(PlayerPredicates.compareByLife()));
return true;
@@ -157,13 +157,13 @@ public class CountersPutAi extends SpellAbilityAi {
// try to kill creature with -1/-1 counters if it can
// receive counters, execpt it has undying
CardCollection oppCreat = CardLists.getTargetableCards(ai.getOpponents().getCreaturesInPlay(), sa);
CardCollection oppCreatM1 = CardLists.filter(oppCreat, CardPredicates.hasCounter(CounterType.M1M1));
CardCollection oppCreatM1 = CardLists.filter(oppCreat, CardPredicates.hasCounter(CounterEnumType.M1M1));
oppCreatM1 = CardLists.getNotKeyword(oppCreatM1, Keyword.UNDYING);
oppCreatM1 = CardLists.filter(oppCreatM1, new Predicate<Card>() {
@Override
public boolean apply(Card input) {
return input.getNetToughness() <= 1 && input.canReceiveCounters(CounterType.M1M1);
return input.getNetToughness() <= 1 && input.canReceiveCounters(CounterType.get(CounterEnumType.M1M1));
}
});
@@ -244,7 +244,7 @@ public class CountersPutAi extends SpellAbilityAi {
int totBlkPower = Aggregates.sum(blocked, CardPredicates.Accessors.fnGetNetPower);
int totBlkToughness = Aggregates.min(blocked, CardPredicates.Accessors.fnGetNetToughness);
int numActivations = ai.getCounters(CounterType.ENERGY) / sa.getPayCosts().getCostEnergy().convertAmount();
int numActivations = ai.getCounters(CounterEnumType.ENERGY) / sa.getPayCosts().getCostEnergy().convertAmount();
if (sa.getHostCard().getNetToughness() + numActivations > totBlkPower
|| sa.getHostCard().getNetPower() + numActivations >= totBlkToughness) {
return true;
@@ -259,7 +259,7 @@ public class CountersPutAi extends SpellAbilityAi {
AiCardMemory.rememberCard(ai, source, AiCardMemory.MemorySet.ACTIVATED_THIS_TURN);
return true;
}
} else if (ai.getCounters(CounterType.ENERGY) > ComputerUtilCard.getMaxSAEnergyCostOnBattlefield(ai) + sa.getPayCosts().getCostEnergy().convertAmount()) {
} else if (ai.getCounters(CounterEnumType.ENERGY) > ComputerUtilCard.getMaxSAEnergyCostOnBattlefield(ai) + sa.getPayCosts().getCostEnergy().convertAmount()) {
// outside of combat, this logic only works if the relevant AI profile option is enabled
// and if there is enough energy saved
if (!onlyInCombat) {
@@ -293,7 +293,7 @@ public class CountersPutAi extends SpellAbilityAi {
if (sa.getConditions() != null && !sa.getConditions().areMet(sa) && sa.getSubAbility() == null) {
return false;
}
if (sourceName.equals("Feat of Resistance")) { // sub-ability should take precedence
CardCollection prot = ProtectAi.getProtectCreatures(ai, sa.getSubAbility());
if (!prot.isEmpty()) {
@@ -322,7 +322,7 @@ public class CountersPutAi extends SpellAbilityAi {
Game game = ai.getGame();
Combat combat = game.getCombat();
if (!source.canReceiveCounters(CounterType.P1P1) || source.getCounters(CounterType.P1P1) > 0) {
if (!source.canReceiveCounters(CounterType.get(CounterEnumType.P1P1)) || source.getCounters(CounterEnumType.P1P1) > 0) {
return false;
} else if (combat != null && ph.is(PhaseType.COMBAT_DECLARE_BLOCKERS)) {
return doCombatAdaptLogic(source, amount, combat);
@@ -336,7 +336,7 @@ public class CountersPutAi extends SpellAbilityAi {
}
return FightAi.canFightAi(ai, sa, nPump, nPump);
}
if (amountStr.equals("X")) {
if (source.getSVar(amountStr).equals("Count$xPaid")) {
// By default, set PayX here to maximum value (used for most SAs of this type).
@@ -345,7 +345,7 @@ public class CountersPutAi extends SpellAbilityAi {
if (isClockwork) {
// Clockwork Avian and other similar cards: do not tap all mana for X,
// instead only rewind to max allowed value when the power gets low enough.
int curCtrs = source.getCounters(CounterType.P1P0);
int curCtrs = source.getCounters(CounterEnumType.P1P0);
int maxCtrs = Integer.parseInt(sa.getParam("MaxFromEffect"));
// This will "rewind" clockwork cards when they fall to 50% power or below, consider improving
@@ -396,7 +396,7 @@ public class CountersPutAi extends SpellAbilityAi {
return true;
}
}
if (!ai.getGame().getStack().isEmpty() && !SpellAbilityAi.isSorcerySpeed(sa)) {
final TargetRestrictions abTgt = sa.getTargetRestrictions();
// only evaluates case where all tokens are placed on a single target
@@ -417,8 +417,8 @@ public class CountersPutAi extends SpellAbilityAi {
// Targeting
if (sa.usesTargeting()) {
sa.resetTargets();
sa.resetTargets();
final boolean sacSelf = ComputerUtilCost.isSacrificeSelfCost(abCost);
if (sa.isCurse()) {
@@ -435,7 +435,7 @@ public class CountersPutAi extends SpellAbilityAi {
if (sacSelf && c.equals(source)) {
return false;
}
return sa.canTarget(c) && c.canReceiveCounters(CounterType.valueOf(type));
return sa.canTarget(c) && c.canReceiveCounters(CounterType.getType(type));
}
});
@@ -488,7 +488,7 @@ public class CountersPutAi extends SpellAbilityAi {
}
return false;
}
// target loop
while (sa.canAddMoreTarget()) {
if (list.isEmpty()) {
@@ -558,7 +558,7 @@ public class CountersPutAi extends SpellAbilityAi {
return false;
}
final int currCounters = cards.get(0).getCounters(CounterType.valueOf(type));
final int currCounters = cards.get(0).getCounters(CounterType.get(type));
// each non +1/+1 counter on the card is a 10% chance of not
// activating this ability.
@@ -574,11 +574,11 @@ public class CountersPutAi extends SpellAbilityAi {
}
boolean immediately = ComputerUtil.playImmediately(ai, sa);
if (abCost != null && !ComputerUtilCost.checkSacrificeCost(ai, abCost, source, sa, immediately)) {
return false;
}
if (immediately) {
return true;
}
@@ -612,7 +612,7 @@ public class CountersPutAi extends SpellAbilityAi {
final boolean divided = sa.hasParam("DividedAsYouChoose");
final int amount = AbilityUtils.calculateAmount(sa.getHostCard(), amountStr, sa);
final boolean isMandatoryTrigger = (sa.isTrigger() && !sa.isOptionalTrigger())
final boolean isMandatoryTrigger = (sa.isTrigger() && !sa.isOptionalTrigger())
|| (sa.getRootAbility().isTrigger() && !sa.getRootAbility().isOptionalTrigger());
if (sa.usesTargeting()) {
@@ -692,12 +692,12 @@ public class CountersPutAi extends SpellAbilityAi {
final boolean divided = sa.hasParam("DividedAsYouChoose");
final int amount = AbilityUtils.calculateAmount(sa.getHostCard(), amountStr, sa);
int left = amount;
if (!sa.usesTargeting()) {
// No target. So must be defined
list = new CardCollection(AbilityUtils.getDefinedCards(source, sa.getParam("Defined"), sa));
if (amountStr.equals("X")
if (amountStr.equals("X")
&& !source.hasSVar("PayX") /* SubAbility on something that already had set PayX, e.g. Endless One ETB counters */
&& ((sa.hasParam(amountStr) && sa.getSVar(amountStr).equals("Count$xPaid")) || source.getSVar(amountStr).equals("Count$xPaid") )) {
@@ -727,7 +727,7 @@ public class CountersPutAi extends SpellAbilityAi {
source.setSVar("PayX", Integer.toString(payX));
}
if (!mandatory) {
// TODO - If Trigger isn't mandatory, when wouldn't we want to
// put a counter?
@@ -854,7 +854,7 @@ public class CountersPutAi extends SpellAbilityAi {
List<Card> threatening = CardLists.filter(creats, new Predicate<Card>() {
@Override
public boolean apply(Card c) {
return CombatUtil.canBlock(source, c, !isHaste)
return CombatUtil.canBlock(source, c, !isHaste)
&& (c.getNetToughness() > source.getNetPower() + tributeAmount || c.hasKeyword(Keyword.DEATHTOUCH));
}
});
@@ -889,24 +889,28 @@ public class CountersPutAi extends SpellAbilityAi {
}
@Override
public Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable<Player> options) {
public Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable<Player> options, Map<String, Object> params) {
// used by Tribute, select player with lowest Life
// TODO add more logic using TributeAILogic
List<Player> list = Lists.newArrayList(options);
return Collections.min(list, PlayerPredicates.compareByLife());
}
@Override
protected Card chooseSingleCard(final Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer) {
protected Card chooseSingleCard(final Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer, Map<String, Object> params) {
// Bolster does use this
// TODO need more or less logic there?
final CounterType m1m1 = CounterType.get(CounterEnumType.M1M1);
final CounterType p1p1 = CounterType.get(CounterEnumType.P1P1);
// no logic if there is no options or no to choice
if (!isOptional && Iterables.size(options) <= 1) {
return Iterables.getFirst(options, null);
}
final CounterType type = CounterType.valueOf(sa.getParam("CounterType"));
final CounterType type = params.containsKey("CounterType") ? (CounterType)params.get("CounterType")
: CounterType.getType(sa.getParam("CounterType"));
final String amountStr = sa.getParamOrDefault("CounterNum", "1");
final int amount = AbilityUtils.calculateAmount(sa.getHostCard(), amountStr, sa);
@@ -923,7 +927,7 @@ public class CountersPutAi extends SpellAbilityAi {
return false;
if (ComputerUtilCard.isUselessCreature(ai, input))
return false;
if (CounterType.M1M1.equals(type) && amount >= input.getNetToughness())
if (type.is(CounterEnumType.M1M1) && amount >= input.getNetToughness())
return true;
return ComputerUtil.isNegativeCounter(type, input);
}
@@ -947,6 +951,20 @@ public class CountersPutAi extends SpellAbilityAi {
CardCollection filtered = mine;
// Try to filter out keywords that we already have on cards
if (type.isKeywordCounter()) {
Keyword kw = Keyword.smartValueOf(type.getName());
final CardCollection doNotHaveKeyword = CardLists.filter(filtered, new Predicate<Card>() {
@Override
public boolean apply(Card card) {
return !card.hasKeyword(kw) && card.canBeTargetedBy(sa) && sa.canTarget(card);
}
});
if (doNotHaveKeyword.size() > 0)
filtered = doNotHaveKeyword;
}
final CardCollection notUseless = CardLists.filter(filtered, new Predicate<Card>() {
@Override
public boolean apply(Card input) {
@@ -961,26 +979,26 @@ public class CountersPutAi extends SpellAbilityAi {
}
// some special logic to reload Persist/Undying
if (CounterType.P1P1.equals(type)) {
if (p1p1.equals(type)) {
final CardCollection persist = CardLists.filter(filtered, new Predicate<Card>() {
@Override
public boolean apply(Card input) {
if (!input.hasKeyword(Keyword.PERSIST))
return false;
return input.getCounters(CounterType.M1M1) <= amount;
return input.getCounters(m1m1) <= amount;
}
});
if (!persist.isEmpty()) {
filtered = persist;
}
} else if (CounterType.M1M1.equals(type)) {
} else if (m1m1.equals(type)) {
final CardCollection undying = CardLists.filter(filtered, new Predicate<Card>() {
@Override
public boolean apply(Card input) {
if (!input.hasKeyword(Keyword.UNDYING))
return false;
return input.getCounters(CounterType.P1P1) <= amount && input.getNetToughness() > amount;
return input.getCounters(p1p1) <= amount && input.getNetToughness() > amount;
}
});
@@ -1003,8 +1021,8 @@ public class CountersPutAi extends SpellAbilityAi {
if (e instanceof Card) {
Card c = (Card) e;
if (c.getController().isOpponentOf(ai)) {
if (options.contains(CounterType.M1M1) && !c.hasKeyword(Keyword.UNDYING)) {
return CounterType.M1M1;
if (options.contains(CounterType.get(CounterEnumType.M1M1)) && !c.hasKeyword(Keyword.UNDYING)) {
return CounterType.get(CounterEnumType.M1M1);
}
for (CounterType type : options) {
if (ComputerUtil.isNegativeCounter(type, c)) {
@@ -1021,12 +1039,12 @@ public class CountersPutAi extends SpellAbilityAi {
} else if (e instanceof Player) {
Player p = (Player) e;
if (p.isOpponentOf(ai)) {
if (options.contains(CounterType.POISON)) {
return CounterType.POISON;
if (options.contains(CounterType.get(CounterEnumType.POISON))) {
return CounterType.get(CounterEnumType.POISON);
}
} else {
if (options.contains(CounterType.EXPERIENCE)) {
return CounterType.EXPERIENCE;
if (options.contains(CounterType.get(CounterEnumType.EXPERIENCE))) {
return CounterType.get(CounterEnumType.EXPERIENCE);
}
}

View File

@@ -6,12 +6,12 @@
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
@@ -36,7 +36,7 @@ import java.util.Map;
* <p>
* AbilityFactory_PutOrRemoveCountersAi class.
* </p>
*
*
* @author Forge
* @version $Id$
*/
@@ -44,7 +44,7 @@ public class CountersPutOrRemoveAi extends SpellAbilityAi {
/*
* (non-Javadoc)
*
*
* @see forge.ai.SpellAbilityAi#checkApiLogic(forge.game.player.Player,
* forge.game.spellability.SpellAbility)
*/
@@ -75,7 +75,7 @@ public class CountersPutOrRemoveAi extends SpellAbilityAi {
if (sa.hasParam("CounterType")) {
// currently only Jhoira's Timebug
final CounterType type = CounterType.valueOf(sa.getParam("CounterType"));
final CounterType type = CounterType.getType(sa.getParam("CounterType"));
CardCollection countersList = CardLists.filter(list, CardPredicates.hasCounter(type, amount));
@@ -100,7 +100,7 @@ public class CountersPutOrRemoveAi extends SpellAbilityAi {
if (!ai.isCardInPlay("Marit Lage") || noLegendary) {
CardCollectionView depthsList = CardLists.filter(countersList,
CardPredicates.nameEquals("Dark Depths"), CardPredicates.hasCounter(CounterType.ICE));
CardPredicates.nameEquals("Dark Depths"), CardPredicates.hasCounter(CounterEnumType.ICE));
if (!depthsList.isEmpty()) {
sa.getTargets().add(depthsList.getFirst());
@@ -113,7 +113,7 @@ public class CountersPutOrRemoveAi extends SpellAbilityAi {
CardCollection planeswalkerList = CardLists.filter(
CardLists.filterControlledBy(countersList, ai.getOpponents()),
CardPredicates.Presets.PLANESWALKERS,
CardPredicates.hasLessCounter(CounterType.LOYALTY, amount));
CardPredicates.hasLessCounter(CounterEnumType.LOYALTY, amount));
if (!planeswalkerList.isEmpty()) {
sa.getTargets().add(ComputerUtilCard.getBestPlaneswalkerAI(planeswalkerList));
@@ -123,7 +123,7 @@ public class CountersPutOrRemoveAi extends SpellAbilityAi {
// do as M1M1 part
CardCollection aiList = CardLists.filterControlledBy(countersList, ai);
CardCollection aiM1M1List = CardLists.filter(aiList, CardPredicates.hasCounter(CounterType.M1M1));
CardCollection aiM1M1List = CardLists.filter(aiList, CardPredicates.hasCounter(CounterEnumType.M1M1));
CardCollection aiPersistList = CardLists.getKeyword(aiM1M1List, Keyword.PERSIST);
if (!aiPersistList.isEmpty()) {
@@ -136,7 +136,7 @@ public class CountersPutOrRemoveAi extends SpellAbilityAi {
}
// do as P1P1 part
CardCollection aiP1P1List = CardLists.filter(aiList, CardPredicates.hasCounter(CounterType.P1P1));
CardCollection aiP1P1List = CardLists.filter(aiList, CardPredicates.hasCounter(CounterEnumType.P1P1));
CardCollection aiUndyingList = CardLists.getKeyword(aiM1M1List, Keyword.UNDYING);
if (!aiUndyingList.isEmpty()) {
@@ -183,7 +183,7 @@ public class CountersPutOrRemoveAi extends SpellAbilityAi {
/*
* (non-Javadoc)
*
*
* @see forge.ai.SpellAbilityAi#chooseCounterType(java.util.List,
* forge.game.spellability.SpellAbility, java.util.Map)
*/
@@ -199,18 +199,18 @@ public class CountersPutOrRemoveAi extends SpellAbilityAi {
Card tgt = (Card) params.get("Target");
// planeswalker has high priority for loyalty counters
if (tgt.isPlaneswalker() && options.contains(CounterType.LOYALTY)) {
return CounterType.LOYALTY;
if (tgt.isPlaneswalker() && options.contains(CounterType.get(CounterEnumType.LOYALTY))) {
return CounterType.get(CounterEnumType.LOYALTY);
}
if (tgt.getController().isOpponentOf(ai)) {
// creatures with BaseToughness below or equal zero might be
// killed if their counters are removed
if (tgt.isCreature() && tgt.getBaseToughness() <= 0) {
if (options.contains(CounterType.P1P1)) {
return CounterType.P1P1;
} else if (options.contains(CounterType.M1M1)) {
return CounterType.M1M1;
if (options.contains(CounterType.get(CounterEnumType.P1P1))) {
return CounterType.get(CounterEnumType.P1P1);
} else if (options.contains(CounterType.get(CounterEnumType.M1M1))) {
return CounterType.get(CounterEnumType.M1M1);
}
}
@@ -222,14 +222,14 @@ public class CountersPutOrRemoveAi extends SpellAbilityAi {
}
} else {
// this counters are treat first to be removed
if ("Dark Depths".equals(tgt.getName()) && options.contains(CounterType.ICE)) {
if ("Dark Depths".equals(tgt.getName()) && options.contains(CounterType.get(CounterEnumType.ICE))) {
if (!ai.isCardInPlay("Marit Lage") || noLegendary) {
return CounterType.ICE;
return CounterType.get(CounterEnumType.ICE);
}
} else if (tgt.hasKeyword(Keyword.UNDYING) && options.contains(CounterType.P1P1)) {
return CounterType.P1P1;
} else if (tgt.hasKeyword(Keyword.PERSIST) && options.contains(CounterType.M1M1)) {
return CounterType.M1M1;
} else if (tgt.hasKeyword(Keyword.UNDYING) && options.contains(CounterType.get(CounterEnumType.P1P1))) {
return CounterType.get(CounterEnumType.P1P1);
} else if (tgt.hasKeyword(Keyword.PERSIST) && options.contains(CounterType.get(CounterEnumType.M1M1))) {
return CounterType.get(CounterEnumType.M1M1);
}
// fallback logic, select positive counter to add more
@@ -246,7 +246,7 @@ public class CountersPutOrRemoveAi extends SpellAbilityAi {
/*
* (non-Javadoc)
*
*
* @see
* forge.ai.SpellAbilityAi#chooseBinary(forge.game.player.PlayerController.
* BinaryChoiceType, forge.game.spellability.SpellAbility, java.util.Map)
@@ -262,19 +262,19 @@ public class CountersPutOrRemoveAi extends SpellAbilityAi {
boolean noLegendary = game.getStaticEffects().getGlobalRuleChange(GlobalRuleChange.noLegendRule);
if (tgt.getController().isOpponentOf(ai)) {
if (type.equals(CounterType.LOYALTY) && tgt.isPlaneswalker()) {
if (type.is(CounterEnumType.LOYALTY) && tgt.isPlaneswalker()) {
return false;
}
return ComputerUtil.isNegativeCounter(type, tgt);
} else {
if (type.equals(CounterType.ICE) && "Dark Depths".equals(tgt.getName())) {
if (type.is(CounterEnumType.ICE) && "Dark Depths".equals(tgt.getName())) {
if (!ai.isCardInPlay("Marit Lage") || noLegendary) {
return false;
}
} else if (type.equals(CounterType.M1M1) && tgt.hasKeyword(Keyword.PERSIST)) {
} else if (type.is(CounterEnumType.M1M1) && tgt.hasKeyword(Keyword.PERSIST)) {
return false;
} else if (type.equals(CounterType.P1P1) && tgt.hasKeyword(Keyword.UNDYING)) {
} else if (type.is(CounterEnumType.P1P1) && tgt.hasKeyword(Keyword.UNDYING)) {
return false;
}

View File

@@ -33,7 +33,7 @@ public class CountersRemoveAi extends SpellAbilityAi {
/*
* (non-Javadoc)
*
*
* @see
* forge.ai.SpellAbilityAi#checkPhaseRestrictions(forge.game.player.Player,
* forge.game.spellability.SpellAbility, forge.game.phase.PhaseHandler)
@@ -50,7 +50,7 @@ public class CountersRemoveAi extends SpellAbilityAi {
/*
* (non-Javadoc)
*
*
* @see
* forge.ai.SpellAbilityAi#checkPhaseRestrictions(forge.game.player.Player,
* forge.game.spellability.SpellAbility, forge.game.phase.PhaseHandler,
@@ -68,7 +68,7 @@ public class CountersRemoveAi extends SpellAbilityAi {
/*
* (non-Javadoc)
*
*
* @see forge.ai.SpellAbilityAi#checkApiLogic(forge.game.player.Player,
* forge.game.spellability.SpellAbility)
*/
@@ -82,7 +82,7 @@ public class CountersRemoveAi extends SpellAbilityAi {
}
if (!type.matches("Any") && !type.matches("All")) {
final int currCounters = sa.getHostCard().getCounters(CounterType.valueOf(type));
final int currCounters = sa.getHostCard().getCounters(CounterType.getType(type));
if (currCounters < 1) {
return false;
}
@@ -119,7 +119,7 @@ public class CountersRemoveAi extends SpellAbilityAi {
if (!ai.isCardInPlay("Marit Lage") || noLegendary) {
CardCollectionView depthsList = ai.getCardsIn(ZoneType.Battlefield, "Dark Depths");
depthsList = CardLists.filter(depthsList, CardPredicates.isTargetableBy(sa),
CardPredicates.hasCounter(CounterType.ICE, 3));
CardPredicates.hasCounter(CounterEnumType.ICE, 3));
if (!depthsList.isEmpty()) {
sa.getTargets().add(depthsList.getFirst());
@@ -132,7 +132,7 @@ public class CountersRemoveAi extends SpellAbilityAi {
list = CardLists.filter(list, CardPredicates.isTargetableBy(sa));
CardCollection planeswalkerList = CardLists.filter(list, CardPredicates.Presets.PLANESWALKERS,
CardPredicates.hasCounter(CounterType.LOYALTY, 5));
CardPredicates.hasCounter(CounterEnumType.LOYALTY, 5));
if (!planeswalkerList.isEmpty()) {
sa.getTargets().add(ComputerUtilCard.getBestPlaneswalkerAI(planeswalkerList));
@@ -159,11 +159,11 @@ public class CountersRemoveAi extends SpellAbilityAi {
if (!ai.isCardInPlay("Marit Lage") || noLegendary) {
CardCollectionView depthsList = ai.getCardsIn(ZoneType.Battlefield, "Dark Depths");
depthsList = CardLists.filter(depthsList, CardPredicates.isTargetableBy(sa),
CardPredicates.hasCounter(CounterType.ICE));
CardPredicates.hasCounter(CounterEnumType.ICE));
if (!depthsList.isEmpty()) {
Card depth = depthsList.getFirst();
int ice = depth.getCounters(CounterType.ICE);
int ice = depth.getCounters(CounterEnumType.ICE);
if (amount >= ice) {
sa.getTargets().add(depth);
if (xPay) {
@@ -180,7 +180,7 @@ public class CountersRemoveAi extends SpellAbilityAi {
CardCollection planeswalkerList = CardLists.filter(list,
Predicates.and(CardPredicates.Presets.PLANESWALKERS, CardPredicates.isControlledByAnyOf(ai.getOpponents())),
CardPredicates.hasLessCounter(CounterType.LOYALTY, amount));
CardPredicates.hasLessCounter(CounterEnumType.LOYALTY, amount));
if (!planeswalkerList.isEmpty()) {
Card best = ComputerUtilCard.getBestPlaneswalkerAI(planeswalkerList);
@@ -196,7 +196,7 @@ public class CountersRemoveAi extends SpellAbilityAi {
// do as M1M1 part
CardCollection aiList = CardLists.filterControlledBy(list, ai);
CardCollection aiM1M1List = CardLists.filter(aiList, CardPredicates.hasCounter(CounterType.M1M1));
CardCollection aiM1M1List = CardLists.filter(aiList, CardPredicates.hasCounter(CounterEnumType.M1M1));
CardCollection aiPersistList = CardLists.getKeyword(aiM1M1List, Keyword.PERSIST);
if (!aiPersistList.isEmpty()) {
@@ -209,7 +209,7 @@ public class CountersRemoveAi extends SpellAbilityAi {
}
// do as P1P1 part
CardCollection aiP1P1List = CardLists.filter(aiList, CardPredicates.hasLessCounter(CounterType.P1P1, amount));
CardCollection aiP1P1List = CardLists.filter(aiList, CardPredicates.hasLessCounter(CounterEnumType.P1P1, amount));
CardCollection aiUndyingList = CardLists.getKeyword(aiP1P1List, Keyword.UNDYING);
if (!aiUndyingList.isEmpty()) {
@@ -220,7 +220,7 @@ public class CountersRemoveAi extends SpellAbilityAi {
// remove P1P1 counters from opposing creatures
CardCollection oppP1P1List = CardLists.filter(list,
Predicates.and(CardPredicates.Presets.CREATURES, CardPredicates.isControlledByAnyOf(ai.getOpponents())),
CardPredicates.hasCounter(CounterType.P1P1));
CardPredicates.hasCounter(CounterEnumType.P1P1));
if (!oppP1P1List.isEmpty()) {
sa.getTargets().add(ComputerUtilCard.getBestCreatureAI(oppP1P1List));
return true;
@@ -244,7 +244,7 @@ public class CountersRemoveAi extends SpellAbilityAi {
// no special amount for that one yet
int amount = AbilityUtils.calculateAmount(source, amountStr, sa);
CardCollection aiList = CardLists.filterControlledBy(list, ai);
aiList = CardLists.filter(aiList, CardPredicates.hasCounter(CounterType.M1M1, amount));
aiList = CardLists.filter(aiList, CardPredicates.hasCounter(CounterEnumType.M1M1, amount));
CardCollection aiPersist = CardLists.getKeyword(aiList, Keyword.PERSIST);
if (!aiPersist.isEmpty()) {
@@ -263,7 +263,7 @@ public class CountersRemoveAi extends SpellAbilityAi {
// no special amount for that one yet
int amount = AbilityUtils.calculateAmount(source, amountStr, sa);
list = CardLists.filter(list, CardPredicates.hasCounter(CounterType.P1P1, amount));
list = CardLists.filter(list, CardPredicates.hasCounter(CounterEnumType.P1P1, amount));
// currently only logic for Bloodcrazed Hoplite, but add logic for
// targeting ai creatures too
@@ -309,12 +309,12 @@ public class CountersRemoveAi extends SpellAbilityAi {
amount = AbilityUtils.calculateAmount(source, amountStr, sa);
}
CardCollection timeList = CardLists.filter(list, CardPredicates.hasLessCounter(CounterType.TIME, amount));
CardCollection timeList = CardLists.filter(list, CardPredicates.hasLessCounter(CounterEnumType.TIME, amount));
if (!timeList.isEmpty()) {
Card best = ComputerUtilCard.getBestAI(timeList);
int timeCount = best.getCounters(CounterType.TIME);
int timeCount = best.getCounters(CounterEnumType.TIME);
sa.getTargets().add(best);
if (xPay) {
source.setSVar("PayX", Integer.toString(timeCount));
@@ -335,7 +335,7 @@ public class CountersRemoveAi extends SpellAbilityAi {
CardCollection outlastCreats = CardLists.filter(list, CardPredicates.hasKeyword(Keyword.OUTLAST));
if (!outlastCreats.isEmpty()) {
// outlast cards often benefit from having +1/+1 counters, try not to remove last one
CardCollection betterTargets = CardLists.filter(outlastCreats, CardPredicates.hasCounter(CounterType.P1P1, 2));
CardCollection betterTargets = CardLists.filter(outlastCreats, CardPredicates.hasCounter(CounterEnumType.P1P1, 2));
if (!betterTargets.isEmpty()) {
sa.getTargets().add(ComputerUtilCard.getWorstAI(betterTargets));
@@ -363,7 +363,7 @@ public class CountersRemoveAi extends SpellAbilityAi {
/*
* (non-Javadoc)
*
*
* @see forge.ai.SpellAbilityAi#chooseNumber(forge.game.player.Player,
* forge.game.spellability.SpellAbility, int, int, java.util.Map)
*/
@@ -377,8 +377,8 @@ public class CountersRemoveAi extends SpellAbilityAi {
if (targetCard.getController().isOpponentOf(player)) {
return !ComputerUtil.isNegativeCounter(type, targetCard) ? max : min;
} else {
if (targetCard.hasKeyword(Keyword.UNDYING) && type == CounterType.P1P1
&& targetCard.getCounters(CounterType.P1P1) >= max) {
if (targetCard.hasKeyword(Keyword.UNDYING) && type.is(CounterEnumType.P1P1)
&& targetCard.getCounters(CounterEnumType.P1P1) >= max) {
return max;
}
@@ -387,9 +387,9 @@ public class CountersRemoveAi extends SpellAbilityAi {
} else if (target instanceof Player) {
Player targetPlayer = (Player) target;
if (targetPlayer.isOpponentOf(player)) {
return !type.equals(CounterType.POISON) ? max : min;
return !type.equals(CounterEnumType.POISON) ? max : min;
} else {
return type.equals(CounterType.POISON) ? max : min;
return type.equals(CounterEnumType.POISON) ? max : min;
}
}
@@ -398,7 +398,7 @@ public class CountersRemoveAi extends SpellAbilityAi {
/*
* (non-Javadoc)
*
*
* @see forge.ai.SpellAbilityAi#chooseCounterType(java.util.List,
* forge.game.spellability.SpellAbility, java.util.Map)
*/
@@ -415,7 +415,7 @@ public class CountersRemoveAi extends SpellAbilityAi {
if (targetCard.getController().isOpponentOf(ai)) {
// if its a Planeswalker try to remove Loyality first
if (targetCard.isPlaneswalker()) {
return CounterType.LOYALTY;
return CounterType.get(CounterEnumType.LOYALTY);
}
for (CounterType type : options) {
if (!ComputerUtil.isNegativeCounter(type, targetCard)) {
@@ -423,10 +423,10 @@ public class CountersRemoveAi extends SpellAbilityAi {
}
}
} else {
if (options.contains(CounterType.M1M1) && targetCard.hasKeyword(Keyword.PERSIST)) {
return CounterType.M1M1;
} else if (options.contains(CounterType.P1P1) && targetCard.hasKeyword(Keyword.UNDYING)) {
return CounterType.P1P1;
if (options.contains(CounterType.get(CounterEnumType.M1M1)) && targetCard.hasKeyword(Keyword.PERSIST)) {
return CounterType.get(CounterEnumType.M1M1);
} else if (options.contains(CounterType.get(CounterEnumType.P1P1)) && targetCard.hasKeyword(Keyword.UNDYING)) {
return CounterType.get(CounterEnumType.P1P1);
}
for (CounterType type : options) {
if (ComputerUtil.isNegativeCounter(type, targetCard)) {
@@ -438,13 +438,13 @@ public class CountersRemoveAi extends SpellAbilityAi {
Player targetPlayer = (Player) target;
if (targetPlayer.isOpponentOf(ai)) {
for (CounterType type : options) {
if (!type.equals(CounterType.POISON)) {
if (!type.equals(CounterEnumType.POISON)) {
return type;
}
}
} else {
for (CounterType type : options) {
if (type.equals(CounterType.POISON)) {
if (type.equals(CounterEnumType.POISON)) {
return type;
}
}

View File

@@ -6,7 +6,7 @@ import forge.game.ability.AbilityUtils;
import forge.game.card.Card;
import forge.game.card.CardCollection;
import forge.game.card.CardLists;
import forge.game.card.CounterType;
import forge.game.card.CounterEnumType;
import forge.game.cost.Cost;
import forge.game.keyword.Keyword;
import forge.game.phase.PhaseType;
@@ -39,7 +39,7 @@ public class DamageAllAi extends SpellAbilityAi {
if (!ai.getGame().getStack().isEmpty()) {
return false;
}
int x = -1;
final String damage = sa.getParam("NumDmg");
int dmg = AbilityUtils.calculateAmount(sa.getHostCard(), damage, sa);
@@ -50,7 +50,7 @@ public class DamageAllAi extends SpellAbilityAi {
x = ComputerUtilMana.determineLeftoverMana(sa, ai);
}
if (damage.equals("ChosenX")) {
x = source.getCounters(CounterType.LOYALTY);
x = source.getCounters(CounterEnumType.LOYALTY);
}
if (x == -1) {
if (determineOppToKill(ai, sa, source, dmg) != null) {

View File

@@ -46,9 +46,9 @@ public class DamageDealAi extends DamageAiBase {
if ("MadSarkhanDigDmg".equals(logic)) {
return SpecialCardAi.SarkhanTheMad.considerDig(ai, sa);
}
if (damage.equals("X") && sa.getSVar(damage).equals("Count$ChosenNumber")) {
int energy = ai.getCounters(CounterType.ENERGY);
int energy = ai.getCounters(CounterEnumType.ENERGY);
for (SpellAbility s : source.getSpellAbilities()) {
if ("PayEnergy".equals(s.getParam("AILogic"))) {
energy += AbilityUtils.calculateAmount(source, s.getParam("CounterNum"), sa);
@@ -145,7 +145,7 @@ public class DamageDealAi extends DamageAiBase {
if (sourceName.equals("Crater's Claws") && ai.hasFerocious()) {
dmg += 2;
}
String logic = sa.getParamOrDefault("AILogic", "");
if ("DiscardLands".equals(logic)) {
dmg = 2;
@@ -165,7 +165,7 @@ public class DamageDealAi extends DamageAiBase {
List<Card> wolves = CardLists.getValidCards(ai.getCardsIn(ZoneType.Battlefield), "Creature.Wolf+untapped+YouCtrl+Other", ai, source);
dmg = Aggregates.sum(wolves, CardPredicates.Accessors.fnGetNetPower);
} else if ("Triskelion".equals(logic)) {
final int n = source.getCounters(CounterType.P1P1);
final int n = source.getCounters(CounterEnumType.P1P1);
if (n > 0) {
if (ComputerUtil.playImmediately(ai, sa)) {
/*
@@ -196,9 +196,9 @@ public class DamageDealAi extends DamageAiBase {
}
return false;
}
if (sourceName.equals("Sorin, Grim Nemesis")) {
int loyalty = source.getCounters(CounterType.LOYALTY);
int loyalty = source.getCounters(CounterEnumType.LOYALTY);
for (; loyalty > 0; loyalty--) {
if (this.damageTargetAI(ai, sa, loyalty, false)) {
dmg = ComputerUtilCombat.getEnoughDamageToKill(sa.getTargetCard(), loyalty, source, false, false);
@@ -228,7 +228,7 @@ public class DamageDealAi extends DamageAiBase {
if (!ComputerUtilCost.checkRemoveCounterCost(abCost, source)) {
return false;
}
if ("DiscardLands".equals(sa.getParam("AILogic")) && !ComputerUtilCost.checkDiscardCost(ai, abCost, source)) {
return false;
}
@@ -309,7 +309,7 @@ public class DamageDealAi extends DamageAiBase {
* <p>
* dealDamageChooseTgtC.
* </p>
*
*
* @param d
* a int.
* @param noPrevention
@@ -445,7 +445,7 @@ public class DamageDealAi extends DamageAiBase {
// As of right now, ranks planeswalkers by their Current Loyalty * 10 + Big buff if close to "Ultimate"
int bestScore = 0;
for (Card pw : pws) {
int curLoyalty = pw.getCounters(CounterType.LOYALTY);
int curLoyalty = pw.getCounters(CounterEnumType.LOYALTY);
int pwScore = curLoyalty * 10;
for (SpellAbility sa : pw.getSpellAbilities()) {
@@ -478,7 +478,7 @@ public class DamageDealAi extends DamageAiBase {
int bestScore = Integer.MAX_VALUE;
for (Card pw : pws) {
int curLoyalty = pw.getCounters(CounterType.LOYALTY);
int curLoyalty = pw.getCounters(CounterEnumType.LOYALTY);
if (curLoyalty < bestScore) {
bestScore = curLoyalty;
@@ -515,7 +515,7 @@ public class DamageDealAi extends DamageAiBase {
* <p>
* damageTargetAI.
* </p>
*
*
* @param saMe
* a {@link forge.game.spellability.SpellAbility} object.
* @param dmg
@@ -543,7 +543,7 @@ public class DamageDealAi extends DamageAiBase {
* <p>
* damageChoosingTargets.
* </p>
*
*
* @param sa
* a {@link forge.game.spellability.SpellAbility} object.
* @param tgt
@@ -587,7 +587,7 @@ public class DamageDealAi extends DamageAiBase {
if (tgt.getMaxTargets(source, sa) <= 0 && !logic.equals("AssumeAtLeastOneTarget")) {
return false;
}
immediately |= ComputerUtil.playImmediately(ai, sa);
if (!(sa.getParent() != null && sa.getParent().isTargetNumberValid())) {
@@ -623,7 +623,7 @@ public class DamageDealAi extends DamageAiBase {
continue;
}
final int assignedDamage = ComputerUtilCombat.getEnoughDamageToKill(humanCreature, dmg, source, false, noPrevention);
if (assignedDamage <= dmg
if (assignedDamage <= dmg
&& humanCreature.getShieldCount() == 0 && !ComputerUtil.canRegenerate(humanCreature.getController(), humanCreature)) {
tcs.add(humanCreature);
tgt.addDividedAllocation(humanCreature, assignedDamage);
@@ -756,7 +756,7 @@ public class DamageDealAi extends DamageAiBase {
break;
}
}
} else if (tgt.canTgtCreature() || tgt.canTgtPlaneswalker()) {
final Card c = this.dealDamageChooseTgtC(ai, sa, dmg, noPrevention, enemy, mandatory);
if (c != null) {
@@ -825,8 +825,8 @@ public class DamageDealAi extends DamageAiBase {
* <p>
* damageChooseNontargeted.
* </p>
* @param ai
*
* @param ai
*
* @param saMe
* a {@link forge.game.spellability.SpellAbility} object.
* @param dmg
@@ -881,7 +881,7 @@ public class DamageDealAi extends DamageAiBase {
* <p>
* damageChooseRequiredTargets.
* </p>
*
*
* @param sa
* a {@link forge.game.spellability.SpellAbility} object.
* @param tgt
@@ -1006,7 +1006,7 @@ public class DamageDealAi extends DamageAiBase {
// If I can kill my target by paying less mana, do it
int actualPay = 0;
final boolean noPrevention = sa.hasParam("NoPrevention");
//target is a player
if (!sa.getTargets().isTargetingAnyCard()) {
actualPay = dmg;
@@ -1037,15 +1037,15 @@ public class DamageDealAi extends DamageAiBase {
Player opponent = ai.getOpponents().min(PlayerPredicates.compareByLife());
// TODO: somehow account for the possible cost reduction?
// TODO: somehow account for the possible cost reduction?
int dmg = ComputerUtilMana.determineLeftoverMana(sa, ai, saTgt.getParam("XColor"));
while (!ComputerUtilMana.canPayManaCost(sa, ai, dmg) && dmg > 0) {
// TODO: ideally should never get here, currently put here as a precaution for complex mana base cases where the miscalculation might occur. Will remove later if it proves to never trigger.
dmg--;
System.out.println("Warning: AI could not pay mana cost for a XLifeDrain logic spell. Reducing X value to "+dmg);
}
// set the color map for black X for the purpose of Soul Burn
// TODO: somehow generalize this calculation to allow other potential similar cards to function in the future
if ("Soul Burn".equals(sourceName)) {
@@ -1066,7 +1066,7 @@ public class DamageDealAi extends DamageAiBase {
int toughness = c.getNetToughness();
boolean canDie = !(c.hasKeyword(Keyword.INDESTRUCTIBLE) || ComputerUtil.canRegenerate(c.getController(), c));
// Currently will target creatures with toughness 3+ (or power 5+)
// Currently will target creatures with toughness 3+ (or power 5+)
// and only if the creature can actually die, do not "underdrain"
// unless the creature has high power
if (canDie && toughness <= dmg && ((toughness == dmg && toughness >= 3) || power >= 5)) {

View File

@@ -101,7 +101,7 @@ public class DestroyAi extends SpellAbilityAi {
return SpecialCardAi.SarkhanTheMad.considerMakeDragon(ai, sa);
} else if (logic != null && logic.startsWith("MinLoyalty.")) {
int minLoyalty = Integer.parseInt(logic.substring(logic.indexOf(".") + 1));
if (source.getCounters(CounterType.LOYALTY) < minLoyalty) {
if (source.getCounters(CounterEnumType.LOYALTY) < minLoyalty) {
return false;
}
} else if ("Polymorph".equals(logic)) {
@@ -161,7 +161,7 @@ public class DestroyAi extends SpellAbilityAi {
return false;
}
//Check for undying
return (!c.hasKeyword(Keyword.UNDYING) || c.getCounters(CounterType.P1P1) > 0);
return (!c.hasKeyword(Keyword.UNDYING) || c.getCounters(CounterEnumType.P1P1) > 0);
}
});
}
@@ -388,7 +388,7 @@ public class DestroyAi extends SpellAbilityAi {
if (CardLists.getNotType(list, "Creature").isEmpty()) {
if (!sa.getUniqueTargets().isEmpty() && sa.getParent().getApi() == ApiType.Destroy
&& sa.getUniqueTargets().get(0) instanceof Card) {
// basic ai for Diaochan
// basic ai for Diaochan
c = (Card) sa.getUniqueTargets().get(0);
} else {
c = ComputerUtilCard.getWorstCreatureAI(list);
@@ -413,7 +413,7 @@ public class DestroyAi extends SpellAbilityAi {
Player tgtPlayer = tgtLand.getController();
int oppLandsOTB = tgtPlayer.getLandsInPlay().size();
// AI profile-dependent properties
AiController aic = ((PlayerControllerAi)ai.getController()).getAi();
int amountNoTempoCheck = aic.getIntProperty(AiProps.STRIPMINE_MIN_LANDS_OTB_FOR_NO_TEMPO_CHECK);
@@ -436,7 +436,7 @@ public class DestroyAi extends SpellAbilityAi {
// Non-basic lands are currently not ranked in any way in ComputerUtilCard#getBestLandAI, so if a non-basic land is best target,
// consider killing it off unless there's too much potential tempo loss.
// TODO: actually rank non-basics in that method and then kill off the potentially dangerous (manlands, Valakut) or lucrative
// TODO: actually rank non-basics in that method and then kill off the potentially dangerous (manlands, Valakut) or lucrative
// (dual/triple mana that opens access to a certain color) lands
boolean nonBasicTgt = !tgtLand.isBasicLand();
@@ -448,7 +448,7 @@ public class DestroyAi extends SpellAbilityAi {
boolean isHighPriority = highPriorityIfNoLandDrop && oppSkippedLandDrop;
boolean timingCheck = canManaLock || canColorLock || nonBasicTgt;
boolean tempoCheck = numLandsOTB >= amountNoTempoCheck
boolean tempoCheck = numLandsOTB >= amountNoTempoCheck
|| ((numLandsInHand >= amountLandsInHand || isHighPriority) && ((numLandsInHand + numLandsOTB >= amountNoTimingCheck) || timingCheck));
// For Ghost Quarter, only use it if you have either more lands in play than your opponent

View File

@@ -1,5 +1,7 @@
package forge.ai.ability;
import java.util.Map;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
@@ -132,7 +134,7 @@ public class DigAi extends SpellAbilityAi {
}
@Override
public Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> valid, boolean isOptional, Player relatedPlayer) {
public Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> valid, boolean isOptional, Player relatedPlayer, Map<String, Object> params) {
if ("DigForCreature".equals(sa.getParam("AILogic"))) {
Card bestChoice = ComputerUtilCard.getBestCreatureAI(valid);
if (bestChoice == null) {
@@ -163,7 +165,7 @@ public class DigAi extends SpellAbilityAi {
* @see forge.card.ability.SpellAbilityAi#chooseSinglePlayer(forge.game.player.Player, forge.card.spellability.SpellAbility, java.util.List)
*/
@Override
public Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable<Player> options) {
public Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable<Player> options, Map<String, Object> params) {
// an opponent choose a card from
return Iterables.getFirst(options, null);
}

View File

@@ -25,6 +25,7 @@ import forge.game.ability.ApiType;
import forge.game.card.Card;
import forge.game.card.CardLists;
import forge.game.card.CardPredicates;
import forge.game.card.CounterEnumType;
import forge.game.card.CounterType;
import forge.game.cost.*;
import forge.game.phase.PhaseHandler;
@@ -348,7 +349,7 @@ public class DrawAi extends SpellAbilityAi {
}
// try to make opponent lose to poison
// currently only Caress of Phyrexia
if (getPoison != null && oppA.canReceiveCounters(CounterType.POISON)) {
if (getPoison != null && oppA.canReceiveCounters(CounterType.get(CounterEnumType.POISON))) {
if (oppA.getPoisonCounters() + numCards > 9) {
sa.getTargets().add(oppA);
return true;
@@ -392,7 +393,7 @@ public class DrawAi extends SpellAbilityAi {
}
}
if (getPoison != null && ai.canReceiveCounters(CounterType.POISON)) {
if (getPoison != null && ai.canReceiveCounters(CounterType.get(CounterEnumType.POISON))) {
if (numCards + ai.getPoisonCounters() >= 8) {
aiTarget = false;
}
@@ -451,7 +452,7 @@ public class DrawAi extends SpellAbilityAi {
}
// ally would lose because of poison
if (getPoison != null && ally.canReceiveCounters(CounterType.POISON)) {
if (getPoison != null && ally.canReceiveCounters(CounterType.get(CounterEnumType.POISON))) {
if (ally.getPoisonCounters() + numCards > 9) {
continue;
}

View File

@@ -18,6 +18,7 @@
package forge.ai.ability;
import java.util.List;
import java.util.Map;
import com.google.common.base.Predicate;
@@ -84,7 +85,7 @@ public final class EncodeAi extends SpellAbilityAi {
* forge.game.player.Player)
*/
@Override
public Card chooseSingleCard(final Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer) {
public Card chooseSingleCard(final Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer, Map<String, Object> params) {
return chooseCard(ai, options, isOptional);
}

View File

@@ -1,9 +1,11 @@
package forge.ai.ability;
import java.util.Map;
import com.google.common.collect.Iterables;
import forge.ai.SpellAbilityAi;
import forge.game.card.Card;
import forge.game.card.CounterType;
import forge.game.card.CounterEnumType;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
@@ -23,7 +25,7 @@ public class LegendaryRuleAi extends SpellAbilityAi {
@Override
public Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer) {
public Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer, Map<String, Object> params) {
// Choose a single legendary/planeswalker card to keep
Card firstOption = Iterables.getFirst(options, null);
boolean choosingFromPlanewalkers = firstOption.isPlaneswalker();
@@ -38,16 +40,16 @@ public class LegendaryRuleAi extends SpellAbilityAi {
if (firstOption.getName().equals("Dark Depths")) {
Card best = firstOption;
for (Card c : options) {
if (c.getCounters(CounterType.ICE) < best.getCounters(CounterType.ICE)) {
if (c.getCounters(CounterEnumType.ICE) < best.getCounters(CounterEnumType.ICE)) {
best = c;
}
}
return best;
} else if (firstOption.getCounters(CounterType.KI) > 0) {
} else if (firstOption.getCounters(CounterEnumType.KI) > 0) {
// Extra Rule for KI counter
Card best = firstOption;
for (Card c : options) {
if (c.getCounters(CounterType.KI) > best.getCounters(CounterType.KI)) {
if (c.getCounters(CounterEnumType.KI) > best.getCounters(CounterEnumType.KI)) {
best = c;
}
}

View File

@@ -5,7 +5,7 @@ import forge.ai.ComputerUtilMana;
import forge.ai.SpellAbilityAi;
import forge.game.ability.AbilityUtils;
import forge.game.card.Card;
import forge.game.card.CounterType;
import forge.game.card.CounterEnumType;
import forge.game.phase.PhaseType;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
@@ -130,7 +130,7 @@ public class LifeSetAi extends SpellAbilityAi {
}
if (sourceName.equals("Eternity Vessel")
&& (opponent.isCardInPlay("Vampire Hexmage") || (source.getCounters(CounterType.CHARGE) == 0))) {
&& (opponent.isCardInPlay("Vampire Hexmage") || (source.getCounters(CounterEnumType.CHARGE) == 0))) {
return false;
}

View File

@@ -120,7 +120,7 @@ public class ManaEffectAi extends SpellAbilityAi {
int manaSurplus = 0;
if ("XChoice".equals(host.getSVar("X"))
&& sa.getPayCosts().hasSpecificCostType(CostRemoveCounter.class)) {
CounterType ctrType = CounterType.KI; // Petalmane Baku
CounterType ctrType = CounterType.get(CounterEnumType.KI); // Petalmane Baku
for (CostPart part : sa.getPayCosts().getCostParts()) {
if (part instanceof CostRemoveCounter) {
ctrType = ((CostRemoveCounter)part).counter;

View File

@@ -18,6 +18,7 @@ import forge.game.spellability.SpellAbility;
import forge.game.zone.ZoneType;
import java.util.List;
import java.util.Map;
public class MustBlockAi extends SpellAbilityAi {
@@ -167,7 +168,7 @@ public class MustBlockAi extends SpellAbilityAi {
@Override
protected Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional,
Player targetedPlayer) {
Player targetedPlayer, Map<String, Object> params) {
final Card host = sa.getHostCard();
Card attacker = host;

View File

@@ -20,6 +20,7 @@ import forge.game.zone.ZoneType;
import forge.util.MyRandom;
import java.util.List;
import java.util.Map;
public class PlayAi extends SpellAbilityAi {
@@ -88,7 +89,7 @@ public class PlayAi extends SpellAbilityAi {
minCMC = sa.getPayCosts().getTotalMana().getCMC();
}
validOpts = CardLists.filter(validOpts, CardPredicates.greaterCMC(minCMC));
return chooseSingleCard(ai, sa, validOpts, sa.hasParam("Optional"), null) != null;
return chooseSingleCard(ai, sa, validOpts, sa.hasParam("Optional"), null, null) != null;
}
if (source != null && source.hasKeyword(Keyword.HIDEAWAY) && source.hasRemembered()) {
@@ -142,8 +143,7 @@ public class PlayAi extends SpellAbilityAi {
*/
@Override
public Card chooseSingleCard(final Player ai, final SpellAbility sa, Iterable<Card> options,
final boolean isOptional,
Player targetedPlayer) {
final boolean isOptional, Player targetedPlayer, Map<String, Object> params) {
List<Card> tgtCards = CardLists.filter(options, new Predicate<Card>() {
@Override
public boolean apply(final Card c) {

View File

@@ -5,6 +5,7 @@ import com.google.common.base.Predicate;
import forge.ai.ComputerUtil;
import forge.ai.SpellAbilityAi;
import forge.game.ability.AbilityUtils;
import forge.game.card.CounterEnumType;
import forge.game.card.CounterType;
import forge.game.phase.PhaseHandler;
import forge.game.phase.PhaseType;
@@ -59,7 +60,7 @@ public class PoisonAi extends SpellAbilityAi {
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
if (sa.usesTargeting()) {
return tgtPlayer(ai, sa, mandatory);
} else if (mandatory || !ai.canReceiveCounters(CounterType.POISON)) {
} else if (mandatory || !ai.canReceiveCounters(CounterType.get(CounterEnumType.POISON))) {
// mandatory or ai is uneffected
return true;
} else {
@@ -92,7 +93,7 @@ public class PoisonAi extends SpellAbilityAi {
public boolean apply(Player input) {
if (input.cantLose()) {
return false;
} else if (!input.canReceiveCounters(CounterType.POISON)) {
} else if (!input.canReceiveCounters(CounterType.get(CounterEnumType.POISON))) {
return false;
}
return true;
@@ -113,7 +114,7 @@ public class PoisonAi extends SpellAbilityAi {
if (tgts.isEmpty()) {
if (mandatory) {
// AI is uneffected
if (ai.canBeTargetedBy(sa) && ai.canReceiveCounters(CounterType.POISON)) {
if (ai.canBeTargetedBy(sa) && ai.canReceiveCounters(CounterType.get(CounterEnumType.POISON))) {
sa.getTargets().add(ai);
return true;
}
@@ -127,7 +128,7 @@ public class PoisonAi extends SpellAbilityAi {
if (input.cantLose()) {
return true;
}
return !input.canReceiveCounters(CounterType.POISON);
return !input.canReceiveCounters(CounterType.get(CounterEnumType.POISON));
}
});

View File

@@ -150,7 +150,7 @@ public class PumpAi extends PumpAiBase {
}
final String counterType = moveSA.getParam("CounterType");
final CounterType cType = "Any".equals(counterType) ? null : CounterType.valueOf(counterType);
final CounterType cType = "Any".equals(counterType) ? null : CounterType.getType(counterType);
final PhaseHandler ph = game.getPhaseHandler();
if (ph.inCombat() && ph.getPlayerTurn().isOpponentOf(ai)) {
@@ -185,7 +185,7 @@ public class PumpAi extends PumpAiBase {
// cant use substract on Copy
srcCardCpy.setCounters(cType, srcCardCpy.getCounters(cType) - amount);
if (CounterType.P1P1.equals(cType) && srcCardCpy.getNetToughness() <= 0) {
if (CounterEnumType.P1P1.equals(cType) && srcCardCpy.getNetToughness() <= 0) {
return srcCardCpy.getCounters(cType) > 0 || !card.hasKeyword(Keyword.UNDYING)
|| card.isToken();
}
@@ -235,7 +235,7 @@ public class PumpAi extends PumpAiBase {
// cant use substract on Copy
srcCardCpy.setCounters(cType, srcCardCpy.getCounters(cType) - amount);
if (CounterType.P1P1.equals(cType) && srcCardCpy.getNetToughness() <= 0) {
if (CounterEnumType.P1P1.equals(cType) && srcCardCpy.getNetToughness() <= 0) {
return srcCardCpy.getCounters(cType) > 0 || !card.hasKeyword(Keyword.UNDYING)
|| card.isToken();
}
@@ -402,7 +402,7 @@ public class PumpAi extends PumpAiBase {
if ("DebuffForXCounters".equals(sa.getParam("AILogic")) && sa.getTargetCard() != null) {
// e.g. Skullmane Baku
CounterType ctrType = CounterType.KI;
CounterType ctrType = CounterType.get(CounterEnumType.KI);
for (CostPart part : sa.getPayCosts().getCostParts()) {
if (part instanceof CostRemoveCounter) {
ctrType = ((CostRemoveCounter)part).counter;
@@ -730,7 +730,7 @@ public class PumpAi extends PumpAiBase {
final String numAttack = sa.hasParam("NumAtt") ? sa.getParam("NumAtt") : "";
if (numDefense.equals("-X") && sa.getSVar("X").equals("Count$ChosenNumber")) {
int energy = ai.getCounters(CounterType.ENERGY);
int energy = ai.getCounters(CounterEnumType.ENERGY);
for (SpellAbility s : source.getSpellAbilities()) {
if ("PayEnergy".equals(s.getParam("AILogic"))) {
energy += AbilityUtils.calculateAmount(source, s.getParam("CounterNum"), sa);
@@ -860,7 +860,7 @@ public class PumpAi extends PumpAiBase {
final boolean isInfect = source.hasKeyword(Keyword.INFECT); // Flesh-Eater Imp
int lethalDmg = isInfect ? 10 - defPlayer.getPoisonCounters() : defPlayer.getLife();
if (isInfect && !combat.getDefenderByAttacker(source).canReceiveCounters(CounterType.POISON)) {
if (isInfect && !combat.getDefenderByAttacker(source).canReceiveCounters(CounterType.get(CounterEnumType.POISON))) {
lethalDmg = Integer.MAX_VALUE; // won't be able to deal poison damage to kill the opponent
}
@@ -976,7 +976,7 @@ public class PumpAi extends PumpAiBase {
final boolean isInfect = source.hasKeyword(Keyword.INFECT);
int lethalDmg = isInfect ? 10 - defPlayer.getPoisonCounters() : defPlayer.getLife();
if (isInfect && !combat.getDefenderByAttacker(source).canReceiveCounters(CounterType.POISON)) {
if (isInfect && !combat.getDefenderByAttacker(source).canReceiveCounters(CounterType.get(CounterEnumType.POISON))) {
lethalDmg = Integer.MAX_VALUE; // won't be able to deal poison damage to kill the opponent
}

View File

@@ -16,6 +16,7 @@ import forge.game.zone.ZoneType;
import forge.util.TextUtil;
import java.util.List;
import java.util.Map;
public class RepeatEachAi extends SpellAbilityAi {
@@ -118,7 +119,7 @@ public class RepeatEachAi extends SpellAbilityAi {
}
@Override
protected Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer) {
protected Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer, Map<String, Object> params) {
return ComputerUtilCard.getBestCreatureAI(options);
}
}

View File

@@ -99,7 +99,7 @@ public class ScryAi extends SpellAbilityAi {
} else if ("BrainJar".equals(aiLogic)) {
final Card source = sa.getHostCard();
int counterNum = source.getCounters(CounterType.CHARGE);
int counterNum = source.getCounters(CounterEnumType.CHARGE);
// no need for logic
if (counterNum == 0) {
return false;

View File

@@ -248,7 +248,7 @@ public class SetStateAi extends SpellAbilityAi {
final Card othercard = aiPlayer.getCardsIn(ZoneType.Battlefield, other.getName()).getFirst();
// for legendary KI counter creatures
if (othercard.getCounters(CounterType.KI) >= source.getCounters(CounterType.KI)) {
if (othercard.getCounters(CounterEnumType.KI) >= source.getCounters(CounterEnumType.KI)) {
// if the other legendary is useless try to replace it
return ComputerUtilCard.isUselessCreature(aiPlayer, othercard);
}

View File

@@ -3,6 +3,7 @@ package forge.ai.ability;
import forge.ai.*;
import forge.game.ability.AbilityUtils;
import forge.game.card.Card;
import forge.game.card.CounterEnumType;
import forge.game.card.CounterType;
import forge.game.cost.Cost;
import forge.game.cost.CostPart;
@@ -68,7 +69,7 @@ public class TapAi extends TapAiBase {
} else {
if ("TapForXCounters".equals(sa.getParam("AILogic"))) {
// e.g. Waxmane Baku
CounterType ctrType = CounterType.KI;
CounterType ctrType = CounterType.get(CounterEnumType.KI);
for (CostPart part : sa.getPayCosts().getCostParts()) {
if (part instanceof CostRemoveCounter) {
ctrType = ((CostRemoveCounter)part).counter;

View File

@@ -1,5 +1,7 @@
package forge.ai.ability;
import java.util.Map;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import forge.ai.*;
@@ -296,7 +298,7 @@ public class TokenAi extends SpellAbilityAi {
* @see forge.card.ability.SpellAbilityAi#chooseSinglePlayer(forge.game.player.Player, forge.card.spellability.SpellAbility, Iterable<forge.game.player.Player> options)
*/
@Override
protected Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable<Player> options) {
protected Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable<Player> options, Map<String, Object> params) {
Combat combat = ai.getGame().getCombat();
// TokenAttacking
if (combat != null && sa.hasParam("TokenAttacking")) {
@@ -314,7 +316,7 @@ public class TokenAi extends SpellAbilityAi {
* @see forge.card.ability.SpellAbilityAi#chooseSinglePlayerOrPlaneswalker(forge.game.player.Player, forge.card.spellability.SpellAbility, Iterable<forge.game.GameEntity> options)
*/
@Override
protected GameEntity chooseSinglePlayerOrPlaneswalker(Player ai, SpellAbility sa, Iterable<GameEntity> options) {
protected GameEntity chooseSinglePlayerOrPlaneswalker(Player ai, SpellAbility sa, Iterable<GameEntity> options, Map<String, Object> params) {
Combat combat = ai.getGame().getCombat();
// TokenAttacking
if (combat != null && sa.hasParam("TokenAttacking")) {

View File

@@ -24,6 +24,7 @@ import forge.game.spellability.TargetRestrictions;
import forge.game.zone.ZoneType;
import java.util.List;
import java.util.Map;
public class UntapAi extends SpellAbilityAi {
@Override
@@ -311,7 +312,7 @@ public class UntapAi extends SpellAbilityAi {
}
@Override
public Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> list, boolean isOptional, Player targetedPlayer) {
public Card chooseSingleCard(Player ai, SpellAbility sa, Iterable<Card> list, boolean isOptional, Player targetedPlayer, Map<String, Object> params) {
PlayerCollection pl = new PlayerCollection();
pl.add(ai);
pl.addAll(ai.getAllies());

View File

@@ -76,7 +76,7 @@ public class GameCopier {
newPlayer.addSpellCastThisTurn();
for (int j = 0; j < origPlayer.getLandsPlayedThisTurn(); j++)
newPlayer.addLandPlayedThisTurn();
newPlayer.setCounters(Maps.newEnumMap(origPlayer.getCounters()));
newPlayer.setCounters(Maps.newHashMap(origPlayer.getCounters()));
newPlayer.setLifeLostLastTurn(origPlayer.getLifeLostLastTurn());
newPlayer.setLifeLostThisTurn(origPlayer.getLifeLostThisTurn());
newPlayer.setPreventNextDamage(origPlayer.getPreventNextDamage());
@@ -328,7 +328,7 @@ public class GameCopier {
Map<CounterType, Integer> counters = c.getCounters();
if (!counters.isEmpty()) {
newCard.setCounters(Maps.newEnumMap(counters));
newCard.setCounters(Maps.newHashMap(counters));
}
if (c.getChosenPlayer() != null) {
newCard.setChosenPlayer(playerMap.get(c.getChosenPlayer()));

View File

@@ -3,7 +3,7 @@ package forge.ai.simulation;
import forge.ai.CreatureEvaluator;
import forge.game.Game;
import forge.game.card.Card;
import forge.game.card.CounterType;
import forge.game.card.CounterEnumType;
import forge.game.phase.PhaseType;
import forge.game.player.Player;
import forge.game.zone.ZoneType;
@@ -154,7 +154,7 @@ public class GameStateEvaluator {
// e.g. a 5 CMC permanent results in 200, whereas a 5/5 creature is ~225
int value = 50 + 30 * c.getCMC();
if (c.isPlaneswalker()) {
value += 2 * c.getCounters(CounterType.LOYALTY);
value += 2 * c.getCounters(CounterEnumType.LOYALTY);
}
return value;
}

View File

@@ -36,7 +36,6 @@ import forge.game.player.Player;
import forge.game.replacement.ReplacementEffect;
import forge.game.replacement.ReplacementResult;
import forge.game.replacement.ReplacementType;
import forge.game.spellability.AbilitySub;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.SpellAbilityPredicates;
import forge.game.staticability.StaticAbility;
@@ -180,6 +179,10 @@ public class GameAction {
if (lastKnownInfo == null) {
lastKnownInfo = CardUtil.getLKICopy(c);
}
if (!lastKnownInfo.hasKeyword("Counters remain on CARDNAME as it moves to any zone other than a player's hand or library.") || zoneTo.is(ZoneType.Hand) || zoneTo.is(ZoneType.Library)) {
copied.clearCounters();
}
} else {
// if from Battlefield to Graveyard and Card does exist in LastStateBattlefield
// use that instead
@@ -228,17 +231,14 @@ public class GameAction {
for (final StaticAbility sa : copied.getStaticAbilities()) {
sa.setHostCard(copied);
}
if (c.getName().equals("Skullbriar, the Walking Grave")) {
copied.setCounters(c.getCounters());
}
// ensure that any leftover keyword/type changes are cleared in the state view
copied.updateStateForView();
} else { //Token
copied = c;
}
}
// ensure that any leftover keyword/type changes are cleared in the state view
copied.updateStateForView();
// Clean up temporary variables such as Sunburst value or announced PayX value
if (!(zoneTo.is(ZoneType.Stack) || zoneTo.is(ZoneType.Battlefield))) {
copied.clearTemporaryVars();
@@ -421,13 +421,6 @@ public class GameAction {
return copied;
}
// remove all counters from the card if destination is not the battlefield
// UNLESS we're dealing with Skullbriar, the Walking Grave
if (!c.isToken() && (zoneTo.is(ZoneType.Hand) || zoneTo.is(ZoneType.Library) ||
(!toBattlefield && !c.getName().equals("Skullbriar, the Walking Grave")))) {
copied.clearCounters();
}
if (!c.isToken() && !toBattlefield) {
copied.clearDevoured();
copied.clearDelved();
@@ -1030,8 +1023,10 @@ public class GameAction {
checkAgain |= stateBasedAction704_5r(c); // annihilate +1/+1 counters with -1/-1 ones
if (c.getCounters(CounterType.DREAM) > 7 && c.hasKeyword("CARDNAME can't have more than seven dream counters on it.")) {
c.subtractCounter(CounterType.DREAM, c.getCounters(CounterType.DREAM) - 7);
final CounterType dreamType = CounterType.get(CounterEnumType.DREAM);
if (c.getCounters(dreamType) > 7 && c.hasKeyword("CARDNAME can't have more than seven dream counters on it.")) {
c.subtractCounter(dreamType, c.getCounters(dreamType) - 7);
checkAgain = true;
}
}
@@ -1123,7 +1118,7 @@ public class GameAction {
if (!c.canBeSacrificed()) {
return false;
}
if (c.getCounters(CounterType.LORE) < c.getFinalChapterNr()) {
if (c.getCounters(CounterEnumType.LORE) < c.getFinalChapterNr()) {
return false;
}
if (!game.getStack().hasSimultaneousStackEntries() &&
@@ -1164,16 +1159,18 @@ public class GameAction {
private boolean stateBasedAction704_5r(Card c) {
boolean checkAgain = false;
int plusOneCounters = c.getCounters(CounterType.P1P1);
int minusOneCounters = c.getCounters(CounterType.M1M1);
final CounterType p1p1 = CounterType.get(CounterEnumType.P1P1);
final CounterType m1m1 = CounterType.get(CounterEnumType.M1M1);
int plusOneCounters = c.getCounters(p1p1);
int minusOneCounters = c.getCounters(m1m1);
if (plusOneCounters > 0 && minusOneCounters > 0) {
int remove = Math.min(plusOneCounters, minusOneCounters);
// If a permanent has both a +1/+1 counter and a -1/-1 counter on it,
// N +1/+1 and N -1/-1 counters are removed from it, where N is the
// smaller of the number of +1/+1 and -1/-1 counters on it.
// This should fire remove counters trigger
c.subtractCounter(CounterType.P1P1, remove);
c.subtractCounter(CounterType.M1M1, remove);
c.subtractCounter(p1p1, remove);
c.subtractCounter(m1m1, remove);
checkAgain = true;
}
return checkAgain;
@@ -1294,7 +1291,7 @@ public class GameAction {
//final Multimap<String, Card> uniqueWalkers = ArrayListMultimap.create(); // Not used as of Ixalan
for (Card c : list) {
if (c.getCounters(CounterType.LOYALTY) <= 0) {
if (c.getCounters(CounterEnumType.LOYALTY) <= 0) {
sacrificeDestroy(c, null, table);
// Play the Destroy sound
game.fireEvent(new GameEventCardDestroyed());
@@ -1354,7 +1351,8 @@ public class GameAction {
recheck = true;
Card toKeep = p.getController().chooseSingleEntityForEffect(new CardCollection(cc), new AbilitySub(ApiType.InternalLegendaryRule, null, null, null), "You have multiple legendary permanents named \""+name+"\" in play.\n\nChoose the one to stay on battlefield (the rest will be moved to graveyard)");
Card toKeep = p.getController().chooseSingleEntityForEffect(new CardCollection(cc), new SpellAbility.EmptySa(ApiType.InternalLegendaryRule, null, p),
"You have multiple legendary permanents named \""+name+"\" in play.\n\nChoose the one to stay on battlefield (the rest will be moved to graveyard)", null);
for (Card c: cc) {
if (c != toKeep) {
sacrificeDestroy(c, null, table);

View File

@@ -472,7 +472,7 @@ public final class GameActionUtil {
CardFactoryUtil.setupETBReplacementAbility(saAb);
String desc = "It enters the battlefield with ";
desc += Lang.nounWithNumeral(v, CounterType.P1P1.getName() + " counter");
desc += Lang.nounWithNumeral(v, CounterEnumType.P1P1.getName() + " counter");
desc += " on it.";
String repeffstr = "Event$ Moved | ValidCard$ Card.IsRemembered | Destination$ Battlefield | Description$ " + desc;

View File

@@ -24,6 +24,7 @@ import forge.game.card.CardCollectionView;
import forge.game.card.CardDamageMap;
import forge.game.card.CardLists;
import forge.game.card.CardPredicates;
import forge.game.card.CounterEnumType;
import forge.game.card.CounterType;
import forge.game.event.GameEventCardAttachment;
import forge.game.keyword.Keyword;
@@ -38,6 +39,7 @@ import forge.util.collect.FCollection;
import java.util.Map;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
@@ -48,7 +50,7 @@ public abstract class GameEntity extends GameObject implements IIdentifiable {
private int preventNextDamage = 0;
protected CardCollection attachedCards;
private Map<Card, Map<String, String>> preventionShieldsWithEffects = Maps.newTreeMap();
protected Map<CounterType, Integer> counters = Maps.newEnumMap(CounterType.class);
protected Map<CounterType, Integer> counters = Maps.newTreeMap();
protected GameEntity(int id0) {
id = id0;
@@ -315,7 +317,7 @@ public abstract class GameEntity extends GameObject implements IIdentifiable {
}
// enchanted means attached by Aura
return CardLists.count(attachedCards, CardPredicates.Presets.AURA) > 0;
return Iterables.any(attachedCards, CardPredicates.Presets.AURA);
}
public final boolean hasCardAttachment(Card c) {
@@ -453,6 +455,10 @@ public abstract class GameEntity extends GameObject implements IIdentifiable {
return value == null ? 0 : value;
}
public final int getCounters(final CounterEnumType counterType) {
return getCounters(CounterType.get(counterType));
}
public void setCounters(final CounterType counterType, final Integer num) {
if (num <= 0) {
counters.remove(counterType);
@@ -461,6 +467,10 @@ public abstract class GameEntity extends GameObject implements IIdentifiable {
}
}
public void setCounters(final CounterEnumType counterType, final Integer num) {
setCounters(CounterType.get(counterType), num);
}
abstract public void setCounters(final Map<CounterType, Integer> allCounters);
abstract public boolean canReceiveCounters(final CounterType type);
@@ -468,6 +478,16 @@ public abstract class GameEntity extends GameObject implements IIdentifiable {
abstract public void subtractCounter(final CounterType counterName, final int n);
abstract public void clearCounters();
public boolean canReceiveCounters(final CounterEnumType type) {
return canReceiveCounters(CounterType.get(type));
}
public int addCounter(final CounterEnumType counterType, final int n, final Player source, final boolean applyMultiplier, final boolean fireEvents, GameEntityCounterTable table) {
return addCounter(CounterType.get(counterType), n, source, applyMultiplier, fireEvents, table);
}
public void subtractCounter(final CounterEnumType counterName, final int n) {
subtractCounter(CounterType.get(counterName), n);
}
@Override
public final boolean equals(Object o) {

View File

@@ -95,7 +95,7 @@ public class GameLogFormatter extends IGameEventVisitor.Base<GameLogEntry> {
String action = event.sa.isSpell() ? localizer.getMessage("lblCast")
: event.sa.isTrigger() ? localizer.getMessage("lblTriggered")
: localizer.getMessage("lblActivated");
String object = event.sa.getStackDescription().startsWith("Morph ")
String object = event.si.getStackDescription().startsWith("Morph ")
? localizer.getMessage("lblMorph")
: event.sa.getHostCard().toString();

View File

@@ -53,8 +53,9 @@ public class AbilityUtils {
if ("ReplacedCounterType".equals(name)) {
name = (String) sa.getReplacingObject(AbilityKey.CounterType);
}
try {
//try {
counterType = CounterType.getType(name);
/*
} catch (Exception e) {
String type = sa.getSVar(name);
if (type.equals("")) {
@@ -66,6 +67,7 @@ public class AbilityUtils {
}
counterType = CounterType.getType(type);
}
//*/
return counterType;
}

View File

@@ -13,6 +13,7 @@ import com.google.common.collect.Lists;
import forge.GameCommand;
import forge.card.CardType;
import forge.game.Game;
import forge.game.GameEntity;
import forge.game.GameObject;
import forge.game.card.Card;
import forge.game.card.CardCollection;
@@ -34,7 +35,7 @@ import forge.util.collect.FCollection;
* <p>
* AbilityFactory_AlterLife class.
* </p>
*
*
* @author Forge
* @version $Id: AbilityFactoryAlterLife.java 17656 2012-10-22 19:32:56Z Max mtg $
*/
@@ -93,7 +94,7 @@ public abstract class SpellAbilityEffect {
final String baseDesc = this.getStackDescription(sa);
if (conditionDesc != null) {
sb.append(conditionDesc).append(" ");
}
}
sb.append(baseDesc);
}
@@ -125,7 +126,7 @@ public abstract class SpellAbilityEffect {
/**
* Append the description of a {@link SpellAbility} to a
* {@link StringBuilder}.
*
*
* @param sa
* a {@link SpellAbility}.
* @param sb
@@ -173,7 +174,7 @@ public abstract class SpellAbilityEffect {
private static CardCollection getCards(final boolean definedFirst, final String definedParam, final SpellAbility sa) {
final boolean useTargets = sa.usesTargeting() && (!definedFirst || !sa.hasParam(definedParam));
return useTargets ? new CardCollection(sa.getTargets().getTargetCards())
return useTargets ? new CardCollection(sa.getTargets().getTargetCards())
: AbilityUtils.getDefinedCards(sa.getHostCard(), sa.getParam(definedParam), sa);
}
@@ -196,10 +197,22 @@ public abstract class SpellAbilityEffect {
private static List<SpellAbility> getSpells(final boolean definedFirst, final String definedParam, final SpellAbility sa) {
final boolean useTargets = sa.usesTargeting() && (!definedFirst || !sa.hasParam(definedParam));
return useTargets ? Lists.newArrayList(sa.getTargets().getTargetSpells())
return useTargets ? Lists.newArrayList(sa.getTargets().getTargetSpells())
: AbilityUtils.getDefinedSpellAbilities(sa.getHostCard(), sa.getParam(definedParam), sa);
}
// Targets of card or player type
protected final static List<GameEntity> getTargetEntities(final SpellAbility sa) { return getEntities(false, "Defined", sa); }
protected final static List<GameEntity> getTargetEntities(final SpellAbility sa, final String definedParam) { return getEntities(false, definedParam, sa); }
protected final static List<GameEntity> getDefinedEntitiesOrTargeted(SpellAbility sa, final String definedParam) { return getEntities(true, definedParam, sa); }
private static List<GameEntity> getEntities(final boolean definedFirst, final String definedParam, final SpellAbility sa) {
final boolean useTargets = sa.usesTargeting() && (!definedFirst || !sa.hasParam(definedParam));
return useTargets ? Lists.newArrayList(sa.getTargets().getTargetEntities())
: AbilityUtils.getDefinedEntities(sa.getHostCard(), sa.getParam(definedParam), sa);
}
// Targets of unspecified type
protected final static List<GameObject> getTargets(final SpellAbility sa) { return getTargetables(false, "Defined", sa); }
protected final static List<GameObject> getTargets(final SpellAbility sa, final String definedParam) { return getTargetables(false, definedParam, sa); }
@@ -207,10 +220,10 @@ public abstract class SpellAbilityEffect {
private static List<GameObject> getTargetables(final boolean definedFirst, final String definedParam, final SpellAbility sa) {
final boolean useTargets = sa.usesTargeting() && (!definedFirst || !sa.hasParam(definedParam));
return useTargets ? Lists.newArrayList(sa.getTargets().getTargets())
return useTargets ? Lists.newArrayList(sa.getTargets().getTargets())
: AbilityUtils.getDefinedObjects(sa.getHostCard(), sa.getParam(definedParam), sa);
}
protected static void registerDelayedTrigger(final SpellAbility sa, String location, final List<Card> crds) {
boolean intrinsic = sa.isIntrinsic();
@@ -218,14 +231,14 @@ public abstract class SpellAbilityEffect {
boolean combat = location.endsWith("Combat");
String desc = sa.hasParam("AtEOTDesc") ? sa.getParam("AtEOTDesc") : "";
if (your) {
location = location.substring("Your".length());
}
if (combat) {
location = location.substring(0, location.length() - "Combat".length());
}
if (desc.isEmpty()) {
StringBuilder sb = new StringBuilder();
if (location.equals("Hand")) {
@@ -253,12 +266,12 @@ public abstract class SpellAbilityEffect {
StringBuilder delTrig = new StringBuilder();
delTrig.append("Mode$ Phase | Phase$ ");
delTrig.append(combat ? "EndCombat " : "End Of Turn ");
if (your) {
delTrig.append("| ValidPlayer$ You ");
}
delTrig.append("| TriggerDescription$ ").append(desc);
final Trigger trig = TriggerHandler.parseTrigger(delTrig.toString(), sa.getHostCard(), intrinsic);
for (final Card c : crds) {
trig.addRemembered(c);
@@ -289,7 +302,7 @@ public abstract class SpellAbilityEffect {
trig.setOverridingAbility(newSa);
sa.getActivatingPlayer().getGame().getTriggerHandler().registerDelayedTrigger(trig);
}
protected static void addSelfTrigger(final SpellAbility sa, String location, final Card card) {
String trigStr = "Mode$ Phase | Phase$ End of Turn | TriggerZones$ Battlefield " +
@@ -311,17 +324,17 @@ public abstract class SpellAbilityEffect {
card.setSVar("EndOfTurnLeavePlay", "AtEOT");
}
}
protected static void addForgetOnMovedTrigger(final Card card, final String zone) {
String trig = "Mode$ ChangesZone | ValidCard$ Card.IsRemembered | Origin$ " + zone + " | Destination$ Any | TriggerZones$ Command | Static$ True";
String forgetEffect = "DB$ Pump | ForgetObjects$ TriggeredCard";
String exileEffect = "DB$ ChangeZone | Defined$ Self | Origin$ Command | Destination$ Exile"
+ " | ConditionDefined$ Remembered | ConditionPresent$ Card | ConditionCompare$ EQ0";
SpellAbility saForget = AbilityFactory.getAbility(forgetEffect, card);
AbilitySub saExile = (AbilitySub) AbilityFactory.getAbility(exileEffect, card);
saForget.setSubAbility(saExile);
final Trigger parsedTrigger = TriggerHandler.parseTrigger(trig, card, true);
parsedTrigger.setOverridingAbility(saForget);
final Trigger addedTrigger = card.addTrigger(parsedTrigger);
@@ -336,22 +349,22 @@ public abstract class SpellAbilityEffect {
final Trigger addedTrigger = card.addTrigger(parsedTrigger);
addedTrigger.setIntrinsic(true);
}
protected static void addForgetCounterTrigger(final Card card, final String counterType) {
String trig = "Mode$ CounterRemoved | TriggerZones$ Command | ValidCard$ Card.IsRemembered | CounterType$ " + counterType + " | NewCounterAmount$ 0 | Static$ True";
String forgetEffect = "DB$ Pump | ForgetObjects$ TriggeredCard";
String exileEffect = "DB$ ChangeZone | Defined$ Self | Origin$ Command | Destination$ Exile"
+ " | ConditionDefined$ Remembered | ConditionPresent$ Card | ConditionCompare$ EQ0";
SpellAbility saForget = AbilityFactory.getAbility(forgetEffect, card);
AbilitySub saExile = (AbilitySub) AbilityFactory.getAbility(exileEffect, card);
saForget.setSubAbility(saExile);
final Trigger parsedTrigger = TriggerHandler.parseTrigger(trig, card, true);
parsedTrigger.setOverridingAbility(saForget);
final Trigger addedTrigger = card.addTrigger(parsedTrigger);
addedTrigger.setIntrinsic(true);
addedTrigger.setIntrinsic(true);
}
protected static void addLeaveBattlefieldReplacement(final Card card, final SpellAbility sa, final String zone) {
@@ -378,10 +391,10 @@ public abstract class SpellAbilityEffect {
game.getAction().moveTo(ZoneType.Command, eff, sa);
game.getTriggerHandler().clearSuppression(TriggerType.ChangesZone);
}
protected static void addLeaveBattlefieldReplacement(final Card eff, final String zone) {
final String repeffstr = "Event$ Moved | ValidCard$ Card.IsRemembered "
+ "| Origin$ Battlefield | ExcludeDestination$ " + zone
+ "| Origin$ Battlefield | ExcludeDestination$ " + zone
+ "| Description$ If Creature would leave the battlefield, "
+ " exile it instead of putting it anywhere else.";
String effect = "DB$ ChangeZone | Defined$ ReplacedCard | Origin$ Battlefield | Destination$ " + zone;
@@ -392,7 +405,7 @@ public abstract class SpellAbilityEffect {
re.setOverridingAbility(AbilityFactory.getAbility(effect, eff));
eff.addReplacementEffect(re);
}
// create a basic template for Effect to be used somewhere else
protected static Card createEffect(final SpellAbility sa, final Player controller, final String name,
final String image) {

View File

@@ -1,7 +1,11 @@
package forge.game.ability.effects;
import java.util.Map;
import org.apache.commons.lang3.mutable.MutableBoolean;
import com.google.common.collect.Maps;
import forge.game.Game;
import forge.game.GameEntityCounterTable;
import forge.game.ability.AbilityUtils;
@@ -10,6 +14,7 @@ import forge.game.card.CardCollectionView;
import forge.game.card.CardLists;
import forge.game.card.CardPredicates;
import forge.game.card.CardZoneTable;
import forge.game.card.CounterEnumType;
import forge.game.card.CounterType;
import forge.game.card.token.TokenInfo;
import forge.game.event.GameEventCombatChanged;
@@ -79,13 +84,16 @@ public class AmassEffect extends TokenEffectBase {
game.updateCombatForView();
game.fireEvent(new GameEventCombatChanged());
}
Map<String, Object> params = Maps.newHashMap();
params.put("CounterType", CounterType.get(CounterEnumType.P1P1));
params.put("Amount", 1);
CardCollectionView tgtCards = CardLists.getType(activator.getCardsIn(ZoneType.Battlefield), "Army");
tgtCards = pc.chooseCardsForEffect(tgtCards, sa, Localizer.getInstance().getMessage("lblChooseAnArmy"), 1, 1, false);
tgtCards = pc.chooseCardsForEffect(tgtCards, sa, Localizer.getInstance().getMessage("lblChooseAnArmy"), 1, 1, false, params);
GameEntityCounterTable table = new GameEntityCounterTable();
for(final Card tgtCard : tgtCards) {
tgtCard.addCounter(CounterType.P1P1, amount, activator, true, table);
tgtCard.addCounter(CounterEnumType.P1P1, amount, activator, true, table);
game.updateLastStateForCard(tgtCard);
if (remember) {

View File

@@ -35,7 +35,8 @@ public class AttachEffect extends SpellAbilityEffect {
sa.setHostCard(c);
}
Card source = sa.getHostCard();
final Card source = sa.getHostCard();
final Game game = source.getGame();
CardCollection attachments;
final List<GameObject> targets = getDefinedOrTargeted(sa, "Defined");
@@ -57,15 +58,22 @@ public class AttachEffect extends SpellAbilityEffect {
final Player p = sa.getActivatingPlayer();
if (sa.hasParam("Object")) {
attachments = AbilityUtils.getDefinedCards(source, sa.getParam("Object"), sa);
if (sa.hasParam("ChooseAnObject")) {
Card c = p.getController().chooseSingleEntityForEffect(attachments, sa, sa.getParam("ChooseAnObject"));
attachments.clear();
if (c != null) {
attachments.add(c);
}
if (sa.hasParam("Choices")) {
ZoneType choiceZone = ZoneType.Battlefield;
if (sa.hasParam("ChoiceZone")) {
choiceZone = ZoneType.smartValueOf(sa.getParam("ChoiceZone"));
}
String title = sa.hasParam("ChoiceTitle") ? sa.getParam("ChoiceTitle") : Localizer.getInstance().getMessage("lblChoose") + " ";
CardCollection choices = CardLists.getValidCards(game.getCardsIn(choiceZone), sa.getParam("Choices"), p, source, sa);
Card c = p.getController().chooseSingleEntityForEffect(choices, sa, title, null);
if (c == null) {
return;
}
attachments = new CardCollection(c);
} else if (sa.hasParam("Object")) {
attachments = AbilityUtils.getDefinedCards(source, sa.getParam("Object"), sa);
} else {
attachments = new CardCollection(source);
}
@@ -185,7 +193,8 @@ public class AttachEffect extends SpellAbilityEffect {
players.add(player);
}
}
final Player pa = p.getController().chooseSingleEntityForEffect(players, aura, Localizer.getInstance().getMessage("lblSelectAPlayerAttachSourceTo", CardTranslation.getTranslatedName(source.getName())));
final Player pa = p.getController().chooseSingleEntityForEffect(players, aura,
Localizer.getInstance().getMessage("lblSelectAPlayerAttachSourceTo", CardTranslation.getTranslatedName(source.getName())), null);
if (pa != null) {
handleAura(source, pa);
return true;
@@ -198,7 +207,8 @@ public class AttachEffect extends SpellAbilityEffect {
return false;
}
final Card o = p.getController().chooseSingleEntityForEffect(list, aura, Localizer.getInstance().getMessage("lblSelectACardAttachSourceTo", CardTranslation.getTranslatedName(source.getName())));
final Card o = p.getController().chooseSingleEntityForEffect(list, aura,
Localizer.getInstance().getMessage("lblSelectACardAttachSourceTo", CardTranslation.getTranslatedName(source.getName())), null);
if (o != null) {
handleAura(source, o);
//source.enchantEntity((Card) o);

View File

@@ -31,7 +31,7 @@ public class BondEffect extends SpellAbilityEffect {
Card partner = cards.getFirst();
// skip choice if only one card on list
if (cards.size() > 1) {
partner = sa.getActivatingPlayer().getController().chooseSingleEntityForEffect(cards, sa, Localizer.getInstance().getMessage("lblSelectACardPair"));
partner = sa.getActivatingPlayer().getController().chooseSingleEntityForEffect(cards, sa, Localizer.getInstance().getMessage("lblSelectACardPair"), null);
}
// pair choices together

View File

@@ -18,7 +18,10 @@ import forge.util.CardTranslation;
import org.apache.commons.lang3.StringUtils;
import com.google.common.collect.Maps;
import java.util.List;
import java.util.Map;
public class ChangeCombatantsEffect extends SpellAbilityEffect {
@@ -45,8 +48,12 @@ public class ChangeCombatantsEffect extends SpellAbilityEffect {
final Combat combat = game.getCombat();
final GameEntity originalDefender = combat.getDefenderByAttacker(c);
final FCollectionView<GameEntity> defs = combat.getDefenders();
final GameEntity defender = sa.getActivatingPlayer().getController().chooseSingleEntityForEffect(defs, sa,
Localizer.getInstance().getMessage("lblChooseDefenderToAttackWithCard", CardTranslation.getTranslatedName(c.getName())), false);
String title = Localizer.getInstance().getMessage("lblChooseDefenderToAttackWithCard", CardTranslation.getTranslatedName(c.getName()));
Map<String, Object> params = Maps.newHashMap();
params.put("Attacker", c);
final GameEntity defender = sa.getActivatingPlayer().getController().chooseSingleEntityForEffect(defs, sa, title, false, params);
if (originalDefender != null && !originalDefender.equals(defender)) {
AttackingBand ab = combat.getBandOfAttacker(c);
if (ab != null) {

View File

@@ -507,7 +507,9 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
list = CardLists.getValidCards(game.getCardsIn(ZoneType.Battlefield), sa.getParam("AttachedTo"), tgtC.getController(), tgtC);
}
if (!list.isEmpty()) {
Card attachedTo = player.getController().chooseSingleEntityForEffect(list, sa, Localizer.getInstance().getMessage("lblSelectACardAttachSourceTo", tgtC.toString()));
Map<String, Object> params = Maps.newHashMap();
params.put("Attach", tgtC);
Card attachedTo = player.getController().chooseSingleEntityForEffect(list, sa, Localizer.getInstance().getMessage("lblSelectACardAttachSourceTo", tgtC.toString()), params);
tgtC.attachToEntity(attachedTo);
} else { // When it should enter the battlefield attached to an illegal permanent it fails
continue;
@@ -517,7 +519,9 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
if (sa.hasParam("AttachedToPlayer")) {
FCollectionView<Player> list = AbilityUtils.getDefinedPlayers(hostCard, sa.getParam("AttachedToPlayer"), sa);
if (!list.isEmpty()) {
Player attachedTo = player.getController().chooseSingleEntityForEffect(list, sa, Localizer.getInstance().getMessage("lblSelectAPlayerAttachSourceTo", tgtC.toString()));
Map<String, Object> params = Maps.newHashMap();
params.put("Attach", tgtC);
Player attachedTo = player.getController().chooseSingleEntityForEffect(list, sa, Localizer.getInstance().getMessage("lblSelectAPlayerAttachSourceTo", tgtC.toString()), params);
tgtC.attachToEntity(attachedTo);
}
else { // When it should enter the battlefield attached to an illegal player it fails
@@ -578,7 +582,10 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
}
}
} else {
defender = player.getController().chooseSingleEntityForEffect(e, sa, Localizer.getInstance().getMessage("lblChooseDefenderToAttackWithCard", CardTranslation.getTranslatedName(movedCard.getName())));
String title = Localizer.getInstance().getMessage("lblChooseDefenderToAttackWithCard", CardTranslation.getTranslatedName(movedCard.getName()));
Map<String, Object> params = Maps.newHashMap();
params.put("Attacker", movedCard);
defender = player.getController().chooseSingleEntityForEffect(e, sa, title, params);
}
if (defender != null) {
combat.addAttacker(movedCard, defender);
@@ -1039,7 +1046,10 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
if (!list.isEmpty()) {
Card attachedTo = null;
if (list.size() > 1) {
attachedTo = decider.getController().chooseSingleEntityForEffect(list, sa, Localizer.getInstance().getMessage("lblSelectACardAttachSourceTo", CardTranslation.getTranslatedName(c.getName())));
String title = Localizer.getInstance().getMessage("lblSelectACardAttachSourceTo", CardTranslation.getTranslatedName(c.getName()));
Map<String, Object> params = Maps.newHashMap();
params.put("Attach", c);
attachedTo = decider.getController().chooseSingleEntityForEffect(list, sa, title, params);
}
else {
attachedTo = list.get(0);
@@ -1057,7 +1067,10 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
if (sa.hasParam("AttachedToPlayer")) {
FCollectionView<Player> list = AbilityUtils.getDefinedPlayers(source, sa.getParam("AttachedToPlayer"), sa);
if (!list.isEmpty()) {
Player attachedTo = player.getController().chooseSingleEntityForEffect(list, sa, Localizer.getInstance().getMessage("lblSelectACardAttachSourceTo", CardTranslation.getTranslatedName(c.getName())));
String title = Localizer.getInstance().getMessage("lblSelectACardAttachSourceTo", CardTranslation.getTranslatedName(c.getName()));
Map<String, Object> params = Maps.newHashMap();
params.put("Attach", c);
Player attachedTo = player.getController().chooseSingleEntityForEffect(list, sa, title, params);
c.attachToEntity(attachedTo);
}
else { // When it should enter the battlefield attached to an illegal permanent it fails
@@ -1080,7 +1093,10 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
}
}
} else {
defender = player.getController().chooseSingleEntityForEffect(e, sa, Localizer.getInstance().getMessage("lblChooseDefenderToAttackWithCard", CardTranslation.getTranslatedName(c.getName())));
String title = Localizer.getInstance().getMessage("lblChooseDefenderToAttackWithCard", CardTranslation.getTranslatedName(c.getName()));
Map<String, Object> params = Maps.newHashMap();
params.put("Attacker", c);
defender = player.getController().chooseSingleEntityForEffect(e, sa, title, params);
}
if (defender != null) {
combat.addAttacker(c, defender);

View File

@@ -189,7 +189,7 @@ public class CharmEffect extends SpellAbilityEffect {
//String choosers = sa.getParam("Chooser");
FCollection<Player> opponents = activator.getOpponents(); // all cards have Choser$ Opponent, so it's hardcoded here
chooser = activator.getController().chooseSingleEntityForEffect(opponents, sa, "Choose an opponent");
chooser = activator.getController().chooseSingleEntityForEffect(opponents, sa, "Choose an opponent", null);
source.setChosenPlayer(chooser);
}

View File

@@ -84,7 +84,7 @@ public class ChooseCardEffect extends SpellAbilityEffect {
final CardCollectionView cl = CardLists.getType(land, type);
if (!cl.isEmpty()) {
final String prompt = Localizer.getInstance().getMessage("lblChoose") + " " + Lang.nounWithAmount(1, type);
Card c = p.getController().chooseSingleEntityForEffect(cl, sa, prompt, false);
Card c = p.getController().chooseSingleEntityForEffect(cl, sa, prompt, false, null);
if (c != null) {
chosen.add(c);
}
@@ -100,7 +100,7 @@ public class ChooseCardEffect extends SpellAbilityEffect {
while (!creature.isEmpty()) {
Card c = p.getController().chooseSingleEntityForEffect(creature, sa,
Localizer.getInstance().getMessage("lblSelectCreatureWithTotalPowerLessOrEqualTo", (totP - chosenP - negativeNum))
+ "\r\n(" + Localizer.getInstance().getMessage("lblSelected") + ":" + chosenPool + ")\r\n(" + Localizer.getInstance().getMessage("lblTotalPowerNum", chosenP) + ")", chosenP <= totP);
+ "\r\n(" + Localizer.getInstance().getMessage("lblSelected") + ":" + chosenPool + ")\r\n(" + Localizer.getInstance().getMessage("lblTotalPowerNum", chosenP) + ")", chosenP <= totP, null);
if (c == null) {
if (p.getController().confirmAction(sa, PlayerActionConfirmMode.OptionalChoose, Localizer.getInstance().getMessage("lblCancelChooseConfirm"))) {
break;
@@ -120,7 +120,7 @@ public class ChooseCardEffect extends SpellAbilityEffect {
Aggregates.random(choices, validAmount, chosen);
} else {
String title = sa.hasParam("ChoiceTitle") ? sa.getParam("ChoiceTitle") : Localizer.getInstance().getMessage("lblChooseaCard") + " ";
chosen.addAll(p.getController().chooseCardsForEffect(choices, sa, title, minAmount, validAmount, !sa.hasParam("Mandatory")));
chosen.addAll(p.getController().chooseCardsForEffect(choices, sa, title, minAmount, validAmount, !sa.hasParam("Mandatory"), null));
}
}
}

View File

@@ -8,8 +8,7 @@ import forge.game.card.Card;
import forge.game.event.GameEventCardModeChosen;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import forge.util.MyRandom;
import forge.util.Localizer;
import forge.util.Aggregates;
import java.util.List;
@@ -33,6 +32,7 @@ public class ChooseGenericEffect extends SpellAbilityEffect {
final List<SpellAbility> abilities = Lists.newArrayList(sa.getAdditionalAbilityList("Choices"));
final SpellAbility fallback = sa.getAdditionalAbility("FallbackAbility");
final int amount = AbilityUtils.calculateAmount(host, sa.getParamOrDefault("ChoiceAmount", "1"), sa);
final List<Player> tgtPlayers = getDefinedPlayersOrTargeted(sa);
@@ -43,8 +43,7 @@ public class ChooseGenericEffect extends SpellAbilityEffect {
for (SpellAbility saChoice : abilities) {
if (!saChoice.getRestrictions().checkOtherRestrictions(host, saChoice, sa.getActivatingPlayer()) ) {
saToRemove.add(saChoice);
} else if (saChoice.hasParam("UnlessCost") &&
"Player.IsRemembered".equals(saChoice.getParam("Defined"))) {
} else if (saChoice.hasParam("UnlessCost")) {
String unlessCost = saChoice.getParam("UnlessCost");
// Sac a permanent in presence of Sigarda, Host of Herons
// TODO: generalize this by testing if the unless cost can be paid
@@ -65,27 +64,26 @@ public class ChooseGenericEffect extends SpellAbilityEffect {
continue;
}
SpellAbility chosenSA = null;
List<SpellAbility> chosenSAs = Lists.newArrayList();
if (sa.hasParam("AtRandom")) {
if (!abilities.isEmpty()) {
chosenSA = abilities.get(MyRandom.getRandom().nextInt(abilities.size()));
}
Aggregates.random(abilities, amount, chosenSAs);
} else {
chosenSA = p.getController().chooseSingleSpellForEffect(abilities, sa, Localizer.getInstance().getMessage("lblChooseOne"),
ImmutableMap.of());
chosenSAs = p.getController().chooseSpellAbilitiesForEffect(abilities, sa, "Choose", amount, ImmutableMap.of());
}
if (chosenSA != null) {
String chosenValue = chosenSA.getDescription();
if (sa.hasParam("ShowChoice")) {
boolean dontNotifySelf = sa.getParam("ShowChoice").equals("ExceptSelf");
p.getGame().getAction().nofityOfValue(sa, p, chosenValue, dontNotifySelf ? sa.getActivatingPlayer() : null);
if (!chosenSAs.isEmpty()) {
for (SpellAbility chosenSA : chosenSAs) {
String chosenValue = chosenSA.getDescription();
if (sa.hasParam("ShowChoice")) {
boolean dontNotifySelf = sa.getParam("ShowChoice").equals("ExceptSelf");
p.getGame().getAction().nofityOfValue(sa, p, chosenValue, dontNotifySelf ? sa.getActivatingPlayer() : null);
}
if (sa.hasParam("SetChosenMode")) {
sa.getHostCard().setChosenMode(chosenValue);
}
p.getGame().fireEvent(new GameEventCardModeChosen(p, host.getName(), chosenValue, sa.hasParam("ShowChoice")));
AbilityUtils.resolve(chosenSA);
}
if (sa.hasParam("SetChosenMode")) {
sa.getHostCard().setChosenMode(chosenValue);
}
p.getGame().fireEvent(new GameEventCardModeChosen(p, host.getName(), chosenValue, sa.hasParam("ShowChoice")));
AbilityUtils.resolve(chosenSA);
} else {
// no choices are valid, e.g. maybe all Unless costs are unpayable
if (fallback != null) {

View File

@@ -51,7 +51,7 @@ public class ChoosePlayerEffect extends SpellAbilityEffect {
if (random) {
chosen = choices.isEmpty() ? null : Aggregates.random(choices);
} else {
chosen = choices.isEmpty() ? null : p.getController().chooseSingleEntityForEffect(choices, sa, choiceDesc);
chosen = choices.isEmpty() ? null : p.getController().chooseSingleEntityForEffect(choices, sa, choiceDesc, null);
}
if( null != chosen ) {
card.setChosenPlayer(chosen);

View File

@@ -138,7 +138,7 @@ public class ChooseSourceEffect extends SpellAbilityEffect {
final String choiceTitle = sa.hasParam("ChoiceTitle") ? sa.getParam("ChoiceTitle") : Localizer.getInstance().getMessage("lblChooseSource") + " ";
Card o = null;
do {
o = p.getController().chooseSingleEntityForEffect(sourcesToChooseFrom, sa, choiceTitle);
o = p.getController().chooseSingleEntityForEffect(sourcesToChooseFrom, sa, choiceTitle, null);
} while (o == null);
chosen.add(o);
sourcesToChooseFrom.remove(o);

View File

@@ -75,7 +75,7 @@ public class ClashEffect extends SpellAbilityEffect {
*/
final Card source = sa.getHostCard();
final Player player = source.getController();
final Player opponent = sa.getActivatingPlayer().getController().chooseSingleEntityForEffect(player.getOpponents(), sa, Localizer.getInstance().getMessage("lblChooseOpponent")) ;
final Player opponent = sa.getActivatingPlayer().getController().chooseSingleEntityForEffect(player.getOpponents(), sa, Localizer.getInstance().getMessage("lblChooseOpponent"), null);
final ZoneType lib = ZoneType.Library;
if (sa.hasParam("RememberClasher")) {

View File

@@ -81,7 +81,7 @@ public class CloneEffect extends SpellAbilityEffect {
choices = CardLists.getValidCards(choices, sa.getParam("Choices"), activator, host);
String title = sa.hasParam("ChoiceTitle") ? sa.getParam("ChoiceTitle") : Localizer.getInstance().getMessage("lblChooseaCard") + " ";
cardToCopy = activator.getController().chooseSingleEntityForEffect(choices, sa, title, false);
cardToCopy = activator.getController().chooseSingleEntityForEffect(choices, sa, title, false, null);
} else if (sa.hasParam("Defined")) {
List<Card> cloneSources = AbilityUtils.getDefinedCards(host, sa.getParam("Defined"), sa);
if (!cloneSources.isEmpty()) {

View File

@@ -36,9 +36,9 @@ public class ControlExchangeVariantEffect extends SpellAbilityEffect {
CardCollectionView list2 = AbilityUtils.filterListByType(player2.getCardsIn(zone), type, sa);
int max = Math.min(list1.size(), list2.size());
// choose the same number of cards
CardCollectionView chosen1 = activator.getController().chooseCardsForEffect(list1, sa, Localizer.getInstance().getMessage("lblChooseCards") + ":" + player1, 0, max, true);
CardCollectionView chosen1 = activator.getController().chooseCardsForEffect(list1, sa, Localizer.getInstance().getMessage("lblChooseCards") + ":" + player1, 0, max, true, null);
int num = chosen1.size();
CardCollectionView chosen2 = activator.getController().chooseCardsForEffect(list2, sa, Localizer.getInstance().getMessage("lblChooseCards") + ":" + player2, num, num, true);
CardCollectionView chosen2 = activator.getController().chooseCardsForEffect(list2, sa, Localizer.getInstance().getMessage("lblChooseCards") + ":" + player2, num, num, true, null);
// check all cards can be controlled by the other player
for (final Card c : chosen1) {
if (!c.canBeControlledBy(player2)) {

View File

@@ -2,8 +2,10 @@ package forge.game.ability.effects;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import forge.GameCommand;
import forge.game.Game;
@@ -211,9 +213,10 @@ public class ControlGainEffect extends SpellAbilityEffect {
final Combat combat = game.getCombat();
if ( null != combat ) {
final FCollectionView<GameEntity> e = combat.getDefenders();
final GameEntity defender = sa.getActivatingPlayer().getController().chooseSingleEntityForEffect(e, sa,
Localizer.getInstance().getMessage("lblChooseDefenderToAttackWithCard", CardTranslation.getTranslatedName(tgtC.getName())));
String title = Localizer.getInstance().getMessage("lblChooseDefenderToAttackWithCard", CardTranslation.getTranslatedName(tgtC.getName()));
Map<String, Object> params = Maps.newHashMap();
params.put("Attacker", tgtC);
final GameEntity defender = sa.getActivatingPlayer().getController().chooseSingleEntityForEffect(e, sa, title, params);
if (defender != null) {
combat.addAttacker(tgtC, defender);

View File

@@ -143,7 +143,7 @@ public class CopyPermanentEffect extends TokenEffectBase {
if (!choices.isEmpty()) {
String title = sa.hasParam("ChoiceTitle") ? sa.getParam("ChoiceTitle") : Localizer.getInstance().getMessage("lblChooseaCard") +" ";
Card choosen = chooser.getController().chooseSingleEntityForEffect(choices, sa, title, false);
Card choosen = chooser.getController().chooseSingleEntityForEffect(choices, sa, title, false, null);
if (choosen != null) {
tgtCards.add(choosen);

View File

@@ -146,7 +146,7 @@ public class CopySpellAbilityEffect extends SpellAbilityEffect {
valid.remove(originalTarget);
mayChooseNewTargets = false;
if (sa.hasParam("ChooseOnlyOne")) {
Card choice = controller.getController().chooseSingleEntityForEffect(valid, sa, Localizer.getInstance().getMessage("lblChooseOne"));
Card choice = controller.getController().chooseSingleEntityForEffect(valid, sa, Localizer.getInstance().getMessage("lblChooseOne"), null);
SpellAbility copy = CardFactory.copySpellAbilityAndPossiblyHost(card, chosenSA.getHostCard(), chosenSA, true);
resetFirstTargetOnCopy(copy, choice, targetedSA);
copies.add(copy);

View File

@@ -7,6 +7,7 @@ import forge.game.ability.SpellAbilityEffect;
import forge.game.card.Card;
import forge.game.card.CardCollectionView;
import forge.game.card.CardLists;
import forge.game.card.CardPredicates;
import forge.game.card.CounterType;
import forge.game.player.Player;
import forge.game.player.PlayerController;
@@ -26,17 +27,30 @@ public class CountersMoveEffect extends SpellAbilityEffect {
@Override
protected String getStackDescription(SpellAbility sa) {
final Card host = sa.getHostCard();
final StringBuilder sb = new StringBuilder();
Card source = null;
List<Card> srcCards = getDefinedCardsOrTargeted(sa, "Source");
if (srcCards.size() > 0) {
source = srcCards.get(0);
}
final List<Card> tgtCards = getDefinedCardsOrTargeted(sa);
Card source = null;
if (sa.usesTargeting() && sa.getTargetRestrictions().getMinTargets(host, sa) == 2) {
if (tgtCards.size() < 2) {
return "";
}
source = tgtCards.remove(0);
} else {
List<Card> srcCards = getDefinedCardsOrTargeted(sa, "Source");
if (srcCards.size() > 0) {
source = srcCards.get(0);
}
}
final String countername = sa.getParam("CounterType");
final int amount = AbilityUtils.calculateAmount(sa.getHostCard(), sa.getParam("CounterNum"), sa);
final String counterAmount = sa.getParam("CounterNum");
int amount = 0;
if (!"Any".equals(counterAmount) && !"All".equals(counterAmount)) {
amount = AbilityUtils.calculateAmount(sa.getHostCard(), sa.getParam("CounterNum"), sa);
}
sb.append("Move ");
if ("Any".matches(countername)) {
@@ -45,16 +59,18 @@ public class CountersMoveEffect extends SpellAbilityEffect {
} else {
sb.append(amount).append(" ").append(" counter");
}
} else {
} else if ("All".equals(countername)) {
sb.append("all counter");
} else {
sb.append(amount).append(" ").append(countername).append(" counter");
}
if (amount != 1) {
sb.append("s");
}
sb.append(" from ").append(source).append(" to ");
try{
try {
sb.append(tgtCards.get(0));
} catch(final IndexOutOfBoundsException exception) {
} catch (final IndexOutOfBoundsException exception) {
System.out.println(TextUtil.concatWithSpace("Somehow this is missing targets?", source.toString()));
}
@@ -70,12 +86,12 @@ public class CountersMoveEffect extends SpellAbilityEffect {
final Player player = sa.getActivatingPlayer();
final PlayerController pc = player.getController();
final Game game = host.getGame();
CounterType cType = null;
try {
cType = AbilityUtils.getCounterType(counterName, sa);
} catch (Exception e) {
if (!counterName.matches("Any")) {
if (!counterName.matches("Any") && !counterName.matches("All")) {
try {
cType = AbilityUtils.getCounterType(counterName, sa);
} catch (Exception e) {
System.out.println("Counter type doesn't match, nor does an SVar exist with the type name.");
return;
}
@@ -89,17 +105,12 @@ public class CountersMoveEffect extends SpellAbilityEffect {
CardCollectionView srcCards = game.getCardsIn(ZoneType.Battlefield);
srcCards = CardLists.getValidCards(srcCards, sa.getParam("ValidSource"), player, host, sa);
List<Card> tgtCards = getDefinedCardsOrTargeted(sa);
if (tgtCards.isEmpty()) {
return;
}
Card dest = tgtCards.get(0);
// target cant receive this counter type
if (!dest.canReceiveCounters(cType)) {
return;
}
Card cur = game.getCardState(dest, null);
if (cur == null || !cur.equalsWithTimestamp(dest)) {
// Test to see if the card we're trying to add is in the expected state
@@ -107,48 +118,57 @@ public class CountersMoveEffect extends SpellAbilityEffect {
}
dest = cur;
int csum = 0;
Map<String, Object> params = Maps.newHashMap();
params.put("Target", dest);
// only select cards if the counterNum is any
if (counterNum.equals("Any")) {
srcCards = player.getController().chooseCardsForEffect(srcCards, sa, Localizer.getInstance().getMessage("lblChooseTakeCountersCard", cType.getName()), 0, srcCards.size(), true);
if ("All".equals(counterName)) {
// only select cards if the counterNum is any
if (counterNum.equals("Any")) {
srcCards = CardLists.filter(srcCards, CardPredicates.hasCounters());
srcCards = player.getController().chooseCardsForEffect(srcCards, sa,
Localizer.getInstance().getMessage("lblChooseTakeCountersCard", "any"), 0,
srcCards.size(), true, params);
}
} else {
// target cant receive this counter type
if (!dest.canReceiveCounters(cType)) {
return;
}
srcCards = CardLists.filter(srcCards, CardPredicates.hasCounter(cType));
// only select cards if the counterNum is any
if (counterNum.equals("Any")) {
params.put("CounterType", cType);
srcCards = player.getController().chooseCardsForEffect(srcCards, sa,
Localizer.getInstance().getMessage("lblChooseTakeCountersCard", cType.getName()), 0,
srcCards.size(), true, params);
}
}
Map<CounterType, Integer> countersToAdd = Maps.newHashMap();
for (Card src : srcCards) {
// rule 121.5: If the first and second objects are the same object, nothing happens
// rule 121.5: If the first and second objects are the same object, nothing
// happens
if (src.equals(dest)) {
continue;
}
int cmax = src.getCounters(cType);
if (cmax <= 0) {
continue;
}
int cnum = 0;
if (counterNum.equals("All")) {
cnum = cmax;
} else if (counterNum.equals("Any")) {
Map<String, Object> params = Maps.newHashMap();
params.put("CounterType", cType);
params.put("Source", src);
params.put("Target", dest);
cnum = player.getController().chooseNumber(sa, Localizer.getInstance().getMessage("lblTakeHowManyTargetCounterFromCard", cType.getName(), CardTranslation.getTranslatedName(src.getName())), 0, cmax, params);
if ("All".equals(counterName)) {
final Map<CounterType, Integer> tgtCounters = Maps.newHashMap(src.getCounters());
for (Map.Entry<CounterType, Integer> e : tgtCounters.entrySet()) {
removeCounter(sa, src, dest, e.getKey(), counterNum, countersToAdd);
}
} else {
cnum = AbilityUtils.calculateAmount(host, counterNum, sa);
}
if(cnum > 0) {
src.subtractCounter(cType, cnum);
game.updateLastStateForCard(src);
csum += cnum;
removeCounter(sa, src, dest, cType, counterNum, countersToAdd);
}
}
for (Map.Entry<CounterType, Integer> e : countersToAdd.entrySet()) {
dest.addCounter(e.getKey(), e.getValue(), player, true, table);
}
if (csum > 0) {
dest.addCounter(cType, csum, player, true, table);
game.updateLastStateForCard(dest);
table.triggerCountersPutAll(game);
}
game.updateLastStateForCard(dest);
table.triggerCountersPutAll(game);
return;
} else if (sa.hasParam("ValidDefined")) {
// one Source to many Targets
@@ -163,18 +183,25 @@ public class CountersMoveEffect extends SpellAbilityEffect {
if (source.getCounters(cType) <= 0) {
return;
}
Map<String, Object> params = Maps.newHashMap();
params.put("CounterType", cType);
params.put("Source", source);
CardCollectionView tgtCards = game.getCardsIn(ZoneType.Battlefield);
tgtCards = CardLists.getValidCards(tgtCards, sa.getParam("ValidDefined"), player, host, sa);
if (counterNum.equals("Any")) {
tgtCards = player.getController().chooseCardsForEffect(tgtCards, sa,
Localizer.getInstance().getMessage("lblChooseCardToGetCountersFrom", cType.getName(), CardTranslation.getTranslatedName(source.getName())), 0, tgtCards.size(), true);
tgtCards = player.getController().chooseCardsForEffect(
tgtCards, sa, Localizer.getInstance().getMessage("lblChooseCardToGetCountersFrom",
cType.getName(), CardTranslation.getTranslatedName(source.getName())),
0, tgtCards.size(), true, params);
}
boolean updateSource = false;
for (final Card dest : tgtCards) {
// rule 121.5: If the first and second objects are the same object, nothing happens
// rule 121.5: If the first and second objects are the same object, nothing
// happens
if (source.equals(dest)) {
continue;
}
@@ -188,11 +215,14 @@ public class CountersMoveEffect extends SpellAbilityEffect {
continue;
}
Map<String, Object> params = Maps.newHashMap();
params = Maps.newHashMap();
params.put("CounterType", cType);
params.put("Source", source);
params.put("Target", cur);
int cnum = player.getController().chooseNumber(sa, Localizer.getInstance().getMessage("lblPutHowManyTargetCounterOnCard", cType.getName(), CardTranslation.getTranslatedName(cur.getName())), 0, source.getCounters(cType), params);
int cnum = player.getController().chooseNumber(sa,
Localizer.getInstance().getMessage("lblPutHowManyTargetCounterOnCard", cType.getName(),
CardTranslation.getTranslatedName(cur.getName())),
0, source.getCounters(cType), params);
if (cnum > 0) {
source.subtractCounter(cType, cnum);
@@ -207,96 +237,133 @@ public class CountersMoveEffect extends SpellAbilityEffect {
table.triggerCountersPutAll(game);
}
return;
}
Card source = null;
int cntToMove = 0;
List<Card> srcCards = getDefinedCardsOrTargeted(sa, "Source");
if (srcCards.size() > 0) {
source = srcCards.get(0);
}
// source doesn't has any counters to move
if (!source.hasCounters()) {
return;
}
if (!counterNum.equals("All") && !counterNum.equals("Any")) {
cntToMove = AbilityUtils.calculateAmount(host, counterNum, sa);
} else {
cntToMove = source.getCounters(cType);
}
List<Card> tgtCards = getDefinedCardsOrTargeted(sa);
for (final Card dest : tgtCards) {
if (null != source && null != dest) {
// rule 121.5: If the first and second objects are the same object, nothing happens
if (source.equals(dest)) {
continue;
Card source = null;
List<Card> tgtCards = getDefinedCardsOrTargeted(sa);
// special logic for moving from Target to Target
if (sa.usesTargeting() && sa.getTargetRestrictions().getMinTargets(host, sa) == 2) {
if (tgtCards.size() < 2) {
return;
}
Card cur = game.getCardState(dest, null);
if (cur == null || !cur.equalsWithTimestamp(dest)) {
// Test to see if the card we're trying to add is in the expected state
continue;
source = tgtCards.remove(0);
} else {
List<Card> srcCards = getDefinedCardsOrTargeted(sa, "Source");
if (srcCards.size() > 0) {
source = srcCards.get(0);
}
}
if (source == null) {
return;
}
if (!"Any".matches(counterName)) {
if (!cur.canReceiveCounters(cType)) {
// source doesn't has any counters to move
if (!source.hasCounters()) {
return;
}
for (final Card dest : tgtCards) {
if (null != source && null != dest) {
// rule 121.5: If the first and second objects are the same object, nothing
// happens
if (source.equals(dest)) {
continue;
}
Card cur = game.getCardState(dest, null);
if (cur == null || !cur.equalsWithTimestamp(dest)) {
// Test to see if the card we're trying to add is in the expected state
continue;
}
if (counterNum.equals("Any")) {
Map<String, Object> params = Maps.newHashMap();
params.put("CounterType", cType);
params.put("Source", source);
params.put("Target", cur);
cntToMove = pc.chooseNumber(sa, Localizer.getInstance().getMessage("lblTakeHowManyTargetCounterFromCard", cType.getName(), CardTranslation.getTranslatedName(source.getName())), 0, cntToMove, params);
}
if (source.getCounters(cType) >= cntToMove) {
source.subtractCounter(cType, cntToMove);
cur.addCounter(cType, cntToMove, player, true, table);
game.updateLastStateForCard(cur);
}
} else {
// any counterType currently only Leech Bonder
final Map<CounterType, Integer> tgtCounters = source.getCounters();
final List<CounterType> typeChoices = Lists.newArrayList();
// get types of counters
for (CounterType ct : tgtCounters.keySet()) {
if (dest.canReceiveCounters(ct)) {
typeChoices.add(ct);
Map<CounterType, Integer> countersToAdd = Maps.newHashMap();
if ("All".equals(counterName)) {
final Map<CounterType, Integer> tgtCounters = Maps.newHashMap(source.getCounters());
for (Map.Entry<CounterType, Integer> e : tgtCounters.entrySet()) {
removeCounter(sa, source, cur, e.getKey(), counterNum, countersToAdd);
}
}
if (typeChoices.isEmpty()) {
return;
} else if ("Any".equals(counterName)) {
// any counterType currently only Leech Bonder
final Map<CounterType, Integer> tgtCounters = source.getCounters();
final List<CounterType> typeChoices = Lists.newArrayList();
// get types of counters
for (CounterType ct : tgtCounters.keySet()) {
if (dest.canReceiveCounters(ct)) {
typeChoices.add(ct);
}
}
if (typeChoices.isEmpty()) {
return;
}
Map<String, Object> params = Maps.newHashMap();
params.put("Source", source);
params.put("Target", dest);
String title = Localizer.getInstance().getMessage("lblSelectRemoveCounterType");
CounterType chosenType = pc.chooseCounterType(typeChoices, sa, title, params);
removeCounter(sa, source, cur, chosenType, counterNum, countersToAdd);
} else {
if (!cur.canReceiveCounters(cType)) {
continue;
}
removeCounter(sa, source, cur, cType, counterNum, countersToAdd);
}
Map<String, Object> params = Maps.newHashMap();
params.put("Source", source);
params.put("Target", dest);
String title = Localizer.getInstance().getMessage("lblSelectRemoveCounterType");
CounterType chosenType = pc.chooseCounterType(typeChoices, sa, title, params);
params = Maps.newHashMap();
params.put("CounterType", chosenType);
params.put("Source", source);
params.put("Target", dest);
int chosenAmount = pc.chooseNumber(sa, Localizer.getInstance().getMessage("lblTakeHowManyTargetCounters", chosenType.getName()),
0, Math.min(tgtCounters.get(chosenType), cntToMove), params);
if (chosenAmount > 0) {
dest.addCounter(chosenType, chosenAmount, player, true, table);
source.subtractCounter(chosenType, chosenAmount);
game.updateLastStateForCard(dest);
cntToMove -= chosenAmount;
for (Map.Entry<CounterType, Integer> e : countersToAdd.entrySet()) {
cur.addCounter(e.getKey(), e.getValue(), player, true, table);
}
game.updateLastStateForCard(cur);
}
}
// update source
game.updateLastStateForCard(source);
}
// update source
game.updateLastStateForCard(source);
table.triggerCountersPutAll(game);
} // moveCounterResolve
protected void removeCounter(SpellAbility sa, final Card src, final Card dest, CounterType cType, String counterNum, Map<CounterType, Integer> countersToAdd) {
final Card host = sa.getHostCard();
//final String counterNum = sa.getParam("CounterNum");
final Player player = sa.getActivatingPlayer();
final PlayerController pc = player.getController();
final Game game = host.getGame();
// rule 121.5: If the first and second objects are the same object, nothing
// happens
if (src.equals(dest)) {
return;
}
if (!dest.canReceiveCounters(cType)) {
return;
}
int cmax = src.getCounters(cType);
if (cmax <= 0) {
return;
}
int cnum = 0;
if (counterNum.equals("All")) {
cnum = cmax;
} else if (counterNum.equals("Any")) {
Map<String, Object> params = Maps.newHashMap();
params.put("CounterType", cType);
params.put("Source", src);
params.put("Target", dest);
cnum = pc.chooseNumber(
sa, Localizer.getInstance().getMessage("lblTakeHowManyTargetCounterFromCard",
cType.getName(), CardTranslation.getTranslatedName(src.getName())),
0, cmax, params);
} else {
cnum = Math.min(cmax, AbilityUtils.calculateAmount(host, counterNum, sa));
}
if (cnum > 0) {
src.subtractCounter(cType, cnum);
game.updateLastStateForCard(src);
countersToAdd.put(cType, (countersToAdd.containsKey(cType) ? countersToAdd.get(cType) : 0) + cnum);
}
}
}

View File

@@ -43,7 +43,7 @@ public class CountersProliferateEffect extends SpellAbilityEffect {
list.addAll(CardLists.filter(game.getCardsIn(ZoneType.Battlefield), CardPredicates.hasCounters()));
List<GameEntity> result = pc.chooseEntitiesForEffect(list, 0, list.size(), null, sa,
Localizer.getInstance().getMessage("lblChooseProliferateTarget"), p);
Localizer.getInstance().getMessage("lblChooseProliferateTarget"), p, null);
GameEntityCounterTable table = new GameEntityCounterTable();
for (final GameEntity ge : result) {

View File

@@ -18,7 +18,7 @@ public class CountersPutAllEffect extends SpellAbilityEffect {
protected String getStackDescription(SpellAbility sa) {
final StringBuilder sb = new StringBuilder();
final CounterType cType = CounterType.valueOf(sa.getParam("CounterType"));
final CounterType cType = CounterType.getType(sa.getParam("CounterType"));
final int amount = AbilityUtils.calculateAmount(sa.getHostCard(), sa.getParam("CounterNum"), sa);
final String zone = sa.hasParam("ValidZone") ? sa.getParam("ValidZone") : "Battlefield";
@@ -67,7 +67,7 @@ public class CountersPutAllEffect extends SpellAbilityEffect {
GameEntityCounterTable table = new GameEntityCounterTable();
for (final Card tgtCard : cards) {
boolean inBattlefield = game.getZoneOf(tgtCard).is(ZoneType.Battlefield);
tgtCard.addCounter(CounterType.valueOf(type), counterAmount, placer, inBattlefield, table);
tgtCard.addCounter(CounterType.getType(type), counterAmount, placer, inBattlefield, table);
game.updateLastStateForCard(tgtCard);
}
table.triggerCountersPutAll(game);

View File

@@ -1,5 +1,6 @@
package forge.game.ability.effects;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
@@ -8,20 +9,25 @@ import forge.game.Game;
import forge.game.GameEntity;
import forge.game.GameEntityCounterTable;
import forge.game.GameObject;
import forge.game.ability.AbilityFactory;
import forge.game.ability.AbilityKey;
import forge.game.ability.AbilityUtils;
import forge.game.ability.SpellAbilityEffect;
import forge.game.card.Card;
import forge.game.card.CardCollection;
import forge.game.card.CardFactoryUtil;
import forge.game.card.CardLists;
import forge.game.card.CardPredicates;
import forge.game.card.CardUtil;
import forge.game.card.CounterEnumType;
import forge.game.card.CounterType;
import forge.game.card.CardPredicates.Presets;
import forge.game.player.Player;
import forge.game.player.PlayerActionConfirmMode;
import forge.game.player.PlayerController;
import forge.game.spellability.SpellAbility;
import forge.game.trigger.Trigger;
import forge.game.trigger.TriggerHandler;
import forge.game.trigger.TriggerType;
import forge.game.zone.Zone;
import forge.game.zone.ZoneType;
@@ -31,6 +37,7 @@ import forge.util.CardTranslation;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
@@ -63,7 +70,7 @@ public class CountersPutEffect extends SpellAbilityEffect {
} else if (type.equals("EachFromSource")) {
stringBuilder.append("each counter");
} else {
stringBuilder.append(CounterType.valueOf(type).getName()).append(" counter");
stringBuilder.append(CounterType.getType(type).getName()).append(" counter");
}
if (amount != 1) {
@@ -112,48 +119,27 @@ public class CountersPutEffect extends SpellAbilityEffect {
return stringBuilder.toString();
}
@Override
public void resolve(SpellAbility sa) {
protected void resolvePerType(SpellAbility sa, final Player placer, CounterType counterType, int counterAmount, GameEntityCounterTable table) {
final Card card = sa.getHostCard();
final Game game = card.getGame();
final Player activator = sa.getActivatingPlayer();
final PlayerController pc = activator.getController();
String strTyp = sa.getParam("CounterType");
CounterType counterType = null;
boolean existingCounter = strTyp.equals("ExistingCounter");
boolean eachExistingCounter = sa.hasParam("EachExistingCounter");
boolean eachFromSource = strTyp.equals("EachFromSource");
String amount = sa.getParamOrDefault("CounterNum", "1");
if (!existingCounter && !eachFromSource) {
try {
counterType = AbilityUtils.getCounterType(strTyp, sa);
} catch (Exception e) {
System.out.println("Counter type doesn't match, nor does an SVar exist with the type name.");
return;
}
}
Player placer = activator;
if (sa.hasParam("Placer")) {
final String pstr = sa.getParam("Placer");
placer = AbilityUtils.getDefinedPlayers(sa.getHostCard(), pstr, sa).get(0);
}
final boolean etbcounter = sa.hasParam("ETB");
final boolean remember = sa.hasParam("RememberCounters");
final boolean rememberCards = sa.hasParam("RememberCards");
int counterAmount = AbilityUtils.calculateAmount(sa.getHostCard(), amount, sa);
final int max = sa.hasParam("MaxFromEffect") ? Integer.parseInt(sa.getParam("MaxFromEffect")) : -1;
CardCollection tgtCards = new CardCollection();
boolean existingCounter = sa.hasParam("CounterType") && sa.getParam("CounterType").equals("ExistingCounter");
boolean eachExistingCounter = sa.hasParam("EachExistingCounter");
List<GameObject> tgtObjects = Lists.newArrayList();
if (sa.hasParam("Bolster")) {
CardCollection creatsYouCtrl = CardLists.filter(activator.getCardsIn(ZoneType.Battlefield), Presets.CREATURES);
CardCollection leastToughness = new CardCollection(Aggregates.listWithMin(creatsYouCtrl, CardPredicates.Accessors.fnGetDefense));
tgtCards.addAll(activator.getController().chooseCardsForEffect(leastToughness, sa, Localizer.getInstance().getMessage("lblChooseACreatureWithLeastToughness"), 1, 1, false));
tgtObjects.addAll(tgtCards);
Map<String, Object> params = Maps.newHashMap();
params.put("CounterType", counterType);
Iterables.addAll(tgtObjects, activator.getController().chooseCardsForEffect(leastToughness, sa, Localizer.getInstance().getMessage("lblChooseACreatureWithLeastToughness"), 1, 1, false, params));
} else if (sa.hasParam("Choices")) {
ZoneType choiceZone = ZoneType.Battlefield;
if (sa.hasParam("ChoiceZone")) {
@@ -174,14 +160,22 @@ public class CountersPutEffect extends SpellAbilityEffect {
choices = CardLists.getValidCards(choices, sa.getParam("Choices"), activator, card, sa);
String title = sa.hasParam("ChoiceTitle") ? sa.getParam("ChoiceTitle") : Localizer.getInstance().getMessage("lblChooseaCard") + " ";
tgtObjects.addAll(new CardCollection(chooser.getController().chooseCardsForEffect(choices, sa, title, n, n, sa.hasParam("ChoiceOptional"))));
String title = Localizer.getInstance().getMessage("lblChooseaCard") + " ";
if (sa.hasParam("ChoiceTitle")) {
title = sa.getParam("ChoiceTitle");
// TODO might use better message
if (counterType != null) {
title += " " + counterType.getName();
}
}
Map<String, Object> params = Maps.newHashMap();
params.put("CounterType", counterType);
Iterables.addAll(tgtObjects, chooser.getController().chooseCardsForEffect(choices, sa, title, n, n, sa.hasParam("ChoiceOptional"), params));
} else {
tgtObjects.addAll(getDefinedOrTargeted(sa, "Defined"));
}
GameEntityCounterTable table = new GameEntityCounterTable();
for (final GameObject obj : tgtObjects) {
// check if the object is still in game or if it was moved
Card gameCard = null;
@@ -234,9 +228,8 @@ public class CountersPutEffect extends SpellAbilityEffect {
}
}
if (eachFromSource) {
final CardCollection definedCard = AbilityUtils.getDefinedCards(card, sa.getParam("EachFromSource"), sa);
for (Card c : definedCard) {
if (sa.hasParam("EachFromSource")) {
for (Card c : AbilityUtils.getDefinedCards(card, sa.getParam("EachFromSource"), sa)) {
for (Entry<CounterType, Integer> cti : c.getCounters().entrySet()) {
if (gameCard != null && gameCard.canReceiveCounters(cti.getKey())) {
gameCard.addCounter(cti.getKey(), cti.getValue(), placer, true, table);
@@ -262,7 +255,7 @@ public class CountersPutEffect extends SpellAbilityEffect {
// Adapt need extra logic
if (sa.hasParam("Adapt")) {
if (!(gameCard.getCounters(CounterType.P1P1) == 0
if (!(gameCard.getCounters(CounterEnumType.P1P1) == 0
|| gameCard.hasKeyword("CARDNAME adapts as though it had no +1/+1 counters"))) {
continue;
}
@@ -293,8 +286,13 @@ public class CountersPutEffect extends SpellAbilityEffect {
continue;
}
Map<String, Object> params = Maps.newHashMap();
params.put("CounterType", counterType);
params.put("Amount", counterAmount);
params.put("Target", gameCard);
String message = Localizer.getInstance().getMessage("lblDoYouWantPutTargetP1P1CountersOnCard", String.valueOf(counterAmount), CardTranslation.getTranslatedName(gameCard.getName()));
Player chooser = pc.chooseSingleEntityForEffect(activator.getOpponents(), sa, Localizer.getInstance().getMessage("lblChooseAnOpponent"));
Player chooser = pc.chooseSingleEntityForEffect(activator.getOpponents(), sa, Localizer.getInstance().getMessage("lblChooseAnOpponent"), params);
if (chooser.getController().confirmAction(sa, PlayerActionConfirmMode.Tribute, message)) {
gameCard.setTributed(true);
@@ -307,13 +305,14 @@ public class CountersPutEffect extends SpellAbilityEffect {
if (etbcounter) {
gameCard.addEtbCounter(counterType, counterAmount, placer);
} else {
if (gameCard.addCounter(counterType, counterAmount, placer, true, table) > 0) {
int addedAmount = gameCard.addCounter(counterType, counterAmount, placer, true, table);
if (addedAmount > 0) {
counterAdded = true;
}
}
if (remember) {
final int value = gameCard.getTotalCountersToAdd();
gameCard.addCountersAddedBy(card, counterType, value);
if (sa.hasParam("RemovePhase")) {
addRemovePhaseTrigger(card, sa, sa.getParam("RemovePhase"), gameCard, counterType, addedAmount);
}
}
if (sa.hasParam("Evolve")) {
@@ -356,7 +355,85 @@ public class CountersPutEffect extends SpellAbilityEffect {
pl.addCounter(counterType, counterAmount, placer, true, table);
}
}
}
@Override
public void resolve(SpellAbility sa) {
final Card card = sa.getHostCard();
final Game game = card.getGame();
final Player activator = sa.getActivatingPlayer();
CounterType counterType = null;
String amount = sa.getParamOrDefault("CounterNum", "1");
boolean rememberAmount = sa.hasParam("RememberAmount");
if (!sa.hasParam("EachExistingCounter") && !sa.hasParam("EachFromSource") && !sa.hasParam("SharedKeywords")) {
try {
counterType = AbilityUtils.getCounterType(sa.getParam("CounterType"), sa);
} catch (Exception e) {
System.out.println("Counter type doesn't match, nor does an SVar exist with the type name.");
return;
}
}
Player placer = activator;
if (sa.hasParam("Placer")) {
final String pstr = sa.getParam("Placer");
placer = AbilityUtils.getDefinedPlayers(sa.getHostCard(), pstr, sa).get(0);
}
int counterAmount = AbilityUtils.calculateAmount(sa.getHostCard(), amount, sa);
GameEntityCounterTable table = new GameEntityCounterTable();
if (sa.hasParam("SharedKeywords")) {
List<String> keywords = Arrays.asList(sa.getParam("SharedKeywords").split(" & "));
List<ZoneType> zones = ZoneType.listValueOf(sa.getParam("SharedKeywordsZone"));
String[] restrictions = sa.hasParam("SharedRestrictions") ? sa.getParam("SharedRestrictions").split(",") : new String[]{"Card"};
keywords = CardFactoryUtil.sharedKeywords(keywords, restrictions, zones, sa.getHostCard());
for (String k : keywords) {
resolvePerType(sa, placer, CounterType.getType(k), counterAmount, table);
}
} else {
resolvePerType(sa, placer, counterType, counterAmount, table);
}
int totalAdded = 0;
for (Integer i : table.values()) {
totalAdded += i;
}
if (totalAdded > 0 && rememberAmount) {
// TODO use SpellAbility Remember later
card.addRemembered(Integer.valueOf(totalAdded));
}
table.triggerCountersPutAll(game);
}
protected void addRemovePhaseTrigger(final Card host, final SpellAbility sa, String phase, Card tgt, CounterType ct, int added) {
boolean intrinsic = sa.isIntrinsic();
StringBuilder delTrig = new StringBuilder("Mode$ Phase | Phase$ ");
delTrig.append(phase);
delTrig.append(" | TriggerDescription$ For each ").append(ct.getName()).append(" counter you put on a creature this way, remove a ").append(ct.getName()).append(" counter from that creature at the beginning of the next");
if ("Cleanup".equals(phase)) {
delTrig.append("cleanup step");
} else if ("End of Turn".equals(phase)) {
delTrig.append("next end step");
}
String trigSA = new StringBuilder("DB$ RemoveCounter | Defined$ DelayTriggerRemembered | CounterNum$ 1 | CounterType$ ").append(ct).toString();
// these trigger are one per counter
for (int i = 0; i < added; i++) {
final Trigger trig = TriggerHandler.parseTrigger(delTrig.toString(), sa.getHostCard(), intrinsic);
trig.addRemembered(tgt);
final SpellAbility newSa = AbilityFactory.getAbility(trigSA, sa.getHostCard());
newSa.setIntrinsic(intrinsic);
trig.setOverridingAbility(newSa);
sa.getActivatingPlayer().getGame().getTriggerHandler().registerDelayedTrigger(trig);
}
}
}

View File

@@ -32,7 +32,7 @@ public class CountersPutOrRemoveEffect extends SpellAbilityEffect {
sb.append(sa.getActivatingPlayer().getName());
if (sa.hasParam("CounterType")) {
CounterType ctype = CounterType.valueOf(sa.getParam("CounterType"));
CounterType ctype = CounterType.getType(sa.getParam("CounterType"));
sb.append(" removes a ").append(ctype.getName());
sb.append(" counter from or put another ").append(ctype.getName()).append(" counter on ");
} else {
@@ -56,7 +56,7 @@ public class CountersPutOrRemoveEffect extends SpellAbilityEffect {
CounterType ctype = null;
if (sa.hasParam("CounterType")) {
ctype = CounterType.valueOf(sa.getParam("CounterType"));
ctype = CounterType.getType(sa.getParam("CounterType"));
}
GameEntityCounterTable table = new GameEntityCounterTable();

View File

@@ -18,7 +18,7 @@ public class CountersRemoveAllEffect extends SpellAbilityEffect {
protected String getStackDescription(SpellAbility sa) {
final StringBuilder sb = new StringBuilder();
final CounterType cType = CounterType.valueOf(sa.getParam("CounterType"));
final CounterType cType = CounterType.getType(sa.getParam("CounterType"));
final int amount = AbilityUtils.calculateAmount(sa.getHostCard(), sa.getParam("CounterNum"), sa);
final String zone = sa.hasParam("ValidZone") ? sa.getParam("ValidZone") : "Battlefield";
String amountString = Integer.toString(amount);
@@ -69,11 +69,11 @@ public class CountersRemoveAllEffect extends SpellAbilityEffect {
continue;
}
if (sa.hasParam("AllCounters")) {
counterAmount = tgtCard.getCounters(CounterType.valueOf(type));
counterAmount = tgtCard.getCounters(CounterType.getType(type));
}
if (counterAmount > 0) {
tgtCard.subtractCounter(CounterType.valueOf(type), counterAmount);
tgtCard.subtractCounter(CounterType.getType(type), counterAmount);
game.updateLastStateForCard(tgtCard);
}
}

View File

@@ -31,7 +31,7 @@ public class CountersRemoveEffect extends SpellAbilityEffect {
final String num = sa.getParam("CounterNum");
int amount = 0;
if (!num.equals("All") && !num.equals("Remembered")) {
if (!num.equals("All") && !num.equals("Any")) {
amount = AbilityUtils.calculateAmount(sa.getHostCard(), num, sa);
}
@@ -48,7 +48,7 @@ public class CountersRemoveEffect extends SpellAbilityEffect {
sb.append(amount).append(" ").append(" counter");
}
} else {
sb.append(amount).append(" ").append(CounterType.valueOf(counterName).getName()).append(" counter");
sb.append(amount).append(" ").append(CounterType.getType(counterName).getName()).append(" counter");
}
if (amount != 1) {
sb.append("s");
@@ -80,7 +80,7 @@ public class CountersRemoveEffect extends SpellAbilityEffect {
final String num = sa.getParam("CounterNum");
int cntToRemove = 0;
if (!num.equals("All") && !num.equals("Any") && !num.equals("Remembered")) {
if (!num.equals("All") && !num.equals("Any")) {
cntToRemove = AbilityUtils.calculateAmount(sa.getHostCard(), num, sa);
}
@@ -130,7 +130,10 @@ public class CountersRemoveEffect extends SpellAbilityEffect {
srcCards = game.getCardsIn(ZoneType.Battlefield);
srcCards = CardLists.getValidCards(srcCards, sa.getParam("ValidSource"), player, card, sa);
if (num.equals("Any")) {
srcCards = player.getController().chooseCardsForEffect(srcCards, sa, Localizer.getInstance().getMessage("lblChooseCardsToTakeTargetCounters", counterType.getName()), 0, srcCards.size(), true);
String title = Localizer.getInstance().getMessage("lblChooseCardsToTakeTargetCounters", counterType.getName());
Map<String, Object> params = Maps.newHashMap();
params.put("CounterType", counterType);
srcCards = player.getController().chooseCardsForEffect(srcCards, sa, title, 0, srcCards.size(), true, params);
}
} else {
srcCards = getTargetCards(sa);
@@ -156,8 +159,6 @@ public class CountersRemoveEffect extends SpellAbilityEffect {
continue;
} else if (num.equals("All") || num.equals("Any")) {
cntToRemove = gameCard.getCounters(counterType);
} else if (num.equals("Remembered")) {
cntToRemove = gameCard.getCountersAddedBy(card, counterType);
}
if (type.equals("Any")) {
@@ -169,7 +170,7 @@ public class CountersRemoveEffect extends SpellAbilityEffect {
if (sa.hasParam("UpTo") || num.equals("Any")) {
Map<String, Object> params = Maps.newHashMap();
params.put("Target", gameCard);
params.put("CounterType", type);
params.put("CounterType", counterType);
String title = Localizer.getInstance().getMessage("lblSelectRemoveCountersNumberOfTarget", type);
cntToRemove = pc.chooseNumber(sa, title, 0, cntToRemove, params);
}
@@ -179,6 +180,7 @@ public class CountersRemoveEffect extends SpellAbilityEffect {
gameCard.subtractCounter(counterType, cntToRemove);
if (rememberRemoved) {
for (int i = 0; i < cntToRemove; i++) {
// TODO might need to be more specific
card.addRemembered(Pair.of(counterType, i));
}
}

View File

@@ -166,7 +166,7 @@ public class DigEffect extends SpellAbilityEffect {
if (sa.hasParam("Choser")) {
final FCollectionView<Player> choosers = AbilityUtils.getDefinedPlayers(sa.getHostCard(), sa.getParam("Choser"), sa);
if (!choosers.isEmpty()) {
chooser = player.getController().chooseSingleEntityForEffect(choosers, sa, Localizer.getInstance().getMessage("lblChooser") + ":");
chooser = player.getController().chooseSingleEntityForEffect(choosers, null, sa, Localizer.getInstance().getMessage("lblChooser") + ":", false, p, null);
}
if (sa.hasParam("SetChosenPlayer")) {
host.setChosenPlayer(chooser);
@@ -221,7 +221,7 @@ public class DigEffect extends SpellAbilityEffect {
}
for (final byte pair : MagicColor.COLORPAIR) {
Card chosen = chooser.getController().chooseSingleEntityForEffect(CardLists.filter(valid, CardPredicates.isExactlyColor(pair)),
delayedReveal, sa, Localizer.getInstance().getMessage("lblChooseOne"), false, p);
delayedReveal, sa, Localizer.getInstance().getMessage("lblChooseOne"), false, p, null);
if (chosen != null) {
movedCards.add(chosen);
}
@@ -241,7 +241,7 @@ public class DigEffect extends SpellAbilityEffect {
prompt = Localizer.getInstance().getMessage("lblChooseACardLeaveTarget", p.getName(), destZone2.getTranslatedName());
}
Card chosen = chooser.getController().chooseSingleEntityForEffect(valid, delayedReveal, sa, prompt, false, p);
Card chosen = chooser.getController().chooseSingleEntityForEffect(valid, delayedReveal, sa, prompt, false, p, null);
movedCards.remove(chosen);
if (sa.hasParam("RandomOrder")) {
CardLists.shuffle(movedCards);
@@ -274,7 +274,7 @@ public class DigEffect extends SpellAbilityEffect {
int max = anyNumber ? valid.size() : Math.min(valid.size(),destZone1ChangeNum);
int min = (anyNumber || optional) ? 0 : max;
if ( max > 0 ) { // if max is 0 don't make a choice
chosen = chooser.getController().chooseEntitiesForEffect(valid, min, max, delayedReveal, sa, prompt, p);
chosen = chooser.getController().chooseEntitiesForEffect(valid, min, max, delayedReveal, sa, prompt, p, null);
}
chooser.getController().endTempShowCards();
@@ -316,7 +316,7 @@ public class DigEffect extends SpellAbilityEffect {
final FCollectionView<GameEntity> e = combat.getDefenders();
final GameEntity defender = sa.getActivatingPlayer().getController().chooseSingleEntityForEffect(e, sa,
Localizer.getInstance().getMessage("lblChooseDefenderToAttackWithCard", CardTranslation.getTranslatedName(c.getName())));
Localizer.getInstance().getMessage("lblChooseDefenderToAttackWithCard", CardTranslation.getTranslatedName(c.getName())), null);
if (defender != null) {
combat.addAttacker(c, defender);

View File

@@ -90,7 +90,7 @@ public class DigMultipleEffect extends SpellAbilityEffect {
int amount = AbilityUtils.calculateAmount(host, sa.getParamOrDefault("ChooseAmount", "1"), sa);
final ZoneType chosenZone = sa.hasParam("ChosenZone") ? ZoneType.smartValueOf(sa.getParam("ChosenZone")) : ZoneType.Battlefield;
CardCollectionView extraChosen = chooser.getController().chooseCardsForEffect(chosen, sa, Localizer.getInstance().getMessage("lblChooseCards"), amount, amount, false);
CardCollectionView extraChosen = chooser.getController().chooseCardsForEffect(chosen, sa, Localizer.getInstance().getMessage("lblChooseCards"), amount, amount, false, null);
if (!extraChosen.isEmpty()) {
game.getAction().reveal(extraChosen, chooser, true, Localizer.getInstance().getMessage("lblPlayerPickedCardFrom", chooser.getName()));
}

View File

@@ -20,6 +20,8 @@ import forge.util.collect.FCollectionView;
import java.util.*;
import com.google.common.collect.Maps;
public class DigUntilEffect extends SpellAbilityEffect {
/* (non-Javadoc)
@@ -153,7 +155,7 @@ public class DigUntilEffect extends SpellAbilityEffect {
if (revealed.size() > 0) {
game.getAction().reveal(revealed, p, false);
}
if (foundDest != null) {
// Allow ordering of found cards
@@ -178,11 +180,14 @@ public class DigUntilEffect extends SpellAbilityEffect {
}
if (sa.hasParam("Attacking")) {
final Combat combat = game.getCombat();
if ( null != combat ) {
if (null != combat) {
final FCollectionView<GameEntity> e = combat.getDefenders();
Map<String, Object> params = Maps.newHashMap();
params.put("Attacker", c);
final GameEntity defender = sa.getActivatingPlayer().getController().chooseSingleEntityForEffect(e, sa,
Localizer.getInstance().getMessage("lblChooseDefenderToAttackWithCard", CardTranslation.getTranslatedName(c.getName())));
Localizer.getInstance().getMessage("lblChooseDefenderToAttackWithCard", CardTranslation.getTranslatedName(c.getName())), params);
if (defender != null) {
combat.addAttacker(c, defender);

View File

@@ -86,7 +86,7 @@ public class EffectEffect extends SpellAbilityEffect {
}
if (sa.hasParam("ForgetCounter")) {
CounterType cType = CounterType.valueOf(sa.getParam("ForgetCounter"));
CounterType cType = CounterType.getType(sa.getParam("ForgetCounter"));
rememberList = new FCollection<GameObject>(CardLists.filter(Iterables.filter(rememberList, Card.class), CardPredicates.hasCounter(cType)));
}

View File

@@ -54,7 +54,7 @@ public class EncodeEffect extends SpellAbilityEffect {
Card movedCard = game.getAction().moveTo(ZoneType.Exile, host, sa);
// choose a creature
Card choice = player.getController().chooseSingleEntityForEffect(choices, sa, Localizer.getInstance().getMessage("lblChooseACreatureYouControlToEncode") + " ", true);
Card choice = player.getController().chooseSingleEntityForEffect(choices, sa, Localizer.getInstance().getMessage("lblChooseACreatureYouControlToEncode") + " ", true, null);
if (choice == null) {
return;

View File

@@ -7,7 +7,7 @@ import forge.game.ability.AbilityKey;
import forge.game.ability.SpellAbilityEffect;
import forge.game.card.Card;
import forge.game.card.CardCollection;
import forge.game.card.CounterType;
import forge.game.card.CounterEnumType;
import forge.game.player.Player;
import forge.game.player.PlayerController;
import forge.game.spellability.SpellAbility;
@@ -78,7 +78,7 @@ public class ExploreEffect extends SpellAbilityEffect {
// if the card is not more in the game anymore
// this might still return true but its no problem
if (game.getZoneOf(gamec).is(ZoneType.Battlefield) && gamec.equalsWithTimestamp(c)) {
c.addCounter(CounterType.P1P1, 1, pl, true, table);
c.addCounter(CounterEnumType.P1P1, 1, pl, true, table);
}
}

View File

@@ -40,7 +40,8 @@ public class ManifestEffect extends SpellAbilityEffect {
}
String title = sa.hasParam("ChoiceTitle") ? sa.getParam("ChoiceTitle") : Localizer.getInstance().getMessage("lblChooseCardToManifest") + " ";
tgtCards = new CardCollection(activator.getController().chooseEntitiesForEffect(choices, amount, amount, null, sa, title, p));
tgtCards = new CardCollection(activator.getController().chooseCardsForEffect(choices, sa, title, amount, amount, false, null));
} else if ("TopOfLibrary".equals(defined)) {
tgtCards = p.getTopXCardsFromLibrary(amount);
} else {

View File

@@ -36,7 +36,7 @@ public class MeldEffect extends SpellAbilityEffect {
return;
}
Card secondary = controller.getController().chooseSingleEntityForEffect(field, sa, Localizer.getInstance().getMessage("lblChooseCardToMeld"));
Card secondary = controller.getController().chooseSingleEntityForEffect(field, sa, Localizer.getInstance().getMessage("lblChooseCardToMeld"), null);
secondary = game.getAction().exile(secondary, sa);

View File

@@ -86,7 +86,7 @@ public class MultiplePilesEffect extends SpellAbilityEffect {
for (int i = 1; i < piles; i++) {
int size = pool.size();
CardCollectionView pile = p.getController().chooseCardsForEffect(pool, sa, Localizer.getInstance().getMessage("lblChooseCardsInTargetPile", String.valueOf(i)), 0, size, false);
CardCollectionView pile = p.getController().chooseCardsForEffect(pool, sa, Localizer.getInstance().getMessage("lblChooseCardsInTargetPile", String.valueOf(i)), 0, size, false, null);
pileList.add(pile);
pool.removeAll(pile);
}

View File

@@ -12,8 +12,10 @@ import forge.game.zone.ZoneType;
import forge.util.Localizer;
import java.util.List;
import java.util.Map;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
public class MustBlockEffect extends SpellAbilityEffect {
@@ -23,6 +25,13 @@ public class MustBlockEffect extends SpellAbilityEffect {
final Player activator = sa.getActivatingPlayer();
final Game game = activator.getGame();
List<Card> cards;
if (sa.hasParam("DefinedAttacker")) {
cards = AbilityUtils.getDefinedCards(sa.getHostCard(), sa.getParam("DefinedAttacker"), sa);
} else {
cards = Lists.newArrayList(host);
}
List<Card> tgtCards = Lists.newArrayList();
if (sa.hasParam("Choices")) {
Player chooser = activator;
@@ -35,8 +44,9 @@ public class MustBlockEffect extends SpellAbilityEffect {
choices = CardLists.getValidCards(choices, sa.getParam("Choices"), activator, host);
if (!choices.isEmpty()) {
String title = sa.hasParam("ChoiceTitle") ? sa.getParam("ChoiceTitle") : Localizer.getInstance().getMessage("lblChooseaCard") +" ";
Card choosen = chooser.getController().chooseSingleEntityForEffect(choices, sa, title, false);
Map<String, Object> params = Maps.newHashMap();
params.put("Attackers", cards);
Card choosen = chooser.getController().chooseSingleEntityForEffect(choices, sa, title, false, params);
if (choosen != null) {
tgtCards.add(choosen);
@@ -48,13 +58,6 @@ public class MustBlockEffect extends SpellAbilityEffect {
final boolean mustBlockAll = sa.hasParam("BlockAllDefined");
List<Card> cards;
if (sa.hasParam("DefinedAttacker")) {
cards = AbilityUtils.getDefinedCards(sa.getHostCard(), sa.getParam("DefinedAttacker"), sa);
} else {
cards = Lists.newArrayList(host);
}
for (final Card c : tgtCards) {
if ((!sa.usesTargeting()) || c.canBeTargetedBy(sa)) {
if (mustBlockAll) {

View File

@@ -118,7 +118,7 @@ public class PlayEffect extends SpellAbilityEffect {
final int choicenum = AbilityUtils.calculateAmount(source, sa.getParam("ChoiceNum"), sa);
tgtCards = new CardCollection(
activator.getController().chooseCardsForEffect(choice, sa,
source + " - " + Localizer.getInstance().getMessage("lblChooseUpTo") + " " + Lang.nounWithNumeral(choicenum, "card"), 0, choicenum, true
source + " - " + Localizer.getInstance().getMessage("lblChooseUpTo") + " " + Lang.nounWithNumeral(choicenum, "card"), 0, choicenum, true, null
)
);
}
@@ -145,7 +145,7 @@ public class PlayEffect extends SpellAbilityEffect {
final CardCollection saidNoTo = new CardCollection();
while (tgtCards.size() > saidNoTo.size() && saidNoTo.size() < amount && amount > 0) {
activator.getController().tempShowCards(showCards);
Card tgtCard = controller.getController().chooseSingleEntityForEffect(tgtCards, sa, Localizer.getInstance().getMessage("lblSelectCardToPlay"));
Card tgtCard = controller.getController().chooseSingleEntityForEffect(tgtCards, sa, Localizer.getInstance().getMessage("lblSelectCardToPlay"), null);
activator.getController().endTempShowCards();
if (tgtCard == null) {
return;

View File

@@ -5,7 +5,7 @@ import forge.game.GameEntityCounterTable;
import forge.game.ability.AbilityUtils;
import forge.game.ability.SpellAbilityEffect;
import forge.game.card.Card;
import forge.game.card.CounterType;
import forge.game.card.CounterEnumType;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import forge.util.Lang;
@@ -59,7 +59,7 @@ import java.util.List;
sb.append("s");
}
String type = CounterType.POISON.getName() + " counter";
String type = CounterEnumType.POISON.getName() + " counter";
sb.append(" ").append(Lang.nounWithAmount(amount, type)).append(".");

View File

@@ -164,20 +164,6 @@ public class RepeatEachEffect extends SpellAbilityEffect {
}
}
}
if (sa.hasParam("RepeatCounters")) {
Card target = sa.getTargetCard();
if (target == null) {
target = AbilityUtils.getDefinedCards(source, sa.getParam("Defined"), sa).get(0);
}
for (CounterType type : target.getCounters().keySet()) {
StringBuilder sb = new StringBuilder();
sb.append("Number$").append(target.getCounters(type));
source.setSVar("RepeatSVarCounter", type.getName().toUpperCase());
source.setSVar("RepeatCounterAmount", sb.toString());
AbilityUtils.resolve(repeat);
}
}
if (recordChoice) {
boolean random = sa.hasParam("Random");
Map<Player, List<Card>> recordMap = Maps.newHashMap();
@@ -187,7 +173,7 @@ public class RepeatEachEffect extends SpellAbilityEffect {
if (random) {
p = Aggregates.random(game.getPlayers());
} else {
p = sa.getActivatingPlayer().getController().chooseSingleEntityForEffect(game.getPlayers(), sa, Localizer.getInstance().getMessage("lblChoosePlayer"));
p = sa.getActivatingPlayer().getController().chooseSingleEntityForEffect(game.getPlayers(), sa, Localizer.getInstance().getMessage("lblChoosePlayer"), null);
}
if (recordMap.containsKey(p)) {
recordMap.get(p).add(0, card);
@@ -208,7 +194,7 @@ public class RepeatEachEffect extends SpellAbilityEffect {
valid = CardLists.filterControlledBy(valid,
game.getNextPlayerAfter(p, source.getChosenDirection()));
}
Card card = p.getController().chooseSingleEntityForEffect(valid, sa, Localizer.getInstance().getMessage("lblChooseaCard"));
Card card = p.getController().chooseSingleEntityForEffect(valid, sa, Localizer.getInstance().getMessage("lblChooseaCard"), null);
if (recordMap.containsKey(p)) {
recordMap.get(p).add(0, card);
} else {

Some files were not shown because too many files have changed in this diff Show More