diff --git a/forge-ai/src/main/java/forge/ai/AiAttackController.java b/forge-ai/src/main/java/forge/ai/AiAttackController.java index 51798704399..dbb8567af57 100644 --- a/forge-ai/src/main/java/forge/ai/AiAttackController.java +++ b/forge-ai/src/main/java/forge/ai/AiAttackController.java @@ -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 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 diff --git a/forge-ai/src/main/java/forge/ai/AiBlockController.java b/forge-ai/src/main/java/forge/ai/AiBlockController.java index bef6eaa8961..7f1104d95fd 100644 --- a/forge-ai/src/main/java/forge/ai/AiBlockController.java +++ b/forge-ai/src/main/java/forge/ai/AiBlockController.java @@ -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 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); diff --git a/forge-ai/src/main/java/forge/ai/AiController.java b/forge-ai/src/main/java/forge/ai/AiController.java index b8777e5be1c..8f941ed6886 100644 --- a/forge-ai/src/main/java/forge/ai/AiController.java +++ b/forge-ai/src/main/java/forge/ai/AiController.java @@ -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 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); diff --git a/forge-ai/src/main/java/forge/ai/AiCostDecision.java b/forge-ai/src/main/java/forge/ai/AiCostDecision.java index 95a967e337c..ee480003207 100644 --- a/forge-ai/src/main/java/forge/ai/AiCostDecision.java +++ b/forge-ai/src/main/java/forge/ai/AiCostDecision.java @@ -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 prefs = CardLists.filter(typeList, CardPredicates.hasCounter(CounterType.P1P1, c), + List 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() { @@ -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 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 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; } diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtil.java b/forge-ai/src/main/java/forge/ai/ComputerUtil.java index d87c8a68802..5cd886aaf7f 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtil.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtil.java @@ -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 . */ @@ -65,7 +65,7 @@ import java.util.*; *

* ComputerUtil class. *

- * + * * @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() { @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 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 params = t.getMapParams(); + Map 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 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 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 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(); diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java b/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java index ca62736912f..3009dca985a 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java @@ -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; diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtilCombat.java b/forge-ai/src/main/java/forge/ai/ComputerUtilCombat.java index 0b2ac9d2f5e..baffff9cc5a 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtilCombat.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtilCombat.java @@ -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; } diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtilCost.java b/forge-ai/src/main/java/forge/ai/ComputerUtilCost.java index 89b6ddf4436..78de56e6988 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtilCost.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtilCost.java @@ -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; } diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtilMana.java b/forge-ai/src/main/java/forge/ai/ComputerUtilMana.java index 28e5eaf9835..f90a554dbc6 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtilMana.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtilMana.java @@ -371,7 +371,7 @@ public class ComputerUtilMana { adjustManaCostToAvoidNegEffects(cost, sa.getHostCard(), ai); List manaSpentToPay = test ? new ArrayList<>() : sa.getPayingMana(); boolean purePhyrexian = cost.containsOnlyPhyrexianMana(); - int testEnergyPool = ai.getCounters(CounterType.ENERGY); + int testEnergyPool = ai.getCounters(CounterEnumType.ENERGY); List paymentList = Lists.newArrayList(); diff --git a/forge-ai/src/main/java/forge/ai/CreatureEvaluator.java b/forge-ai/src/main/java/forge/ai/CreatureEvaluator.java index de339fad5c4..917012b236d 100644 --- a/forge-ai/src/main/java/forge/ai/CreatureEvaluator.java +++ b/forge-ai/src/main/java/forge/ai/CreatureEvaluator.java @@ -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 { // 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++) { diff --git a/forge-ai/src/main/java/forge/ai/GameState.java b/forge-ai/src/main/java/forge/ai/GameState.java index baadb5e76c9..600bbe41d7d 100644 --- a/forge-ai/src/main/java/forge/ai/GameState.java +++ b/forge-ai/src/main/java/forge/ai/GameState.java @@ -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 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) diff --git a/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java b/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java index 9c5d555a2e2..924cdcd9d80 100644 --- a/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java +++ b/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java @@ -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 params) { + return brains.chooseCardsForEffect(sourceList, sa, min, max, isOptional, params); } @Override - public T chooseSingleEntityForEffect(FCollectionView optionList, DelayedReveal delayedReveal, SpellAbility sa, String title, boolean isOptional, Player targetedPlayer) { + public T chooseSingleEntityForEffect(FCollectionView optionList, DelayedReveal delayedReveal, SpellAbility sa, String title, boolean isOptional, Player targetedPlayer, Map 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)optionList, isOptional, targetedPlayer); + return SpellApiToAi.Converter.get(api).chooseSingleEntity(player, sa, (FCollection)optionList, isOptional, targetedPlayer, params); } @Override public List chooseEntitiesForEffect( FCollectionView optionList, int min, int max, DelayedReveal delayedReveal, SpellAbility sa, String title, - Player targetedPlayer) { + Player targetedPlayer, Map params) { if (delayedReveal != null) { reveal(delayedReveal.getCards(), delayedReveal.getZone(), delayedReveal.getOwner(), delayedReveal.getMessagePrefix()); } @@ -172,7 +172,7 @@ public class PlayerControllerAi extends PlayerController { List 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 spells, SpellAbility sa, String title, + public List chooseSpellAbilitiesForEffect(List spells, SpellAbility sa, String title, + int num, Map params) { + List remaining = Lists.newArrayList(spells); + List 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 spells, SpellAbility sa, String title, Map params) { ApiType api = sa.getApi(); if (null == api) { diff --git a/forge-ai/src/main/java/forge/ai/SpecialCardAi.java b/forge-ai/src/main/java/forge/ai/SpecialCardAi.java index f5f4435c491..5ed62460108 100644 --- a/forge-ai/src/main/java/forge/ai/SpecialCardAi.java +++ b/forge-ai/src/main/java/forge/ai/SpecialCardAi.java @@ -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 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() { @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++) { diff --git a/forge-ai/src/main/java/forge/ai/SpellAbilityAi.java b/forge-ai/src/main/java/forge/ai/SpellAbilityAi.java index 61d161caace..665e8881f4f 100644 --- a/forge-ai/src/main/java/forge/ai/SpellAbilityAi.java +++ b/forge-ai/src/main/java/forge/ai/SpellAbilityAi.java @@ -307,7 +307,7 @@ public abstract class SpellAbilityAi { } @SuppressWarnings("unchecked") - public T chooseSingleEntity(Player ai, SpellAbility sa, Collection options, boolean isOptional, Player targetedPlayer) { + public T chooseSingleEntity(Player ai, SpellAbility sa, Collection options, boolean isOptional, Player targetedPlayer, Map 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) options); + return (T) chooseSinglePlayerOrPlaneswalker(ai, sa, (Collection) options, params); } else if (hasCard) { - return (T) chooseSingleCard(ai, sa, (Collection) options, isOptional, targetedPlayer); + return (T) chooseSingleCard(ai, sa, (Collection) options, isOptional, targetedPlayer, params); } else if (hasPlayer) { - return (T) chooseSinglePlayer(ai, sa, (Collection) options); + return (T) chooseSinglePlayer(ai, sa, (Collection) options, params); } return null; @@ -339,17 +339,17 @@ public abstract class SpellAbilityAi { return spells.get(0); } - protected Card chooseSingleCard(Player ai, SpellAbility sa, Iterable options, boolean isOptional, Player targetedPlayer) { + protected Card chooseSingleCard(Player ai, SpellAbility sa, Iterable options, boolean isOptional, Player targetedPlayer, Map 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 options) { + protected Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable options, Map 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 options) { + protected GameEntity chooseSinglePlayerOrPlaneswalker(Player ai, SpellAbility sa, Iterable options, Map 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); } diff --git a/forge-ai/src/main/java/forge/ai/ability/AmassAi.java b/forge-ai/src/main/java/forge/ai/ability/AmassAi.java index 0fd0319c3ef..ab54f83224d 100644 --- a/forge-ai/src/main/java/forge/ai/ability/AmassAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/AmassAi.java @@ -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 options, boolean isOptional, Player targetedPlayer) { - Iterable better = CardLists.filter(options, CardPredicates.canReceiveCounters(CounterType.P1P1)); + protected Card chooseSingleCard(Player ai, SpellAbility sa, Iterable options, boolean isOptional, Player targetedPlayer, Map params) { + Iterable better = CardLists.filter(options, CardPredicates.canReceiveCounters(CounterEnumType.P1P1)); if (Iterables.isEmpty(better)) { better = options; } diff --git a/forge-ai/src/main/java/forge/ai/ability/AttachAi.java b/forge-ai/src/main/java/forge/ai/ability/AttachAi.java index 1bc5028175f..37602483b41 100644 --- a/forge-ai/src/main/java/forge/ai/ability/AttachAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/AttachAi.java @@ -1704,12 +1704,12 @@ public class AttachAi extends SpellAbilityAi { } @Override - protected Card chooseSingleCard(Player ai, SpellAbility sa, Iterable options, boolean isOptional, Player targetedPlayer) { + protected Card chooseSingleCard(Player ai, SpellAbility sa, Iterable options, boolean isOptional, Player targetedPlayer, Map params) { return attachToCardAIPreferences(ai, sa, true); } @Override - protected Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable options) { + protected Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable options, Map params) { return attachToPlayerAIPreferences(ai, sa, true); } } diff --git a/forge-ai/src/main/java/forge/ai/ability/BondAi.java b/forge-ai/src/main/java/forge/ai/ability/BondAi.java index 03c950d3f2e..8619983df69 100644 --- a/forge-ai/src/main/java/forge/ai/ability/BondAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/BondAi.java @@ -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 options, boolean isOptional, Player targetedPlayer) { + protected Card chooseSingleCard(Player ai, SpellAbility sa, Iterable options, boolean isOptional, Player targetedPlayer, Map params) { return ComputerUtilCard.getBestCreatureAI(options); } diff --git a/forge-ai/src/main/java/forge/ai/ability/ChangeCombatantsAi.java b/forge-ai/src/main/java/forge/ai/ability/ChangeCombatantsAi.java index 3e4228c09bd..1187dc81cdc 100644 --- a/forge-ai/src/main/java/forge/ai/ability/ChangeCombatantsAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/ChangeCombatantsAi.java @@ -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 chooseSingleEntity(Player ai, SpellAbility sa, Collection options, boolean isOptional, Player targetedPlayer) { + public T chooseSingleEntity(Player ai, SpellAbility sa, Collection options, boolean isOptional, Player targetedPlayer, Map params) { PlayerCollection targetableOpps = new PlayerCollection(); for (GameEntity p : options) { if (p instanceof Player && !p.equals(sa.getHostCard().getController())) { diff --git a/forge-ai/src/main/java/forge/ai/ability/ChangeZoneAi.java b/forge-ai/src/main/java/forge/ai/ability/ChangeZoneAi.java index 160066bea92..2894660612a 100644 --- a/forge-ai/src/main/java/forge/ai/ability/ChangeZoneAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/ChangeZoneAi.java @@ -142,7 +142,7 @@ public class ChangeZoneAi extends SpellAbilityAi { *

* @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. *

* @param ai - * + * * @param list * a List 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 { *

* areAllBasics. *

- * + * * @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() { - @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() { + @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() { @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 options, boolean isOptional, Player targetedPlayer) { + public Card chooseSingleCard(Player ai, SpellAbility sa, Iterable options, boolean isOptional, Player targetedPlayer, Map 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 options) { + public Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable options, Map 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; } diff --git a/forge-ai/src/main/java/forge/ai/ability/CharmAi.java b/forge-ai/src/main/java/forge/ai/ability/CharmAi.java index 1f9343599df..296a695b871 100644 --- a/forge-ai/src/main/java/forge/ai/ability/CharmAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/CharmAi.java @@ -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 opponents) { + public Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable opponents, Map params) { return Aggregates.random(opponents); } } diff --git a/forge-ai/src/main/java/forge/ai/ability/ChooseCardAi.java b/forge-ai/src/main/java/forge/ai/ability/ChooseCardAi.java index e16e748ed8a..f8a48d2ec25 100644 --- a/forge-ai/src/main/java/forge/ai/ability/ChooseCardAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/ChooseCardAi.java @@ -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 options, boolean isOptional, Player targetedPlayer) { + public Card chooseSingleCard(final Player ai, final SpellAbility sa, Iterable options, boolean isOptional, Player targetedPlayer, Map 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; } }); diff --git a/forge-ai/src/main/java/forge/ai/ability/ChooseCardNameAi.java b/forge-ai/src/main/java/forge/ai/ability/ChooseCardNameAi.java index ff0bfc2ceaa..f9c3b3d9798 100644 --- a/forge-ai/src/main/java/forge/ai/ability/ChooseCardNameAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/ChooseCardNameAi.java @@ -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 options, boolean isOptional, Player targetedPlayer) { + public Card chooseSingleCard(final Player ai, SpellAbility sa, Iterable options, boolean isOptional, Player targetedPlayer, Map 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 { diff --git a/forge-ai/src/main/java/forge/ai/ability/ChooseCompanionAi.java b/forge-ai/src/main/java/forge/ai/ability/ChooseCompanionAi.java index 7f6d1484608..1240d9ab54f 100644 --- a/forge-ai/src/main/java/forge/ai/ability/ChooseCompanionAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/ChooseCompanionAi.java @@ -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 options, boolean isOptional, Player targetedPlayer) { + public Card chooseSingleCard(final Player ai, final SpellAbility sa, Iterable options, boolean isOptional, Player targetedPlayer, Map params) { List cards = Lists.newArrayList(options); if (cards.isEmpty()) { return null; diff --git a/forge-ai/src/main/java/forge/ai/ability/ChooseGenericEffectAi.java b/forge-ai/src/main/java/forge/ai/ability/ChooseGenericEffectAi.java index 3a1a87200ab..b814b347224 100644 --- a/forge-ai/src/main/java/forge/ai/ability/ChooseGenericEffectAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/ChooseGenericEffectAi.java @@ -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; } diff --git a/forge-ai/src/main/java/forge/ai/ability/ChoosePlayerAi.java b/forge-ai/src/main/java/forge/ai/ability/ChoosePlayerAi.java index ddb90e701f1..5b98b108cda 100644 --- a/forge-ai/src/main/java/forge/ai/ability/ChoosePlayerAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/ChoosePlayerAi.java @@ -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 choices) { + public Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable choices, Map params) { Player chosen = null; if ("Curse".equals(sa.getParam("AILogic"))) { for (Player pc : choices) { diff --git a/forge-ai/src/main/java/forge/ai/ability/ChooseSourceAi.java b/forge-ai/src/main/java/forge/ai/ability/ChooseSourceAi.java index f7a694e0c67..038f9a843ab 100644 --- a/forge-ai/src/main/java/forge/ai/ability/ChooseSourceAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/ChooseSourceAi.java @@ -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 options, boolean isOptional, Player targetedPlayer) { + public Card chooseSingleCard(final Player aiChoser, SpellAbility sa, Iterable options, boolean isOptional, Player targetedPlayer, Map params) { if ("NeedsPrevention".equals(sa.getParam("AILogic"))) { final Player ai = sa.getActivatingPlayer(); final Game game = ai.getGame(); diff --git a/forge-ai/src/main/java/forge/ai/ability/ClashAi.java b/forge-ai/src/main/java/forge/ai/ability/ClashAi.java index 5800ecefb32..b9f7e8d7e6f 100644 --- a/forge-ai/src/main/java/forge/ai/ability/ClashAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/ClashAi.java @@ -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 options) { + protected Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable options, Map 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); diff --git a/forge-ai/src/main/java/forge/ai/ability/CloneAi.java b/forge-ai/src/main/java/forge/ai/ability/CloneAi.java index 3d6870d033d..3a3de604029 100644 --- a/forge-ai/src/main/java/forge/ai/ability/CloneAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/CloneAi.java @@ -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 options, boolean isOptional, - Player targetedPlayer) { + Player targetedPlayer, Map params) { final Card host = sa.getHostCard(); final Player ctrl = host.getController(); diff --git a/forge-ai/src/main/java/forge/ai/ability/ControlGainAi.java b/forge-ai/src/main/java/forge/ai/ability/ControlGainAi.java index 7a29ee98068..617da8098eb 100644 --- a/forge-ai/src/main/java/forge/ai/ability/ControlGainAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/ControlGainAi.java @@ -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 options) { + protected Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable options, Map params) { final List cards = Lists.newArrayList(); for (Player p : options) { cards.addAll(p.getCreaturesInPlay()); diff --git a/forge-ai/src/main/java/forge/ai/ability/CopyPermanentAi.java b/forge-ai/src/main/java/forge/ai/ability/CopyPermanentAi.java index 7ce9dfbc473..e455230988f 100644 --- a/forge-ai/src/main/java/forge/ai/ability/CopyPermanentAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/CopyPermanentAi.java @@ -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 options, boolean isOptional, Player targetedPlayer) { + public Card chooseSingleCard(Player ai, SpellAbility sa, Iterable options, boolean isOptional, Player targetedPlayer, Map 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 options) { + protected Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable options, Map params) { final List cards = new PlayerCollection(options).getCreaturesInPlay(); Card chosen = ComputerUtilCard.getBestCreatureAI(cards); return chosen != null ? chosen.getController() : Iterables.getFirst(options, null); diff --git a/forge-ai/src/main/java/forge/ai/ability/CountersAi.java b/forge-ai/src/main/java/forge/ai/ability/CountersAi.java index 2535c29505d..659b05de3cd 100644 --- a/forge-ai/src/main/java/forge/ai/ability/CountersAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/CountersAi.java @@ -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 . */ @@ -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; *

* AbilityFactory_Counters class. *

- * + * * @author Forge * @version $Id$ */ @@ -46,7 +46,7 @@ public abstract class CountersAi { *

* chooseCursedTarget. *

- * + * * @param list * a {@link forge.CardList} object. * @param type @@ -77,7 +77,7 @@ public abstract class CountersAi { *

* chooseBoonTarget. *

- * + * * @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() { @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); diff --git a/forge-ai/src/main/java/forge/ai/ability/CountersMoveAi.java b/forge-ai/src/main/java/forge/ai/ability/CountersMoveAi.java index bf142184a33..b6e75e62b91 100644 --- a/forge-ai/src/main/java/forge/ai/ability/CountersMoveAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/CountersMoveAi.java @@ -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 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 srcCards = AbilityUtils.getDefinedCards(host, sa.getParam("Source"), sa); final List 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 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 options, boolean isOptional, - Player targetedPlayer) { + Player targetedPlayer, Map params) { if (sa.hasParam("AiLogic")) { String logic = sa.getParam("AiLogic"); diff --git a/forge-ai/src/main/java/forge/ai/ability/CountersMultiplyAi.java b/forge-ai/src/main/java/forge/ai/ability/CountersMultiplyAi.java index b20e99ace3b..1dc29e1a89b 100644 --- a/forge-ai/src/main/java/forge/ai/ability/CountersMultiplyAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/CountersMultiplyAi.java @@ -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 typeList = Lists.newArrayList(CounterType.LOYALTY, CounterType.P1P1, CounterType.CHARGE); - for (CounterType type : typeList) { + List 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); } diff --git a/forge-ai/src/main/java/forge/ai/ability/CountersProliferateAi.java b/forge-ai/src/main/java/forge/ai/ability/CountersProliferateAi.java index 52538a0827c..7dd9618dafe 100644 --- a/forge-ai/src/main/java/forge/ai/ability/CountersProliferateAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/CountersProliferateAi.java @@ -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() { @@ -115,17 +116,19 @@ public class CountersProliferateAi extends SpellAbilityAi { */ @SuppressWarnings("unchecked") @Override - public T chooseSingleEntity(Player ai, SpellAbility sa, Collection options, boolean isOptional, Player targetedPlayer) { + public T chooseSingleEntity(Player ai, SpellAbility sa, Collection options, boolean isOptional, Player targetedPlayer, Map 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; } } diff --git a/forge-ai/src/main/java/forge/ai/ability/CountersPutAi.java b/forge-ai/src/main/java/forge/ai/ability/CountersPutAi.java index 70f568a6954..c3448d4af15 100644 --- a/forge-ai/src/main/java/forge/ai/ability/CountersPutAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/CountersPutAi.java @@ -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() { @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 threatening = CardLists.filter(creats, new Predicate() { @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 options) { + public Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable options, Map params) { // used by Tribute, select player with lowest Life // TODO add more logic using TributeAILogic List list = Lists.newArrayList(options); return Collections.min(list, PlayerPredicates.compareByLife()); } - + @Override - protected Card chooseSingleCard(final Player ai, SpellAbility sa, Iterable options, boolean isOptional, Player targetedPlayer) { + protected Card chooseSingleCard(final Player ai, SpellAbility sa, Iterable options, boolean isOptional, Player targetedPlayer, Map 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() { + @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() { @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() { @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() { @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); } } diff --git a/forge-ai/src/main/java/forge/ai/ability/CountersPutOrRemoveAi.java b/forge-ai/src/main/java/forge/ai/ability/CountersPutOrRemoveAi.java index 4195a7924b7..a261270dec9 100644 --- a/forge-ai/src/main/java/forge/ai/ability/CountersPutOrRemoveAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/CountersPutOrRemoveAi.java @@ -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 . */ @@ -36,7 +36,7 @@ import java.util.Map; *

* AbilityFactory_PutOrRemoveCountersAi class. *

- * + * * @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; } diff --git a/forge-ai/src/main/java/forge/ai/ability/CountersRemoveAi.java b/forge-ai/src/main/java/forge/ai/ability/CountersRemoveAi.java index 24a0ff0fa9d..2c4ad3bbfe0 100644 --- a/forge-ai/src/main/java/forge/ai/ability/CountersRemoveAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/CountersRemoveAi.java @@ -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; } } diff --git a/forge-ai/src/main/java/forge/ai/ability/DamageAllAi.java b/forge-ai/src/main/java/forge/ai/ability/DamageAllAi.java index e36a2a236dc..79e83ac65a4 100644 --- a/forge-ai/src/main/java/forge/ai/ability/DamageAllAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/DamageAllAi.java @@ -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) { diff --git a/forge-ai/src/main/java/forge/ai/ability/DamageDealAi.java b/forge-ai/src/main/java/forge/ai/ability/DamageDealAi.java index 1a5e908a7a4..936e2187357 100644 --- a/forge-ai/src/main/java/forge/ai/ability/DamageDealAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/DamageDealAi.java @@ -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 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 { *

* dealDamageChooseTgtC. *

- * + * * @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 { *

* damageTargetAI. *

- * + * * @param saMe * a {@link forge.game.spellability.SpellAbility} object. * @param dmg @@ -543,7 +543,7 @@ public class DamageDealAi extends DamageAiBase { *

* damageChoosingTargets. *

- * + * * @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 { *

* damageChooseNontargeted. *

- * @param ai - * + * @param ai + * * @param saMe * a {@link forge.game.spellability.SpellAbility} object. * @param dmg @@ -881,7 +881,7 @@ public class DamageDealAi extends DamageAiBase { *

* damageChooseRequiredTargets. *

- * + * * @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)) { diff --git a/forge-ai/src/main/java/forge/ai/ability/DestroyAi.java b/forge-ai/src/main/java/forge/ai/ability/DestroyAi.java index 392c1c52e0c..8c3855385e6 100644 --- a/forge-ai/src/main/java/forge/ai/ability/DestroyAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/DestroyAi.java @@ -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 diff --git a/forge-ai/src/main/java/forge/ai/ability/DigAi.java b/forge-ai/src/main/java/forge/ai/ability/DigAi.java index b8b13d18fd3..7099250d925 100644 --- a/forge-ai/src/main/java/forge/ai/ability/DigAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/DigAi.java @@ -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 valid, boolean isOptional, Player relatedPlayer) { + public Card chooseSingleCard(Player ai, SpellAbility sa, Iterable valid, boolean isOptional, Player relatedPlayer, Map 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 options) { + public Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable options, Map params) { // an opponent choose a card from return Iterables.getFirst(options, null); } diff --git a/forge-ai/src/main/java/forge/ai/ability/DrawAi.java b/forge-ai/src/main/java/forge/ai/ability/DrawAi.java index 6fbe29ccdd1..4047d12ee77 100644 --- a/forge-ai/src/main/java/forge/ai/ability/DrawAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/DrawAi.java @@ -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; } diff --git a/forge-ai/src/main/java/forge/ai/ability/EncodeAi.java b/forge-ai/src/main/java/forge/ai/ability/EncodeAi.java index fcfe5c957ba..53587f59cd6 100644 --- a/forge-ai/src/main/java/forge/ai/ability/EncodeAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/EncodeAi.java @@ -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 options, boolean isOptional, Player targetedPlayer) { + public Card chooseSingleCard(final Player ai, SpellAbility sa, Iterable options, boolean isOptional, Player targetedPlayer, Map params) { return chooseCard(ai, options, isOptional); } diff --git a/forge-ai/src/main/java/forge/ai/ability/LegendaryRuleAi.java b/forge-ai/src/main/java/forge/ai/ability/LegendaryRuleAi.java index 87e9d2d9464..4ad913b7a25 100644 --- a/forge-ai/src/main/java/forge/ai/ability/LegendaryRuleAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/LegendaryRuleAi.java @@ -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 options, boolean isOptional, Player targetedPlayer) { + public Card chooseSingleCard(Player ai, SpellAbility sa, Iterable options, boolean isOptional, Player targetedPlayer, Map 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; } } diff --git a/forge-ai/src/main/java/forge/ai/ability/LifeSetAi.java b/forge-ai/src/main/java/forge/ai/ability/LifeSetAi.java index cc4d851bd9b..2afc4b47137 100644 --- a/forge-ai/src/main/java/forge/ai/ability/LifeSetAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/LifeSetAi.java @@ -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; } diff --git a/forge-ai/src/main/java/forge/ai/ability/ManaEffectAi.java b/forge-ai/src/main/java/forge/ai/ability/ManaEffectAi.java index 6158b777e9b..80f7b916f11 100644 --- a/forge-ai/src/main/java/forge/ai/ability/ManaEffectAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/ManaEffectAi.java @@ -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; diff --git a/forge-ai/src/main/java/forge/ai/ability/MustBlockAi.java b/forge-ai/src/main/java/forge/ai/ability/MustBlockAi.java index 925f8ce02a1..fe921f05e72 100644 --- a/forge-ai/src/main/java/forge/ai/ability/MustBlockAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/MustBlockAi.java @@ -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 options, boolean isOptional, - Player targetedPlayer) { + Player targetedPlayer, Map params) { final Card host = sa.getHostCard(); Card attacker = host; diff --git a/forge-ai/src/main/java/forge/ai/ability/PlayAi.java b/forge-ai/src/main/java/forge/ai/ability/PlayAi.java index 3d5922a9122..7168f4c911e 100644 --- a/forge-ai/src/main/java/forge/ai/ability/PlayAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/PlayAi.java @@ -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 options, - final boolean isOptional, - Player targetedPlayer) { + final boolean isOptional, Player targetedPlayer, Map params) { List tgtCards = CardLists.filter(options, new Predicate() { @Override public boolean apply(final Card c) { diff --git a/forge-ai/src/main/java/forge/ai/ability/PoisonAi.java b/forge-ai/src/main/java/forge/ai/ability/PoisonAi.java index 0eba3082f50..5761c79832c 100644 --- a/forge-ai/src/main/java/forge/ai/ability/PoisonAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/PoisonAi.java @@ -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)); } }); diff --git a/forge-ai/src/main/java/forge/ai/ability/PumpAi.java b/forge-ai/src/main/java/forge/ai/ability/PumpAi.java index af93f8a1b72..ced79a57318 100644 --- a/forge-ai/src/main/java/forge/ai/ability/PumpAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/PumpAi.java @@ -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 } diff --git a/forge-ai/src/main/java/forge/ai/ability/RepeatEachAi.java b/forge-ai/src/main/java/forge/ai/ability/RepeatEachAi.java index 8ba1494b666..e357ff98b0f 100644 --- a/forge-ai/src/main/java/forge/ai/ability/RepeatEachAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/RepeatEachAi.java @@ -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 options, boolean isOptional, Player targetedPlayer) { + protected Card chooseSingleCard(Player ai, SpellAbility sa, Iterable options, boolean isOptional, Player targetedPlayer, Map params) { return ComputerUtilCard.getBestCreatureAI(options); } } diff --git a/forge-ai/src/main/java/forge/ai/ability/ScryAi.java b/forge-ai/src/main/java/forge/ai/ability/ScryAi.java index 1733c7eaee7..2ec63fb82ac 100644 --- a/forge-ai/src/main/java/forge/ai/ability/ScryAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/ScryAi.java @@ -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; diff --git a/forge-ai/src/main/java/forge/ai/ability/SetStateAi.java b/forge-ai/src/main/java/forge/ai/ability/SetStateAi.java index 7e03c1cf0f5..ca7404dee76 100644 --- a/forge-ai/src/main/java/forge/ai/ability/SetStateAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/SetStateAi.java @@ -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); } diff --git a/forge-ai/src/main/java/forge/ai/ability/TapAi.java b/forge-ai/src/main/java/forge/ai/ability/TapAi.java index a4eb933dbe4..54da422ee87 100644 --- a/forge-ai/src/main/java/forge/ai/ability/TapAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/TapAi.java @@ -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; diff --git a/forge-ai/src/main/java/forge/ai/ability/TokenAi.java b/forge-ai/src/main/java/forge/ai/ability/TokenAi.java index 5af081fbdaa..f6a62f567ef 100644 --- a/forge-ai/src/main/java/forge/ai/ability/TokenAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/TokenAi.java @@ -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 options) */ @Override - protected Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable options) { + protected Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable options, Map 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 options) */ @Override - protected GameEntity chooseSinglePlayerOrPlaneswalker(Player ai, SpellAbility sa, Iterable options) { + protected GameEntity chooseSinglePlayerOrPlaneswalker(Player ai, SpellAbility sa, Iterable options, Map params) { Combat combat = ai.getGame().getCombat(); // TokenAttacking if (combat != null && sa.hasParam("TokenAttacking")) { diff --git a/forge-ai/src/main/java/forge/ai/ability/UntapAi.java b/forge-ai/src/main/java/forge/ai/ability/UntapAi.java index 16170b99919..be7eb4f2ae9 100644 --- a/forge-ai/src/main/java/forge/ai/ability/UntapAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/UntapAi.java @@ -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 list, boolean isOptional, Player targetedPlayer) { + public Card chooseSingleCard(Player ai, SpellAbility sa, Iterable list, boolean isOptional, Player targetedPlayer, Map params) { PlayerCollection pl = new PlayerCollection(); pl.add(ai); pl.addAll(ai.getAllies()); diff --git a/forge-ai/src/main/java/forge/ai/simulation/GameCopier.java b/forge-ai/src/main/java/forge/ai/simulation/GameCopier.java index 903f6e474a9..1fac17f367a 100644 --- a/forge-ai/src/main/java/forge/ai/simulation/GameCopier.java +++ b/forge-ai/src/main/java/forge/ai/simulation/GameCopier.java @@ -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 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())); diff --git a/forge-ai/src/main/java/forge/ai/simulation/GameStateEvaluator.java b/forge-ai/src/main/java/forge/ai/simulation/GameStateEvaluator.java index f2d0f01bf85..e3dc9e8a64c 100644 --- a/forge-ai/src/main/java/forge/ai/simulation/GameStateEvaluator.java +++ b/forge-ai/src/main/java/forge/ai/simulation/GameStateEvaluator.java @@ -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; } diff --git a/forge-game/src/main/java/forge/game/GameAction.java b/forge-game/src/main/java/forge/game/GameAction.java index be845713549..6456b345b3d 100644 --- a/forge-game/src/main/java/forge/game/GameAction.java +++ b/forge-game/src/main/java/forge/game/GameAction.java @@ -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 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); diff --git a/forge-game/src/main/java/forge/game/GameActionUtil.java b/forge-game/src/main/java/forge/game/GameActionUtil.java index f9c54617a94..28b6a51e0f3 100644 --- a/forge-game/src/main/java/forge/game/GameActionUtil.java +++ b/forge-game/src/main/java/forge/game/GameActionUtil.java @@ -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; diff --git a/forge-game/src/main/java/forge/game/GameEntity.java b/forge-game/src/main/java/forge/game/GameEntity.java index f042378ec64..cbd00a9144f 100644 --- a/forge-game/src/main/java/forge/game/GameEntity.java +++ b/forge-game/src/main/java/forge/game/GameEntity.java @@ -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> preventionShieldsWithEffects = Maps.newTreeMap(); - protected Map counters = Maps.newEnumMap(CounterType.class); + protected Map 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 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) { diff --git a/forge-game/src/main/java/forge/game/GameLogFormatter.java b/forge-game/src/main/java/forge/game/GameLogFormatter.java index f098cb088e7..cb83ff742e6 100644 --- a/forge-game/src/main/java/forge/game/GameLogFormatter.java +++ b/forge-game/src/main/java/forge/game/GameLogFormatter.java @@ -95,7 +95,7 @@ public class GameLogFormatter extends IGameEventVisitor.Base { 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(); diff --git a/forge-game/src/main/java/forge/game/ability/AbilityUtils.java b/forge-game/src/main/java/forge/game/ability/AbilityUtils.java index 5005ecb07b2..331a181c550 100644 --- a/forge-game/src/main/java/forge/game/ability/AbilityUtils.java +++ b/forge-game/src/main/java/forge/game/ability/AbilityUtils.java @@ -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; } diff --git a/forge-game/src/main/java/forge/game/ability/SpellAbilityEffect.java b/forge-game/src/main/java/forge/game/ability/SpellAbilityEffect.java index 0dbd45f1a43..ae557bfd3fe 100644 --- a/forge-game/src/main/java/forge/game/ability/SpellAbilityEffect.java +++ b/forge-game/src/main/java/forge/game/ability/SpellAbilityEffect.java @@ -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; *

* AbilityFactory_AlterLife class. *

- * + * * @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 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 getTargetEntities(final SpellAbility sa) { return getEntities(false, "Defined", sa); } + protected final static List getTargetEntities(final SpellAbility sa, final String definedParam) { return getEntities(false, definedParam, sa); } + protected final static List getDefinedEntitiesOrTargeted(SpellAbility sa, final String definedParam) { return getEntities(true, definedParam, sa); } + + private static List 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 getTargets(final SpellAbility sa) { return getTargetables(false, "Defined", sa); } protected final static List getTargets(final SpellAbility sa, final String definedParam) { return getTargetables(false, definedParam, sa); } @@ -207,10 +220,10 @@ public abstract class SpellAbilityEffect { private static List 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 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) { diff --git a/forge-game/src/main/java/forge/game/ability/effects/AmassEffect.java b/forge-game/src/main/java/forge/game/ability/effects/AmassEffect.java index 45c8797362d..1612c2ae3da 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/AmassEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/AmassEffect.java @@ -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 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) { diff --git a/forge-game/src/main/java/forge/game/ability/effects/AttachEffect.java b/forge-game/src/main/java/forge/game/ability/effects/AttachEffect.java index ff0c74c1020..cd8325cdba9 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/AttachEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/AttachEffect.java @@ -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 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); diff --git a/forge-game/src/main/java/forge/game/ability/effects/BondEffect.java b/forge-game/src/main/java/forge/game/ability/effects/BondEffect.java index b41e6a265c5..4d9789bac9f 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/BondEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/BondEffect.java @@ -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 diff --git a/forge-game/src/main/java/forge/game/ability/effects/ChangeCombatantsEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ChangeCombatantsEffect.java index 5d9d68b9a53..0b807bdd2bc 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ChangeCombatantsEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ChangeCombatantsEffect.java @@ -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 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 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) { diff --git a/forge-game/src/main/java/forge/game/ability/effects/ChangeZoneEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ChangeZoneEffect.java index c68a48b9836..8711a63ad8e 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ChangeZoneEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ChangeZoneEffect.java @@ -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 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 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 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 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 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 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 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 params = Maps.newHashMap(); + params.put("Attacker", c); + defender = player.getController().chooseSingleEntityForEffect(e, sa, title, params); } if (defender != null) { combat.addAttacker(c, defender); diff --git a/forge-game/src/main/java/forge/game/ability/effects/CharmEffect.java b/forge-game/src/main/java/forge/game/ability/effects/CharmEffect.java index ee1cca795e7..faed7543bcc 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/CharmEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/CharmEffect.java @@ -189,7 +189,7 @@ public class CharmEffect extends SpellAbilityEffect { //String choosers = sa.getParam("Chooser"); FCollection 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); } diff --git a/forge-game/src/main/java/forge/game/ability/effects/ChooseCardEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ChooseCardEffect.java index 1a601c45a2a..3d55ced65bc 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ChooseCardEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ChooseCardEffect.java @@ -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)); } } } diff --git a/forge-game/src/main/java/forge/game/ability/effects/ChooseGenericEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ChooseGenericEffect.java index a1d21c94c0f..f5f59dfb4e5 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ChooseGenericEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ChooseGenericEffect.java @@ -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 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 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 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) { diff --git a/forge-game/src/main/java/forge/game/ability/effects/ChoosePlayerEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ChoosePlayerEffect.java index 0d57e4a4c2c..6f25e23d7b4 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ChoosePlayerEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ChoosePlayerEffect.java @@ -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); diff --git a/forge-game/src/main/java/forge/game/ability/effects/ChooseSourceEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ChooseSourceEffect.java index b35e17c0a25..34004b1a310 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ChooseSourceEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ChooseSourceEffect.java @@ -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); diff --git a/forge-game/src/main/java/forge/game/ability/effects/ClashEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ClashEffect.java index 191b1db43e9..a775ddd281d 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ClashEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ClashEffect.java @@ -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")) { diff --git a/forge-game/src/main/java/forge/game/ability/effects/CloneEffect.java b/forge-game/src/main/java/forge/game/ability/effects/CloneEffect.java index 9cf8118d66b..0b27812a57e 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/CloneEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/CloneEffect.java @@ -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 cloneSources = AbilityUtils.getDefinedCards(host, sa.getParam("Defined"), sa); if (!cloneSources.isEmpty()) { diff --git a/forge-game/src/main/java/forge/game/ability/effects/ControlExchangeVariantEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ControlExchangeVariantEffect.java index f39c38616d8..92db1453cb4 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ControlExchangeVariantEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ControlExchangeVariantEffect.java @@ -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)) { diff --git a/forge-game/src/main/java/forge/game/ability/effects/ControlGainEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ControlGainEffect.java index a69a02623f3..e0e2f7ca921 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ControlGainEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ControlGainEffect.java @@ -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 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 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); diff --git a/forge-game/src/main/java/forge/game/ability/effects/CopyPermanentEffect.java b/forge-game/src/main/java/forge/game/ability/effects/CopyPermanentEffect.java index cf1c6e9630c..c83bca2d61d 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/CopyPermanentEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/CopyPermanentEffect.java @@ -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); diff --git a/forge-game/src/main/java/forge/game/ability/effects/CopySpellAbilityEffect.java b/forge-game/src/main/java/forge/game/ability/effects/CopySpellAbilityEffect.java index 9f050cb5b0d..43e13b56480 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/CopySpellAbilityEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/CopySpellAbilityEffect.java @@ -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); diff --git a/forge-game/src/main/java/forge/game/ability/effects/CountersMoveEffect.java b/forge-game/src/main/java/forge/game/ability/effects/CountersMoveEffect.java index 30245cc137b..9bef78eab96 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/CountersMoveEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/CountersMoveEffect.java @@ -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 srcCards = getDefinedCardsOrTargeted(sa, "Source"); - - if (srcCards.size() > 0) { - source = srcCards.get(0); - } final List 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 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 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 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 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 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 tgtCounters = Maps.newHashMap(src.getCounters()); + for (Map.Entry 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 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 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 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 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 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 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 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 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 tgtCounters = source.getCounters(); - - final List typeChoices = Lists.newArrayList(); - // get types of counters - for (CounterType ct : tgtCounters.keySet()) { - if (dest.canReceiveCounters(ct)) { - typeChoices.add(ct); + Map countersToAdd = Maps.newHashMap(); + if ("All".equals(counterName)) { + final Map tgtCounters = Maps.newHashMap(source.getCounters()); + for (Map.Entry 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 tgtCounters = source.getCounters(); + + final List typeChoices = Lists.newArrayList(); + // get types of counters + for (CounterType ct : tgtCounters.keySet()) { + if (dest.canReceiveCounters(ct)) { + typeChoices.add(ct); + } + } + if (typeChoices.isEmpty()) { + return; + } + + Map 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 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 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 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 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); + } + } } diff --git a/forge-game/src/main/java/forge/game/ability/effects/CountersProliferateEffect.java b/forge-game/src/main/java/forge/game/ability/effects/CountersProliferateEffect.java index 587cc53995c..fd81398e5e9 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/CountersProliferateEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/CountersProliferateEffect.java @@ -43,7 +43,7 @@ public class CountersProliferateEffect extends SpellAbilityEffect { list.addAll(CardLists.filter(game.getCardsIn(ZoneType.Battlefield), CardPredicates.hasCounters())); List 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) { diff --git a/forge-game/src/main/java/forge/game/ability/effects/CountersPutAllEffect.java b/forge-game/src/main/java/forge/game/ability/effects/CountersPutAllEffect.java index d611df3baf0..4d69ba3d4e1 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/CountersPutAllEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/CountersPutAllEffect.java @@ -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); diff --git a/forge-game/src/main/java/forge/game/ability/effects/CountersPutEffect.java b/forge-game/src/main/java/forge/game/ability/effects/CountersPutEffect.java index 19e58dce90e..61d58891566 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/CountersPutEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/CountersPutEffect.java @@ -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 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 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 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 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 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 keywords = Arrays.asList(sa.getParam("SharedKeywords").split(" & ")); + List 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); + } + } } diff --git a/forge-game/src/main/java/forge/game/ability/effects/CountersPutOrRemoveEffect.java b/forge-game/src/main/java/forge/game/ability/effects/CountersPutOrRemoveEffect.java index 61e89d134ec..14298c46617 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/CountersPutOrRemoveEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/CountersPutOrRemoveEffect.java @@ -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(); diff --git a/forge-game/src/main/java/forge/game/ability/effects/CountersRemoveAllEffect.java b/forge-game/src/main/java/forge/game/ability/effects/CountersRemoveAllEffect.java index f53a1b58264..74110e12e3e 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/CountersRemoveAllEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/CountersRemoveAllEffect.java @@ -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); } } diff --git a/forge-game/src/main/java/forge/game/ability/effects/CountersRemoveEffect.java b/forge-game/src/main/java/forge/game/ability/effects/CountersRemoveEffect.java index 5698fb50bb0..286cf8589a7 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/CountersRemoveEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/CountersRemoveEffect.java @@ -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 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 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)); } } diff --git a/forge-game/src/main/java/forge/game/ability/effects/DigEffect.java b/forge-game/src/main/java/forge/game/ability/effects/DigEffect.java index bc4329777a0..343bb983c8d 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/DigEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/DigEffect.java @@ -166,7 +166,7 @@ public class DigEffect extends SpellAbilityEffect { if (sa.hasParam("Choser")) { final FCollectionView 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 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); diff --git a/forge-game/src/main/java/forge/game/ability/effects/DigMultipleEffect.java b/forge-game/src/main/java/forge/game/ability/effects/DigMultipleEffect.java index f4eb10f558c..9fc679605fd 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/DigMultipleEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/DigMultipleEffect.java @@ -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())); } diff --git a/forge-game/src/main/java/forge/game/ability/effects/DigUntilEffect.java b/forge-game/src/main/java/forge/game/ability/effects/DigUntilEffect.java index cb20a91cad8..eff16192f72 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/DigUntilEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/DigUntilEffect.java @@ -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 e = combat.getDefenders(); + Map 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); diff --git a/forge-game/src/main/java/forge/game/ability/effects/EffectEffect.java b/forge-game/src/main/java/forge/game/ability/effects/EffectEffect.java index 7e59dde1f78..326d56bf27d 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/EffectEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/EffectEffect.java @@ -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(CardLists.filter(Iterables.filter(rememberList, Card.class), CardPredicates.hasCounter(cType))); } diff --git a/forge-game/src/main/java/forge/game/ability/effects/EncodeEffect.java b/forge-game/src/main/java/forge/game/ability/effects/EncodeEffect.java index b2f88def518..fce3d683d35 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/EncodeEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/EncodeEffect.java @@ -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; diff --git a/forge-game/src/main/java/forge/game/ability/effects/ExploreEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ExploreEffect.java index c31e4db297a..212e6ad83fc 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ExploreEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ExploreEffect.java @@ -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); } } diff --git a/forge-game/src/main/java/forge/game/ability/effects/ManifestEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ManifestEffect.java index fed406415c2..e4c0b312ee0 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ManifestEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ManifestEffect.java @@ -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 { diff --git a/forge-game/src/main/java/forge/game/ability/effects/MeldEffect.java b/forge-game/src/main/java/forge/game/ability/effects/MeldEffect.java index b89a17b08e0..50443ae04b1 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/MeldEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/MeldEffect.java @@ -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); diff --git a/forge-game/src/main/java/forge/game/ability/effects/MultiplePilesEffect.java b/forge-game/src/main/java/forge/game/ability/effects/MultiplePilesEffect.java index ec3de6b43eb..57b74d34c9b 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/MultiplePilesEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/MultiplePilesEffect.java @@ -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); } diff --git a/forge-game/src/main/java/forge/game/ability/effects/MustBlockEffect.java b/forge-game/src/main/java/forge/game/ability/effects/MustBlockEffect.java index f83f720d8ad..1a950048965 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/MustBlockEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/MustBlockEffect.java @@ -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 cards; + if (sa.hasParam("DefinedAttacker")) { + cards = AbilityUtils.getDefinedCards(sa.getHostCard(), sa.getParam("DefinedAttacker"), sa); + } else { + cards = Lists.newArrayList(host); + } + List 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 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 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) { diff --git a/forge-game/src/main/java/forge/game/ability/effects/PlayEffect.java b/forge-game/src/main/java/forge/game/ability/effects/PlayEffect.java index c5b07d427b6..17a633afa6a 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/PlayEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/PlayEffect.java @@ -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; diff --git a/forge-game/src/main/java/forge/game/ability/effects/PoisonEffect.java b/forge-game/src/main/java/forge/game/ability/effects/PoisonEffect.java index 21d6f14715a..a5baf906e78 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/PoisonEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/PoisonEffect.java @@ -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("."); diff --git a/forge-game/src/main/java/forge/game/ability/effects/RepeatEachEffect.java b/forge-game/src/main/java/forge/game/ability/effects/RepeatEachEffect.java index 54e32d8e6d6..78a9aa8ae51 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/RepeatEachEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/RepeatEachEffect.java @@ -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> 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 { diff --git a/forge-game/src/main/java/forge/game/ability/effects/SacrificeEffect.java b/forge-game/src/main/java/forge/game/ability/effects/SacrificeEffect.java index f4a84e42a43..7e05cec8c9f 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/SacrificeEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/SacrificeEffect.java @@ -45,13 +45,13 @@ public class SacrificeEffect extends SpellAbilityEffect { } } else if (sa.hasParam("CumulativeUpkeep")) { GameEntityCounterTable table = new GameEntityCounterTable(); - card.addCounter(CounterType.AGE, 1, activator, true, table); + card.addCounter(CounterEnumType.AGE, 1, activator, true, table); table.triggerCountersPutAll(game); Cost cumCost = new Cost(sa.getParam("CumulativeUpkeep"), true); Cost payCost = new Cost(ManaCost.ZERO, true); - int n = card.getCounters(CounterType.AGE); + int n = card.getCounters(CounterEnumType.AGE); // multiply cost for (int i = 0; i < n; ++i) { diff --git a/forge-game/src/main/java/forge/game/ability/effects/SetStateEffect.java b/forge-game/src/main/java/forge/game/ability/effects/SetStateEffect.java index e25e9cc2ef3..56d064457b8 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/SetStateEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/SetStateEffect.java @@ -8,7 +8,7 @@ import forge.game.ability.SpellAbilityEffect; import forge.game.card.Card; import forge.game.card.CardCollection; import forge.game.card.CardUtil; -import forge.game.card.CounterType; +import forge.game.card.CounterEnumType; import forge.game.event.GameEventCardStatsChanged; import forge.game.player.Player; import forge.game.player.PlayerActionConfirmMode; @@ -125,7 +125,7 @@ public class SetStateEffect extends SpellAbilityEffect { } game.fireEvent(new GameEventCardStatsChanged(tgt)); if (sa.hasParam("Mega")) { - tgt.addCounter(CounterType.P1P1, 1, p, true, table); + tgt.addCounter(CounterEnumType.P1P1, 1, p, true, table); } if (remChanged) { host.addRemembered(tgt); diff --git a/forge-game/src/main/java/forge/game/ability/effects/TokenEffectBase.java b/forge-game/src/main/java/forge/game/ability/effects/TokenEffectBase.java index f4ad424f1a9..6768eb14849 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/TokenEffectBase.java +++ b/forge-game/src/main/java/forge/game/ability/effects/TokenEffectBase.java @@ -2,11 +2,13 @@ package forge.game.ability.effects; import java.util.Arrays; import java.util.List; +import java.util.Map; import org.apache.commons.lang3.mutable.MutableBoolean; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; +import com.google.common.collect.Maps; import com.google.common.collect.Sets; import forge.GameCommand; @@ -127,7 +129,10 @@ public abstract class TokenEffectBase extends SpellAbilityEffect { // into battlefield attacking only should work if you are the attacking player if (combat.getAttackingPlayer().equals(controller)) { final FCollectionView defs = combat.getDefenders(); - defender = controller.getController().chooseSingleEntityForEffect(defs, sa, Localizer.getInstance().getMessage("lblChooseDefenderToAttackWithCard", CardTranslation.getTranslatedName(c.getName())), false); + Map params = Maps.newHashMap(); + params.put("Attacker", c); + defender = controller.getController().chooseSingleEntityForEffect(defs, sa, + Localizer.getInstance().getMessage("lblChooseDefenderToAttackWithCard", CardTranslation.getTranslatedName(c.getName())), false, params); } } else { defender = Iterables.getFirst(AbilityUtils.getDefinedEntities(host, attacking, sa), null); diff --git a/forge-game/src/main/java/forge/game/ability/effects/TwoPilesEffect.java b/forge-game/src/main/java/forge/game/ability/effects/TwoPilesEffect.java index 9c4d2d881a3..d5b03eb83eb 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/TwoPilesEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/TwoPilesEffect.java @@ -95,7 +95,7 @@ public class TwoPilesEffect extends SpellAbilityEffect { card.clearRemembered(); // first, separate the cards into piles - final CardCollectionView pile1 = separator.getController().chooseCardsForEffect(pool, sa, title, 0, size, false); + final CardCollectionView pile1 = separator.getController().chooseCardsForEffect(pool, sa, title, 0, size, false, null); final CardCollection pile2 = new CardCollection(pool); pile2.removeAll(pile1); diff --git a/forge-game/src/main/java/forge/game/ability/effects/UntapEffect.java b/forge-game/src/main/java/forge/game/ability/effects/UntapEffect.java index 584a1739ffb..d2c1a94dbd5 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/UntapEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/UntapEffect.java @@ -81,7 +81,7 @@ public class UntapEffect extends SpellAbilityEffect { valid, sa.getActivatingPlayer(), sa.getHostCard()); list = CardLists.filter(list, Presets.TAPPED); - final CardCollectionView selected = p.getController().chooseCardsForEffect(list, sa, Localizer.getInstance().getMessage("lblSelectCardToUntap"), mandatory ? num : 0, num, !mandatory); + final CardCollectionView selected = p.getController().chooseCardsForEffect(list, sa, Localizer.getInstance().getMessage("lblSelectCardToUntap"), mandatory ? num : 0, num, !mandatory, null); if (selected != null) { for (final Card c : selected) { c.untap(); diff --git a/forge-game/src/main/java/forge/game/ability/effects/ZoneExchangeEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ZoneExchangeEffect.java index 1353090efd6..b2a41fed62d 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ZoneExchangeEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ZoneExchangeEffect.java @@ -70,7 +70,7 @@ public class ZoneExchangeEffect extends SpellAbilityEffect { return; } - Card object2 = p.getController().chooseSingleEntityForEffect(list, sa, Localizer.getInstance().getMessage("lblChooseaCard"), !sa.hasParam("Mandatory")); + Card object2 = p.getController().chooseSingleEntityForEffect(list, sa, Localizer.getInstance().getMessage("lblChooseaCard"), !sa.hasParam("Mandatory"), null); if (object2 == null || !object2.isInZone(zone2) || (type != null && !object2.getType().hasStringType(type))) { return; } diff --git a/forge-game/src/main/java/forge/game/card/Card.java b/forge-game/src/main/java/forge/game/card/Card.java index fb291b7f9a5..24cf16e6bef 100644 --- a/forge-game/src/main/java/forge/game/card/Card.java +++ b/forge-game/src/main/java/forge/game/card/Card.java @@ -93,7 +93,6 @@ public class Card extends GameEntity implements Comparable { private SpellAbility castSA = null; private final CardDamageHistory damageHistory = new CardDamageHistory(); - private Map> countersAddedBy = Maps.newTreeMap(); // Hidden keywords won't be displayed on the card private final KeywordCollection hiddenExtrinsicKeyword = new KeywordCollection(); @@ -126,7 +125,7 @@ public class Card extends GameEntity implements Comparable { private final Multimap cantHaveKeywords = MultimapBuilder.hashKeys().enumSetValues(Keyword.class).build(); - private final Map counterTypeTimestamps = Maps.newEnumMap(CounterType.class); + private final Map counterTypeTimestamps = Maps.newHashMap(); private final Map canBlockAdditional = Maps.newTreeMap(); private final Set canBlockAny = Sets.newHashSet(); @@ -264,8 +263,6 @@ public class Card extends GameEntity implements Comparable { // breaking when the LKI object is changed to a different card state. private int lkiCMC = -1; - private int countersAdded = 0; - private CardRules cardRules; private final CardView view; @@ -1171,7 +1168,6 @@ public class Card extends GameEntity implements Comparable { @Override public final boolean canReceiveCounters(final CounterType type) { - // CantPutCounter static abilities for (final Card ca : getGame().getCardsIn(ZoneType.STATIC_ABILITIES_SOURCE_ZONES)) { for (final StaticAbility stAb : ca.getStaticAbilities()) { @@ -1180,34 +1176,27 @@ public class Card extends GameEntity implements Comparable { } } } - - if (type == CounterType.DREAM) { - // need to be done extra because it is also a state based action - return !hasKeyword("CARDNAME can't have more than seven dream counters on it.") || getCounters(CounterType.DREAM) <= 6; - } return true; } - public final int getTotalCountersToAdd() { - return countersAdded; - } - - public final void setTotalCountersToAdd(int value) { - countersAdded = value; - } - public final int addCounter(final CounterType counterType, final int n, final Player source, final boolean applyMultiplier, GameEntityCounterTable table) { return addCounter(counterType, n, source, applyMultiplier, true, table); } public final int addCounterFireNoEvents(final CounterType counterType, final int n, final Player source, final boolean applyMultiplier, GameEntityCounterTable table) { return addCounter(counterType, n, source, applyMultiplier, false, table); } + public final int addCounter(final CounterEnumType counterType, final int n, final Player source, final boolean applyMultiplier, GameEntityCounterTable table) { + return addCounter(counterType, n, source, applyMultiplier, true, table); + } + public final int addCounterFireNoEvents(final CounterEnumType counterType, final int n, final Player source, final boolean applyMultiplier, GameEntityCounterTable table) { + return addCounter(counterType, n, source, applyMultiplier, false, table); + } @Override public int addCounter(final CounterType counterType, final int n, final Player source, final boolean applyMultiplier, final boolean fireEvents, GameEntityCounterTable table) { int addAmount = n; - if(addAmount <= 0) { - addAmount = 0; // As per rule 107.1b + if(addAmount <= 0 || !canReceiveCounters(counterType)) { + // As per rule 107.1b return 0; } final Map repParams = AbilityKey.mapFromAffected(this); @@ -1227,19 +1216,9 @@ public class Card extends GameEntity implements Comparable { return 0; } - if (canReceiveCounters(counterType)) { - if (counterType == CounterType.DREAM && hasKeyword("CARDNAME can't have more than seven dream counters on it.")) { - addAmount = Math.min(7 - getCounters(CounterType.DREAM), addAmount); - } - } - else { - addAmount = 0; - } - if (addAmount <= 0) { return 0; } - setTotalCountersToAdd(addAmount); final Integer oldValue = getCounters(counterType); final Integer newValue = addAmount + (oldValue == null ? 0 : oldValue); @@ -1304,7 +1283,7 @@ public class Card extends GameEntity implements Comparable { long timestamp = game.getNextTimestamp(); counterTypeTimestamps.put(counterType, timestamp); - addChangedCardKeywords(ImmutableList.of(counterType.getKeyword().toString()), null, false, false, timestamp, updateView); + addChangedCardKeywords(ImmutableList.of(counterType.toString()), null, false, false, timestamp, updateView); return true; } @@ -1320,38 +1299,6 @@ public class Card extends GameEntity implements Comparable { return old != null; } - /** - *

- * addCountersAddedBy. - *

- * @param source - the card adding the counters to this card - * @param counterType - the counter type added - * @param counterAmount - the amount of counters added - */ - public final void addCountersAddedBy(final Card source, final CounterType counterType, final int counterAmount) { - final Map counterMap = Maps.newTreeMap(); - counterMap.put(counterType, counterAmount); - countersAddedBy.put(source, counterMap); - } - - /** - *

- * getCountersAddedBy. - *

- * @param source - the card the counters were added by - * @param counterType - the counter type added - * @return the amount of counters added. - */ - public final int getCountersAddedBy(final Card source, final CounterType counterType) { - int counterAmount = 0; - if (countersAddedBy.containsKey(source)) { - final Map counterMap = countersAddedBy.get(source); - counterAmount = counterMap.containsKey(counterType) ? counterMap.get(counterType) : 0; - countersAddedBy.remove(source); - } - return counterAmount; - } - @Override public final void subtractCounter(final CounterType counterName, final int n) { int oldValue = getCounters(counterName); @@ -1419,7 +1366,7 @@ public class Card extends GameEntity implements Comparable { view.updateCounters(this); boolean changed = false; - for (CounterType ct : counterTypeTimestamps.keySet()) { + for (CounterType ct : Lists.newArrayList(counterTypeTimestamps.keySet())) { if (removeCounterTimestamp(ct, false)) { changed = true; } @@ -1691,7 +1638,7 @@ public class Card extends GameEntity implements Comparable { } else { s.append(getName()); s.append(" enters the battlefield with "); - s.append(Lang.nounWithNumeral(p[2], CounterType.valueOf(p[1]).getName() + " counter")); + s.append(Lang.nounWithNumeral(p[2], CounterType.getType(p[1]).getName() + " counter")); s.append(" on it."); } sbLong.append(s).append("\r\n"); @@ -2742,11 +2689,17 @@ public class Card extends GameEntity implements Comparable { public final void addFacedownCommand(final GameCommand c) { facedownCommandList.add(c); } - + public final void runUntapCommands() { + for (final GameCommand c : untapCommandList) { + c.run(); + } + untapCommandList.clear(); + } public final void runUnattachCommands() { for (final GameCommand c : unattachCommandList) { c.run(); } + unattachCommandList.clear(); } public final void runFaceupCommands() { @@ -2931,7 +2884,7 @@ public class Card extends GameEntity implements Comparable { return false; } - return CardLists.count(attachedCards, CardPredicates.Presets.EQUIPMENT) > 0; + return Iterables.any(attachedCards, CardPredicates.Presets.EQUIPMENT); } public final boolean isEquippedBy(Card c) { return this.hasCardAttachment(c); @@ -2953,7 +2906,7 @@ public class Card extends GameEntity implements Comparable { return false; } - return CardLists.count(attachedCards, CardPredicates.Presets.FORTIFICATION) > 0; + return Iterables.any(attachedCards, CardPredicates.Presets.FORTIFICATION); } public final boolean isFortifiedBy(Card c) { // 301.5e + 301.6 @@ -3258,7 +3211,7 @@ public class Card extends GameEntity implements Comparable { } public final int getCurrentLoyalty() { - return getCounters(CounterType.LOYALTY); + return getCounters(CounterEnumType.LOYALTY); } // values that are printed on card @@ -3509,9 +3462,9 @@ public class Card extends GameEntity implements Comparable { } public final int getPowerBonusFromCounters() { - return getCounters(CounterType.P1P1) + getCounters(CounterType.P1P2) + getCounters(CounterType.P1P0) - - getCounters(CounterType.M1M1) + 2 * getCounters(CounterType.P2P2) - 2 * getCounters(CounterType.M2M1) - - 2 * getCounters(CounterType.M2M2) - getCounters(CounterType.M1M0) + 2 * getCounters(CounterType.P2P0); + return getCounters(CounterEnumType.P1P1) + getCounters(CounterEnumType.P1P2) + getCounters(CounterEnumType.P1P0) + - getCounters(CounterEnumType.M1M1) + 2 * getCounters(CounterEnumType.P2P2) - 2 * getCounters(CounterEnumType.M2M1) + - 2 * getCounters(CounterEnumType.M2M2) - getCounters(CounterEnumType.M1M0) + 2 * getCounters(CounterEnumType.P2P0); } public final StatBreakdown getNetPowerBreakdown() { @@ -3567,10 +3520,10 @@ public class Card extends GameEntity implements Comparable { } public final int getToughnessBonusFromCounters() { - return getCounters(CounterType.P1P1) + 2 * getCounters(CounterType.P1P2) - getCounters(CounterType.M1M1) - + getCounters(CounterType.P0P1) - 2 * getCounters(CounterType.M0M2) + 2 * getCounters(CounterType.P2P2) - - getCounters(CounterType.M0M1) - getCounters(CounterType.M2M1) - 2 * getCounters(CounterType.M2M2) - + 2 * getCounters(CounterType.P0P2); + return getCounters(CounterEnumType.P1P1) + 2 * getCounters(CounterEnumType.P1P2) - getCounters(CounterEnumType.M1M1) + + getCounters(CounterEnumType.P0P1) - 2 * getCounters(CounterEnumType.M0M2) + 2 * getCounters(CounterEnumType.P2P2) + - getCounters(CounterEnumType.M0M1) - getCounters(CounterEnumType.M2M1) - 2 * getCounters(CounterEnumType.M2M2) + + 2 * getCounters(CounterEnumType.P0P2); } public final StatBreakdown getNetToughnessBreakdown() { @@ -3696,9 +3649,7 @@ public class Card extends GameEntity implements Comparable { // Run triggers getGame().getTriggerHandler().runTrigger(TriggerType.Untaps, AbilityKey.mapFromCard(this), false); - for (final GameCommand var : untapCommandList) { - var.run(); - } + runUntapCommands(); setTapped(false); getGame().fireEvent(new GameEventCardTapped(this, false)); } @@ -4266,7 +4217,7 @@ public class Card extends GameEntity implements Comparable { public final boolean hasSuspend() { return hasKeyword(Keyword.SUSPEND) && getLastKnownZone().is(ZoneType.Exile) - && getCounters(CounterType.TIME) >= 1; + && getCounters(CounterEnumType.TIME) >= 1; } public final boolean isPhasedOut() { @@ -5143,7 +5094,7 @@ public class Card extends GameEntity implements Comparable { GameEventCardDamaged.DamageType damageType = DamageType.Normal; if (isPlaneswalker()) { - subtractCounter(CounterType.LOYALTY, damageIn); + subtractCounter(CounterType.get(CounterEnumType.LOYALTY), damageIn); } if (isCreature()) { final Game game = source.getGame(); @@ -5153,7 +5104,7 @@ public class Card extends GameEntity implements Comparable { if (isInPlay()) { if (wither) { - addCounter(CounterType.M1M1, damageIn, source.getController(), true, counterTable); + addCounter(CounterType.get(CounterEnumType.M1M1), damageIn, source.getController(), true, counterTable); damageType = DamageType.M1M1Counters; } else { diff --git a/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java b/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java index e4f9d12fc83..a583250c15d 100644 --- a/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java +++ b/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java @@ -836,26 +836,11 @@ public class CardFactoryUtil { // Count$CountersAddedToPermYouCtrl if (l[0].startsWith("CountersAddedToPermYouCtrl")) { final String[] components = l[0].split(" ", 2); - final CounterType counterType = CounterType.valueOf(components[1]); + final CounterType counterType = CounterType.getType(components[1]); int n = cc.getCounterToPermThisTurn(counterType); return doXMath(n, m, c); } - // Count$CountersAdded - if (l[0].startsWith("CountersAdded")) { - final String[] components = l[0].split(" ", 3); - final CounterType counterType = CounterType.valueOf(components[1]); - String restrictions = components[2]; - final String[] rest = restrictions.split(","); - CardCollection candidates = CardLists.getValidCards(game.getCardsInGame(), rest, cc, c, null); - - int added = 0; - for (final Card counterSource : candidates) { - added += c.getCountersAddedBy(counterSource, counterType); - } - return doXMath(added, m, c); - } - if (l[0].startsWith("CommanderCastFromCommandZone")) { // only used by Opal Palace, and it does add the trigger to the card return doXMath(cc.getCommanderCast(c), m, c); @@ -2150,7 +2135,7 @@ public class CardFactoryUtil { String[] splitkw = parse.split(":"); String desc = "CARDNAME enters the battlefield with "; - desc += Lang.nounWithNumeral(splitkw[2], CounterType.valueOf(splitkw[1]).getName() + " counter"); + desc += Lang.nounWithNumeral(splitkw[2], CounterType.getType(splitkw[1]).getName() + " counter"); desc += " on it."; String extraparams = ""; @@ -3591,7 +3576,7 @@ public class CardFactoryUtil { } else if (keyword.equals("Sunburst")) { // Rule 702.43a If this object is entering the battlefield as a creature, // ignoring any type-changing effects that would affect it - CounterType t = card.isCreature() ? CounterType.P1P1 : CounterType.CHARGE; + CounterType t = CounterType.get(card.isCreature() ? CounterEnumType.P1P1 : CounterEnumType.CHARGE); StringBuilder sb = new StringBuilder("etbCounter:"); sb.append(t).append(":Sunburst:no Condition:"); @@ -4302,7 +4287,7 @@ public class CardFactoryUtil { int counters = AbilityUtils.calculateAmount(c, k[1], this); GameEntityCounterTable table = new GameEntityCounterTable(); - c.addCounter(CounterType.TIME, counters, getActivatingPlayer(), true, table); + c.addCounter(CounterEnumType.TIME, counters, getActivatingPlayer(), true, table); table.triggerCountersPutAll(game); String sb = TextUtil.concatWithSpace(getActivatingPlayer().toString(),"has suspended", c.getName(), "with", String.valueOf(counters),"time counters on it."); diff --git a/forge-game/src/main/java/forge/game/card/CardPredicates.java b/forge-game/src/main/java/forge/game/card/CardPredicates.java index 3994c04e3e7..6104f053fd2 100644 --- a/forge-game/src/main/java/forge/game/card/CardPredicates.java +++ b/forge-game/src/main/java/forge/game/card/CardPredicates.java @@ -329,6 +329,9 @@ public final class CardPredicates { public static final Predicate hasCounter(final CounterType type) { return hasCounter(type, 1); } + public static final Predicate hasCounter(final CounterEnumType type) { + return hasCounter(type, 1); + } public static final Predicate hasCounter(final CounterType type, final int n) { return new Predicate() { @@ -338,6 +341,9 @@ public final class CardPredicates { } }; } + public static final Predicate hasCounter(final CounterEnumType type, final int n) { + return hasCounter(CounterType.get(type), n); + } public static final Predicate hasLessCounter(final CounterType type, final int n) { return new Predicate() { @@ -348,6 +354,9 @@ public final class CardPredicates { } }; } + public static final Predicate hasLessCounter(final CounterEnumType type, final int n) { + return hasLessCounter(CounterType.get(type), n); + } public static Predicate canReceiveCounters(final CounterType counter) { return new Predicate() { @@ -357,6 +366,9 @@ public final class CardPredicates { } }; } + public static Predicate canReceiveCounters(final CounterEnumType counter) { + return canReceiveCounters(CounterType.get(counter)); + } public static final Predicate hasGreaterPowerThan(final int minPower) { return new Predicate() { @@ -376,6 +388,9 @@ public final class CardPredicates { } }; } + public static final Comparator compareByCounterType(final CounterEnumType type) { + return compareByCounterType(CounterType.get(type)); + } public static final Predicate hasSVar(final String name) { return new Predicate() { diff --git a/forge-game/src/main/java/forge/game/card/CardUtil.java b/forge-game/src/main/java/forge/game/card/CardUtil.java index a0af629a150..a88fb4ed226 100644 --- a/forge-game/src/main/java/forge/game/card/CardUtil.java +++ b/forge-game/src/main/java/forge/game/card/CardUtil.java @@ -244,7 +244,7 @@ public final class CardUtil { newCopy.setBasePower(in.getCurrentPower() + in.getTempPowerBoost()); newCopy.setBaseToughness(in.getCurrentToughness() + in.getTempToughnessBoost()); - newCopy.setCounters(Maps.newEnumMap(in.getCounters())); + newCopy.setCounters(Maps.newHashMap(in.getCounters())); newCopy.setColor(in.determineColor().getColor()); newCopy.setReceivedDamageFromThisTurn(in.getReceivedDamageFromThisTurn()); diff --git a/forge-game/src/main/java/forge/game/card/CounterEnumType.java b/forge-game/src/main/java/forge/game/card/CounterEnumType.java new file mode 100644 index 00000000000..23190fdafbb --- /dev/null +++ b/forge-game/src/main/java/forge/game/card/CounterEnumType.java @@ -0,0 +1,390 @@ +/* + * Forge: Play Magic: the Gathering. + * Copyright (C) 2011 Forge Team + * + * This program is free software: you can redistribute it and/or modify + * 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 . + */ + +package forge.game.card; + +import com.google.common.collect.ImmutableList; + +/** + * The class Counters. + * + * @author Clemens Koza + * @version V0.0 17.02.2010 + */ +public enum CounterEnumType { + + M1M1("-1/-1", "-1/-1", 255, 110, 106), + P1P1("+1/+1", "+1/+1", 96, 226, 23), + + LOYALTY("LOYAL", 198, 198, 198), + + AGE("AGE", 255, 137, 57), + + AIM("AIM", 255, 180, 0), + + ARROW("ARROW", 237, 195, 0), + + ARROWHEAD("ARWHD", 230, 191, 167), + + AWAKENING("AWAKE", 0, 231, 79), + + BLAZE("BLAZE", 255, 124, 82), + + BLOOD("BLOOD", 255, 108, 111), + + BOUNTY("BOUNT", 255, 158, 0), + + BRIBERY("BRIBE", 172, 201, 235), + + BRICK("BRICK", 226, 192, 164), + + CAGE("CAGE", 155, 155, 155), + + CARRION("CRRON", 255, 163, 222), + + CHARGE("CHARG", 246, 192, 0), + + COIN("COIN",255,215,0), + + CORPSE("CRPSE", 230, 186, 209), + + CREDIT("CRDIT", 188, 197, 234), + + CRYSTAL("CRYST", 255, 85, 206), + + CUBE("CUBE", 148, 219, 0), + + CURRENCY("CURR", 223, 200, 0), + + DEATH("DEATH", 255, 108, 110), + + DELAY("DELAY", 102, 206, 255), + + DEPLETION("DPLT", 185, 201, 208), + + DESPAIR("DESPR", 238, 186, 187), + + DEVOTION("DEVOT", 255, 111, 255), + + DIVINITY("DVNTY", 0, 233, 255), + + DOOM("DOOM", 255, 104, 118), + + DREAM("DREAM", 190, 189, 255), + + ECHO("ECHO", 225, 180, 255), + + EGG("EGG", 255, 245, 195), + + ELIXIR("ELIXR", 81, 221, 175), + + EON("EON", 23, 194, 255), + + EYEBALL("EYE", 184, 202, 201), + + FADE("FADE", 159, 209, 192), + + FATE("FATE", 255, 164, 226), + + FEATHER("FTHR", 195, 202, 165), + + FILIBUSTER("FLBTR", 255, 179, 119), + + FLAME("FLAME", 255, 143, 43), + + FLOOD("FLOOD", 0, 203, 255), + + FORESHADOW("FRSHD",144,99, 207), + + FUNGUS("FNGUS", 121, 219, 151), + + FURY("FURY", 255, 120, 89), + + FUSE("FUSE", 255, 122, 85), + + GEM("GEM", 255, 99, 251), + + GLYPH("GLYPH", 184, 202, 199), + + GOLD("GOLD", 248, 191, 0), + + GROWTH("GRWTH", 87, 226, 32), + + HATCHLING("HATCH", 201, 199, 186), + + HEALING("HEAL", 255, 166, 236), + + HIT("HIT", 255, 245, 195), + + HOOFPRINT("HOOF", 233, 189, 170), + + HOUR("HOUR", 198, 197, 210), + + HOURGLASS("HRGLS", 0, 215, 255), + + HUNGER("HUNGR", 255, 91, 149), + + ICE("ICE", 0, 239, 255), + + INFECTION("INFCT", 0, 230, 66), + + INTERVENTION("INTRV", 205, 203, 105), + + ISOLATION("ISOLT", 250, 190, 0), + + JAVELIN("JAVLN", 180, 206, 172), + + KI("KI", 190, 189, 255), + + KNOWLEDGE("KNOWLEDGE", 0, 115, 255), + + LANDMARK("LNMRK", 186, 28, 28), + + LEVEL("LEVEL", 60, 222, 185), + + LORE("LORE", 209, 198, 161), + + LUCK("LUCK", 185, 174, 255), + + M0M1("-0/-1", "-0/-1", 255, 110, 106), + + M0M2("-0/-2", "-0/-2", 255, 110, 106), + + M1M0("-1/-0", "-1/-0", 255, 110, 106), + + M2M1("-2/-1", "-2/-1", 255, 110, 106), + + M2M2("-2/-2", "-2/-2", 255, 110, 106), + + MAGNET("MAGNT", 198, 197, 210), + + MANA("MANA", 0, 237, 152), + + MANIFESTATION("MNFST", 104, 225, 8), + + MANNEQUIN("MANQN", 206, 199, 162), + + MATRIX("MATRX", 183, 174, 255), + + MINE("MINE", 255, 100, 127), + + MINING("MINNG", 184, 201, 207), + + MIRE("MIRE", 153, 209, 199), + + MUSIC("MUSIC", 255, 138, 255), + + MUSTER("MUSTR", 235, 196, 0), + + NET("NET", 0, 221, 251), + + OMEN("OMEN", 255, 178, 120), + + ORE("ORE", 200, 201, 163), + + PAGE("PAGE", 218, 195, 162), + + PAIN("PAIN", 255, 108, 111), + + PARALYZATION("PRLYZ", 220, 201, 0), + + PETAL("PETAL", 255, 162, 216), + + PETRIFICATION("PETRI", 185, 201, 208), + + PIN("PIN", 194, 196, 233), + + PLAGUE("PLGUE", 94, 226, 25), + + PLOT("PLOT", 255, 172, 133), + + PRESSURE("PRESS", 255, 164, 159), + + PHYLACTERY("PHYLA", 117, 219, 153), + + POLYP("POLYP", 236, 185, 198), + + PREY("PREY", 240, 0, 0), + + PUPA("PUPA", 0, 223, 203), + + P0P1("+0/+1", "+0/+1", 96, 226, 23), + + P0P2("+0/+2", "+0/+2", 96, 226, 23), + + P1P0("+1/+0", "+1/+0", 96, 226, 23), + + P1P2("+1/+2", "+1/+2", 96, 226, 23), + + P2P0("+2/+0", "+2/+0", 96, 226, 23), + + P2P2("+2/+2", "+2/+2", 96, 226, 23), + + QUEST("QUEST", 251, 189, 0), + + RUST("RUST", 255, 181, 116), + + SCREAM("SCREM", 0, 220, 255), + + SCROLL("SCRLL", 206, 199, 162), + + SHELL("SHELL", 190, 207, 111), + + SHIELD("SHLD", 202, 198, 186), + + SHRED("SHRED", 255, 165, 152), + + SILVER("SILVER", 192, 192, 192), + + SLEEP("SLEEP", 178, 192, 255), + + SLUMBER("SLMBR", 178, 205, 255), + + SLEIGHT("SLGHT", 185, 174, 255), + + SLIME("SLIME", 101, 220, 163), + + SOUL("SOUL", 243, 190, 247), + + SOOT("SOOT", 211, 194, 198), + + SPITE("SPITE", 0, 218, 255), + + SPORE("SPORE", 122, 218, 150), + + STORAGE("STORG", 255, 177, 121), + + STRIFE("STRFE", 255, 89, 223), + + STUDY("STUDY", 226, 192, 165), + + TASK("TASK", 191, 63, 49), + + THEFT("THEFT", 255, 176, 125), + + TIDE("TIDE", 0, 212, 187), + + TIME("TIME", 255, 121, 255), + + TOWER("tower", "TOWER", 0, 239, 255), + + TRAINING("TRAIN", 220, 201, 0), + + TRAP("TRAP", 255, 121, 86), + + TREASURE("TRSUR", 255, 184, 0), + + UNITY("UNITY", 242, 156, 255), + + VELOCITY("VELO", 255, 95, 138), + + VERSE("VERSE", 0, 237, 155), + + VITALITY("VITAL", 255, 94, 142), + + VORTEX("VORTX", 142, 200, 255), + + WAGE("WAGE", 242, 190, 106), + + WINCH("WINCH", 208, 195, 203), + + WIND("WIND", 0, 236, 255), + + WISH("WISH", 255, 85, 206), + + // Player Counters + + ENERGY("ENRGY"), + + EXPERIENCE("EXP"), + + POISON("POISN"), + + // Keyword Counters +/* + FLYING("Flying"), + FIRSTSTRIKE("First Strike"), + DOUBLESTRIKE("Double Strike"), + DEATHTOUCH("Deathtouch"), + HEXPROOF("Hexproof"), + INDESTRUCTIBLE("Indestructible"), + LIFELINK("Lifelink"), + MENACE("Menace"), + REACH("Reach"), + TRAMPLE("Trample"), + VIGILANCE("Vigilance") +//*/ + ; + + private String name, counterOnCardDisplayName; + private int red, green, blue; + + CounterEnumType() { + this.name = this.name().substring(0, 1).toUpperCase() + this.name().substring(1).toLowerCase(); + if (red == 0 && green == 0 && blue == 0) { + red = 255; + green = 255; + blue = 255; + } + } + + CounterEnumType(final String counterOnCardDisplayName) { + this(); + this.counterOnCardDisplayName = counterOnCardDisplayName; + } + + CounterEnumType(final String counterOnCardDisplayName, final int red, final int green, final int blue) { + this(counterOnCardDisplayName); + this.red = red; + this.green = green; + this.blue = blue; + } + + CounterEnumType(final String name, final String counterOnCardDisplayName, final int red, final int green, final int blue) { + this(counterOnCardDisplayName, red, green, blue); + this.name = name; + } + + public String getName() { + return this.name; + } + + public int getRed() { + return red; + } + + public int getGreen() { + return green; + } + + public int getBlue() { + return blue; + } + + public String getCounterOnCardDisplayName() { + return counterOnCardDisplayName; + } + + public static CounterEnumType getType(final String name) { + final String replacedName = name.replace("/", "").replaceAll("\\+", "p").replaceAll("\\-", "m").toUpperCase(); + return Enum.valueOf(CounterEnumType.class, replacedName); + } + + public static final ImmutableList values = ImmutableList.copyOf(values()); + +} diff --git a/forge-game/src/main/java/forge/game/card/CounterType.java b/forge-game/src/main/java/forge/game/card/CounterType.java index b754e28d976..22dfca9ebda 100644 --- a/forge-game/src/main/java/forge/game/card/CounterType.java +++ b/forge-game/src/main/java/forge/game/card/CounterType.java @@ -1,425 +1,130 @@ -/* - * Forge: Play Magic: the Gathering. - * Copyright (C) 2011 Forge Team - * - * This program is free software: you can redistribute it and/or modify - * 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 . - */ - package forge.game.card; +import java.util.Map; +import java.util.Objects; + +import org.apache.commons.lang3.builder.EqualsBuilder; + +import com.google.common.collect.ComparisonChain; import com.google.common.collect.ImmutableList; - -import forge.game.keyword.Keyword; - -/** - * The class Counters. - * - * @author Clemens Koza - * @version V0.0 17.02.2010 - */ -public enum CounterType { - - M1M1("-1/-1", "-1/-1", 255, 110, 106), - P1P1("+1/+1", "+1/+1", 96, 226, 23), - - LOYALTY("LOYAL", 198, 198, 198), - - AGE("AGE", 255, 137, 57), - - AIM("AIM", 255, 180, 0), - - ARROW("ARROW", 237, 195, 0), - - ARROWHEAD("ARWHD", 230, 191, 167), - - AWAKENING("AWAKE", 0, 231, 79), - - BLAZE("BLAZE", 255, 124, 82), - - BLOOD("BLOOD", 255, 108, 111), - - BOUNTY("BOUNT", 255, 158, 0), - - BRIBERY("BRIBE", 172, 201, 235), - - BRICK("BRICK", 226, 192, 164), - - CAGE("CAGE", 155, 155, 155), - - CARRION("CRRON", 255, 163, 222), - - CHARGE("CHARG", 246, 192, 0), - - COIN("COIN",255,215,0), - - CORPSE("CRPSE", 230, 186, 209), - - CREDIT("CRDIT", 188, 197, 234), - - CRYSTAL("CRYST", 255, 85, 206), - - CUBE("CUBE", 148, 219, 0), - - CURRENCY("CURR", 223, 200, 0), - - DEATH("DEATH", 255, 108, 110), - - DELAY("DELAY", 102, 206, 255), - - DEPLETION("DPLT", 185, 201, 208), - - DESPAIR("DESPR", 238, 186, 187), - - DEVOTION("DEVOT", 255, 111, 255), - - DIVINITY("DVNTY", 0, 233, 255), - - DOOM("DOOM", 255, 104, 118), - - DREAM("DREAM", 190, 189, 255), - - ECHO("ECHO", 225, 180, 255), - - EGG("EGG", 255, 245, 195), - - ELIXIR("ELIXR", 81, 221, 175), - - EON("EON", 23, 194, 255), - - EYEBALL("EYE", 184, 202, 201), - - FADE("FADE", 159, 209, 192), - - FATE("FATE", 255, 164, 226), - - FEATHER("FTHR", 195, 202, 165), - - FILIBUSTER("FLBTR", 255, 179, 119), - - FLAME("FLAME", 255, 143, 43), - - FLOOD("FLOOD", 0, 203, 255), - - FORESHADOW("FRSHD",144,99, 207), - - FUNGUS("FNGUS", 121, 219, 151), - - FURY("FURY", 255, 120, 89), - - FUSE("FUSE", 255, 122, 85), - - GEM("GEM", 255, 99, 251), - - GLYPH("GLYPH", 184, 202, 199), - - GOLD("GOLD", 248, 191, 0), - - GROWTH("GRWTH", 87, 226, 32), - - HATCHLING("HATCH", 201, 199, 186), - - HEALING("HEAL", 255, 166, 236), - - HIT("HIT", 255, 245, 195), - - HOOFPRINT("HOOF", 233, 189, 170), - - HOUR("HOUR", 198, 197, 210), - - HOURGLASS("HRGLS", 0, 215, 255), - - HUNGER("HUNGR", 255, 91, 149), - - ICE("ICE", 0, 239, 255), - - INFECTION("INFCT", 0, 230, 66), - - INTERVENTION("INTRV", 205, 203, 105), - - ISOLATION("ISOLT", 250, 190, 0), - - JAVELIN("JAVLN", 180, 206, 172), - - KI("KI", 190, 189, 255), - - KNOWLEDGE("KNOWLEDGE", 0, 115, 255), - - LANDMARK("LNMRK", 186, 28, 28), - - LEVEL("LEVEL", 60, 222, 185), - - LORE("LORE", 209, 198, 161), - - LUCK("LUCK", 185, 174, 255), - - M0M1("-0/-1", "-0/-1", 255, 110, 106), - - M0M2("-0/-2", "-0/-2", 255, 110, 106), - - M1M0("-1/-0", "-1/-0", 255, 110, 106), - - M2M1("-2/-1", "-2/-1", 255, 110, 106), - - M2M2("-2/-2", "-2/-2", 255, 110, 106), - - MAGNET("MAGNT", 198, 197, 210), - - MANA("MANA", 0, 237, 152), - - MANIFESTATION("MNFST", 104, 225, 8), - - MANNEQUIN("MANQN", 206, 199, 162), - - MATRIX("MATRX", 183, 174, 255), - - MINE("MINE", 255, 100, 127), - - MINING("MINNG", 184, 201, 207), - - MIRE("MIRE", 153, 209, 199), - - MUSIC("MUSIC", 255, 138, 255), - - MUSTER("MUSTR", 235, 196, 0), - - NET("NET", 0, 221, 251), - - OMEN("OMEN", 255, 178, 120), - - ORE("ORE", 200, 201, 163), - - PAGE("PAGE", 218, 195, 162), - - PAIN("PAIN", 255, 108, 111), - - PARALYZATION("PRLYZ", 220, 201, 0), - - PETAL("PETAL", 255, 162, 216), - - PETRIFICATION("PETRI", 185, 201, 208), - - PIN("PIN", 194, 196, 233), - - PLAGUE("PLGUE", 94, 226, 25), - - PLOT("PLOT", 255, 172, 133), - - PRESSURE("PRESS", 255, 164, 159), - - PHYLACTERY("PHYLA", 117, 219, 153), - - POLYP("POLYP", 236, 185, 198), - - PREY("PREY", 240, 0, 0), - - PUPA("PUPA", 0, 223, 203), - - P0P1("+0/+1", "+0/+1", 96, 226, 23), - - P0P2("+0/+2", "+0/+2", 96, 226, 23), - - P1P0("+1/+0", "+1/+0", 96, 226, 23), - - P1P2("+1/+2", "+1/+2", 96, 226, 23), - - P2P0("+2/+0", "+2/+0", 96, 226, 23), - - P2P2("+2/+2", "+2/+2", 96, 226, 23), - - QUEST("QUEST", 251, 189, 0), - - RUST("RUST", 255, 181, 116), - - SCREAM("SCREM", 0, 220, 255), - - SCROLL("SCRLL", 206, 199, 162), - - SHELL("SHELL", 190, 207, 111), - - SHIELD("SHLD", 202, 198, 186), - - SHRED("SHRED", 255, 165, 152), - - SILVER("SILVER", 192, 192, 192), - - SLEEP("SLEEP", 178, 192, 255), - - SLUMBER("SLMBR", 178, 205, 255), - - SLEIGHT("SLGHT", 185, 174, 255), - - SLIME("SLIME", 101, 220, 163), - - SOUL("SOUL", 243, 190, 247), - - SOOT("SOOT", 211, 194, 198), - - SPITE("SPITE", 0, 218, 255), - - SPORE("SPORE", 122, 218, 150), - - STORAGE("STORG", 255, 177, 121), - - STRIFE("STRFE", 255, 89, 223), - - STUDY("STUDY", 226, 192, 165), - - TASK("TASK", 191, 63, 49), - - THEFT("THEFT", 255, 176, 125), - - TIDE("TIDE", 0, 212, 187), - - TIME("TIME", 255, 121, 255), - - TOWER("tower", "TOWER", 0, 239, 255), - - TRAINING("TRAIN", 220, 201, 0), - - TRAP("TRAP", 255, 121, 86), - - TREASURE("TRSUR", 255, 184, 0), - - UNITY("UNITY", 242, 156, 255), - - VELOCITY("VELO", 255, 95, 138), - - VERSE("VERSE", 0, 237, 155), - - VITALITY("VITAL", 255, 94, 142), - - VORTEX("VORTX", 142, 200, 255), - - WAGE("WAGE", 242, 190, 106), - - WINCH("WINCH", 208, 195, 203), - - WIND("WIND", 0, 236, 255), - - WISH("WISH", 255, 85, 206), - - // Player Counters - - ENERGY("ENRGY"), - - EXPERIENCE("EXP"), - - POISON("POISN"), - - // Keyword Counters - - FLYING("Flying"), - FIRSTSTRIKE("First Strike"), - DOUBLESTRIKE("Double Strike"), - DEATHTOUCH("Deathtouch"), - HEXPROOF("Hexproof"), - INDESTRUCTIBLE("Indestructible"), - LIFELINK("Lifelink"), - MENACE("Menace"), - REACH("Reach"), - TRAMPLE("Trample"), - VIGILANCE("Vigilance") - - ; - - private String name, counterOnCardDisplayName; - private int red, green, blue; - - CounterType() { - this.name = this.name().substring(0, 1).toUpperCase() + this.name().substring(1).toLowerCase(); - if (red == 0 && green == 0 && blue == 0) { - red = 255; - green = 255; - blue = 255; +import com.google.common.collect.Maps; +import com.google.common.collect.Ordering; + +public class CounterType implements Comparable { + private CounterEnumType eVal = null; + private String sVal = null; + + // Rule 122.1b + static ImmutableList keywordCounter = ImmutableList.of( + "Flying", "First Strike", "Double Strike", "Deathtouch", "Haste", "Hexproof", + "Indestructible", "Lifelink", "Menace", "Reach", "Trample", "Vigilance"); + + private static Map eMap = Maps.newEnumMap(CounterEnumType.class); + private static Map sMap = Maps.newHashMap(); + + private CounterType(CounterEnumType e, String s) { + this.eVal = e; + this.sVal = s; + } + + public static CounterType get(CounterEnumType e) { + if (!eMap.containsKey(e)) { + eMap.put(e, new CounterType(e, null)); + } + return eMap.get(e); + } + + public static CounterType get(String s) { + if (!sMap.containsKey(s)) { + sMap.put(s, new CounterType(null, s)); + } + return sMap.get(s); + } + + public static CounterType getType(String name) { + try { + return get(CounterEnumType.getType(name)); + } catch (final IllegalArgumentException ex) { + return get(name); } } - CounterType(final String counterOnCardDisplayName) { - this(); - this.counterOnCardDisplayName = counterOnCardDisplayName; + + @Override + public int hashCode() { + return Objects.hash(eVal, sVal); } - CounterType(final String counterOnCardDisplayName, final int red, final int green, final int blue) { - this(counterOnCardDisplayName); - this.red = red; - this.green = green; - this.blue = blue; + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (this == obj) { + return true; + } + if (obj.getClass() != getClass()) { + return false; + } + CounterType rhs = (CounterType) obj; + return new EqualsBuilder() + .append(eVal, rhs.eVal) + .append(sVal, rhs.sVal) + .isEquals(); } - CounterType(final String name, final String counterOnCardDisplayName, final int red, final int green, final int blue) { - this(counterOnCardDisplayName, red, green, blue); - this.name = name; + @Override + public String toString() { + return eVal != null ? eVal.toString() : sVal; } public String getName() { - return this.name; - } - - public int getRed() { - return red; - } - - public int getGreen() { - return green; - } - - public int getBlue() { - return blue; + return eVal != null ? eVal.getName() : getKeywordDescription(); } public String getCounterOnCardDisplayName() { - return counterOnCardDisplayName; + return eVal != null ? eVal.getCounterOnCardDisplayName() : getKeywordDescription(); } - public static CounterType getType(final String name) { - final String replacedName = name.replace("/", "").replaceAll("\\+", "p").replaceAll("\\-", "m").toUpperCase(); - return Enum.valueOf(CounterType.class, replacedName); + private String getKeywordDescription() { + if (sVal.startsWith("Hexproof:")) { + final String[] k = sVal.split(":"); + return "Hexproof from " + k[2]; + } + return sVal; + } + + @Override + public int compareTo(CounterType o) { + return ComparisonChain.start() + .compare(eVal, o.eVal, Ordering.natural().nullsLast()) + .compare(sVal, o.sVal, Ordering.natural().nullsLast()) + .result(); + } + + public boolean is(CounterEnumType eType) { + return eVal == eType; } public boolean isKeywordCounter() { - return this.getKeyword() != null; - } - - public Keyword getKeyword() { - switch (this) { - case FLYING: - return Keyword.FLYING; - case FIRSTSTRIKE: - return Keyword.FIRST_STRIKE; - case DOUBLESTRIKE: - return Keyword.DOUBLE_STRIKE; - case DEATHTOUCH: - return Keyword.DEATHTOUCH; - case HEXPROOF: - return Keyword.HEXPROOF; - case INDESTRUCTIBLE: - return Keyword.INDESTRUCTIBLE; - case LIFELINK: - return Keyword.LIFELINK; - case MENACE: - return Keyword.MENACE; - case REACH: - return Keyword.REACH; - case TRAMPLE: - return Keyword.TRAMPLE; - case VIGILANCE: - return Keyword.VIGILANCE; - default: - return null; + if (eVal != null) { + return false; } + if (sVal.startsWith("Hexproof:")) { + return true; + } + return keywordCounter.contains(sVal); } - public static final ImmutableList values = ImmutableList.copyOf(values()); + public int getRed() { + return eVal != null ? eVal.getRed() : 255; + } + public int getGreen() { + return eVal != null ? eVal.getGreen() : 255; + } + + public int getBlue() { + return eVal != null ? eVal.getBlue() : 255; + } } diff --git a/forge-game/src/main/java/forge/game/combat/AttackConstraints.java b/forge-game/src/main/java/forge/game/combat/AttackConstraints.java index 181c27e769e..cee20c3d256 100644 --- a/forge-game/src/main/java/forge/game/combat/AttackConstraints.java +++ b/forge-game/src/main/java/forge/game/combat/AttackConstraints.java @@ -37,7 +37,7 @@ public class AttackConstraints { // Number of "must attack" constraints on each creature with a magnet counter (equal to the number of permanents requiring that constraint). int nMagnetRequirements = 0; - final CardCollectionView magnetAttackers = CardLists.filter(possibleAttackers, CardPredicates.hasCounter(CounterType.MAGNET)); + final CardCollectionView magnetAttackers = CardLists.filter(possibleAttackers, CardPredicates.hasCounter(CounterEnumType.MAGNET)); // Only require if a creature with a magnet counter on it attacks. if (!magnetAttackers.isEmpty()) { nMagnetRequirements = CardLists.getAmountOfKeyword( @@ -68,7 +68,7 @@ public class AttackConstraints { } } - if (possibleAttacker.getCounters(CounterType.MAGNET) > 0) { + if (possibleAttacker.getCounters(CounterEnumType.MAGNET) > 0) { for (final Card c : magnetAttackers) { if (c != possibleAttacker) { causesToAttack.add(c, nMagnetRequirements); diff --git a/forge-game/src/main/java/forge/game/cost/Cost.java b/forge-game/src/main/java/forge/game/cost/Cost.java index e8e92ce1aff..46c2ab66987 100644 --- a/forge-game/src/main/java/forge/game/cost/Cost.java +++ b/forge-game/src/main/java/forge/game/cost/Cost.java @@ -311,7 +311,7 @@ public class Cost implements Serializable { final String description = splitStr.length > 3 ? splitStr[3] : null; final ZoneType zone = splitStr.length > 4 ? ZoneType.smartValueOf(splitStr[4]) : ZoneType.Battlefield; - return new CostRemoveCounter(splitStr[0], CounterType.valueOf(splitStr[1]), type, description, zone); + return new CostRemoveCounter(splitStr[0], CounterType.getType(splitStr[1]), type, description, zone); } if (parse.startsWith("AddCounter<")) { @@ -319,7 +319,7 @@ public class Cost implements Serializable { final String[] splitStr = abCostParse(parse, 4); final String target = splitStr.length > 2 ? splitStr[2] : "CARDNAME"; final String description = splitStr.length > 3 ? splitStr[3] : null; - return new CostPutCounter(splitStr[0], CounterType.valueOf(splitStr[1]), target, description); + return new CostPutCounter(splitStr[0], CounterType.getType(splitStr[1]), target, description); } // While no card has "PayLife<2> PayLife<3> there might be a card that diff --git a/forge-game/src/main/java/forge/game/cost/CostPayEnergy.java b/forge-game/src/main/java/forge/game/cost/CostPayEnergy.java index 768a7876c15..5402e5e6749 100644 --- a/forge-game/src/main/java/forge/game/cost/CostPayEnergy.java +++ b/forge-game/src/main/java/forge/game/cost/CostPayEnergy.java @@ -21,7 +21,7 @@ import com.google.common.base.Strings; import forge.game.ability.AbilityUtils; 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; @@ -89,7 +89,7 @@ public class CostPayEnergy extends CostPart { } } - return payer.getCounters(CounterType.ENERGY) >= amount; + return payer.getCounters(CounterEnumType.ENERGY) >= amount; } @Override diff --git a/forge-game/src/main/java/forge/game/cost/CostPutCounter.java b/forge-game/src/main/java/forge/game/cost/CostPutCounter.java index 5ce5d63305d..d48ab6b73ed 100644 --- a/forge-game/src/main/java/forge/game/cost/CostPutCounter.java +++ b/forge-game/src/main/java/forge/game/cost/CostPutCounter.java @@ -21,6 +21,7 @@ import forge.game.GameEntityCounterTable; 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.player.Player; import forge.game.spellability.SpellAbility; @@ -78,7 +79,7 @@ public class CostPutCounter extends CostPartWithList { @Override public boolean isReusable() { - return counter != CounterType.M1M1; + return !counter.is(CounterEnumType.M1M1); } /* @@ -89,7 +90,7 @@ public class CostPutCounter extends CostPartWithList { @Override public final String toString() { final StringBuilder sb = new StringBuilder(); - if (this.counter == CounterType.LOYALTY) { + if (this.counter.is(CounterEnumType.LOYALTY)) { if (this.getAmount().equals("0")) { sb.append("0"); } diff --git a/forge-game/src/main/java/forge/game/cost/CostRemoveCounter.java b/forge-game/src/main/java/forge/game/cost/CostRemoveCounter.java index 7434d7add6f..d03d99cd45b 100644 --- a/forge-game/src/main/java/forge/game/cost/CostRemoveCounter.java +++ b/forge-game/src/main/java/forge/game/cost/CostRemoveCounter.java @@ -21,6 +21,7 @@ import com.google.common.collect.Lists; import forge.game.card.Card; import forge.game.card.CardLists; +import forge.game.card.CounterEnumType; import forge.game.card.CounterType; import forge.game.player.Player; import forge.game.spellability.SpellAbility; @@ -82,7 +83,7 @@ public class CostRemoveCounter extends CostPartWithList { @Override public final String toString() { final StringBuilder sb = new StringBuilder(); - if (this.counter == CounterType.LOYALTY) { + if (this.counter.is(CounterEnumType.LOYALTY)) { sb.append("-").append(this.getAmount()); } else { sb.append("Remove "); diff --git a/forge-game/src/main/java/forge/game/phase/PhaseHandler.java b/forge-game/src/main/java/forge/game/phase/PhaseHandler.java index 1e2ca2085d5..f74e2061a93 100644 --- a/forge-game/src/main/java/forge/game/phase/PhaseHandler.java +++ b/forge-game/src/main/java/forge/game/phase/PhaseHandler.java @@ -28,9 +28,9 @@ 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.CardPredicates.Presets; import forge.game.card.CardZoneTable; +import forge.game.card.CounterEnumType; import forge.game.combat.Combat; import forge.game.combat.CombatUtil; import forge.game.cost.Cost; @@ -268,7 +268,7 @@ public class PhaseHandler implements java.io.Serializable { // all Saga get Lore counter at the begin of pre combat for (Card c : playerTurn.getCardsIn(ZoneType.Battlefield)) { if (c.getType().hasSubtype("Saga")) { - c.addCounter(CounterType.LORE, 1, null, false, table); + c.addCounter(CounterEnumType.LORE, 1, null, false, table); } } table.triggerCountersPutAll(game); @@ -861,7 +861,7 @@ public class PhaseHandler implements java.io.Serializable { boolean untapTimeVault = nextPlayer.getController().chooseBinary(fakeSA, "Skip a turn to untap a Time Vault?", BinaryChoiceType.UntapTimeVault, false); if (untapTimeVault) { if (vaults.size() > 1) { - Card c = nextPlayer.getController().chooseSingleEntityForEffect(vaults, fakeSA, "Which Time Vault do you want to Untap?"); + Card c = nextPlayer.getController().chooseSingleEntityForEffect(vaults, fakeSA, "Which Time Vault do you want to Untap?", null); if (c != null) crd = c; } diff --git a/forge-game/src/main/java/forge/game/phase/Untap.java b/forge-game/src/main/java/forge/game/phase/Untap.java index 6b317fa3dff..6fe6668b151 100644 --- a/forge-game/src/main/java/forge/game/phase/Untap.java +++ b/forge-game/src/main/java/forge/game/phase/Untap.java @@ -185,7 +185,7 @@ public class Untap extends Phase { } } Card chosen = player.getController().chooseSingleEntityForEffect(cardList, new SpellAbility.EmptySa(ApiType.Untap, null, player), - "Select a card to untap\r\n(Selected:" + restrictUntapped + ")\r\n" + "Remaining cards that can untap: " + remaining); + "Select a card to untap\r\n(Selected:" + restrictUntapped + ")\r\n" + "Remaining cards that can untap: " + remaining, null); if (chosen != null) { for (Entry rest : restrictUntap.entrySet()) { if (chosen.isValid(rest.getKey(), player, null, null)) { diff --git a/forge-game/src/main/java/forge/game/player/Player.java b/forge-game/src/main/java/forge/game/player/Player.java index 5082c6e7b63..5c78d34c4b7 100644 --- a/forge-game/src/main/java/forge/game/player/Player.java +++ b/forge-game/src/main/java/forge/game/player/Player.java @@ -114,7 +114,7 @@ public class Player extends GameEntity implements Comparable { private CardCollection sacrificedThisTurn = new CardCollection(); - private Map countersAddedtoPermThisTurn = Maps.newEnumMap(CounterType.class); + private Map countersAddedtoPermThisTurn = Maps.newHashMap(); /** A list of tokens not in play, but on their way. * This list is kept in order to not break ETB-replacement @@ -544,17 +544,17 @@ public class Player extends GameEntity implements Comparable { } public final boolean canPayEnergy(final int energyPayment) { - int cnt = getCounters(CounterType.ENERGY); + int cnt = getCounters(CounterEnumType.ENERGY); return cnt >= energyPayment; } public final int loseEnergy(int lostEnergy) { - int cnt = getCounters(CounterType.ENERGY); + int cnt = getCounters(CounterEnumType.ENERGY); if (lostEnergy > cnt) { return -1; } cnt -= lostEnergy; - this.setCounters(CounterType.ENERGY, cnt, true); + this.setCounters(CounterEnumType.ENERGY, cnt, true); return cnt; } @@ -909,12 +909,8 @@ public class Player extends GameEntity implements Comparable { @Override public int addCounter(CounterType counterType, int n, final Player source, boolean applyMultiplier, boolean fireEvents, GameEntityCounterTable table) { - if (!canReceiveCounters(counterType)) { - return 0; - } - int addAmount = n; - if (addAmount <= 0) { + if (addAmount <= 0 || !canReceiveCounters(counterType)) { // Can't add negative or 0 counters, bail out now return 0; } @@ -935,6 +931,10 @@ public class Player extends GameEntity implements Comparable { default: return 0; } + if (addAmount <= 0) { + // Can't add negative or 0 counters, bail out now + return 0; + } final int oldValue = getCounters(counterType); final int newValue = addAmount + oldValue; @@ -985,6 +985,10 @@ public class Player extends GameEntity implements Comparable { getGame().fireEvent(new GameEventPlayerCounters(this, null, 0, 0)); } + public void setCounters(final CounterEnumType counterType, final Integer num, boolean fireEvents) { + this.setCounters(CounterType.get(counterType), num, fireEvents); + } + public void setCounters(final CounterType counterType, final Integer num, boolean fireEvents) { Integer old = getCounters(counterType); setCounters(counterType, num); @@ -1003,26 +1007,26 @@ public class Player extends GameEntity implements Comparable { // TODO Merge These calls into the primary counter calls public final int getPoisonCounters() { - return getCounters(CounterType.POISON); + return getCounters(CounterEnumType.POISON); } public final void setPoisonCounters(final int num, Card source) { - int oldPoison = getCounters(CounterType.POISON); - setCounters(CounterType.POISON, num, true); + int oldPoison = getCounters(CounterEnumType.POISON); + setCounters(CounterEnumType.POISON, num, true); game.fireEvent(new GameEventPlayerPoisoned(this, source, oldPoison, num)); } public final void addPoisonCounters(final int num, final Card source, GameEntityCounterTable table) { - int oldPoison = getCounters(CounterType.POISON); - addCounter(CounterType.POISON, num, source.getController(), false, true, table); + int oldPoison = getCounters(CounterEnumType.POISON); + addCounter(CounterEnumType.POISON, num, source.getController(), false, true, table); - if (oldPoison != getCounters(CounterType.POISON)) { + if (oldPoison != getCounters(CounterEnumType.POISON)) { game.fireEvent(new GameEventPlayerPoisoned(this, source, oldPoison, num)); } } public final void removePoisonCounters(final int num, final Card source) { - int oldPoison = getCounters(CounterType.POISON); - subtractCounter(CounterType.POISON, num); + int oldPoison = getCounters(CounterEnumType.POISON); + subtractCounter(CounterEnumType.POISON, num); - if (oldPoison != getCounters(CounterType.POISON)) { + if (oldPoison != getCounters(CounterEnumType.POISON)) { game.fireEvent(new GameEventPlayerPoisoned(this, source, oldPoison, num)); } } @@ -1989,7 +1993,7 @@ public class Player extends GameEntity implements Comparable { } // Rule 704.5c - If a player has ten or more poison counters, he or she loses the game. - if (getCounters(CounterType.POISON) >= 10) { + if (getCounters(CounterEnumType.POISON) >= 10) { return loseConditionMet(GameLossReason.Poisoned, null); } @@ -2867,7 +2871,7 @@ public class Player extends GameEntity implements Comparable { } } } - + public boolean allCardsUniqueManaSymbols() { for (final Card c : getCardsIn(ZoneType.Library)) { Set cardStateNames = c.isSplitCard() ? EnumSet.of(CardStateName.LeftSplit, CardStateName.RightSplit) : EnumSet.of(CardStateName.Original); @@ -2958,16 +2962,8 @@ public class Player extends GameEntity implements Comparable { CardCollectionView view = CardCollection.getView(legalCompanions); - Card firstCompanion = legalCompanions.get(0); - SpellAbility fakeSa = AbilityFactory.getAbility( - AbilityFactory.AbilityRecordType.Spell, - ApiType.CompanionChoose, - new HashMap<>(), - firstCompanion.getFirstSpellAbility().getPayCosts(), - firstCompanion, - null - ); - return controller.chooseSingleEntityForEffect(view, fakeSa, Localizer.getInstance().getMessage("lblChooseACompanion"), true); + SpellAbility fakeSa = new SpellAbility.EmptySa(ApiType.CompanionChoose, null, this); + return controller.chooseSingleEntityForEffect(view, fakeSa, Localizer.getInstance().getMessage("lblChooseACompanion"), true, null); } public boolean deckMatchesDeckRestriction(Card source, String restriction) { diff --git a/forge-game/src/main/java/forge/game/player/PlayerController.java b/forge-game/src/main/java/forge/game/player/PlayerController.java index 81a022dc51f..0ef21340298 100644 --- a/forge-game/src/main/java/forge/game/player/PlayerController.java +++ b/forge-game/src/main/java/forge/game/player/PlayerController.java @@ -106,15 +106,18 @@ public abstract class PlayerController { public abstract Pair chooseTarget(SpellAbility sa, List> allTargets); // Q: why is there min/max and optional at once? A: This is to handle cases like 'choose 3 to 5 cards or none at all' - public abstract CardCollectionView chooseCardsForEffect(CardCollectionView sourceList, SpellAbility sa, String title, int min, int max, boolean isOptional); + public abstract CardCollectionView chooseCardsForEffect(CardCollectionView sourceList, SpellAbility sa, String title, int min, int max, boolean isOptional, Map params); - public final T chooseSingleEntityForEffect(FCollectionView optionList, SpellAbility sa, String title) { return chooseSingleEntityForEffect(optionList, null, sa, title, false, null); } - public final T chooseSingleEntityForEffect(FCollectionView optionList, SpellAbility sa, String title, boolean isOptional) { return chooseSingleEntityForEffect(optionList, null, sa, title, isOptional, null); } - public abstract T chooseSingleEntityForEffect(FCollectionView optionList, DelayedReveal delayedReveal, SpellAbility sa, String title, boolean isOptional, Player relatedPlayer); + public final T chooseSingleEntityForEffect(FCollectionView optionList, SpellAbility sa, String title, Map params) { return chooseSingleEntityForEffect(optionList, null, sa, title, false, null, params); } + public final T chooseSingleEntityForEffect(FCollectionView optionList, SpellAbility sa, String title, boolean isOptional, Map params) { return chooseSingleEntityForEffect(optionList, null, sa, title, isOptional, null, params); } + public abstract T chooseSingleEntityForEffect(FCollectionView optionList, DelayedReveal delayedReveal, SpellAbility sa, String title, boolean isOptional, Player relatedPlayer, Map params); + + public abstract List chooseSpellAbilitiesForEffect(List spells, SpellAbility sa, String title, int num, Map params); + public abstract SpellAbility chooseSingleSpellForEffect(List spells, SpellAbility sa, String title, Map params); - public abstract List chooseEntitiesForEffect(FCollectionView optionList, int min, int max, DelayedReveal delayedReveal, SpellAbility sa, String title, Player relatedPlayer); + public abstract List chooseEntitiesForEffect(FCollectionView optionList, int min, int max, DelayedReveal delayedReveal, SpellAbility sa, String title, Player relatedPlayer, Map params); public abstract boolean confirmAction(SpellAbility sa, PlayerActionConfirmMode mode, String message); public abstract boolean confirmBidAction(SpellAbility sa, PlayerActionConfirmMode bidlife, String string, int bid, Player winner); diff --git a/forge-game/src/main/java/forge/game/player/PlayerPredicates.java b/forge-game/src/main/java/forge/game/player/PlayerPredicates.java index 01c1d0d35cc..a8068a2e0d7 100644 --- a/forge-game/src/main/java/forge/game/player/PlayerPredicates.java +++ b/forge-game/src/main/java/forge/game/player/PlayerPredicates.java @@ -7,6 +7,7 @@ import com.google.common.base.Predicates; import forge.game.card.Card; import forge.game.card.CardLists; +import forge.game.card.CounterEnumType; import forge.game.card.CounterType; import forge.game.spellability.SpellAbility; import forge.game.zone.ZoneType; @@ -102,6 +103,14 @@ public final class PlayerPredicates { } }; } + + public static final Predicate hasCounter(final CounterEnumType type) { + return hasCounter(CounterType.get(type), 1); + } + + public static final Predicate hasCounter(final CounterEnumType type, final int n) { + return hasCounter(CounterType.get(type), n); + } public static final Predicate hasKeyword(final String keyword) { return new Predicate() { diff --git a/forge-game/src/main/java/forge/game/player/PlayerView.java b/forge-game/src/main/java/forge/game/player/PlayerView.java index f90a121977b..1bf7565454b 100644 --- a/forge-game/src/main/java/forge/game/player/PlayerView.java +++ b/forge-game/src/main/java/forge/game/player/PlayerView.java @@ -20,6 +20,7 @@ import forge.card.MagicColor; import forge.game.GameEntityView; import forge.game.card.Card; import forge.game.card.CardView; +import forge.game.card.CounterEnumType; import forge.game.zone.PlayerZone; import forge.game.zone.ZoneType; import forge.trackable.TrackableCollection; @@ -194,6 +195,9 @@ public class PlayerView extends GameEntityView { } return 0; } + public int getCounters(CounterEnumType counterType) { + return getCounters(CounterType.get(counterType)); + } void updateCounters(Player p) { set(TrackableProperty.Counters, p.getCounters()); } diff --git a/forge-game/src/main/java/forge/game/spellability/AbilityManaPart.java b/forge-game/src/main/java/forge/game/spellability/AbilityManaPart.java index 67ea16a993e..ddee3945b53 100644 --- a/forge-game/src/main/java/forge/game/spellability/AbilityManaPart.java +++ b/forge-game/src/main/java/forge/game/spellability/AbilityManaPart.java @@ -261,7 +261,7 @@ public class AbilityManaPart implements java.io.Serializable { CardFactoryUtil.setupETBReplacementAbility(sa); String desc = "It enters the battlefield with "; - desc += Lang.nounWithNumeral(parse[2], CounterType.valueOf(parse[1]).getName() + " counter"); + desc += Lang.nounWithNumeral(parse[2], CounterType.getType(parse[1]).getName() + " counter"); desc += " on it."; String repeffstr = "Event$ Moved | ValidCard$ Card.IsRemembered | Destination$ Battlefield | Description$ " + desc; diff --git a/forge-game/src/main/java/forge/game/spellability/SpellAbility.java b/forge-game/src/main/java/forge/game/spellability/SpellAbility.java index d98d29df52b..7ae20008dcd 100644 --- a/forge-game/src/main/java/forge/game/spellability/SpellAbility.java +++ b/forge-game/src/main/java/forge/game/spellability/SpellAbility.java @@ -1099,6 +1099,17 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit } } + if (tr.isSameController()) { + Player newController; + if (entity instanceof Card) { + newController = ((Card) entity).getController(); + for (final Card c : targetChosen.getTargetCards()) { + if (entity != c && !c.getController().equals(newController)) + return false; + } + } + } + if (tr.isDifferentControllers()) { Player newController; if (entity instanceof Card) { diff --git a/forge-game/src/main/java/forge/game/spellability/TargetChoices.java b/forge-game/src/main/java/forge/game/spellability/TargetChoices.java index 4d1c812d141..1559a847038 100644 --- a/forge-game/src/main/java/forge/game/spellability/TargetChoices.java +++ b/forge-game/src/main/java/forge/game/spellability/TargetChoices.java @@ -19,6 +19,7 @@ package forge.game.spellability; import com.google.common.collect.Iterables; +import forge.game.GameEntity; import forge.game.GameObject; import forge.game.card.Card; import forge.game.card.CardCollection; @@ -116,6 +117,14 @@ public class TargetChoices implements Cloneable { return targetSpells; } + public final List getTargetEntities() { + final List tgts = new ArrayList<>(); + tgts.addAll(targetPlayers); + tgts.addAll(targetCards); + + return tgts; + } + public final List getTargets() { final List tgts = new ArrayList<>(); tgts.addAll(targetPlayers); diff --git a/forge-game/src/main/java/forge/game/staticability/StaticAbilityCantPutCounter.java b/forge-game/src/main/java/forge/game/staticability/StaticAbilityCantPutCounter.java index c66923a5168..37952b2e563 100644 --- a/forge-game/src/main/java/forge/game/staticability/StaticAbilityCantPutCounter.java +++ b/forge-game/src/main/java/forge/game/staticability/StaticAbilityCantPutCounter.java @@ -11,7 +11,7 @@ public class StaticAbilityCantPutCounter { final Card hostCard = staticAbility.getHostCard(); if (staticAbility.hasParam("CounterType")) { - CounterType t = CounterType.valueOf(staticAbility.getParam("CounterType")); + CounterType t = CounterType.getType(staticAbility.getParam("CounterType")); if (t != null && !type.equals(t)) { return false; } @@ -34,7 +34,7 @@ public class StaticAbilityCantPutCounter { final Card hostCard = staticAbility.getHostCard(); if (staticAbility.hasParam("CounterType")) { - CounterType t = CounterType.valueOf(staticAbility.getParam("CounterType")); + CounterType t = CounterType.getType(staticAbility.getParam("CounterType")); if (t != null && !type.equals(t)) { return false; } diff --git a/forge-game/src/main/java/forge/game/trigger/WrappedAbility.java b/forge-game/src/main/java/forge/game/trigger/WrappedAbility.java index 55532468591..68ee4cdd483 100644 --- a/forge-game/src/main/java/forge/game/trigger/WrappedAbility.java +++ b/forge-game/src/main/java/forge/game/trigger/WrappedAbility.java @@ -563,4 +563,11 @@ public class WrappedAbility extends Ability { public void setXManaCostPaid(final Integer n) { sa.setXManaCostPaid(n); } + + public Card getOriginalHost() { + return sa.getOriginalHost(); + } + public void setOriginalHost(final Card c) { + sa.setOriginalHost(c); + } } \ No newline at end of file diff --git a/forge-game/src/main/java/forge/trackable/TrackableTypes.java b/forge-game/src/main/java/forge/trackable/TrackableTypes.java index c9aeae9ca53..38ba05d4b47 100644 --- a/forge-game/src/main/java/forge/trackable/TrackableTypes.java +++ b/forge-game/src/main/java/forge/trackable/TrackableTypes.java @@ -584,9 +584,9 @@ public class TrackableTypes { public Map deserialize(TrackableDeserializer td, Map oldValue) { int size = td.readInt(); if (size > 0) { - Map map = Maps.newEnumMap(CounterType.class); + Map map = Maps.newHashMap(); for (int i = 0; i < size; i++) { - map.put(CounterType.valueOf(td.readString()), td.readInt()); + map.put(CounterType.getType(td.readString()), td.readInt()); } return map; } @@ -597,7 +597,7 @@ public class TrackableTypes { public void serialize(TrackableSerializer ts, Map value) { ts.write(value.size()); for (Entry entry : value.entrySet()) { - ts.write(entry.getKey().name()); + ts.write(entry.getKey().toString()); ts.write(entry.getValue()); } } diff --git a/forge-gui-desktop/src/main/java/forge/screens/match/VAssignCombatDamage.java b/forge-gui-desktop/src/main/java/forge/screens/match/VAssignCombatDamage.java index 760fe13890c..673e4950c89 100644 --- a/forge-gui-desktop/src/main/java/forge/screens/match/VAssignCombatDamage.java +++ b/forge-gui-desktop/src/main/java/forge/screens/match/VAssignCombatDamage.java @@ -40,7 +40,7 @@ import com.google.common.collect.Maps; import forge.game.GameEntityView; import forge.game.card.CardView; -import forge.game.card.CounterType; +import forge.game.card.CounterEnumType; import forge.game.player.PlayerView; import forge.gui.SOverlayUtils; import forge.toolbox.FButton; @@ -433,7 +433,7 @@ public class VAssignCombatDamage { if (card == null) { if (defender instanceof PlayerView) { final PlayerView p = (PlayerView)defender; - lethalDamage = attackerHasInfect ? matchUI.getGameView().getPoisonCountersToLose() - p.getCounters(CounterType.POISON) : p.getLife(); + lethalDamage = attackerHasInfect ? matchUI.getGameView().getPoisonCountersToLose() - p.getCounters(CounterEnumType.POISON) : p.getLife(); } else if (defender instanceof CardView) { // planeswalker final CardView pw = (CardView)defender; diff --git a/forge-gui-desktop/src/main/java/forge/screens/match/views/VField.java b/forge-gui-desktop/src/main/java/forge/screens/match/views/VField.java index c00993e0474..308b8e73f5e 100644 --- a/forge-gui-desktop/src/main/java/forge/screens/match/views/VField.java +++ b/forge-gui-desktop/src/main/java/forge/screens/match/views/VField.java @@ -29,7 +29,7 @@ import javax.swing.border.LineBorder; import net.miginfocom.swing.MigLayout; import forge.assets.FSkinProp; -import forge.game.card.CounterType; +import forge.game.card.CounterEnumType; import forge.game.player.PlayerView; import forge.game.zone.ZoneType; import forge.gui.framework.DragCell; @@ -285,9 +285,9 @@ public class VField implements IVDoc { } // Update poison and/or energy counters, poison counters take precedence - final int poison = player.getCounters(CounterType.POISON); - final int energy = player.getCounters(CounterType.ENERGY); - final int experience = player.getCounters(CounterType.EXPERIENCE); + final int poison = player.getCounters(CounterEnumType.POISON); + final int energy = player.getCounters(CounterEnumType.ENERGY); + final int experience = player.getCounters(CounterEnumType.EXPERIENCE); if (poison > 0) { removeLblEnergy(); diff --git a/forge-gui-desktop/src/test/java/forge/ai/simulation/GameSimulatorTest.java b/forge-gui-desktop/src/test/java/forge/ai/simulation/GameSimulatorTest.java index 6ade05b1eb0..9b847db1c8b 100644 --- a/forge-gui-desktop/src/test/java/forge/ai/simulation/GameSimulatorTest.java +++ b/forge-gui-desktop/src/test/java/forge/ai/simulation/GameSimulatorTest.java @@ -8,7 +8,7 @@ import forge.game.Game; import forge.game.ability.ApiType; import forge.game.card.Card; import forge.game.card.CardCollection; -import forge.game.card.CounterType; +import forge.game.card.CounterEnumType; import forge.game.keyword.Keyword; import forge.game.phase.PhaseType; import forge.game.player.Player; @@ -217,7 +217,7 @@ public class GameSimulatorTest extends SimulationTestCase { Game game = initAndCreateGame(); Player p = game.getPlayers().get(1); Card sorin = addCard("Sorin, Solemn Visitor", p); - sorin.addCounter(CounterType.LOYALTY, 5, p, false, null); + sorin.addCounter(CounterEnumType.LOYALTY, 5, p, false, null); game.getPhaseHandler().devModeSet(PhaseType.MAIN2, p); game.getAction().checkStateEffects(true); @@ -261,7 +261,7 @@ public class GameSimulatorTest extends SimulationTestCase { String bearCardName = "Runeclaw Bear"; addCard(bearCardName, p); Card gideon = addCard("Gideon, Ally of Zendikar", p); - gideon.addCounter(CounterType.LOYALTY, 4, p, false, null); + gideon.addCounter(CounterEnumType.LOYALTY, 4, p, false, null); game.getPhaseHandler().devModeSet(PhaseType.MAIN2, p); game.getAction().checkStateEffects(true); @@ -380,7 +380,7 @@ public class GameSimulatorTest extends SimulationTestCase { Game game = initAndCreateGame(); Player p = game.getPlayers().get(1); Card sarkhan = addCard(sarkhanCardName, p); - sarkhan.addCounter(CounterType.LOYALTY, 4, p, false, null); + sarkhan.addCounter(CounterEnumType.LOYALTY, 4, p, false, null); game.getPhaseHandler().devModeSet(PhaseType.MAIN2, p); game.getAction().checkStateEffects(true); @@ -414,7 +414,7 @@ public class GameSimulatorTest extends SimulationTestCase { addCard(ornithoperCardName, p); addCard(bearCardName, p); Card ajani = addCard(ajaniCardName, p); - ajani.addCounter(CounterType.LOYALTY, 4, p, false, null); + ajani.addCounter(CounterEnumType.LOYALTY, 4, p, false, null); game.getPhaseHandler().devModeSet(PhaseType.MAIN2, p); game.getAction().checkStateEffects(true); @@ -429,7 +429,7 @@ public class GameSimulatorTest extends SimulationTestCase { Game simGame = sim.getSimulatedGameState(); Card thopterSim = findCardWithName(simGame, ornithoperCardName); Card bearSim = findCardWithName(simGame, bearCardName); - assertEquals(3, thopterSim.getCounters(CounterType.P1P1) + bearSim.getCounters(CounterType.P1P1)); + assertEquals(3, thopterSim.getCounters(CounterEnumType.P1P1) + bearSim.getCounters(CounterEnumType.P1P1)); } } @@ -445,7 +445,7 @@ public class GameSimulatorTest extends SimulationTestCase { SpellAbility boltSA = boltCard.getFirstSpellAbility(); Card ajani = addCard(ajaniCardName, p); - ajani.addCounter(CounterType.LOYALTY, 8, p, false, null); + ajani.addCounter(CounterEnumType.LOYALTY, 8, p, false, null); game.getPhaseHandler().devModeSet(PhaseType.MAIN2, p); game.getAction().checkStateEffects(true); @@ -465,7 +465,7 @@ public class GameSimulatorTest extends SimulationTestCase { // only triggered once assertTrue(simSelfless.hasCounters()); - assertEquals(2, simSelfless.getCounters(CounterType.P1P1)); + assertEquals(2, simSelfless.getCounters(CounterEnumType.P1P1)); assertEquals(2, simSelfless.getToughnessBonusFromCounters()); assertEquals(2, simSelfless.getPowerBonusFromCounters()); } @@ -494,7 +494,7 @@ public class GameSimulatorTest extends SimulationTestCase { addCard("Swamp", p); addCard("Swamp", p); Card depths = addCard("Dark Depths", p); - depths.addCounter(CounterType.ICE, 10, p, false, null); + depths.addCounter(CounterEnumType.ICE, 10, p, false, null); Card thespian = addCard("Thespian's Stage", p); game.getPhaseHandler().devModeSet(PhaseType.MAIN2, p); game.getAction().checkStateEffects(true); @@ -693,7 +693,7 @@ public class GameSimulatorTest extends SimulationTestCase { // only triggered once assertTrue(simPridemate.hasCounters()); - assertEquals(1, simPridemate.getCounters(CounterType.P1P1)); + assertEquals(1, simPridemate.getCounters(CounterEnumType.P1P1)); assertEquals(1, simPridemate.getToughnessBonusFromCounters()); assertEquals(1, simPridemate.getPowerBonusFromCounters()); @@ -749,7 +749,7 @@ public class GameSimulatorTest extends SimulationTestCase { // only triggered once assertTrue(simPridemate.hasCounters()); - assertEquals(1, simPridemate.getCounters(CounterType.P1P1)); + assertEquals(1, simPridemate.getCounters(CounterEnumType.P1P1)); assertEquals(1, simPridemate.getToughnessBonusFromCounters()); assertEquals(1, simPridemate.getPowerBonusFromCounters()); @@ -813,7 +813,7 @@ public class GameSimulatorTest extends SimulationTestCase { // only triggered once assertTrue(simPridemate.hasCounters()); - assertEquals(1, simPridemate.getCounters(CounterType.P1P1)); + assertEquals(1, simPridemate.getCounters(CounterEnumType.P1P1)); assertEquals(1, simPridemate.getToughnessBonusFromCounters()); assertEquals(1, simPridemate.getPowerBonusFromCounters()); @@ -873,7 +873,7 @@ public class GameSimulatorTest extends SimulationTestCase { // lifegain assertNotNull(simPridemate); assertTrue(simPridemate.hasCounters()); - assertEquals(1, simPridemate.getCounters(CounterType.P1P1)); + assertEquals(1, simPridemate.getCounters(CounterEnumType.P1P1)); assertEquals(1, simPridemate.getToughnessBonusFromCounters()); assertEquals(1, simPridemate.getPowerBonusFromCounters()); @@ -901,21 +901,21 @@ public class GameSimulatorTest extends SimulationTestCase { // no Lifegain because of Everlasting Torment assertNotNull(simPridemate2); assertFalse(simPridemate2.hasCounters()); - assertEquals(0, simPridemate2.getCounters(CounterType.P1P1)); + assertEquals(0, simPridemate2.getCounters(CounterEnumType.P1P1)); assertEquals(0, simPridemate2.getToughnessBonusFromCounters()); assertEquals(0, simPridemate2.getPowerBonusFromCounters()); assertNotNull(simBear2); assertEquals(0, simBear2.getDamage()); assertTrue(simBear2.hasCounters()); - assertEquals(1, simBear2.getCounters(CounterType.M1M1)); + assertEquals(1, simBear2.getCounters(CounterEnumType.M1M1)); assertEquals(-1, simBear2.getToughnessBonusFromCounters()); assertEquals(-1, simBear2.getPowerBonusFromCounters()); assertNotNull(simGiant2); assertEquals(0, simGiant2.getDamage()); assertTrue(simGiant2.hasCounters()); - assertEquals(2, simGiant2.getCounters(CounterType.M1M1)); + assertEquals(2, simGiant2.getCounters(CounterEnumType.M1M1)); assertEquals(-2, simGiant2.getToughnessBonusFromCounters()); assertEquals(-2, simGiant2.getPowerBonusFromCounters()); @@ -937,21 +937,21 @@ public class GameSimulatorTest extends SimulationTestCase { // no Lifegain because of Everlasting Torment assertNotNull(simPridemate3); assertFalse(simPridemate3.hasCounters()); - assertEquals(0, simPridemate3.getCounters(CounterType.P1P1)); + assertEquals(0, simPridemate3.getCounters(CounterEnumType.P1P1)); assertEquals(0, simPridemate3.getToughnessBonusFromCounters()); assertEquals(0, simPridemate3.getPowerBonusFromCounters()); assertNotNull(simBear3); assertEquals(0, simBear3.getDamage()); assertFalse(simBear3.hasCounters()); - assertEquals(0, simBear3.getCounters(CounterType.M1M1)); + assertEquals(0, simBear3.getCounters(CounterEnumType.M1M1)); assertEquals(0, simBear3.getToughnessBonusFromCounters()); assertEquals(0, simBear3.getPowerBonusFromCounters()); assertNotNull(simGiant3); assertEquals(0, simGiant3.getDamage()); assertFalse(simGiant3.hasCounters()); - assertEquals(0, simGiant3.getCounters(CounterType.M1M1)); + assertEquals(0, simGiant3.getCounters(CounterEnumType.M1M1)); assertEquals(0, simGiant3.getToughnessBonusFromCounters()); assertEquals(0, simGiant3.getPowerBonusFromCounters()); @@ -1005,19 +1005,19 @@ public class GameSimulatorTest extends SimulationTestCase { game.getPhaseHandler().devModeSet(PhaseType.MAIN2, p); game.getAction().checkStateEffects(true); - assertEquals(0, p.getCounters(CounterType.ENERGY)); + assertEquals(0, p.getCounters(CounterEnumType.ENERGY)); SpellAbility playTurtle = turtleCard.getSpellAbilities().get(0); GameSimulator sim = createSimulator(game, p); sim.simulateSpellAbility(playTurtle); Game simGame = sim.getSimulatedGameState(); Player simP = simGame.getPlayers().get(1); - assertEquals(2, simP.getCounters(CounterType.ENERGY)); + assertEquals(2, simP.getCounters(CounterEnumType.ENERGY)); GameCopier copier = new GameCopier(simGame); Game copy = copier.makeCopy(); Player copyP = copy.getPlayers().get(1); - assertEquals(2, copyP.getCounters(CounterType.ENERGY)); + assertEquals(2, copyP.getCounters(CounterEnumType.ENERGY)); } public void testFloatingMana() { @@ -1118,7 +1118,7 @@ public class GameSimulatorTest extends SimulationTestCase { // only triggered once assertTrue(simPridemate.hasCounters()); - assertEquals(1, simPridemate.getCounters(CounterType.P1P1)); + assertEquals(1, simPridemate.getCounters(CounterEnumType.P1P1)); assertEquals(1, simPridemate.getToughnessBonusFromCounters()); assertEquals(1, simPridemate.getPowerBonusFromCounters()); @@ -1182,7 +1182,7 @@ public class GameSimulatorTest extends SimulationTestCase { // only triggered once assertTrue(simPridemate.hasCounters()); - assertEquals(1, simPridemate.getCounters(CounterType.P1P1)); + assertEquals(1, simPridemate.getCounters(CounterEnumType.P1P1)); assertEquals(1, simPridemate.getToughnessBonusFromCounters()); assertEquals(1, simPridemate.getPowerBonusFromCounters()); @@ -1251,7 +1251,7 @@ public class GameSimulatorTest extends SimulationTestCase { // only triggered twice assertTrue(simPridemate.hasCounters()); - assertEquals(2, simPridemate.getCounters(CounterType.P1P1)); + assertEquals(2, simPridemate.getCounters(CounterEnumType.P1P1)); assertEquals(2, simPridemate.getToughnessBonusFromCounters()); assertEquals(2, simPridemate.getPowerBonusFromCounters()); @@ -1530,7 +1530,7 @@ public class GameSimulatorTest extends SimulationTestCase { Card simGoblin = findCardWithName(simGame, goblinName); assertNotNull(simGoblin); - int effects = simGoblin.getCounters(CounterType.P1P1) + simGoblin.getKeywordMagnitude(Keyword.HASTE); + int effects = simGoblin.getCounters(CounterEnumType.P1P1) + simGoblin.getKeywordMagnitude(Keyword.HASTE); assertEquals(2, effects); } @@ -1851,8 +1851,8 @@ public class GameSimulatorTest extends SimulationTestCase { assertNotNull(simSpark); assertTrue(simSpark.isInZone(ZoneType.Battlefield)); - assertEquals(1, simSpark.getCounters(CounterType.P1P1)); - assertEquals(5, simSpark.getCounters(CounterType.LOYALTY)); + assertEquals(1, simSpark.getCounters(CounterEnumType.P1P1)); + assertEquals(5, simSpark.getCounters(CounterEnumType.LOYALTY)); } public void testVituGhaziAndCytoshape() { @@ -1884,7 +1884,7 @@ public class GameSimulatorTest extends SimulationTestCase { assertNotNull(awakened); assertEquals("Vitu-Ghazi", awakened.getName()); - assertEquals(9, awakened.getCounters(CounterType.P1P1)); + assertEquals(9, awakened.getCounters(CounterEnumType.P1P1)); assertTrue(awakened.hasKeyword(Keyword.HASTE)); assertTrue(awakened.getType().hasSubtype("Goblin")); } @@ -1932,7 +1932,7 @@ public class GameSimulatorTest extends SimulationTestCase { Card epoOTB = findCardWithName(sim.getSimulatedGameState(), "Epochrasite"); assertNotNull(epoOTB); - assertEquals(3, epoOTB.getCounters(CounterType.P1P1)); + assertEquals(3, epoOTB.getCounters(CounterEnumType.P1P1)); } @SuppressWarnings("unused") diff --git a/forge-gui-desktop/src/test/java/forge/ai/simulation/SpellAbilityPickerTest.java b/forge-gui-desktop/src/test/java/forge/ai/simulation/SpellAbilityPickerTest.java index 6cb11fc7d25..489b6ad476d 100644 --- a/forge-gui-desktop/src/test/java/forge/ai/simulation/SpellAbilityPickerTest.java +++ b/forge-gui-desktop/src/test/java/forge/ai/simulation/SpellAbilityPickerTest.java @@ -4,7 +4,7 @@ import java.util.List; import forge.game.Game; import forge.game.card.Card; -import forge.game.card.CounterType; +import forge.game.card.CounterEnumType; import forge.game.combat.Combat; import forge.game.phase.PhaseType; import forge.game.player.Player; @@ -219,12 +219,12 @@ public class SpellAbilityPickerTest extends SimulationTestCase { addCardToZone("Urborg, Tomb of Yawgmoth", p, ZoneType.Library); addCardToZone("Swamp", p, ZoneType.Library); - darkDepths.setCounters(CounterType.ICE, 10); + darkDepths.setCounters(CounterEnumType.ICE, 10); game.getPhaseHandler().devModeSet(PhaseType.MAIN2, p); game.getAction().checkStateEffects(true); - assertEquals(10, darkDepths.getCounters(CounterType.ICE)); + assertEquals(10, darkDepths.getCounters(CounterEnumType.ICE)); SpellAbilityPicker picker = new SpellAbilityPicker(game, p); SpellAbility sa = picker.chooseSpellAbilityToPlay(null); assertEquals(cropRotation.getSpellAbilities().get(0), sa); diff --git a/forge-gui-desktop/src/test/java/forge/gamesimulationtests/util/PlayerControllerForTests.java b/forge-gui-desktop/src/test/java/forge/gamesimulationtests/util/PlayerControllerForTests.java index 2ecb8fceaff..0d55b936008 100644 --- a/forge-gui-desktop/src/test/java/forge/gamesimulationtests/util/PlayerControllerForTests.java +++ b/forge-gui-desktop/src/test/java/forge/gamesimulationtests/util/PlayerControllerForTests.java @@ -156,12 +156,12 @@ public class PlayerControllerForTests extends PlayerController { } @Override - public CardCollectionView chooseCardsForEffect(CardCollectionView sourceList, SpellAbility sa, String title, int min, int max, boolean isOptional) { + public CardCollectionView chooseCardsForEffect(CardCollectionView sourceList, SpellAbility sa, String title, int min, int max, boolean isOptional, Map params) { return chooseItems(sourceList, max); } @Override - public T chooseSingleEntityForEffect(FCollectionView optionList, DelayedReveal delayedReveal, SpellAbility sa, String title, boolean isOptional, Player targetedPlayer) { + public T chooseSingleEntityForEffect(FCollectionView optionList, DelayedReveal delayedReveal, SpellAbility sa, String title, boolean isOptional, Player targetedPlayer, Map params) { if (delayedReveal != null) { reveal(delayedReveal.getCards(), delayedReveal.getZone(), delayedReveal.getOwner(), delayedReveal.getMessagePrefix()); } @@ -175,7 +175,7 @@ public class PlayerControllerForTests extends PlayerController { } @Override - public List chooseEntitiesForEffect(FCollectionView optionList, int min, int max, DelayedReveal delayedReveal, SpellAbility sa, String title, Player relatedPlayer) { + public List chooseEntitiesForEffect(FCollectionView optionList, int min, int max, DelayedReveal delayedReveal, SpellAbility sa, String title, Player relatedPlayer, Map params) { // this isn't used return null; } @@ -497,7 +497,7 @@ public class PlayerControllerForTests extends PlayerController { @Override public CounterType chooseCounterType(List options, SpellAbility sa, String prompt, Map params) { - return Iterables.getFirst(options, CounterType.P1P1); + return Iterables.getFirst(options, CounterType.get(CounterEnumType.P1P1)); } @Override @@ -702,4 +702,11 @@ public class PlayerControllerForTests extends PlayerController { // TODO Auto-generated method stub return new CardCollection(); } + + @Override + public List chooseSpellAbilitiesForEffect(List spells, SpellAbility sa, String title, + int num, Map params) { + // TODO Auto-generated method stub + return null; + } } diff --git a/forge-gui-mobile/src/forge/screens/match/views/VAssignCombatDamage.java b/forge-gui-mobile/src/forge/screens/match/views/VAssignCombatDamage.java index 2c4accac3f7..6b709930750 100644 --- a/forge-gui-mobile/src/forge/screens/match/views/VAssignCombatDamage.java +++ b/forge-gui-mobile/src/forge/screens/match/views/VAssignCombatDamage.java @@ -27,7 +27,7 @@ import forge.assets.FSkinColor.Colors; import forge.card.CardZoom; import forge.game.GameEntityView; import forge.game.card.CardView; -import forge.game.card.CounterType; +import forge.game.card.CounterEnumType; import forge.game.player.PlayerView; import forge.screens.match.MatchController; import forge.toolbox.FCardPanel; @@ -445,7 +445,7 @@ public class VAssignCombatDamage extends FDialog { if (source == null) { if (defender instanceof PlayerView) { PlayerView p = (PlayerView)defender; - lethalDamage = attackerHasInfect ? MatchController.instance.getGameView().getPoisonCountersToLose() - p.getCounters(CounterType.POISON) : p.getLife(); + lethalDamage = attackerHasInfect ? MatchController.instance.getGameView().getPoisonCountersToLose() - p.getCounters(CounterEnumType.POISON) : p.getLife(); } else if (defender instanceof CardView) { // planeswalker CardView pw = (CardView)defender; diff --git a/forge-gui-mobile/src/forge/screens/match/views/VAvatar.java b/forge-gui-mobile/src/forge/screens/match/views/VAvatar.java index 57d1baaad5b..fed9fdb447f 100644 --- a/forge-gui-mobile/src/forge/screens/match/views/VAvatar.java +++ b/forge-gui-mobile/src/forge/screens/match/views/VAvatar.java @@ -7,7 +7,7 @@ import com.badlogic.gdx.utils.Align; import forge.Graphics; import forge.assets.FImage; import forge.assets.FSkinFont; -import forge.game.card.CounterType; +import forge.game.card.CounterEnumType; import forge.game.player.PlayerView; import forge.screens.match.MatchController; import forge.toolbox.FDisplayObject; @@ -70,7 +70,7 @@ public class VAvatar extends FDisplayObject { g.drawImage(image, 0, 0, w, h); //display XP in lower right corner of avatar - int xp = player.getCounters(CounterType.EXPERIENCE); + int xp = player.getCounters(CounterEnumType.EXPERIENCE); if (xp > 0) { //use font and padding from phase indicator so text lines up FSkinFont font = VPhaseIndicator.BASE_FONT; diff --git a/forge-gui-mobile/src/forge/screens/match/views/VPlayerPanel.java b/forge-gui-mobile/src/forge/screens/match/views/VPlayerPanel.java index 72fd71397a5..558fa4d8ee7 100644 --- a/forge-gui-mobile/src/forge/screens/match/views/VPlayerPanel.java +++ b/forge-gui-mobile/src/forge/screens/match/views/VPlayerPanel.java @@ -15,7 +15,7 @@ import forge.assets.FSkinFont; import forge.assets.FSkinImage; import forge.assets.FSkinColor.Colors; import forge.game.card.CardView; -import forge.game.card.CounterType; +import forge.game.card.CounterEnumType; import forge.game.player.PlayerView; import forge.game.zone.ZoneType; import forge.model.FModel; @@ -359,8 +359,8 @@ public class VPlayerPanel extends FContainer { private class LifeLabel extends FDisplayObject { private int life = player.getLife(); - private int poisonCounters = player.getCounters(CounterType.POISON); - private int energyCounters = player.getCounters(CounterType.ENERGY); + private int poisonCounters = player.getCounters(CounterEnumType.POISON); + private int energyCounters = player.getCounters(CounterEnumType.ENERGY); private String lifeStr = String.valueOf(life); private LifeLabel() { @@ -378,16 +378,16 @@ public class VPlayerPanel extends FContainer { lifeStr = String.valueOf(life); } - delta = player.getCounters(CounterType.POISON) - poisonCounters; + delta = player.getCounters(CounterEnumType.POISON) - poisonCounters; if (delta != 0) { if (delta > 0) { //TODO: Show animation on avatar for gaining poison counters vibrateDuration += delta * 200; } - poisonCounters = player.getCounters(CounterType.POISON); + poisonCounters = player.getCounters(CounterEnumType.POISON); } - energyCounters = player.getCounters(CounterType.ENERGY); + energyCounters = player.getCounters(CounterEnumType.ENERGY); //when gui player loses life, vibrate device for a length of time based on amount of life lost if (vibrateDuration > 0 && MatchController.instance.isLocalPlayer(player) && diff --git a/forge-gui/res/cardsfolder/a/avian_oddity.txt b/forge-gui/res/cardsfolder/a/avian_oddity.txt index 1703792d6f8..16b5821916c 100755 --- a/forge-gui/res/cardsfolder/a/avian_oddity.txt +++ b/forge-gui/res/cardsfolder/a/avian_oddity.txt @@ -5,6 +5,6 @@ PT:2/4 K:Flying K:Cycling:2 U T:Mode$ Cycled | ValidCard$ Card.Self | Execute$ TrigPutCounter | TriggerDescription$ When you cycle CARDNAME, put a flying counter on target creature you control. -SVar:TrigPutCounter:DB$ PutCounter | ValidTgts$ Creature.YouCtrl | TgtPrompt$ Select target creature you control | CounterType$ FLYING | CounterNum$ 1 +SVar:TrigPutCounter:DB$ PutCounter | ValidTgts$ Creature.YouCtrl | TgtPrompt$ Select target creature you control | CounterType$ Flying | CounterNum$ 1 DeckHas:Ability$Counters Oracle:Flying\nCycling {2}{U} ({2}{U}, Discard this card: Draw a card.)\nWhen you cycle Avian Oddity, put a flying counter on target creature you control. diff --git a/forge-gui/res/cardsfolder/b/bioshift.txt b/forge-gui/res/cardsfolder/b/bioshift.txt index e907ce94a0c..f9fc47c266f 100644 --- a/forge-gui/res/cardsfolder/b/bioshift.txt +++ b/forge-gui/res/cardsfolder/b/bioshift.txt @@ -1,13 +1,8 @@ Name:Bioshift ManaCost:GU Types:Instant -A:SP$ Pump | Cost$ GU | ValidTgts$ Creature | TgtPrompt$ Select target creature to remove +1/+1 counter | SubAbility$ DBChooseNum | RememberObjects$ Targeted | StackDescription$ SpellDescription | SpellDescription$ Move any number of +1/+1 counters from target creature onto another target creature with the same controller. -SVar:DBChooseNum:DB$ ChooseNumber | Min$ 0 | Max$ X | References$ X | ListTitle$ Choose how many counters to move | SubAbility$ DBMove | StackDescription$ None -SVar:DBMove:DB$ MoveCounter | Source$ Remembered | ValidTgts$ Creature | TgtPrompt$ Select another target creature with the same controller to get +1/+1 counter | TargetUnique$ True | TargetsWithSameController$ True | CounterType$ P1P1 | CounterNum$ Y | References$ Y | ConditionDefined$ Targeted | ConditionPresent$ Creature.sharesControllerWith Remembered | ConditionCompare$ EQ1 | SubAbility$ DBCleanup | StackDescription$ None -SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True +A:SP$ MoveCounter | Cost$ GU | ValidTgts$ Creature | TargetMin$ 2 | TargetMax$ 2 | TgtPrompt$ Select target creatures to move +1/+1 counters | TargetsWithSameController$ True | CounterType$ P1P1 | CounterNum$ Any | StackDescription$ SpellDescription | SpellDescription$ Move any number of +1/+1 counters from target creature onto another target creature with the same controller. AI:RemoveDeck:All -SVar:Y:Count$ChosenNumber -SVar:X:Count$TotalCounters_P1P1_Card.IsRemembered DeckHas:Ability$Counters SVar:Picture:http://www.wizards.com/global/images/magic/general/bioshift.jpg Oracle:Move any number of +1/+1 counters from target creature onto another target creature with the same controller. diff --git a/forge-gui/res/cardsfolder/b/boot_nipper.txt b/forge-gui/res/cardsfolder/b/boot_nipper.txt index 7fc399ea046..83c7d098349 100755 --- a/forge-gui/res/cardsfolder/b/boot_nipper.txt +++ b/forge-gui/res/cardsfolder/b/boot_nipper.txt @@ -4,7 +4,7 @@ Types:Creature Beast PT:2/1 K:ETBReplacement:Other:CounterChoice SVar:CounterChoice:DB$ GenericChoice | Defined$ You | Choices$ Deathtouch,Lifelink | SpellDescription$ CARDNAME enters the battlefield with your choice of a flying counter or a hexproof counter on it. -SVar:Deathtouch:DB$ PutCounter | CounterType$ DEATHTOUCH | CounterNum$ 1 | SpellDescription$ CARDNAME enters the battlefield with a deathtouch counter on it -SVar:Lifelink:DB$ PutCounter | CounterType$ LIFELINK | CounterNum$ 1 | SpellDescription$ CARDNAME enters the battlefield with a lifelink counter on it +SVar:Deathtouch:DB$ PutCounter | CounterType$ Deathtouch | CounterNum$ 1 | SpellDescription$ CARDNAME enters the battlefield with a deathtouch counter on it +SVar:Lifelink:DB$ PutCounter | CounterType$ Lifelink | CounterNum$ 1 | SpellDescription$ CARDNAME enters the battlefield with a lifelink counter on it DeckHints:Ability$Counters Oracle:Boot Nipper enters the battlefield with your choice of a deathtouch counter or a lifelink counter on it. diff --git a/forge-gui/res/cardsfolder/b/bounty_of_the_hunt.txt b/forge-gui/res/cardsfolder/b/bounty_of_the_hunt.txt index 71845fb630c..18847d2c6eb 100644 --- a/forge-gui/res/cardsfolder/b/bounty_of_the_hunt.txt +++ b/forge-gui/res/cardsfolder/b/bounty_of_the_hunt.txt @@ -1,11 +1,8 @@ Name:Bounty of the Hunt ManaCost:3 G G Types:Instant -A:SP$ PutCounter | Cost$ 3 G G | ValidTgts$ Creature | TgtPrompt$ Select target creature to distribute counters to | CounterType$ P1P1 | CounterNum$ 3 | TargetMin$ 1 | TargetMax$ 3 | DividedAsYouChoose$ 3 | RememberCounters$ True | SubAbility$ DelayedRemoveCounters | RememberTargets$ True | SpellDescription$ Distribute three +1/+1 counters among one, two, or three target creatures. For each +1/+1 counter you put on a creature this way, remove a +1/+1 counter from that creature at the beginning of the next cleanup step. +A:SP$ PutCounter | Cost$ 3 G G | ValidTgts$ Creature | TgtPrompt$ Select target creature to distribute counters to | CounterType$ P1P1 | CounterNum$ 3 | TargetMin$ 1 | TargetMax$ 3 | DividedAsYouChoose$ 3 | RemovePhase$ Cleanup | SpellDescription$ Distribute three +1/+1 counters among one, two, or three target creatures. For each +1/+1 counter you put on a creature this way, remove a +1/+1 counter from that creature at the beginning of the next cleanup step. SVar:AltCost:Cost$ExileFromHand<1/Card.Green> | Description$ You may exile a green card from your hand rather than pay CARDNAME's mana cost. -SVar:DelayedRemoveCounters:DB$ DelayedTrigger | Mode$ Phase | Phase$ Cleanup | Execute$ TrigRemoveCounter | Secondary$ True | TriggerDescription$ For each +1/+1 counter you put on a creature this way, remove a +1/+1 counter from that creature at the beginning of the next cleanup step. -SVar:TrigRemoveCounter:DB$ RemoveCounter | Defined$ Remembered | CounterType$ P1P1 | CounterNum$ Remembered | SubAbility$ DBCleanup -SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True DeckHas:Ability$Counters AI:RemoveDeck:All SVar:Picture:http://www.wizards.com/global/images/magic/general/bounty_of_the_hunt.jpg diff --git a/forge-gui/res/cardsfolder/c/call_of_the_death_dweller.txt b/forge-gui/res/cardsfolder/c/call_of_the_death_dweller.txt index 8feccf3fcd7..f9185748a4d 100755 --- a/forge-gui/res/cardsfolder/c/call_of_the_death_dweller.txt +++ b/forge-gui/res/cardsfolder/c/call_of_the_death_dweller.txt @@ -2,8 +2,8 @@ Name:Call of the Death-Dweller ManaCost:2 B Types:Sorcery A:SP$ ChangeZone | Cost$ 2 B | Origin$ Graveyard | Destination$ Battlefield | TargetMin$ 0 | TargetMax$ 2 | MaxTotalTargetCMC$ 3 | ValidTgts$ Creature.YouOwn | TgtPrompt$ Select up to two target creature cards with total converted mana cost 3 or less | SubAbility$ DBPutCounter | RememberChanged$ True | StackDescription$ SpellDescription | SpellDescription$ Return up to two target creature cards with total converted mana cost 3 or less from your graveyard to the battlefield. Put a deathtouch counter on either of them. Then put a menace counter on either of them. -SVar:DBPutCounter:DB$ PutCounter | Choices$ Creature.IsRemembered | ChoiceTitle$ Choose a creature to put a deathtouch counter on | CounterType$ DEATHTOUCH | CounterNum$ 1 | SubAbility$ DBPutCounter2 | StackDescription$ None -SVar:DBPutCounter2:DB$ PutCounter | Choices$ Creature.IsRemembered | ChoiceTitle$ Choose a creature to put a menace counter on | CounterType$ MENACE | CounterNum$ 1 | SubAbility$ DBCleanup | StackDescription$ None +SVar:DBPutCounter:DB$ PutCounter | Choices$ Creature.IsRemembered | ChoiceTitle$ Choose a creature to put a deathtouch counter on | CounterType$ Deathtouch | CounterNum$ 1 | SubAbility$ DBPutCounter2 | StackDescription$ None +SVar:DBPutCounter2:DB$ PutCounter | Choices$ Creature.IsRemembered | ChoiceTitle$ Choose a creature to put a menace counter on | CounterType$ Menace | CounterNum$ 1 | SubAbility$ DBCleanup | StackDescription$ None SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True DeckHas:Ability$Counters Oracle:Return up to two target creature cards with total converted mana cost 3 or less from your graveyard to the battlefield. Put a deathtouch counter on either of them. Then put a menace counter on either of them. diff --git a/forge-gui/res/cardsfolder/c/combine_guildmage.txt b/forge-gui/res/cardsfolder/c/combine_guildmage.txt index 63b9762a7ef..41b076dc25b 100644 --- a/forge-gui/res/cardsfolder/c/combine_guildmage.txt +++ b/forge-gui/res/cardsfolder/c/combine_guildmage.txt @@ -6,7 +6,5 @@ A:AB$ Effect | Cost$ 1 G T | Name$ CARDNAME Effect | ReplacementEffects$ ExtraET SVar:ExtraETBCounter:Event$ Moved | ActiveZones$ Command | Destination$ Battlefield | ValidCard$ Creature.YouCtrl+Other | ReplaceWith$ AddExtraCounter | Description$ This turn, each creature you control enters the battlefield with an additional +1/+1 counter on it. SVar:AddExtraCounter:DB$ PutCounter | ETB$ True | Defined$ ReplacedCard | CounterType$ P1P1 | CounterNum$ 1 | SubAbility$ MoveToBattlefield SVar:MoveToBattlefield:DB$ ChangeZone | Origin$ All | Destination$ Battlefield | Defined$ ReplacedCard -A:AB$ Pump | Cost$ 1 U T | ValidTgts$ Creature.YouCtrl | TgtPrompt$ Select target creature to remove +1/+1 counter | SubAbility$ DBMove | RememberObjects$ Targeted | StackDescription$ None | SpellDescription$ Move a +1/+1 counter from target creature you control onto another target creature you control. -SVar:DBMove:DB$ MoveCounter | Source$ Remembered | ValidTgts$ Creature.YouCtrl | TgtPrompt$ Select another target creature with the same controller to get +1/+1 counter | TargetUnique$ True | TargetsWithSameController$ True | CounterType$ P1P1 | CounterNum$ 1 | ConditionDefined$ Targeted | ConditionPresent$ Creature.sharesControllerWith Remembered | ConditionCompare$ EQ1 | SubAbility$ DBCleanup -SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True +A:AB$ MoveCounter | Cost$ 1 U T | ValidTgts$ Creature.YouCtrl | TargetMin$ 2 | TargetMax$ 2 | TgtPrompt$ Select target creatures to move +1/+1 counter | CounterType$ P1P1 | CounterNum$ 1 | StackDescription$ SpellDescription | SpellDescription$ Move a +1/+1 counter from target creature you control onto another target creature you control. Oracle:{1}{G}, {T}: This turn, each creature you control enters the battlefield with an additional +1/+1 counter on it.\n{1}{U}, {T}: Move a +1/+1 counter from target creature you control onto another target creature you control. diff --git a/forge-gui/res/cardsfolder/c/crystalline_giant.txt b/forge-gui/res/cardsfolder/c/crystalline_giant.txt index 06cc2104249..00ac2b2d21a 100755 --- a/forge-gui/res/cardsfolder/c/crystalline_giant.txt +++ b/forge-gui/res/cardsfolder/c/crystalline_giant.txt @@ -4,16 +4,16 @@ Types:Artifact Creature Giant PT:3/3 T:Mode$ Phase | Phase$ BeginCombat | ValidPlayer$ You | Execute$ TrigGenericChoice | TriggerZones$ Battlefield | TriggerDescription$ At the beginning of combat on your turn, choose a kind of counter at random that CARDNAME doesn’t have on it from among flying, first strike, deathtouch, hexproof, lifelink, menace, reach, trample, vigilance, or +1/+1. Put a counter of that kind on CARDNAME. SVar:TrigGenericChoice:DB$ GenericChoice | AtRandom$ True | Choices$ Flying,FirstStrike,Deathtouch,Hexproof,Lifelink,Menace,Reach,Trample,Vigilance,P1P1 -SVar:Flying:DB$ PutCounter | IsPresent$ Card.Self+counters_EQ0_FLYING | RememberCards$ True | CounterType$ FLYING | CounterNum$ 1 | SpellDescription$ FLY -SVar:FirstStrike:DB$ PutCounter | IsPresent$ Card.Self+counters_EQ0_FIRSTSTRIKE | RememberCards$ True | CounterType$ FIRSTSTRIKE | CounterNum$ 1 | SpellDescription$ FIR -SVar:Deathtouch:DB$ PutCounter | IsPresent$ Card.Self+counters_EQ0_DEATHTOUCH | RememberCards$ True | CounterType$ DEATHTOUCH | CounterNum$ 1 | SpellDescription$ DEA -SVar:Hexproof:DB$ PutCounter | IsPresent$ Card.Self+counters_EQ0_HEXPROOF | RememberCards$ True | CounterType$ HEXPROOF | CounterNum$ 1 | SpellDescription$ HEX -SVar:Lifelink:DB$ PutCounter | IsPresent$ Card.Self+counters_EQ0_LIFELINK | RememberCards$ True | CounterType$ LIFELINK | CounterNum$ 1 | SpellDescription$ LIF -SVar:Menace:DB$ PutCounter | IsPresent$ Card.Self+counters_EQ0_MENACE | RememberCards$ True | CounterType$ MENACE | CounterNum$ 1 | SpellDescription$ MEN -SVar:Reach:DB$ PutCounter | IsPresent$ Card.Self+counters_EQ0_REACH | RememberCards$ True | CounterType$ REACH | CounterNum$ 1 | SpellDescription$ REA -SVar:Trample:DB$ PutCounter | IsPresent$ Card.Self+counters_EQ0_TRAMPLE | RememberCards$ True | CounterType$ TRAMPLE | CounterNum$ 1 | SpellDescription$ TRA -SVar:Vigilance:DB$ PutCounter | IsPresent$ Card.Self+counters_EQ0_VIGILANCE | RememberCards$ True | CounterType$ VIGILANCE | CounterNum$ 1 | SpellDescription$ VIG -SVar:P1P1:DB$ PutCounter | IsPresent$ Card.Self+counters_EQ0_P1P1 | RememberCards$ True | CounterType$ P1P1 | CounterNum$ 1 | SpellDescription$ P1P1 +SVar:Flying:DB$ PutCounter | IsPresent$ Card.Self+counters_EQ0_Flying | CounterType$ Flying | CounterNum$ 1 | SpellDescription$ FLY +SVar:FirstStrike:DB$ PutCounter | IsPresent$ Card.Self+counters_EQ0_First Strike | CounterType$ First Strike | CounterNum$ 1 | SpellDescription$ FIR +SVar:Deathtouch:DB$ PutCounter | IsPresent$ Card.Self+counters_EQ0_Deathtouch | CounterType$ Deathtouch | CounterNum$ 1 | SpellDescription$ DEA +SVar:Hexproof:DB$ PutCounter | IsPresent$ Card.Self+counters_EQ0_Hexproof | CounterType$ Hexproof | CounterNum$ 1 | SpellDescription$ HEX +SVar:Lifelink:DB$ PutCounter | IsPresent$ Card.Self+counters_EQ0_Lifelink | CounterType$ Lifelink | CounterNum$ 1 | SpellDescription$ LIF +SVar:Menace:DB$ PutCounter | IsPresent$ Card.Self+counters_EQ0_Menace | CounterType$ Menace | CounterNum$ 1 | SpellDescription$ MEN +SVar:Reach:DB$ PutCounter | IsPresent$ Card.Self+counters_EQ0_Reach | CounterType$ Reach | CounterNum$ 1 | SpellDescription$ REA +SVar:Trample:DB$ PutCounter | IsPresent$ Card.Self+counters_EQ0_Trample | CounterType$ Trample | CounterNum$ 1 | SpellDescription$ TRA +SVar:Vigilance:DB$ PutCounter | IsPresent$ Card.Self+counters_EQ0_Vigilance | CounterType$ Vigilance | CounterNum$ 1 | SpellDescription$ VIG +SVar:P1P1:DB$ PutCounter | IsPresent$ Card.Self+counters_EQ0_P1P1 | CounterType$ P1P1 | CounterNum$ 1 | SpellDescription$ P1P1 SVar:PlayMain1:TRUE DeckHas:Ability$Counters Oracle:At the beginning of combat on your turn, choose a kind of counter at random that Crystalline Giant doesn’t have on it from among flying, first strike, deathtouch, hexproof, lifelink, menace, reach, trample, vigilance, or +1/+1. Put a counter of that kind on Crystalline Giant. diff --git a/forge-gui/res/cardsfolder/d/duskfang_mentor.txt b/forge-gui/res/cardsfolder/d/duskfang_mentor.txt index 9ca5ac37b7f..44010700474 100755 --- a/forge-gui/res/cardsfolder/d/duskfang_mentor.txt +++ b/forge-gui/res/cardsfolder/d/duskfang_mentor.txt @@ -3,7 +3,7 @@ ManaCost:2 B Types:Creature Human Cleric PT:1/3 T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigPut | TriggerDescription$ When CARDNAME enters the battlefield, put a lifelink counter on target non-Human creature you control. -SVar:TrigPut:DB$ PutCounter | ValidTgts$ Creature.nonHuman+YouCtrl | TgtPrompt$ Select target non-Human creature | CounterType$ LIFELINK | CounterNum$ 1 +SVar:TrigPut:DB$ PutCounter | ValidTgts$ Creature.nonHuman+YouCtrl | TgtPrompt$ Select target non-Human creature | CounterType$ Lifelink | CounterNum$ 1 A:AB$ PutCounterAll | Cost$ 1 B T | ValidCards$ Creature.YouCtrl+withLifelink | CounterType$ P1P1 | CounterNum$ 1 | SpellDescription$ Put a +1/+1 counter on each creature you control with lifelink. DeckHas:Ability$Counters & Ability$LifeGain Oracle:When Duskfang Mentor enters the battlefield, put a lifelink counter on target non-Human creature you control.\n{1}{B}, {T}: Put a +1/+1 counter on each creature you control with lifelink. diff --git a/forge-gui/res/cardsfolder/f/fate_transfer.txt b/forge-gui/res/cardsfolder/f/fate_transfer.txt index 276936bdca6..817616aa303 100644 --- a/forge-gui/res/cardsfolder/f/fate_transfer.txt +++ b/forge-gui/res/cardsfolder/f/fate_transfer.txt @@ -1,12 +1,7 @@ Name:Fate Transfer ManaCost:1 UB Types:Instant -A:SP$ Pump | Cost$ 1 UB | ValidTgts$ Creature | TgtPrompt$ Select target creature to remove counters | ImprintCards$ Targeted | SubAbility$ DBRemember | SpellDescription$ Move all counters from target creature onto another target creature. -SVar:DBRemember:DB$ Pump | ValidTgts$ Creature | TgtPrompt$ Select target creature to get counters | RememberObjects$ Targeted | SubAbility$ DBMove -SVar:DBMove:DB$ RepeatEach | Defined$ Imprinted | RepeatCounters$ True | RepeatSubAbility$ MoveCounters | SubAbility$ DBCleanup -SVar:MoveCounters:DB$ MoveCounter | Source$ Imprinted | Defined$ Remembered | CounterType$ RepeatSVarCounter | CounterNum$ RepeatCounterAmount -SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True | ClearImprinted$ True +A:SP$ MoveCounter | Cost$ 1 UB | TargetMin$ 2 | TargetMax$ 2 | ValidTgts$ Creature | TgtPrompt$ Select target creatures to move counters | CounterType$ All | CounterNum$ All | SpellDescription$ Move all counters from target creature onto another target creature. AI:RemoveDeck:All AI:RemoveDeck:Random -SVar:Picture:http://www.wizards.com/global/images/magic/general/fate_transfer.jpg Oracle:Move all counters from target creature onto another target creature. diff --git a/forge-gui/res/cardsfolder/f/ferocious_tigorilla.txt b/forge-gui/res/cardsfolder/f/ferocious_tigorilla.txt index 2ee099b69b2..06b499918a7 100755 --- a/forge-gui/res/cardsfolder/f/ferocious_tigorilla.txt +++ b/forge-gui/res/cardsfolder/f/ferocious_tigorilla.txt @@ -4,7 +4,7 @@ Types:Creature Cat Ape PT:4/3 K:ETBReplacement:Other:CounterChoice SVar:CounterChoice:DB$ GenericChoice | Defined$ You | Choices$ Trample,Menace | SpellDescription$ CARDNAME enters the battlefield with your choice of a trample counter or a menace counter on it. -SVar:Trample:DB$ PutCounter | CounterType$ TRAMPLE | CounterNum$ 1 | SpellDescription$ CARDNAME enters the battlefield with a trample counter -SVar:Menace:DB$ PutCounter | CounterType$ MENACE | CounterNum$ 1 | SpellDescription$ CARDNAME enters the battlefield with a menace counter +SVar:Trample:DB$ PutCounter | CounterType$ Trample | CounterNum$ 1 | SpellDescription$ CARDNAME enters the battlefield with a trample counter +SVar:Menace:DB$ PutCounter | CounterType$ Menace | CounterNum$ 1 | SpellDescription$ CARDNAME enters the battlefield with a menace counter DeckHas:Ability$Counters Oracle:Ferocious Tigorilla enters the battlefield with your choice of a trample counter or a menace counter on it. (A creature with menace can't be blocked except by two or more creatures.) diff --git a/forge-gui/res/cardsfolder/f/flycatcher_giraffid.txt b/forge-gui/res/cardsfolder/f/flycatcher_giraffid.txt index e8aa64dfced..75fb6a261a3 100644 --- a/forge-gui/res/cardsfolder/f/flycatcher_giraffid.txt +++ b/forge-gui/res/cardsfolder/f/flycatcher_giraffid.txt @@ -4,7 +4,7 @@ Types:Creature Antelope Lizard PT:3/5 K:ETBReplacement:Other:CounterChoice SVar:CounterChoice:DB$ GenericChoice | Defined$ You | Choices$ Vigilance,Reach | SpellDescription$ CARDNAME enters the battlefield with your choice of a vigilance counter or a reach counter on it. -SVar:Vigilance:DB$ PutCounter | CounterType$ VIGILANCE | CounterNum$ 1 | SpellDescription$ CARDNAME enters the battlefield with a vigilance counter on it -SVar:Reach:DB$ PutCounter | CounterType$ REACH | CounterNum$ 1 | SpellDescription$ CARDNAME enters the battlefield with a reach counter on it +SVar:Vigilance:DB$ PutCounter | CounterType$ Vigilance | CounterNum$ 1 | SpellDescription$ CARDNAME enters the battlefield with a vigilance counter on it +SVar:Reach:DB$ PutCounter | CounterType$ Reach | CounterNum$ 1 | SpellDescription$ CARDNAME enters the battlefield with a reach counter on it DeckHas:Ability$Counters Oracle:Flycatcher Giraffid enters the battlefield with your choice of a vigilance counter or a reach counter on it. diff --git a/forge-gui/res/cardsfolder/g/grimdancer.txt b/forge-gui/res/cardsfolder/g/grimdancer.txt index 23037a2fe79..479f467ba13 100755 --- a/forge-gui/res/cardsfolder/g/grimdancer.txt +++ b/forge-gui/res/cardsfolder/g/grimdancer.txt @@ -3,11 +3,9 @@ ManaCost:1 B B Types:Creature Nightmare PT:3/3 K:ETBReplacement:Other:CounterChoice -SVar:CounterChoice:DB$ GenericChoice | Defined$ You | Choices$ MeDe,DeLi,MeLi | SpellDescription$ CARDNAME enters the battlefield with your choice of two different counters on it from among menace, deathtouch, and lifelink. -SVar:MeDe:DB$ PutCounter | CounterType$ MENACE | CounterNum$ 1 | SubAbility$ Deathtouch | SpellDescription$ Menace and Deathtouch -SVar:Deathtouch:DB$ PutCounter | CounterType$ DEATHTOUCH | CounterNum$ 1 -SVar:DeLi:DB$ PutCounter | CounterType$ DEATHTOUCH | CounterNum$ 1 | SubAbility$ Lifelink | SpellDescription$ Deathtouch and Lifelink -SVar:Lifelink:DB$ PutCounter | CounterType$ LIFELINK | CounterNum$ 1 -SVar:MeLi:DB$ PutCounter | CounterType$ MENACE | CounterNum$ 1 | SubAbility$ Lifelink | SpellDescription$ Menace and Lifelink +SVar:CounterChoice:DB$ GenericChoice | Defined$ You | Choices$ Menace,Deathtouch,Lifelink | ChoiceAmount$ 2 | SpellDescription$ CARDNAME enters the battlefield with your choice of two different counters on it from among menace, deathtouch, and lifelink. +SVar:Menace:DB$ PutCounter | CounterType$ Menace | CounterNum$ 1 | ETB$ True | SpellDescription$ Menace +SVar:Deathtouch:DB$ PutCounter | CounterType$ Deathtouch | CounterNum$ 1 | ETB$ True | SpellDescription$ Deathtouch +SVar:Lifelink:DB$ PutCounter | CounterType$ Lifelink | CounterNum$ 1 | ETB$ True | SpellDescription$ Lifelink DeckHas:Ability$Counters Oracle:Grimdancer enters the battlefield with your choice of two different counters on it from among menace, deathtouch, and lifelink. diff --git a/forge-gui/res/cardsfolder/h/heightened_reflexes.txt b/forge-gui/res/cardsfolder/h/heightened_reflexes.txt index 5cf059b0361..15d828f84e2 100755 --- a/forge-gui/res/cardsfolder/h/heightened_reflexes.txt +++ b/forge-gui/res/cardsfolder/h/heightened_reflexes.txt @@ -2,6 +2,6 @@ Name:Heightened Reflexes ManaCost:R Types:Instant A:SP$ Pump | Cost$ R | ValidTgts$ Creature | TgtPrompt$ Select target creature | NumAtt$ +1 | SubAbility$ DBPutCounter | SpellDescription$ Target creature gets +1/+0 until end of turn. Put a first strike counter on it. -SVar:DBPutCounter:DB$ PutCounter | Defined$ Targeted | CounterType$ FIRSTSTRIKE | CounterNum$ 1 +SVar:DBPutCounter:DB$ PutCounter | Defined$ Targeted | CounterType$ First Strike | CounterNum$ 1 DeckHas:Ability$Counters Oracle:Target creature gets +1/+0 until end of turn. Put a first strike counter on it. diff --git a/forge-gui/res/cardsfolder/h/helica_glider.txt b/forge-gui/res/cardsfolder/h/helica_glider.txt index 84c11c62d9c..7686c61801c 100755 --- a/forge-gui/res/cardsfolder/h/helica_glider.txt +++ b/forge-gui/res/cardsfolder/h/helica_glider.txt @@ -4,7 +4,7 @@ Types:Creature Nightmare Squirrel PT:2/2 K:ETBReplacement:Other:CounterChoice SVar:CounterChoice:DB$ GenericChoice | Defined$ You | Choices$ Flying,FirstStrike | SpellDescription$ CARDNAME enters the battlefield with your choice of a flying counter or a first strike counter on it. -SVar:Flying:DB$ PutCounter | CounterType$ FLYING | CounterNum$ 1 | SpellDescription$ CARDNAME enters the battlefield with a flying counter on it -SVar:FirstStrike:DB$ PutCounter | CounterType$ FIRSTSTRIKE | CounterNum$ 1 | SpellDescription$ CARDNAME enters the battlefield with a first strike counter on it +SVar:Flying:DB$ PutCounter | CounterType$ Flying | CounterNum$ 1 | SpellDescription$ CARDNAME enters the battlefield with a flying counter on it +SVar:FirstStrike:DB$ PutCounter | CounterType$ First Strike | CounterNum$ 1 | SpellDescription$ CARDNAME enters the battlefield with a first strike counter on it DeckHints:Ability$Counters Oracle:Helica Glider enters the battlefield with your choice of a flying counter or a first strike counter on it. diff --git a/forge-gui/res/cardsfolder/h/hunted_nightmare.txt b/forge-gui/res/cardsfolder/h/hunted_nightmare.txt index 0375a63b97e..08b534e82ca 100644 --- a/forge-gui/res/cardsfolder/h/hunted_nightmare.txt +++ b/forge-gui/res/cardsfolder/h/hunted_nightmare.txt @@ -4,5 +4,5 @@ Types:Creature Nightmare PT:4/5 K:Menace T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Creature.Self | Execute$ TrigPutCounter | TriggerDescription$ When CARDNAME enters the battlefield, target opponent puts a deathtouch counter on a creature they control. -SVar:TrigPutCounter:DB$ PutCounter | ValidTgts$ Opponent | TgtPrompt$ Select target opponent | Choices$ Creature.ControlledBy TargetedPlayer | ChoiceTitle$ Choose a creature you control | Chooser$ TargetedPlayer | Placer$ TargetedPlayer | CounterType$ DEATHTOUCH | CounterNum$ 1 +SVar:TrigPutCounter:DB$ PutCounter | ValidTgts$ Opponent | TgtPrompt$ Select target opponent | Choices$ Creature.ControlledBy TargetedPlayer | ChoiceTitle$ Choose a creature you control | Chooser$ TargetedPlayer | Placer$ TargetedPlayer | CounterType$ Deathtouch | CounterNum$ 1 Oracle:Menace\nWhen Hunted Nightmare enters the battlefield, target opponent puts a deathtouch counter on a creature they control. diff --git a/forge-gui/res/cardsfolder/k/kathril_aspect_warper.txt b/forge-gui/res/cardsfolder/k/kathril_aspect_warper.txt index 4cccff94fc8..f2c0ac00d9d 100755 --- a/forge-gui/res/cardsfolder/k/kathril_aspect_warper.txt +++ b/forge-gui/res/cardsfolder/k/kathril_aspect_warper.txt @@ -2,39 +2,10 @@ Name:Kathril, Aspect Warper ManaCost:2 W B G Types:Legendary Creature Nightmare Insect PT:3/3 -T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ Flying | TriggerDescription$ When CARDNAME enters the battlefield, put a flying counter on any creature you control if a creature card in your graveyard has flying. Repeat this process for first strike, double strike, deathtouch, hexproof, indestructible, lifelink, menace, reach, trample, and vigilance. Then put a +1/+1 counter on CARDNAME for each counter put on a creature this way. -SVar:Flying:DB$ PutCounter | Choices$ Creature.YouCtrl | ChoiceTitle$ Choose a creature to put a flying counter on | CounterType$ FLYING | CounterNum$ 1 | ConditionCheckSVar$ X | ConditionSVarCompare$ GE1 | References$ X | SubAbility$ FirstStrike -SVar:FirstStrike:DB$ PutCounter | Choices$ Creature.YouCtrl | ChoiceTitle$ Choose a creature to put a first strike counter on | CounterType$ FIRSTSTRIKE | CounterNum$ 1 | ConditionCheckSVar$ Y | ConditionSVarCompare$ GE1 | References$ Y | SubAbility$ DoubleStrike -SVar:DoubleStrike:DB$ PutCounter | Choices$ Creature.YouCtrl | ChoiceTitle$ Choose a creature to put a double strike counter on | CounterType$ DOUBLESTRIKE | CounterNum$ 1 | ConditionCheckSVar$ Z | ConditionSVarCompare$ GE1 | References$ Z | SubAbility$ Deathtouch -SVar:Deathtouch:DB$ PutCounter | Choices$ Creature.YouCtrl | ChoiceTitle$ Choose a creature to put a deathtouch counter on | CounterType$ DEATHTOUCH | CounterNum$ 1 | ConditionCheckSVar$ A | ConditionSVarCompare$ GE1 | References$ A | SubAbility$ Hexproof -SVar:Hexproof:DB$ PutCounter | Choices$ Creature.YouCtrl | ChoiceTitle$ Choose a creature to put a hexproof counter on | CounterType$ HEXPROOF | CounterNum$ 1 | ConditionCheckSVar$ B | ConditionSVarCompare$ GE1 | References$ B | SubAbility$ Indestructible -SVar:Indestructible:DB$ PutCounter | Choices$ Creature.YouCtrl | ChoiceTitle$ Choose a creature to put an indestructible counter on | CounterType$ INDESTRUCTIBLE | CounterNum$ 1 | ConditionCheckSVar$ C | ConditionSVarCompare$ GE1 | References$ C | SubAbility$ Lifelink -SVar:Lifelink:DB$ PutCounter | Choices$ Creature.YouCtrl | ChoiceTitle$ Choose a creature to put a lifelink counter on | CounterType$ LIFELINK | CounterNum$ 1 | ConditionCheckSVar$ D | ConditionSVarCompare$ GE1 | References$ D | SubAbility$ Menace -SVar:Menace:DB$ PutCounter | Choices$ Creature.YouCtrl | ChoiceTitle$ Choose a creature to put a menace counter on | CounterType$ MENACE | CounterNum$ 1 | ConditionCheckSVar$ E | ConditionSVarCompare$ GE1 | References$ E | SubAbility$ Reach -SVar:Reach:DB$ PutCounter | Choices$ Creature.YouCtrl | ChoiceTitle$ Choose a creature to put a reach counter on | CounterType$ REACH | CounterNum$ 1 | ConditionCheckSVar$ F | ConditionSVarCompare$ GE1 | References$ F | SubAbility$ Trample -SVar:Trample:DB$ PutCounter | Choices$ Creature.YouCtrl | ChoiceTitle$ Choose a creature to put a trample counter on | CounterType$ TRAMPLE | CounterNum$ 1 | ConditionCheckSVar$ G | ConditionSVarCompare$ GE1 | References$ G | SubAbility$ Vigilance -SVar:Vigilance:DB$ PutCounter | Choices$ Creature.YouCtrl | ChoiceTitle$ Choose a creature to put a vigilance counter on | CounterType$ VIGILANCE | CounterNum$ 1 | ConditionCheckSVar$ H | ConditionSVarCompare$ GE1 | References$ H | SubAbility$ PutCounters -SVar:PutCounters:DB$ PutCounter | Defined$ Self | CounterType$ P1P1 | CounterNum$ GH | References$ GH,FG,EF,DE,CD,BC,AB,ZA,YZ,XY,X -SVar:X:Count$ValidGraveyard Creature.YouOwn+withFlying/LimitMax.1 -SVar:Y:Count$ValidGraveyard Creature.YouOwn+withFirst Strike/LimitMax.1 -SVar:Z:Count$ValidGraveyard Creature.YouOwn+withDouble Strike/LimitMax.1 -SVar:A:Count$ValidGraveyard Creature.YouOwn+withDeathtouch/LimitMax.1 -SVar:B:Count$ValidGraveyard Creature.YouOwn+withHexproof/LimitMax.1 -SVar:C:Count$ValidGraveyard Creature.YouOwn+withIndestructible/LimitMax.1 -SVar:D:Count$ValidGraveyard Creature.YouOwn+withLifelink/LimitMax.1 -SVar:E:Count$ValidGraveyard Creature.YouOwn+withMenace/LimitMax.1 -SVar:F:Count$ValidGraveyard Creature.YouOwn+withReach/LimitMax.1 -SVar:G:Count$ValidGraveyard Creature.YouOwn+withTrample/LimitMax.1 -SVar:H:Count$ValidGraveyard Creature.YouOwn+withVigilance/LimitMax.1 -SVar:XY:SVar$X/Plus.Y -SVar:YZ:SVar$XY/Plus.Z -SVar:ZA:SVar$YZ/Plus.A -SVar:AB:SVar$ZA/Plus.B -SVar:BC:SVar$AB/Plus.C -SVar:CD:SVar$BC/Plus.D -SVar:DE:SVar$CD/Plus.E -SVar:EF:SVar$DE/Plus.F -SVar:FG:SVar$EF/Plus.G -SVar:GH:SVar$FG/Plus.H +T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ PutKeywordCounter | TriggerDescription$ When CARDNAME enters the battlefield, put a flying counter on any creature you control if a creature card in your graveyard has flying. Repeat this process for first strike, double strike, deathtouch, hexproof, indestructible, lifelink, menace, reach, trample, and vigilance. Then put a +1/+1 counter on CARDNAME for each counter put on a creature this way. +SVar:PutKeywordCounter:DB$ PutCounter | Choices$ Creature.YouCtrl | ChoiceTitle$ Choose a creature | SharedKeywords$ Flying & First Strike & Double Strike & Deathtouch & Hexproof & Indestructible & Lifelink & Menace & Reach & Trample & Vigilance | SharedKeywordsZone$ Graveyard | CounterNum$ 1 | RememberAmount$ True | SubAbility$ PutCounters +SVar:PutCounters:DB$ PutCounter | Defined$ Self | CounterType$ P1P1 | CounterNum$ X | References$ X | SubAbility$ DBCleanup +SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True +SVar:X:Count$RememberedNumber DeckHas:Ability$Counters Oracle:When Kathril, Aspect Warper enters the battlefield, put a flying counter on any creature you control if a creature card in your graveyard has flying. Repeat this process for first strike, double strike, deathtouch, hexproof, indestructible, lifelink, menace, reach, trample, and vigilance. Then put a +1/+1 counter on Kathril for each counter put on a creature this way. diff --git a/forge-gui/res/cardsfolder/k/keensight_mentor.txt b/forge-gui/res/cardsfolder/k/keensight_mentor.txt index 7a24f0250fd..d5b2bbe267f 100755 --- a/forge-gui/res/cardsfolder/k/keensight_mentor.txt +++ b/forge-gui/res/cardsfolder/k/keensight_mentor.txt @@ -3,7 +3,7 @@ ManaCost:2 W Types:Creature Human Cleric PT:1/4 T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigPut | TriggerDescription$ When CARDNAME enters the battlefield, put a vigilance counter on target non-Human creature you control. -SVar:TrigPut:DB$ PutCounter | ValidTgts$ Creature.nonHuman+YouCtrl | TgtPrompt$ Select target non-Human creature you control | CounterType$ VIGILANCE | CounterNum$ 1 +SVar:TrigPut:DB$ PutCounter | ValidTgts$ Creature.nonHuman+YouCtrl | TgtPrompt$ Select target non-Human creature you control | CounterType$ Vigilance | CounterNum$ 1 SVar:PlayMain1:TRUE A:AB$ PutCounterAll | Cost$ 1 W T | ValidCards$ Creature.YouCtrl+withVigilance | CounterType$ P1P1 | CounterNum$ 1 | StackDescription$ SpellDescription | SpellDescription$ Put a +1/+1 counter on each creature you control with vigilance. DeckHas:Ability$Counters diff --git a/forge-gui/res/cardsfolder/l/luminous_broodmoth.txt b/forge-gui/res/cardsfolder/l/luminous_broodmoth.txt index d50df5a9aea..8c836611083 100755 --- a/forge-gui/res/cardsfolder/l/luminous_broodmoth.txt +++ b/forge-gui/res/cardsfolder/l/luminous_broodmoth.txt @@ -4,7 +4,6 @@ Types:Creature Insect PT:3/4 K:Flying T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Creature.YouCtrl+withoutFlying | Execute$ TrigReturn | TriggerZones$ Battlefield | TriggerDescription$ Whenever a creature you control without flying dies, return it to the battlefield under its owner's control with a flying counter on it. -SVar:TrigReturn:DB$ ChangeZone | Origin$ Graveyard | Destination$ Battlefield | Defined$ TriggeredCard | SubAbility$ DBPutCounter -SVar:DBPutCounter:DB$ PutCounter | CounterType$ FLYING | CounterNum$ 1 | Defined$ TriggeredCard +SVar:TrigReturn:DB$ ChangeZone | Origin$ Graveyard | Destination$ Battlefield | Defined$ TriggeredCard | WithCounters$ Flying_1 DeckHas:Ability$Counters Oracle:Flying\nWhenever a creature you control without flying dies, return it to the battlefield under its owner's control with a flying counter on it. diff --git a/forge-gui/res/cardsfolder/m/momentum_rumbler.txt b/forge-gui/res/cardsfolder/m/momentum_rumbler.txt index eb07f303bb8..c3249f8e7a5 100755 --- a/forge-gui/res/cardsfolder/m/momentum_rumbler.txt +++ b/forge-gui/res/cardsfolder/m/momentum_rumbler.txt @@ -3,7 +3,7 @@ ManaCost:3 R Types:Creature Dinosaur PT:3/3 T:Mode$ Attacks | ValidCard$ Card.Self | Execute$ TrigPutCounter | IsPresent$ Card.Self+withFirst Strike | PresentCompare$ EQ0 | TriggerDescription$ Whenever CARDNAME attacks, if it doesn't have first strike, put a first strike counter on it. -SVar:TrigPutCounter:DB$ PutCounter | Defined$ TriggeredAttackerLKICopy | CounterType$ FIRSTSTRIKE | CounterNum$ 1 +SVar:TrigPutCounter:DB$ PutCounter | Defined$ TriggeredAttackerLKICopy | CounterType$ First Strike | CounterNum$ 1 T:Mode$ Attacks | ValidCard$ Card.Self | Execute$ TrigPump | IsPresent$ Card.Self+withFirst Strike | PresentCompare$ EQ1 | TriggerDescription$ Whenever CARDNAME attacks, if it has first strike, it gains double strike until end of turn. SVar:TrigPump:DB$ Pump | Defined$ TriggeredAttacker | KW$ Double Strike DeckHas:Ability$Counters diff --git a/forge-gui/res/cardsfolder/n/nahiri_the_lithomancer.txt b/forge-gui/res/cardsfolder/n/nahiri_the_lithomancer.txt index f10d8a12a9e..e30dfe552d0 100644 --- a/forge-gui/res/cardsfolder/n/nahiri_the_lithomancer.txt +++ b/forge-gui/res/cardsfolder/n/nahiri_the_lithomancer.txt @@ -5,7 +5,7 @@ Loyalty:3 Text:CARDNAME can be your commander. A:AB$ Token | Cost$ AddCounter<2/LOYALTY> | Planeswalker$ True | TokenAmount$ 1 | TokenScript$ w_1_1_kor_soldier | TokenOwner$ You | LegacyImage$ w 1 1 kor soldier c14 | RememberTokens$ True | SubAbility$ DBChooseToken | SpellDescription$ Create a 1/1 white Kor Soldier creature token. You may attach an Equipment you control to it. SVar:DBChooseToken:DB$ ChooseCard | DefinedCards$ Remembered | Mandatory$ True | ChoiceTitle$ Choose a token | SubAbility$ DBAttach | StackDescription$ None -SVar:DBAttach:DB$ Attach | Optional$ True | Object$ Valid Equipment.YouCtrl | ChooseAnObject$ Choose an Equipment you control | Defined$ ChosenCard | SubAbility$ DBCleanup | StackDescription$ None +SVar:DBAttach:DB$ Attach | Optional$ True | Choices$ Equipment.YouCtrl | ChoiceTitle$ Choose an Equipment you control | Defined$ ChosenCard | SubAbility$ DBCleanup | StackDescription$ None SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True | ClearChosenCard$ True A:AB$ ChangeZone | Cost$ SubCounter<2/LOYALTY> | Origin$ Hand,Graveyard | Destination$ Battlefield | Hidden$ True | Planeswalker$ True | ChangeType$ Equipment.YouCtrl | Optional$ True | SpellDescription$ You may put an Equipment card from your hand or graveyard onto the battlefield. A:AB$ Token | Cost$ SubCounter<10/LOYALTY> | Planeswalker$ True | Ultimate$ True | TokenAmount$ 1 | TokenScript$ stoneforged_blade | LegacyImage$ stoneforged blade c14 | TokenOwner$ You | SpellDescription$ Create a colorless Equipment artifact token named Stoneforged Blade. It has indestructible, "Equipped creature gets +5/+5 and has double strike," and equip {0}. diff --git a/forge-gui/res/cardsfolder/s/sanctuary_smasher.txt b/forge-gui/res/cardsfolder/s/sanctuary_smasher.txt index 3154f231f84..ad58eff8b97 100755 --- a/forge-gui/res/cardsfolder/s/sanctuary_smasher.txt +++ b/forge-gui/res/cardsfolder/s/sanctuary_smasher.txt @@ -5,6 +5,6 @@ PT:6/4 K:First Strike K:Cycling:2 R T:Mode$ Cycled | ValidCard$ Card.Self | Execute$ TrigPutCounter | TriggerDescription$ When you cycle CARDNAME, put a first strike counter on target creature you control. -SVar:TrigPutCounter:DB$ PutCounter | ValidTgts$ Creature.YouCtrl | TgtPrompt$ Select target creature you control | CounterType$ FIRSTSTRIKE | CounterNum$ 1 +SVar:TrigPutCounter:DB$ PutCounter | ValidTgts$ Creature.YouCtrl | TgtPrompt$ Select target creature you control | CounterType$ First Strike | CounterNum$ 1 DeckHas:Ability$Counters Oracle:First strike\nCycling {2}{R} ({2}{R}, Discard this card: Draw a card.)\nWhen you cycle Sanctuary Smasher, put a first strike counter on target creature you control. diff --git a/forge-gui/res/cardsfolder/s/shambling_swarm.txt b/forge-gui/res/cardsfolder/s/shambling_swarm.txt index 373b311c60a..748848236e3 100644 --- a/forge-gui/res/cardsfolder/s/shambling_swarm.txt +++ b/forge-gui/res/cardsfolder/s/shambling_swarm.txt @@ -3,10 +3,7 @@ ManaCost:1 B B B Types:Creature Horror PT:3/3 T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self | Execute$ SwarmSpread | TriggerController$ TriggeredCardController | TriggerDescription$ When CARDNAME dies, distribute three -1/-1 counters among one, two, or three target creatures. For each -1/-1 counter you put on a creature this way, remove a -1/-1 counter from that creature at the beginning of the next end step. -SVar:SwarmSpread:DB$ PutCounter | ValidTgts$ Creature | TgtPrompt$ Select target creature to distribute counters to | IsCurse$ True | CounterType$ M1M1 | CounterNum$ 3 | TargetMin$ 1 | TargetMax$ 3 | DividedAsYouChoose$ 3 | RememberCounters$ True | SubAbility$ DelayedRemoveCounters | RememberTargets$ True -SVar:DelayedRemoveCounters:DB$ DelayedTrigger | Mode$ Phase | Phase$ End of Turn | Execute$ TrigRemoveCounter | Secondary$ True | TriggerDescription$ For each -1/-1 counter you put on a creature this way, remove a -1/-1 counter from that creature at the beginning of the next end step. -SVar:TrigRemoveCounter:DB$ RemoveCounter | Defined$ Remembered | CounterType$ M1M1 | CounterNum$ Remembered | SubAbility$ DBCleanup -SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True +SVar:SwarmSpread:DB$ PutCounter | ValidTgts$ Creature | TgtPrompt$ Select target creature to distribute counters to | IsCurse$ True | CounterType$ M1M1 | CounterNum$ 3 | TargetMin$ 1 | TargetMax$ 3 | DividedAsYouChoose$ 3 | RemovePhase$ End of Turn AI:RemoveDeck:All SVar:Picture:http://www.wizards.com/global/images/magic/general/shambling_swarm.jpg Oracle:When Shambling Swarm dies, distribute three -1/-1 counters among one, two, or three target creatures. For each -1/-1 counter you put on a creature this way, remove a -1/-1 counter from that creature at the beginning of the next end step. diff --git a/forge-gui/res/cardsfolder/s/simic_guildmage.txt b/forge-gui/res/cardsfolder/s/simic_guildmage.txt index 79123e586f3..6fb9ae985e2 100644 --- a/forge-gui/res/cardsfolder/s/simic_guildmage.txt +++ b/forge-gui/res/cardsfolder/s/simic_guildmage.txt @@ -2,12 +2,11 @@ Name:Simic Guildmage ManaCost:GU GU Types:Creature Elf Wizard PT:2/2 -A:AB$ Pump | Cost$ 1 G | ValidTgts$ Creature | TgtPrompt$ Select target creature to remove +1/+1 counter | SubAbility$ DBMove | RememberObjects$ Targeted | StackDescription$ None | SpellDescription$ Move a +1/+1 counter from target creature onto another target creature with the same controller. -SVar:DBMove:DB$ MoveCounter | Source$ Remembered | ValidTgts$ Creature | TgtPrompt$ Select another target creature with the same controller to get +1/+1 counter | TargetUnique$ True | TargetsWithSameController$ True | CounterType$ P1P1 | CounterNum$ 1 | ConditionDefined$ Targeted | ConditionPresent$ Creature.sharesControllerWith Remembered | ConditionCompare$ EQ1 | SubAbility$ DBCleanup -SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True -A:AB$ Pump| Cost$ 1 U | ValidTgts$ Aura.AttachedTo Permanent | TgtPrompt$ Select target aura to move | RememberObjects$ Valid Permanent.EnchantedBy Targeted | SubAbility$ ChooseNewHost | StackDescription$ None | SpellDescription$ Attach target Aura attached to a permanent to another permanent with the same controller. +A:AB$ MoveCounter | Cost$ 1 G | ValidTgts$ Creature | TargetMin$ 2 | TargetMax$ 2 | TgtPrompt$ Select target creatures to move +1/+1 counters | TargetsWithSameController$ True | CounterType$ P1P1 | CounterNum$ 1 | StackDescription$ SpellDescription | SpellDescription$ Move a +1/+1 counter from target creature onto another target creature with the same controller. +A:AB$ Pump | Cost$ 1 U | ValidTgts$ Aura.AttachedTo Permanent | TgtPrompt$ Select target aura to move | RememberObjects$ Valid Permanent.EnchantedBy Targeted | SubAbility$ ChooseNewHost | StackDescription$ None | SpellDescription$ Attach target Aura attached to a permanent to another permanent with the same controller. SVar:ChooseNewHost:DB$ ChooseCard | Defined$ You | Amount$ 1 | Choices$ Permanent.NotEnchantedByTargeted+sharesControllerWith Remembered+CanBeEnchantedByTargeted | ChoiceZone$ Battlefield | SubAbility$ DBAttach | AILogic$ AtLeast1 SVar:DBAttach:DB$ Attach | Object$ ParentTarget | Defined$ ChosenCard | SubAbility$ DBCleanup +SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True AI:RemoveDeck:All SVar:Picture:http://www.wizards.com/global/images/magic/general/simic_guildmage.jpg Oracle:({G/U} can be paid with either {G} or {U}.)\n{1}{G}: Move a +1/+1 counter from target creature onto another target creature with the same controller.\n{1}{U}: Attach target Aura attached to a permanent to another permanent with the same controller. diff --git a/forge-gui/res/cardsfolder/s/skullbriar_the_walking_grave.txt b/forge-gui/res/cardsfolder/s/skullbriar_the_walking_grave.txt index b470d48e165..8c87fe8e0e7 100644 --- a/forge-gui/res/cardsfolder/s/skullbriar_the_walking_grave.txt +++ b/forge-gui/res/cardsfolder/s/skullbriar_the_walking_grave.txt @@ -1,10 +1,10 @@ Name:Skullbriar, the Walking Grave ManaCost:B G Types:Legendary Creature Zombie Elemental -Text:Counters remain on CARDNAME as it moves to any zone other than a player's hand or library. PT:1/1 K:Haste +K:Counters remain on CARDNAME as it moves to any zone other than a player's hand or library. T:Mode$ DamageDone | ValidSource$ Card.Self | ValidTarget$ Player | Execute$ TrigPutCounter | TriggerDescription$ Whenever CARDNAME deals combat damage to a player, put a +1/+1 counter on it. -SVar:TrigPutCounter:DB$PutCounter | Defined$ Self | CounterType$ P1P1 | CounterNum$ 1 +SVar:TrigPutCounter:DB$ PutCounter | Defined$ Self | CounterType$ P1P1 | CounterNum$ 1 SVar:Picture:http://www.wizards.com/global/images/magic/general/skullbriar_the_walking_grave.jpg Oracle:Haste\nWhenever Skullbriar, the Walking Grave deals combat damage to a player, put a +1/+1 counter on it.\nCounters remain on Skullbriar as it moves to any zone other than a player's hand or library. diff --git a/forge-gui/res/cardsfolder/s/splendor_mare.txt b/forge-gui/res/cardsfolder/s/splendor_mare.txt index 5faba38914e..29b3eba0750 100755 --- a/forge-gui/res/cardsfolder/s/splendor_mare.txt +++ b/forge-gui/res/cardsfolder/s/splendor_mare.txt @@ -5,5 +5,5 @@ PT:3/3 K:Lifelink K:Cycling:1 W T:Mode$ Cycled | ValidCard$ Card.Self | Execute$ TrigPutCounter | TriggerDescription$ When you cycle CARDNAME, put a lifelink counter on target creature you control. -SVar:TrigPutCounter:DB$ PutCounter | ValidTgts$ Creature.YouCtrl | TgtPrompt$ Select target creature you control | CounterType$ LIFELINK | CounterNum$ 1 +SVar:TrigPutCounter:DB$ PutCounter | ValidTgts$ Creature.YouCtrl | TgtPrompt$ Select target creature you control | CounterType$ Lifelink | CounterNum$ 1 Oracle:Lifelink\nCycling {1}{W} ({1}{W}, Discard this card: Draw a card.)\nWhen you cycle Splendor Mare, put a lifelink counter on target creature you control. diff --git a/forge-gui/res/cardsfolder/s/spontaneous_flight.txt b/forge-gui/res/cardsfolder/s/spontaneous_flight.txt index 049dc48dc8c..1f113f74b8f 100755 --- a/forge-gui/res/cardsfolder/s/spontaneous_flight.txt +++ b/forge-gui/res/cardsfolder/s/spontaneous_flight.txt @@ -2,6 +2,6 @@ Name:Spontaneous Flight ManaCost:2 W Types:Instant A:SP$ Pump | Cost$ 2 W | ValidTgts$ Creature | TgtPrompt$ Select target creature | NumAtt$ +2 | NumDef$ +2 | SubAbility$ DBPutCounter | SpellDescription$ Target creature gets +2/+2 until end of turn. Put a flying counter on it. -SVar:DBPutCounter:DB$ PutCounter | Defined$ Targeted | CounterType$ FLYING | CounterNum$ 1 +SVar:DBPutCounter:DB$ PutCounter | Defined$ Targeted | CounterType$ Flying | CounterNum$ 1 DeckHas:Ability$Counters Oracle:Target creature gets +2/+2 until end of turn. Put a flying counter on it. diff --git a/forge-gui/res/cardsfolder/s/sudden_spinnerets.txt b/forge-gui/res/cardsfolder/s/sudden_spinnerets.txt index bd1f79c7ef7..78e77648596 100755 --- a/forge-gui/res/cardsfolder/s/sudden_spinnerets.txt +++ b/forge-gui/res/cardsfolder/s/sudden_spinnerets.txt @@ -2,7 +2,7 @@ Name:Sudden Spinnerets ManaCost:G Types:Instant A:SP$ Pump | Cost$ G | ValidTgts$ Creature | TgtPrompt$ Select target creature | NumAtt$ +1 | NumDef$ +3 | SubAbility$ DBPutCounter | SpellDescription$ Target creature gets +1/+3 until end of turn. Put a reach counter on it. Untap it. -SVar:DBPutCounter:DB$ PutCounter | Defined$ Targeted | CounterType$ REACH | CounterNum$ 1 | SubAbility$ DBUntap +SVar:DBPutCounter:DB$ PutCounter | Defined$ Targeted | CounterType$ Reach | CounterNum$ 1 | SubAbility$ DBUntap SVar:DBUntap:DB$ Untap | Defined$ Targeted DeckHas:Ability$Counters Oracle:Target creature gets +1/+3 until end of turn. Put a reach counter on it. Untap it. diff --git a/forge-gui/res/cardsfolder/t/the_ozolith.txt b/forge-gui/res/cardsfolder/t/the_ozolith.txt index a06308838c4..f2158f01e83 100644 --- a/forge-gui/res/cardsfolder/t/the_ozolith.txt +++ b/forge-gui/res/cardsfolder/t/the_ozolith.txt @@ -4,9 +4,6 @@ Types:Legendary Artifact T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Any | ValidCard$ Creature.YouCtrl+HasCounters | TriggerZones$ Battlefield | Execute$ TrigPutCounter | TriggerDescription$ Whenever a creature you control leaves the battlefield, if it had counters on it, put those counters on CARDNAME. SVar:TrigPutCounter:DB$ PutCounter | Defined$ Self | CounterType$ EachFromSource | EachFromSource$ TriggeredCardLKICopy T:Mode$ Phase | Phase$ BeginCombat | ValidPlayer$ You | IsPresent$ Card.Self+HasCounters | TriggerZones$ Battlefield | Execute$ TrigMoveCounter | OptionalDecider$ You | TriggerDescription$ At the beginning of combat on your turn, if CARDNAME has counters on it, you may move all counters from CARDNAME onto target creature. -SVar:TrigMoveCounter:DB$ Pump | ValidTgts$ Creature | TgtPrompt$ Select target creature to get counters | RememberObjects$ Targeted | SubAbility$ DBMove -SVar:DBMove:DB$ RepeatEach | RepeatCounters$ True | RepeatSubAbility$ MoveCounters | SubAbility$ DBCleanup -SVar:MoveCounters:DB$ MoveCounter | Source$ Self | Defined$ Remembered | CounterType$ RepeatSVarCounter | CounterNum$ RepeatCounterAmount -SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True +SVar:TrigMoveCounter:DB$ MoveCounter | Source$ Self | ValidTgts$ Creature | TgtPrompt$ Select target creature to get counters | CounterType$ All | CounterNum$ All DeckHas:Ability$Counters Oracle:Whenever a creature you control leaves the battlefield, if it had counters on it, put those counters on The Ozolith.\nAt the beginning of combat on your turn, if The Ozolith has counters on it, you may move all counters from The Ozolith onto target creature. diff --git a/forge-gui/res/cardsfolder/t/thought_gorger.txt b/forge-gui/res/cardsfolder/t/thought_gorger.txt index a2e22f18da7..e8ae5953232 100644 --- a/forge-gui/res/cardsfolder/t/thought_gorger.txt +++ b/forge-gui/res/cardsfolder/t/thought_gorger.txt @@ -4,10 +4,10 @@ Types:Creature Horror PT:2/2 K:Trample T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ EatMyThoughts | TriggerController$ TriggeredCardController | TriggerDescription$ When CARDNAME enters the battlefield, put a +1/+1 counter on it for each card in your hand. If you do, discard your hand. -SVar:EatMyThoughts:DB$ PutCounter | Defined$ Self | CounterNum$ OldThoughts | CounterType$ P1P1 | References$ OldThoughts | RememberCounters$ True | SubAbility$ GorgeOnThoughts -SVar:GorgeOnThoughts:DB$ Discard | Mode$ Hand | Defined$ You | ConditionDescription$ If you do, | ConditionCheckSVar$ StrengthOfThoughts | ConditionSVarCompare$ GE1 +SVar:EatMyThoughts:DB$ PutCounter | Defined$ Self | CounterNum$ OldThoughts | CounterType$ P1P1 | References$ OldThoughts | RememberCards$ True | SubAbility$ GorgeOnThoughts +SVar:GorgeOnThoughts:DB$ Discard | Mode$ Hand | Defined$ You | ConditionDescription$ If you do, | ConditionDefined$ Remembered | ConditionPresent$ Card | SubAbility$ DBCleanup +SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True SVar:OldThoughts:Count$InYourHand -SVar:StrengthOfThoughts:Count$CountersAdded P1P1 Card.Self T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Any | ValidCard$ Card.Self | Execute$ BringBackThoughts | TriggerController$ TriggeredCardController | TriggerDescription$ When CARDNAME leaves the battlefield, draw a card for each +1/+1 counter on it. SVar:BringBackThoughts:DB$ Draw | NumCards$ Disgorge | References$ Disgorge | Defined$ TriggeredCardController SVar:Disgorge:TriggeredCard$CardCounters.P1P1 diff --git a/forge-gui/res/cardsfolder/u/unbreakable_bond.txt b/forge-gui/res/cardsfolder/u/unbreakable_bond.txt index f73dcd462dc..097a61f828d 100755 --- a/forge-gui/res/cardsfolder/u/unbreakable_bond.txt +++ b/forge-gui/res/cardsfolder/u/unbreakable_bond.txt @@ -1,7 +1,6 @@ Name:Unbreakable Bond ManaCost:4 B Types:Sorcery -A:SP$ ChangeZone | Cost$ 4 B | Origin$ Graveyard | Destination$ Battlefield | TgtPrompt$ Choose target creature card in your graveyard | ValidTgts$ Creature.YouOwn | SubAbility$ DBPutCounter | SpellDescription$ Return target creature card from your graveyard to the battlefield with a lifelink counter on it. -SVar:DBPutCounter:DB$ PutCounter | Defined$ Targeted | CounterType$ LIFELINK | CounterNum$ 1 +A:SP$ ChangeZone | Cost$ 4 B | Origin$ Graveyard | Destination$ Battlefield | TgtPrompt$ Choose target creature card in your graveyard | ValidTgts$ Creature.YouOwn | WithCounters$ Lifelink_1 | SpellDescription$ Return target creature card from your graveyard to the battlefield with a lifelink counter on it. DeckHas:Ability$Counters Oracle:Return target creature card from your graveyard to the battlefield with a lifelink counter on it. diff --git a/forge-gui/res/cardsfolder/u/unexpected_fangs.txt b/forge-gui/res/cardsfolder/u/unexpected_fangs.txt index fe6371d7f02..c33822d4368 100755 --- a/forge-gui/res/cardsfolder/u/unexpected_fangs.txt +++ b/forge-gui/res/cardsfolder/u/unexpected_fangs.txt @@ -2,6 +2,6 @@ Name:Unexpected Fangs ManaCost:1 B Types:Instant A:SP$ PutCounter | Cost$ 1 B | ValidTgts$ Creature | TgtPrompt$ Select target creature | CounterType$ P1P1 | CounterNum$ 1 | SubAbility$ DBPutCounter | SpellDescription$ Put a +1/+1 counter and a lifelink counter on target creature. -SVar:DBPutCounter:DB$ PutCounter | CounterType$ LIFELINK | CounterNum$ 1 | Defined$ Targeted +SVar:DBPutCounter:DB$ PutCounter | CounterType$ Lifelink | CounterNum$ 1 | Defined$ Targeted DeckHas:Ability$Counters Oracle:Put a +1/+1 counter and a lifelink counter on target creature. diff --git a/forge-gui/res/cardsfolder/upcoming/slippery_bogbonder.txt b/forge-gui/res/cardsfolder/upcoming/slippery_bogbonder.txt new file mode 100644 index 00000000000..9ba82bec495 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/slippery_bogbonder.txt @@ -0,0 +1,10 @@ +Name:Slippery Bogbonder +ManaCost:3 G +Types:Creature Human Druid +PT:3/3 +K:Flash +K:Hexproof +T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigPutCounter | TriggerDescription$ When CARDNAME enters the battlefield, put a hexproof counter on target creature. Then move any number of counters from among creatures you control onto that creature. +SVar:TrigPutCounter:DB$ PutCounter | ValidTgts$ Creature | TgtPrompt$ Select target creature | CounterType$ Hexproof | CounterNum$ 1 | SubAbility$ MoveAllCounter +SVar:MoveAllCounter:DB$ MoveCounter | ValidSource$ Creature.YouCtrl | CounterType$ All | CounterNum$ Any | Defined$ Targeted +Oracle:Flash\nHexproof\nWhen Slippery Bogbonder enters the battlefield, put a hexproof counter on target creature. Then move any number of counters from among creatures you control onto that creature. diff --git a/forge-gui/res/cardsfolder/v/vitality_hunter.txt b/forge-gui/res/cardsfolder/v/vitality_hunter.txt index 7059a027d2c..44975e27d4f 100755 --- a/forge-gui/res/cardsfolder/v/vitality_hunter.txt +++ b/forge-gui/res/cardsfolder/v/vitality_hunter.txt @@ -5,7 +5,7 @@ PT:3/4 K:Lifelink K:Monstrosity:X:X W W T:Mode$ BecomeMonstrous | ValidCard$ Card.Self | TriggerZones$ Battlefield | Execute$ TrigPutCounter | TriggerDescription$ When CARDNAME becomes monstrous, put a lifelink counter on each of up to X target creatures. -SVar:TrigPutCounter:DB$ PutCounter | CounterNum$ 1 | CounterType$ LIFELINK | TargetMin$ 0 | TargetMax$ MaxTgts | References$ X,MaxTgts | ValidTgts$ Creature | TgtPrompt$ Select target creatures | SpellDescription$ Put a lifelink counter on each of up to X target creatures. +SVar:TrigPutCounter:DB$ PutCounter | CounterNum$ 1 | CounterType$ Lifelink | TargetMin$ 0 | TargetMax$ MaxTgts | References$ X,MaxTgts | ValidTgts$ Creature | TgtPrompt$ Select target creatures | SpellDescription$ Put a lifelink counter on each of up to X target creatures. SVar:X:Count$xPaid SVar:MaxTgts:TriggerCount$MonstrosityAmount DeckHas:Ability$Counters diff --git a/forge-gui/res/cardsfolder/v/vivien_monsters_advocate.txt b/forge-gui/res/cardsfolder/v/vivien_monsters_advocate.txt index 03a10bd91aa..8f674f0c37f 100755 --- a/forge-gui/res/cardsfolder/v/vivien_monsters_advocate.txt +++ b/forge-gui/res/cardsfolder/v/vivien_monsters_advocate.txt @@ -7,9 +7,9 @@ S:Mode$ Continuous | Affected$ Creature.TopLibrary+YouCtrl | MayPlay$ True | Aff SVar:NonStackingEffect:True A:AB$ Token | Cost$ AddCounter<1/LOYALTY> | Planeswalker$ True | TokenAmount$ 1 | TokenScript$ g_3_3_beast | TokenOwner$ You | LegacyImage$ g 3 3 beast iko | RememberTokens$ True | SubAbility$ DBGenericChoice | SpellDescription$ Create a 3/3 green Beast creature token. Put your choice of a vigilance counter, a reach counter, or a trample counter on it. SVar:DBGenericChoice:DB$ GenericChoice | Defined$ You | Choices$ Vigilance,Reach,Trample -SVar:Reach:DB$ PutCounter | Choices$ Card.IsRemembered | ChoiceTitle$ Choose a token to put a reach counter on | CounterType$ REACH | CounterNum$ 1 | SubAbility$ DBCleanup | SpellDescription$ Reach -SVar:Trample:DB$ PutCounter | Choices$ Card.IsRemembered | ChoiceTitle$ Choose a token to put a trample counter on | CounterType$ TRAMPLE | CounterNum$ 1 | SubAbility$ DBCleanup | SpellDescription$ Trample -SVar:Vigilance:DB$ PutCounter | Choices$ Card.IsRemembered | ChoiceTitle$ Choose a token to put a vigilance counter on | CounterType$ VIGILANCE | CounterNum$ 1 | SubAbility$ DBCleanup | SpellDescription$ Vigilance +SVar:Reach:DB$ PutCounter | Choices$ Card.IsRemembered | ChoiceTitle$ Choose a token to put a reach counter on | CounterType$ Reach | CounterNum$ 1 | SubAbility$ DBCleanup | SpellDescription$ Reach +SVar:Trample:DB$ PutCounter | Choices$ Card.IsRemembered | ChoiceTitle$ Choose a token to put a trample counter on | CounterType$ Trample | CounterNum$ 1 | SubAbility$ DBCleanup | SpellDescription$ Trample +SVar:Vigilance:DB$ PutCounter | Choices$ Card.IsRemembered | ChoiceTitle$ Choose a token to put a vigilance counter on | CounterType$ Vigilance | CounterNum$ 1 | SubAbility$ DBCleanup | SpellDescription$ Vigilance SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True A:AB$ Effect | Cost$ SubCounter<2/LOYALTY> | Planeswalker$ True | Triggers$ TrigSearch | SVars$ DBSearch,X | SpellDescription$ When you cast your next creature spell this turn, search your library for a creature card with lesser converted mana cost, put it onto the battlefield, then shuffle your library. SVar:TrigSearch:Mode$ SpellCast | ValidCard$ Creature | ValidActivatingPlayer$ You | OneOff$ True | TriggerZones$ Command | Execute$ DBSearch | TriggerDescription$ When you cast your next creature spell this turn, search your library for a creature card with lesser converted mana cost, put it onto the battlefield, then shuffle your library. diff --git a/forge-gui/res/cardsfolder/v/void_beckoner.txt b/forge-gui/res/cardsfolder/v/void_beckoner.txt index 75c8d30989d..4c1d3b7eeb4 100755 --- a/forge-gui/res/cardsfolder/v/void_beckoner.txt +++ b/forge-gui/res/cardsfolder/v/void_beckoner.txt @@ -5,6 +5,6 @@ PT:8/8 K:Deathtouch K:Cycling:2 B T:Mode$ Cycled | ValidCard$ Card.Self | Execute$ TrigPutCounter | TriggerDescription$ When you cycle CARDNAME, put a deathtouch counter on target creature you control. -SVar:TrigPutCounter:DB$ PutCounter | ValidTgts$ Creature.YouCtrl | TgtPrompt$ Select target creature you control | CounterType$ DEATHTOUCH | CounterNum$ 1 +SVar:TrigPutCounter:DB$ PutCounter | ValidTgts$ Creature.YouCtrl | TgtPrompt$ Select target creature you control | CounterType$ Deathtouch | CounterNum$ 1 DeckHas:Ability$Counters Oracle:Deathtouch\nCycling {2}{B} ({2}{B}, Discard this card: Draw a card.)\nWhen you cycle Void Beckoner, put a deathtouch counter on target creature you control. diff --git a/forge-gui/res/cardsfolder/w/wingfold_pteron.txt b/forge-gui/res/cardsfolder/w/wingfold_pteron.txt index 142c64fa85e..4707dfcdee3 100755 --- a/forge-gui/res/cardsfolder/w/wingfold_pteron.txt +++ b/forge-gui/res/cardsfolder/w/wingfold_pteron.txt @@ -4,7 +4,7 @@ Types:Creature Dinosaur PT:3/6 K:ETBReplacement:Other:CounterChoice SVar:CounterChoice:DB$ GenericChoice | Defined$ You | Choices$ Flying,Hexproof | SpellDescription$ CARDNAME enters the battlefield with your choice of a flying counter or a hexproof counter on it. -SVar:Flying:DB$ PutCounter | CounterType$ FLYING | CounterNum$ 1 | SpellDescription$ CARDNAME enters the battlefield with a flying counter on it -SVar:Hexproof:DB$ PutCounter | CounterType$ HEXPROOF | CounterNum$ 1 | SpellDescription$ CARDNAME enters the battlefield with a hexproof counter on it +SVar:Flying:DB$ PutCounter | CounterType$ Flying | CounterNum$ 1 | SpellDescription$ CARDNAME enters the battlefield with a flying counter on it +SVar:Hexproof:DB$ PutCounter | CounterType$ Hexproof | CounterNum$ 1 | SpellDescription$ CARDNAME enters the battlefield with a hexproof counter on it DeckHas:Ability$Counters Oracle:Wingfold Pteron enters the battlefield with your choice of a flying counter or a hexproof counter on it. (A creature with hexproof can't be the target of spells or abilities your opponents control.) diff --git a/forge-gui/res/cardsfolder/w/wingspan_mentor.txt b/forge-gui/res/cardsfolder/w/wingspan_mentor.txt index 0c6fd836596..d45df236adb 100755 --- a/forge-gui/res/cardsfolder/w/wingspan_mentor.txt +++ b/forge-gui/res/cardsfolder/w/wingspan_mentor.txt @@ -3,7 +3,7 @@ ManaCost:2 U Types:Creature Human Wizard PT:1/3 T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Creature.Self | Execute$ TrigPut | TriggerDescription$ When CARDNAME enters the battlefield, put a flying counter on target non-Human creature you control. -SVar:TrigPut:DB$PutCounter | ValidTgts$ Creature.nonHuman+YouCtrl | TgtPrompt$ Select target non-Human creature | CounterType$ FLYING | CounterNum$ 1 +SVar:TrigPut:DB$PutCounter | ValidTgts$ Creature.nonHuman+YouCtrl | TgtPrompt$ Select target non-Human creature | CounterType$ Flying | CounterNum$ 1 A:AB$ PutCounterAll | Cost$ 2 U T | ValidCards$ Creature.YouCtrl+withFlying | CounterType$ P1P1 | CounterNum$ 1 | StackDescription$ SpellDescription | SpellDescription$ Put a +1/+1 counter on each creature you control with flying. SVar:PlayMain1:TRUE DeckHas:Ability$Counters diff --git a/forge-gui/src/main/java/forge/control/FControlGameEventHandler.java b/forge-gui/src/main/java/forge/control/FControlGameEventHandler.java index f3f3f0a1b72..e7ab5bce285 100644 --- a/forge-gui/src/main/java/forge/control/FControlGameEventHandler.java +++ b/forge-gui/src/main/java/forge/control/FControlGameEventHandler.java @@ -17,7 +17,6 @@ import forge.game.card.CardView; import forge.game.event.*; import forge.game.player.Player; import forge.game.player.PlayerView; -import forge.game.zone.PlayerZone; import forge.game.zone.Zone; import forge.game.zone.ZoneType; import forge.interfaces.IGuiGame; @@ -294,7 +293,7 @@ public class FControlGameEventHandler extends IGameEventVisitor.Base { @Override public Void visit(final GameEventCardAttachment event) { final Game game = event.equipment.getGame(); - final PlayerZone zEq = (PlayerZone)game.getZoneOf(event.equipment); + final Zone zEq = (Zone)game.getZoneOf(event.equipment); if (event.oldEntiy instanceof Card) { updateZone(game.getZoneOf((Card)event.oldEntiy)); } diff --git a/forge-gui/src/main/java/forge/match/input/InputSelectTargets.java b/forge-gui/src/main/java/forge/match/input/InputSelectTargets.java index e6ba852fce2..5de5545d36e 100644 --- a/forge-gui/src/main/java/forge/match/input/InputSelectTargets.java +++ b/forge-gui/src/main/java/forge/match/input/InputSelectTargets.java @@ -189,6 +189,21 @@ public final class InputSelectTargets extends InputSyncronizedBase { } } + // If all cards must have different controllers + if (tgt.isSameController()) { + final List targetedControllers = new ArrayList<>(); + for (final GameObject o : targetDepth.keySet()) { + if (o instanceof Card) { + final Player p = ((Card) o).getController(); + targetedControllers.add(p); + } + } + if (!targetedControllers.isEmpty() && !targetedControllers.contains(card.getController())) { + showMessage(sa.getHostCard() + " - Cannot target this card (must have same controller)"); + return false; + } + } + // If all cards must have different controllers if (tgt.isDifferentControllers()) { final List targetedControllers = new ArrayList<>(); @@ -333,7 +348,6 @@ public final class InputSelectTargets extends InputSyncronizedBase { return tgt.isMaxTargetsChosen(sa.getHostCard(), sa) || ( tgt.getStillToDivide() == 0 && tgt.isDividedAsYouChoose()); } - @Override protected void onStop() { getController().getGui().clearSelectables(); diff --git a/forge-gui/src/main/java/forge/player/HumanCostDecision.java b/forge-gui/src/main/java/forge/player/HumanCostDecision.java index 2e2a5dea062..0bcac737676 100644 --- a/forge-gui/src/main/java/forge/player/HumanCostDecision.java +++ b/forge-gui/src/main/java/forge/player/HumanCostDecision.java @@ -24,6 +24,7 @@ import forge.game.card.CardLists; import forge.game.card.CardPredicates; import forge.game.card.CardPredicates.Presets; import forge.game.card.CardView; +import forge.game.card.CounterEnumType; import forge.game.card.CounterType; import forge.game.cost.*; import forge.game.player.Player; @@ -625,7 +626,7 @@ public class HumanCostDecision extends CostDecisionMakerBase { @Override public PaymentDecision visit(final CostPayEnergy cost) { final String amount = cost.getAmount(); - final int energy = player.getCounters(CounterType.ENERGY); + final int energy = player.getCounters(CounterEnumType.ENERGY); Integer c = cost.convertAmount(); if (c == null) { @@ -641,7 +642,7 @@ public class HumanCostDecision extends CostDecisionMakerBase { } if (player.canPayEnergy(c) && - player.getController().confirmPayment(cost, Localizer.getInstance().getMessage("lblPayEnergyConfirm", cost.toString(), String.valueOf(player.getCounters(CounterType.ENERGY)), "{E}"), ability)) { + player.getController().confirmPayment(cost, Localizer.getInstance().getMessage("lblPayEnergyConfirm", cost.toString(), String.valueOf(player.getCounters(CounterEnumType.ENERGY)), "{E}"), ability)) { return PaymentDecision.number(c); } return null; diff --git a/forge-gui/src/main/java/forge/player/HumanPlay.java b/forge-gui/src/main/java/forge/player/HumanPlay.java index b7b4a93f735..97b8f5d2e03 100644 --- a/forge-gui/src/main/java/forge/player/HumanPlay.java +++ b/forge-gui/src/main/java/forge/player/HumanPlay.java @@ -566,7 +566,7 @@ public class HumanPlay { } } else if (part instanceof CostPayEnergy) { - CounterType counterType = CounterType.ENERGY; + CounterType counterType = CounterType.get(CounterEnumType.ENERGY); int amount = getAmountFromPartX(part, source, sourceAbility); if (!part.canPay(sourceAbility, p)) { diff --git a/forge-gui/src/main/java/forge/player/HumanPlaySpellAbility.java b/forge-gui/src/main/java/forge/player/HumanPlaySpellAbility.java index 370d250b308..6917e797ac6 100644 --- a/forge-gui/src/main/java/forge/player/HumanPlaySpellAbility.java +++ b/forge-gui/src/main/java/forge/player/HumanPlaySpellAbility.java @@ -214,7 +214,7 @@ public class HumanPlaySpellAbility { final FCollection candidates = AbilityUtils.getDefinedPlayers(source, currentAbility.getParam("TargetingPlayer"), currentAbility); // activator chooses targeting player targetingPlayer = ability.getActivatingPlayer().getController().chooseSingleEntityForEffect( - candidates, currentAbility, "Choose the targeting player"); + candidates, currentAbility, "Choose the targeting player", null); } else { targetingPlayer = ability.getActivatingPlayer(); } diff --git a/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java b/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java index d2bdbf9033b..f323ad043cb 100644 --- a/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java +++ b/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java @@ -392,13 +392,13 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont @Override public CardCollectionView chooseCardsForEffect(final CardCollectionView sourceList, final SpellAbility sa, - final String title, final int min, final int max, final boolean isOptional) { + final String title, final int min, final int max, final boolean isOptional, Map params) { // If only one card to choose, use a dialog box. // Otherwise, use the order dialog to be able to grab multiple cards in // one shot if (max == 1) { - final Card singleChosen = chooseSingleEntityForEffect(sourceList, sa, title, isOptional); + final Card singleChosen = chooseSingleEntityForEffect(sourceList, sa, title, isOptional, params); return singleChosen == null ? CardCollection.EMPTY : new CardCollection(singleChosen); } @@ -427,7 +427,7 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont @Override public T chooseSingleEntityForEffect(final FCollectionView optionList, final DelayedReveal delayedReveal, final SpellAbility sa, final String title, final boolean isOptional, - final Player targetedPlayer) { + final Player targetedPlayer, Map params) { // Human is supposed to read the message and understand from it what to // choose if (optionList.isEmpty()) { @@ -465,6 +465,7 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont final GameEntityView result = getGui().chooseSingleEntityForEffect(title, gameCacheChoose.getTrackableKeys(), delayedReveal, isOptional); endTempShowCards(); + if (result != null || !gameCacheChoose.containsKey(result)) { return null; } @@ -472,9 +473,9 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont } @Override - public List chooseEntitiesForEffect(final FCollectionView optionList, final int min, - final int max, final DelayedReveal delayedReveal, final SpellAbility sa, final String title, - final Player targetedPlayer) { + public List chooseEntitiesForEffect(final FCollectionView optionList, final int min, final int max, + final DelayedReveal delayedReveal, final SpellAbility sa, final String title, final Player targetedPlayer, Map params) { + // useful details for debugging problems with the mass select logic Sentry.getContext().addExtra("Card", sa.getCardView().toString()); @@ -554,6 +555,22 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont return spellViewCache.get(choice); } + @Override + public List chooseSpellAbilitiesForEffect(List spells, SpellAbility sa, String title, int num, Map params) { + List result = Lists.newArrayList(); + // create a mapping between a spell's view and the spell itself + Map spellViewCache = SpellAbilityView.getMap(spells); + + List chosen = getGui().many(title, "", num, Lists.newArrayList(spellViewCache.keySet()), sa.getHostCard().getView()); + + for(SpellAbilityView view : chosen) { + if (spellViewCache.containsKey(view)) { + result.add(spellViewCache.get(view)); + } + } + return result; + } + /* * (non-Javadoc) * @@ -1848,13 +1865,13 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont public Card chooseSingleCardForZoneChange(final ZoneType destination, final List origin, final SpellAbility sa, final CardCollection fetchList, final DelayedReveal delayedReveal, final String selectPrompt, final boolean isOptional, final Player decider) { - return chooseSingleEntityForEffect(fetchList, delayedReveal, sa, selectPrompt, isOptional, decider); + return chooseSingleEntityForEffect(fetchList, delayedReveal, sa, selectPrompt, isOptional, decider, null); } public List chooseCardsForZoneChange(final ZoneType destination, final List origin, final SpellAbility sa, final CardCollection fetchList, final int min, final int max, final DelayedReveal delayedReveal, final String selectPrompt, final Player decider) { - return chooseEntitiesForEffect(fetchList, min, max, delayedReveal, sa, selectPrompt, decider); + return chooseEntitiesForEffect(fetchList, min, max, delayedReveal, sa, selectPrompt, decider, null); } @Override @@ -2200,7 +2217,12 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont final Card card = gameCacheCounters.get(cv); final ImmutableList counters = subtract ? ImmutableList.copyOf(card.getCounters().keySet()) - : CounterType.values; + : ImmutableList.copyOf(Collections2.transform(CounterEnumType.values, new Function() { + @Override + public CounterType apply(CounterEnumType input) { + return CounterType.get(input); + } + })); final CounterType counter = getGui().oneOrNone(localizer.getMessage("lblWhichTypeofCounter"), counters); if (counter == null) { @@ -2987,7 +3009,7 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont public CardCollection chooseCardsForEffectMultiple(Map validMap, SpellAbility sa, String title, boolean isOptional) { CardCollection result = new CardCollection(); for (Map.Entry e : validMap.entrySet()) { - result.addAll(chooseCardsForEffect(e.getValue(), sa, title + " " + e.getKey(), 0, 1, isOptional)); + result.addAll(chooseCardsForEffect(e.getValue(), sa, title + " " + e.getKey(), 0, 1, isOptional, null)); } return result; }