diff --git a/forge-ai/src/main/java/forge/ai/AiAttackController.java b/forge-ai/src/main/java/forge/ai/AiAttackController.java index d6204530436..f8309d07283 100644 --- a/forge-ai/src/main/java/forge/ai/AiAttackController.java +++ b/forge-ai/src/main/java/forge/ai/AiAttackController.java @@ -358,7 +358,7 @@ public class AiAttackController { // In addition, if the computer guesses it needs no blockers, make sure // that // it won't be surprised by Exalted - final int humanExaltedBonus = countExaltedBonus(opp); + final int humanExaltedBonus = opp.countExaltedBonus(); if (humanExaltedBonus > 0) { final boolean finestHour = opp.isCardInPlay("Finest Hour"); @@ -1049,24 +1049,6 @@ public class AiAttackController { } } // getAttackers() - /** - *

- * countExaltedBonus. - *

- * - * @param player - * a {@link forge.game.player.Player} object. - * @return a int. - */ - public final static int countExaltedBonus(final Player player) { - int bonus = 0; - for (Card c : player.getCardsIn(ZoneType.Battlefield)) { - bonus += c.getAmountOfKeyword("Exalted"); - } - - return bonus; - } - /** *

* getAttack. diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtilCombat.java b/forge-ai/src/main/java/forge/ai/ComputerUtilCombat.java index e41429127e6..7368fe77f8b 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtilCombat.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtilCombat.java @@ -36,6 +36,7 @@ import forge.game.card.*; import forge.game.combat.Combat; import forge.game.combat.CombatUtil; import forge.game.cost.CostPayment; +import forge.game.keyword.Keyword; import forge.game.keyword.KeywordInterface; import forge.game.phase.Untap; import forge.game.player.Player; @@ -226,14 +227,15 @@ public class ComputerUtilCombat { int damage = attacker.getNetCombatDamage(); int poison = 0; damage += ComputerUtilCombat.predictPowerBonusOfAttacker(attacker, null, null, false); - if (attacker.hasKeyword("Infect")) { - poison += ComputerUtilCombat.predictDamageTo(attacked, damage, attacker, true); - if (attacker.hasKeyword("Double Strike")) { - poison += ComputerUtilCombat.predictDamageTo(attacked, damage, attacker, true); + if (attacker.hasKeyword(Keyword.INFECT)) { + int pd = ComputerUtilCombat.predictDamageTo(attacked, damage, attacker, true); + poison += pd; + if (attacker.hasKeyword(Keyword.DOUBLE_STRIKE)) { + poison += pd; } } - if (attacker.hasKeyword("Poisonous") && (damage > 0)) { - poison += attacker.getKeywordMagnitude("Poisonous"); + if (attacker.hasKeyword(Keyword.POISONOUS) && (damage > 0)) { + poison += attacker.getKeywordMagnitude(Keyword.POISONOUS); } return poison; } @@ -343,13 +345,13 @@ public class ComputerUtilCombat { || attacker.hasKeyword("You may have CARDNAME assign its combat damage" + " as though it weren't blocked.")) { unblocked.add(attacker); - } else if (attacker.hasKeyword("Trample") + } else if (attacker.hasKeyword(Keyword.TRAMPLE) && (ComputerUtilCombat.getAttack(attacker) > ComputerUtilCombat.totalShieldDamage(attacker, blockers))) { - if (attacker.hasKeyword("Infect")) { + if (attacker.hasKeyword(Keyword.INFECT)) { poison += ComputerUtilCombat.getAttack(attacker) - ComputerUtilCombat.totalShieldDamage(attacker, blockers); } - if (attacker.hasKeyword("Poisonous")) { - poison += attacker.getKeywordMagnitude("Poisonous"); + if (attacker.hasKeyword(Keyword.POISONOUS)) { + poison += attacker.getKeywordMagnitude(Keyword.POISONOUS); } } } @@ -595,20 +597,21 @@ public class ComputerUtilCombat { } int flankingMagnitude = 0; - if (attacker.hasKeyword("Flanking") && !defender.hasKeyword("Flanking")) { + if (attacker.hasKeyword(Keyword.FLANKING) && !defender.hasKeyword(Keyword.FLANKING)) { - flankingMagnitude = attacker.getAmountOfKeyword("Flanking"); + flankingMagnitude = attacker.getAmountOfKeyword(Keyword.FLANKING); if (flankingMagnitude >= defender.getNetToughness()) { return 0; } if ((flankingMagnitude >= (defender.getNetToughness() - defender.getDamage())) - && !defender.hasKeyword("Indestructible")) { + && !defender.hasKeyword(Keyword.INDESTRUCTIBLE)) { return 0; } } // flanking - if (attacker.hasKeyword("Indestructible") && !(defender.hasKeyword("Wither") || defender.hasKeyword("Infect"))) { + if (attacker.hasKeyword(Keyword.INDESTRUCTIBLE) + && !(defender.hasKeyword(Keyword.WITHER) || defender.hasKeyword(Keyword.INFECT))) { return 0; } @@ -667,21 +670,21 @@ public class ComputerUtilCombat { } int flankingMagnitude = 0; - if (attacker.hasKeyword("Flanking") && !blocker.hasKeyword("Flanking")) { + if (attacker.hasKeyword(Keyword.FLANKING) && !blocker.hasKeyword(Keyword.FLANKING)) { - flankingMagnitude = attacker.getAmountOfKeyword("Flanking"); + flankingMagnitude = attacker.getAmountOfKeyword(Keyword.FLANKING); if (flankingMagnitude >= blocker.getNetToughness()) { return 0; } if ((flankingMagnitude >= (blocker.getNetToughness() - blocker.getDamage())) - && !blocker.hasKeyword("Indestructible")) { + && !blocker.hasKeyword(Keyword.INDESTRUCTIBLE)) { return 0; } } // flanking - final int defBushidoMagnitude = blocker.getKeywordMagnitude("Bushido"); + final int defBushidoMagnitude = blocker.getKeywordMagnitude(Keyword.BUSHIDO); final int defenderDefense = (blocker.getLethalDamage() - flankingMagnitude) + defBushidoMagnitude; @@ -727,16 +730,16 @@ public class ComputerUtilCombat { for (final Card defender : blockers) { if (ComputerUtilCombat.canDestroyAttacker(ai, attacker, defender, combat, true) - && !(defender.hasKeyword("Wither") || defender.hasKeyword("Infect"))) { + && !(defender.hasKeyword(Keyword.WITHER) || defender.hasKeyword(Keyword.INFECT))) { return true; } - if (defender.hasKeyword("First Strike") || defender.hasKeyword("Double Strike")) { + if (defender.hasKeyword(Keyword.FIRST_STRIKE) || defender.hasKeyword(Keyword.DOUBLE_STRIKE)) { firstStrikeBlockerDmg += defender.getNetCombatDamage(); } } // Consider first strike and double strike - if (attacker.hasKeyword("First Strike") || attacker.hasKeyword("Double Strike")) { + if (attacker.hasKeyword(Keyword.FIRST_STRIKE) || attacker.hasKeyword(Keyword.DOUBLE_STRIKE)) { return firstStrikeBlockerDmg >= ComputerUtilCombat.getDamageToKill(attacker); } @@ -1069,8 +1072,8 @@ public class ComputerUtilCombat { public static int predictToughnessBonusOfBlocker(final Card attacker, final Card blocker, boolean withoutAbilities) { int toughness = 0; - if (attacker.hasKeyword("Flanking") && !blocker.hasKeyword("Flanking")) { - toughness -= attacker.getAmountOfKeyword("Flanking"); + if (attacker.hasKeyword(Keyword.FLANKING) && !blocker.hasKeyword(Keyword.FLANKING)) { + toughness -= attacker.getAmountOfKeyword(Keyword.FLANKING); } if (blocker.getName().equals("Shape Stealer")) { @@ -1239,9 +1242,7 @@ public class ComputerUtilCombat { //check Exalted only for the first attacker if (combat != null && combat.getAttackers().isEmpty()) { - for (Card card : attacker.getController().getCardsIn(ZoneType.Battlefield)) { - power += card.getAmountOfKeyword("Exalted"); - } + power += attacker.getController().countExaltedBonus(); } // Serene Master switches power with attacker @@ -1263,8 +1264,8 @@ public class ComputerUtilCombat { // less damage than expected if (null != blocker) { if (ComputerUtilCombat.dealsFirstStrikeDamage(blocker, withoutAbilities, combat) - && (blocker.hasKeyword("Wither") || blocker.hasKeyword("Infect")) - && !ComputerUtilCombat.dealsFirstStrikeDamage(attacker, withoutAbilities, combat) + && (blocker.hasKeyword(Keyword.WITHER) || blocker.hasKeyword(Keyword.INFECT)) + && !ComputerUtilCombat.dealsFirstStrikeDamage(attacker, withoutAbilities, combat) && !attacker.hasKeyword("CARDNAME can't have counters put on it.")) { power -= blocker.getNetCombatDamage(); } @@ -1446,9 +1447,7 @@ public class ComputerUtilCombat { //check Exalted only for the first attacker if (combat != null && combat.getAttackers().isEmpty()) { - for (Card card : attacker.getController().getCardsIn(ZoneType.Battlefield)) { - toughness += card.getAmountOfKeyword("Exalted"); - } + toughness += attacker.getController().countExaltedBonus(); } if (blocker != null && attacker.getName().equals("Shape Stealer")) { @@ -1766,24 +1765,24 @@ public class ComputerUtilCombat { } int flankingMagnitude = 0; - if (attacker.hasKeyword("Flanking") && !blocker.hasKeyword("Flanking")) { + if (attacker.hasKeyword(Keyword.FLANKING) && !blocker.hasKeyword(Keyword.FLANKING)) { - flankingMagnitude = attacker.getAmountOfKeyword("Flanking"); + flankingMagnitude = attacker.getAmountOfKeyword(Keyword.FLANKING); if (flankingMagnitude >= blocker.getNetToughness()) { return false; } if ((flankingMagnitude >= (blocker.getNetToughness() - blocker.getDamage())) - && !blocker.hasKeyword("Indestructible")) { + && !blocker.hasKeyword(Keyword.INDESTRUCTIBLE)) { return false; } } // flanking - if (((attacker.hasKeyword("Indestructible") || (ComputerUtil.canRegenerate(ai, attacker) && !withoutAbilities)) - && !(blocker.hasKeyword("Wither") || blocker.hasKeyword("Infect"))) - || (attacker.hasKeyword("Persist") && !attacker.canReceiveCounters(CounterType.M1M1) && (attacker + 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("Undying") && !attacker.canReceiveCounters(CounterType.P1P1) && (attacker + || (attacker.hasKeyword(Keyword.UNDYING) && !attacker.canReceiveCounters(CounterType.P1P1) && (attacker .getCounters(CounterType.P1P1) == 0))) { return false; } @@ -1895,7 +1894,7 @@ public class ComputerUtilCombat { for (Card attacker : attackers) { if (ComputerUtilCombat.canDestroyBlocker(ai, blocker, attacker, combat, true) - && !(attacker.hasKeyword("Wither") || attacker.hasKeyword("Infect"))) { + && !(attacker.hasKeyword(Keyword.WITHER) || attacker.hasKeyword(Keyword.INFECT))) { return true; } } @@ -1913,19 +1912,21 @@ public class ComputerUtilCombat { } int flankingMagnitude = 0; - if (attacker.hasKeyword("Flanking") && !blocker.hasKeyword("Flanking")) { + if (attacker.hasKeyword(Keyword.FLANKING) && !blocker.hasKeyword(Keyword.FLANKING)) { - flankingMagnitude = attacker.getAmountOfKeyword("Flanking"); + flankingMagnitude = attacker.getAmountOfKeyword(Keyword.FLANKING); if (flankingMagnitude >= blocker.getNetToughness()) { return true; } - if ((flankingMagnitude >= ComputerUtilCombat.getDamageToKill(blocker)) && !blocker.hasKeyword("Indestructible")) { + if ((flankingMagnitude >= ComputerUtilCombat.getDamageToKill(blocker)) + && !blocker.hasKeyword(Keyword.INDESTRUCTIBLE)) { return true; } } // flanking - if (blocker.hasKeyword("Indestructible") || dontTestRegen || ComputerUtil.canRegenerate(blocker.getController(), blocker)) { + if (blocker.hasKeyword(Keyword.INDESTRUCTIBLE) || dontTestRegen + || ComputerUtil.canRegenerate(blocker.getController(), blocker)) { return false; } @@ -2516,7 +2517,7 @@ public class ComputerUtilCombat { public static boolean attackerHasThreateningAfflict(Card attacker, Player aiDefender) { // TODO: expand this to account for more complex situations like the Wildfire Eternal unblocked trigger - int afflictDmg = attacker.getKeywordMagnitude("Afflict"); + int afflictDmg = attacker.getKeywordMagnitude(Keyword.AFFLICT); return afflictDmg > attacker.getNetPower() || afflictDmg >= aiDefender.getLife(); } diff --git a/forge-ai/src/main/java/forge/ai/CreatureEvaluator.java b/forge-ai/src/main/java/forge/ai/CreatureEvaluator.java index e9e2b7bf894..5c54f7de1a3 100644 --- a/forge-ai/src/main/java/forge/ai/CreatureEvaluator.java +++ b/forge-ai/src/main/java/forge/ai/CreatureEvaluator.java @@ -7,6 +7,7 @@ import forge.game.ability.ApiType; import forge.game.card.Card; import forge.game.card.CounterType; import forge.game.cost.CostPayEnergy; +import forge.game.keyword.Keyword; import forge.game.keyword.KeywordInterface; import forge.game.spellability.SpellAbility; @@ -53,10 +54,10 @@ public class CreatureEvaluator implements Function { } // Evasion keywords - if (c.hasKeyword("Flying")) { + if (c.hasKeyword(Keyword.FLYING)) { value += addValue(power * 10, "flying"); } - if (c.hasKeyword("Horsemanship")) { + if (c.hasKeyword(Keyword.HORSEMANSHIP)) { value += addValue(power * 10, "horses"); } if (c.hasKeyword("Unblockable")) { @@ -65,13 +66,13 @@ public class CreatureEvaluator implements Function { if (c.hasKeyword("You may have CARDNAME assign its combat damage as though it weren't blocked.")) { value += addValue(power * 6, "thorns"); } - if (c.hasKeyword("Fear")) { + if (c.hasKeyword(Keyword.FEAR)) { value += addValue(power * 6, "fear"); } - if (c.hasKeyword("Intimidate")) { + if (c.hasKeyword(Keyword.INTIMIDATE)) { value += addValue(power * 6, "intimidate"); } - if (c.hasStartOfKeyword("Menace")) { + if (c.hasKeyword(Keyword.MENACE)) { value += addValue(power * 4, "menace"); } if (c.hasStartOfKeyword("CantBeBlockedBy")) { @@ -81,49 +82,49 @@ public class CreatureEvaluator implements Function { // Other good keywords if (power > 0) { - if (c.hasKeyword("Double Strike")) { + if (c.hasKeyword(Keyword.DOUBLE_STRIKE)) { value += addValue(10 + (power * 15), "ds"); } else if (c.hasKeyword("First Strike")) { value += addValue(10 + (power * 5), "fs"); } - if (c.hasKeyword("Deathtouch")) { + if (c.hasKeyword(Keyword.DEATHTOUCH)) { value += addValue(25, "dt"); } - if (c.hasKeyword("Lifelink")) { + if (c.hasKeyword(Keyword.LIFELINK)) { value += addValue(power * 10, "lifelink"); } - if (power > 1 && c.hasKeyword("Trample")) { + if (power > 1 && c.hasKeyword(Keyword.TRAMPLE)) { value += addValue((power - 1) * 5, "trample"); } - if (c.hasKeyword("Vigilance")) { + if (c.hasKeyword(Keyword.VIGILANCE)) { value += addValue((power * 5) + (toughness * 5), "vigilance"); } - if (c.hasKeyword("Wither")) { + if (c.hasKeyword(Keyword.WITHER)) { value += addValue(power * 10, "Wither"); } - if (c.hasKeyword("Infect")) { + if (c.hasKeyword(Keyword.INFECT)) { value += addValue(power * 15, "infect"); } - value += addValue(c.getKeywordMagnitude("Rampage"), "rampage"); - value += addValue(c.getKeywordMagnitude("Afflict") * 5, "afflict"); + value += addValue(c.getKeywordMagnitude(Keyword.RAMPAGE), "rampage"); + value += addValue(c.getKeywordMagnitude(Keyword.AFFLICT) * 5, "afflict"); } - value += addValue(c.getKeywordMagnitude("Bushido") * 16, "bushido"); - value += addValue(c.getAmountOfKeyword("Flanking") * 15, "flanking"); - value += addValue(c.getAmountOfKeyword("Exalted") * 15, "exalted"); - value += addValue(c.getKeywordMagnitude("Annihilator") * 50, "eldrazi"); - value += addValue(c.getKeywordMagnitude("Absorb") * 11, "absorb"); + value += addValue(c.getKeywordMagnitude(Keyword.BUSHIDO) * 16, "bushido"); + value += addValue(c.getAmountOfKeyword(Keyword.FLANKING) * 15, "flanking"); + value += addValue(c.getAmountOfKeyword(Keyword.EXALTED) * 15, "exalted"); + value += addValue(c.getKeywordMagnitude(Keyword.ANNIHILATOR) * 50, "eldrazi"); + value += addValue(c.getKeywordMagnitude(Keyword.ABSORB) * 11, "absorb"); // Keywords that may produce temporary or permanent buffs over time if (c.hasKeyword("Prowess")) { value += addValue(5, "prowess"); } - if (c.hasKeyword("Outlast")) { + if (c.hasKeyword(Keyword.OUTLAST)) { value += addValue(10, "outlast"); } // Defensive Keywords - if (c.hasKeyword("Reach") && !c.hasKeyword("Flying")) { + if (c.hasKeyword(Keyword.REACH) && !c.hasKeyword(Keyword.FLYING)) { value += addValue(5, "reach"); } if (c.hasKeyword("CARDNAME can block creatures with shadow as though they didn't have shadow.")) { @@ -131,7 +132,7 @@ public class CreatureEvaluator implements Function { } // Protection - if (c.hasKeyword("Indestructible")) { + if (c.hasKeyword(Keyword.INDESTRUCTIBLE)) { value += addValue(70, "darksteel"); } if (c.hasKeyword("Prevent all damage that would be dealt to CARDNAME.")) { @@ -139,20 +140,17 @@ public class CreatureEvaluator implements Function { } else if (c.hasKeyword("Prevent all combat damage that would be dealt to CARDNAME.")) { value += addValue(50, "fogbank"); } - if (c.hasKeyword("Hexproof")) { + if (c.hasKeyword(Keyword.HEXPROOF)) { value += addValue(35, "hexproof"); - } else if (c.hasKeyword("Shroud")) { + } else if (c.hasKeyword(Keyword.SHROUD)) { value += addValue(30, "shroud"); } if (c.hasStartOfKeyword("Protection")) { value += addValue(20, "protection"); } - if (c.hasStartOfKeyword("PreventAllDamageBy")) { - value += addValue(10, "prevent-dmg"); - } // Bad keywords - if (c.hasKeyword("Defender") || c.hasKeyword("CARDNAME can't attack.")) { + if (c.hasKeyword(Keyword.DEFENDER) || c.hasKeyword("CARDNAME can't attack.")) { value -= subValue((power * 9) + 40, "defender"); } else if (c.getSVar("SacrificeEndCombat").equals("True")) { value -= subValue(40, "sac-end"); @@ -195,10 +193,10 @@ public class CreatureEvaluator implements Function { if (c.hasStartOfKeyword("At the beginning of your upkeep, CARDNAME deals")) { value -= subValue(20, "upkeep-dmg"); } - if (c.hasStartOfKeyword("Fading")) { + if (c.hasKeyword(Keyword.FADING)) { value -= subValue(20, "fading"); } - if (c.hasStartOfKeyword("Vanishing")) { + if (c.hasKeyword(Keyword.VANISHING)) { value -= subValue(20, "vanishing"); } if (c.getSVar("Targeting").equals("Dies")) { diff --git a/forge-core/src/main/java/forge/util/Visitor.java b/forge-core/src/main/java/forge/util/Visitor.java index 029218000b4..b8e6bf4ba6d 100644 --- a/forge-core/src/main/java/forge/util/Visitor.java +++ b/forge-core/src/main/java/forge/util/Visitor.java @@ -1,7 +1,7 @@ package forge.util; public abstract class Visitor { - public abstract void visit(T object); + public abstract boolean visit(T object); public void visitAll(Iterable objects) { for (T obj : objects) { diff --git a/forge-game/src/main/java/forge/game/Game.java b/forge-game/src/main/java/forge/game/Game.java index 0f82a49665c..4f5fdc5c46a 100644 --- a/forge-game/src/main/java/forge/game/Game.java +++ b/forge-game/src/main/java/forge/game/Game.java @@ -553,8 +553,9 @@ public class Game { final CardCollection all = new CardCollection(); Visitor visitor = new Visitor() { @Override - public void visit(Card card) { + public boolean visit(Card card) { all.add(card); + return true; } }; forEachCardInGame(visitor); diff --git a/forge-game/src/main/java/forge/game/GameAction.java b/forge-game/src/main/java/forge/game/GameAction.java index 7c84dc29923..87618eaa0eb 100644 --- a/forge-game/src/main/java/forge/game/GameAction.java +++ b/forge-game/src/main/java/forge/game/GameAction.java @@ -792,7 +792,7 @@ public class GameAction { game.forEachCardInGame(new Visitor() { @Override - public void visit(final Card c) { + public boolean visit(final Card c) { // need to get Card from preList if able final Card co = preList.get(c); for (int i = 0; i < co.getStaticAbilities().size(); i++) { @@ -808,6 +808,7 @@ public class GameAction { if (!co.getStaticCommandList().isEmpty()) { staticList.add(co); } + return true; } }); diff --git a/forge-game/src/main/java/forge/game/GameActionUtil.java b/forge-game/src/main/java/forge/game/GameActionUtil.java index 70295ebc2b3..5eea36a7da2 100644 --- a/forge-game/src/main/java/forge/game/GameActionUtil.java +++ b/forge-game/src/main/java/forge/game/GameActionUtil.java @@ -31,6 +31,7 @@ import forge.game.ability.ApiType; import forge.game.card.*; import forge.game.card.CardPlayOption.PayManaCost; import forge.game.cost.Cost; +import forge.game.keyword.Keyword; import forge.game.keyword.KeywordInterface; import forge.game.player.Player; import forge.game.spellability.*; @@ -482,8 +483,8 @@ public final class GameActionUtil { } } - if (source.hasKeyword("Conspire")) { - int amount = source.getAmountOfKeyword("Conspire"); + if (source.hasKeyword(Keyword.CONSPIRE)) { + int amount = source.getAmountOfKeyword(Keyword.CONSPIRE); for (int kwInstance = 1; kwInstance <= amount; kwInstance++) { for (int i = 0; i < abilities.size(); i++) { final SpellAbility newSA = abilities.get(i).copy(); diff --git a/forge-game/src/main/java/forge/game/GameEntity.java b/forge-game/src/main/java/forge/game/GameEntity.java index 1cdfd7997ef..61c5ad6457a 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.CardDamageMap; import forge.game.card.CounterType; import forge.game.event.GameEventCardAttachment; import forge.game.event.GameEventCardAttachment.AttachMethod; +import forge.game.keyword.Keyword; import forge.game.spellability.SpellAbility; import forge.game.trigger.TriggerType; import forge.util.collect.FCollection; @@ -275,6 +276,7 @@ public abstract class GameEntity extends GameObject implements IIdentifiable { } public abstract boolean hasKeyword(final String keyword); + public abstract boolean hasKeyword(final Keyword keyword); // GameEntities can now be Enchanted public final CardCollectionView getEnchantedBy(boolean allowModify) { 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 e9c3f75c299..7ba0c4a52e0 100644 --- a/forge-game/src/main/java/forge/game/card/Card.java +++ b/forge-game/src/main/java/forge/game/card/Card.java @@ -1007,11 +1007,11 @@ public class Card extends GameEntity implements Comparable { } public final boolean hasFirstStrike() { - return hasKeyword("First Strike"); + return hasKeyword(Keyword.FIRST_STRIKE); } public final boolean hasDoubleStrike() { - return hasKeyword("Double Strike"); + return hasKeyword(Keyword.DOUBLE_STRIKE); } public final boolean hasSecondStrike() { @@ -2338,11 +2338,11 @@ public class Card extends GameEntity implements Comparable { } public final boolean hasSickness() { - return sickness && !hasKeyword("Haste"); + return sickness && !hasKeyword(Keyword.HASTE); } public final boolean isSick() { - return sickness && isCreature() && !hasKeyword("Haste"); + return sickness && isCreature() && !hasKeyword(Keyword.HASTE); } public boolean hasBecomeTargetThisTurn() { @@ -3217,6 +3217,15 @@ public class Card extends GameEntity implements Comparable { visitHiddenExtreinsicKeywords(visitor); } + @Override + public final boolean hasKeyword(Keyword keyword) { + return hasKeyword(keyword, currentState); + } + + public final boolean hasKeyword(Keyword key, CardState state) { + return state.hasKeyword(key); + } + @Override public final boolean hasKeyword(String keyword) { return hasKeyword(keyword, currentState); @@ -3227,9 +3236,9 @@ public class Card extends GameEntity implements Comparable { keyword = keyword.substring(7); } - CountKeywordVisitor visitor = new CountKeywordVisitor(keyword); + HasKeywordVisitor visitor = new HasKeywordVisitor(keyword, false); visitKeywords(state, visitor); - return visitor.getCount() > 0; + return visitor.getResult(); } public final void updateKeywords() { @@ -3345,20 +3354,26 @@ public class Card extends GameEntity implements Comparable { } } - state.setCachedKeywords(keywords.getValues()); + state.setCachedKeywords(keywords); } private void visitUnhiddenKeywords(CardState state, Visitor visitor) { if (changedCardKeywords.isEmpty()) { // Fast path that doesn't involve temp allocations. for (KeywordInterface kw : state.getIntrinsicKeywords()) { - visitor.visit(kw); + if (!visitor.visit(kw)) { + return; + } } for (KeywordInterface kw : extrinsicKeyword.getValues()) { - visitor.visit(kw); + if (!visitor.visit(kw)) { + return; + } } } else { for (KeywordInterface kw : getUnhiddenKeywords(state)) { - visitor.visit(kw); + if (!visitor.visit(kw)) { + return; + } } } } @@ -3571,7 +3586,9 @@ public class Card extends GameEntity implements Comparable { } private void visitHiddenExtreinsicKeywords(Visitor visitor) { for (KeywordInterface inst : hiddenExtrinsicKeyword.getValues()) { - visitor.visit(inst); + if (!visitor.visit(inst)) { + return; + } } } @@ -3820,18 +3837,18 @@ public class Card extends GameEntity implements Comparable { return hasStartOfKeyword(keyword, currentState); } public final boolean hasStartOfKeyword(String keyword, CardState state) { - CountKeywordVisitor visitor = new CountKeywordVisitor(keyword, true); + HasKeywordVisitor visitor = new HasKeywordVisitor(keyword, true); visitKeywords(state, visitor); - return visitor.getCount() > 0; + return visitor.getResult(); } public final boolean hasStartOfUnHiddenKeyword(String keyword) { return hasStartOfUnHiddenKeyword(keyword, currentState); } public final boolean hasStartOfUnHiddenKeyword(String keyword, CardState state) { - CountKeywordVisitor visitor = new CountKeywordVisitor(keyword, true); + HasKeywordVisitor visitor = new HasKeywordVisitor(keyword, true); visitUnhiddenKeywords(state, visitor); - return visitor.getCount() > 0; + return visitor.getResult(); } public final boolean hasAnyKeyword(final Iterable keywords) { @@ -3856,25 +3873,42 @@ public class Card extends GameEntity implements Comparable { return visitor.getCount(); } + public final int getAmountOfKeyword(final Keyword k) { + return getAmountOfKeyword(k, currentState); + } + public final int getAmountOfKeyword(final Keyword k, CardState state) { + return state.getCachedKeyword(k).size(); + } + // This is for keywords with a number like Bushido, Annihilator and Rampage. // It returns the total. - public final int getKeywordMagnitude(final String k) { + public final int getKeywordMagnitude(final Keyword k) { return getKeywordMagnitude(k, currentState); } - public final int getKeywordMagnitude(final String k, CardState state) { + + /** + * use it only for real keywords and not with hidden ones + * + * @param Keyword k + * @param CardState state + * @return Int + */ + public final int getKeywordMagnitude(final Keyword k, CardState state) { int count = 0; - for (final KeywordInterface inst : getKeywords(state)) { + for (final KeywordInterface inst : state.getCachedKeyword(k)) { String kw = inst.getOriginal(); - if (kw.startsWith(k)) { - final String[] parse = kw.contains(":") ? kw.split(":") : kw.split(" "); - final String s = parse[1]; - if (StringUtils.isNumeric(s)) { - count += Integer.parseInt(s); - } else { - String svar = StringUtils.join(parse); - if (state.hasSVar(svar)) { - count += AbilityUtils.calculateAmount(this, state.getSVar(svar), null); - } + // this can't be used yet for everything because of X values in Bushido X + // KeywordInterface#getAmount + // KeywordCollection#getAmount + + final String[] parse = kw.contains(":") ? kw.split(":") : kw.split(" "); + final String s = parse[1]; + if (StringUtils.isNumeric(s)) { + count += Integer.parseInt(s); + } else { + String svar = StringUtils.join(parse); + if (state.hasSVar(svar)) { + count += AbilityUtils.calculateAmount(this, state.getSVar(svar), null); } } } @@ -4538,7 +4572,7 @@ public class Card extends GameEntity implements Comparable { final Game game = source.getGame(); boolean wither = (game.getStaticEffects().getGlobalRuleChange(GlobalRuleChange.alwaysWither) - || source.hasKeyword("Wither") || source.hasKeyword("Infect")); + || source.hasKeyword(Keyword.WITHER) || source.hasKeyword(Keyword.INFECT)); if (isInPlay()) { if (wither) { @@ -4551,7 +4585,7 @@ public class Card extends GameEntity implements Comparable { } } - if (source.hasKeyword("Deathtouch") && isCreature()) { + if (source.hasKeyword(Keyword.DEATHTOUCH) && isCreature()) { setHasBeenDealtDeathtouchDamage(true); damageType = DamageType.Deathtouch; } @@ -4943,7 +4977,7 @@ public class Card extends GameEntity implements Comparable { } public final boolean canBeDestroyed() { - return isInPlay() && (!hasKeyword("Indestructible") || (isCreature() && getNetToughness() <= 0)); + return isInPlay() && (!hasKeyword(Keyword.INDESTRUCTIBLE) || (isCreature() && getNetToughness() <= 0)); } public final boolean canBeSacrificed() { @@ -4983,10 +5017,7 @@ public class Card extends GameEntity implements Comparable { final MutableBoolean result = new MutableBoolean(true); visitKeywords(currentState, new Visitor() { @Override - public void visit(KeywordInterface kw) { - if (result.isFalse()) { - return; - } + public boolean visit(KeywordInterface kw) { switch (kw.getOriginal()) { case "Shroud": StringBuilder sb = new StringBuilder(); @@ -5015,6 +5046,7 @@ public class Card extends GameEntity implements Comparable { } break; } + return result.isTrue(); } }); if (result.isFalse()) { @@ -5506,11 +5538,12 @@ public class Card extends GameEntity implements Comparable { } @Override - public void visit(KeywordInterface inst) { + public boolean visit(KeywordInterface inst) { final String kw = inst.getOriginal(); if ((startOf && kw.startsWith(keyword)) || kw.equals(keyword)) { count++; } + return true; } public int getCount() { @@ -5518,13 +5551,38 @@ public class Card extends GameEntity implements Comparable { } } + private static final class HasKeywordVisitor extends Visitor { + private String keyword; + private final MutableBoolean result = new MutableBoolean(false); + + private boolean startOf; + private HasKeywordVisitor(String keyword, boolean startOf) { + this.keyword = keyword; + this.startOf = startOf; + } + + @Override + public boolean visit(KeywordInterface inst) { + final String kw = inst.getOriginal(); + if ((startOf && kw.startsWith(keyword)) || kw.equals(keyword)) { + result.setTrue(); + } + return result.isFalse(); + } + + public boolean getResult() { + return result.isTrue(); + } + } + // Collects all the keywords into a list. private static final class ListKeywordVisitor extends Visitor { private List keywords = Lists.newArrayList(); @Override - public void visit(KeywordInterface kw) { + public boolean visit(KeywordInterface kw) { keywords.add(kw); + return true; } public List getKeywords() { @@ -5645,7 +5703,7 @@ public class Card extends GameEntity implements Comparable { } public boolean withFlash(Player p) { - if (hasKeyword("Flash")) { + if (hasKeyword(Keyword.FLASH)) { return true; } if (withFlash.containsValue(p)) { diff --git a/forge-game/src/main/java/forge/game/card/CardDamageMap.java b/forge-game/src/main/java/forge/game/card/CardDamageMap.java index e007f4725ae..13dc81b57f0 100644 --- a/forge-game/src/main/java/forge/game/card/CardDamageMap.java +++ b/forge-game/src/main/java/forge/game/card/CardDamageMap.java @@ -12,6 +12,7 @@ import com.google.common.collect.Sets; import com.google.common.collect.Table; import forge.game.GameEntity; +import forge.game.keyword.Keyword; import forge.game.spellability.SpellAbility; import forge.game.trigger.TriggerType; @@ -53,7 +54,7 @@ public class CardDamageMap extends ForwardingTable { sourceLKI.getGame().getTriggerHandler().runTrigger(TriggerType.DamageDealtOnce, runParams, false); - if (sourceLKI.hasKeyword("Lifelink")) { + if (sourceLKI.hasKeyword(Keyword.LIFELINK)) { sourceLKI.getController().gainLife(sum, sourceLKI, sa); } } 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 724c580f4ef..62f298bb84e 100644 --- a/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java +++ b/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java @@ -1196,7 +1196,7 @@ public class CardFactoryUtil { } if (sq[0].contains("BushidoPoint")) { - return doXMath(c.getKeywordMagnitude("Bushido"), m, c); + return doXMath(c.getKeywordMagnitude(Keyword.BUSHIDO), m, c); } if (sq[0].contains("TimesKicked")) { return doXMath(c.getKickerMagnitude(), m, c); @@ -4036,7 +4036,7 @@ public class CardFactoryUtil { return false; } - if (this.getHostCard().isInstant() || this.getHostCard().hasKeyword("Flash")) { + if (this.getHostCard().isInstant() || this.getHostCard().hasKeyword(Keyword.FLASH)) { return true; } diff --git a/forge-game/src/main/java/forge/game/card/CardLists.java b/forge-game/src/main/java/forge/game/card/CardLists.java index 3689a2182d2..7b1c477f089 100644 --- a/forge-game/src/main/java/forge/game/card/CardLists.java +++ b/forge-game/src/main/java/forge/game/card/CardLists.java @@ -26,6 +26,7 @@ import com.google.common.base.Predicates; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; +import forge.game.keyword.Keyword; import forge.game.player.Player; import forge.game.spellability.SpellAbility; import forge.util.collect.FCollectionView; @@ -250,6 +251,13 @@ public class CardLists { } return nKeyword; } + public static int getAmountOfKeyword(final Iterable cardList, final Keyword keyword) { + int nKeyword = 0; + for (final Card c : cardList) { + nKeyword += c.getAmountOfKeyword(keyword); + } + return nKeyword; + } // cardType is like "Land" or "Goblin", returns a new CardCollection that is a // subset of current CardList public static CardCollection getNotType(Iterable cardList, String cardType) { diff --git a/forge-game/src/main/java/forge/game/card/CardState.java b/forge-game/src/main/java/forge/game/card/CardState.java index fb781195398..2703dd056d7 100644 --- a/forge-game/src/main/java/forge/game/card/CardState.java +++ b/forge-game/src/main/java/forge/game/card/CardState.java @@ -19,7 +19,6 @@ package forge.game.card; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; -import com.google.common.collect.Lists; import com.google.common.collect.Maps; import forge.card.*; import forge.card.mana.ManaCost; @@ -28,6 +27,7 @@ import forge.game.CardTraitBase; import forge.game.ForgeScript; import forge.game.GameObject; import forge.game.card.CardView.CardStateView; +import forge.game.keyword.Keyword; import forge.game.keyword.KeywordCollection; import forge.game.keyword.KeywordInterface; import forge.game.player.Player; @@ -60,7 +60,7 @@ public class CardState extends GameObject { private String imageKey = ""; private Map sVars = Maps.newTreeMap(); - private List cachedKeywords = Lists.newArrayList(); + private KeywordCollection cachedKeywords = new KeywordCollection(); private CardRarity rarity = CardRarity.Unknown; private String setCode = CardEdition.UNKNOWN.getCode(); @@ -167,12 +167,19 @@ public class CardState extends GameObject { } public final Collection getCachedKeywords() { - return cachedKeywords; + return cachedKeywords.getValues(); } - public final void setCachedKeywords(final Collection col) { - cachedKeywords.clear(); - cachedKeywords.addAll(col); + public final Collection getCachedKeyword(final Keyword keyword) { + return cachedKeywords.getValues(keyword); + } + + public final void setCachedKeywords(final KeywordCollection col) { + cachedKeywords = col; + } + + public final boolean hasKeyword(Keyword key) { + return cachedKeywords.contains(key); } public final Collection getIntrinsicKeywords() { diff --git a/forge-game/src/main/java/forge/game/combat/Combat.java b/forge-game/src/main/java/forge/game/combat/Combat.java index c3d8576a8bf..fb9c095306b 100644 --- a/forge-game/src/main/java/forge/game/combat/Combat.java +++ b/forge-game/src/main/java/forge/game/combat/Combat.java @@ -27,6 +27,7 @@ import forge.game.card.Card; import forge.game.card.CardCollection; import forge.game.card.CardCollectionView; import forge.game.card.CardDamageMap; +import forge.game.keyword.Keyword; import forge.game.player.Player; import forge.game.spellability.SpellAbilityStackInstance; import forge.game.trigger.TriggerType; @@ -692,7 +693,7 @@ public class Combat { continue; } - boolean trampler = attacker.hasKeyword("Trample"); + boolean trampler = attacker.hasKeyword(Keyword.TRAMPLE); orderedBlockers = blockersOrderedForDamageAssignment.get(attacker); assignedDamage = true; // If the Attacker is unblocked, or it's a trampler and has 0 blockers, deal damage to defender diff --git a/forge-game/src/main/java/forge/game/combat/CombatUtil.java b/forge-game/src/main/java/forge/game/combat/CombatUtil.java index 1c3a7326463..a025cf5db21 100644 --- a/forge-game/src/main/java/forge/game/combat/CombatUtil.java +++ b/forge-game/src/main/java/forge/game/combat/CombatUtil.java @@ -29,6 +29,7 @@ import forge.game.GameEntity; import forge.game.GlobalRuleChange; import forge.game.card.*; import forge.game.cost.Cost; +import forge.game.keyword.Keyword; import forge.game.keyword.KeywordInterface; import forge.game.phase.PhaseType; import forge.game.player.Player; @@ -985,17 +986,17 @@ public class CombatUtil { } // rare case: - if (blocker.hasKeyword("Shadow") + if (blocker.hasKeyword(Keyword.SHADOW) && blocker.hasKeyword("CARDNAME can block creatures with shadow as though they didn't have shadow.")) { return false; } - if (attacker.hasKeyword("Shadow") && !blocker.hasKeyword("Shadow") + if (attacker.hasKeyword(Keyword.SHADOW) && !blocker.hasKeyword(Keyword.SHADOW) && !blocker.hasKeyword("CARDNAME can block creatures with shadow as though they didn't have shadow.")) { return false; } - if (!attacker.hasKeyword("Shadow") && blocker.hasKeyword("Shadow")) { + if (!attacker.hasKeyword(Keyword.SHADOW) && blocker.hasKeyword(Keyword.SHADOW)) { return false; } @@ -1034,11 +1035,11 @@ public class CombatUtil { } } - if (blocker.hasKeyword("CARDNAME can block only creatures with flying.") && !attacker.hasKeyword("Flying")) { + if (blocker.hasKeyword("CARDNAME can block only creatures with flying.") && !attacker.hasKeyword(Keyword.FLYING)) { return false; } - if (attacker.hasKeyword("Flying") && !blocker.hasKeyword("Flying") && !blocker.hasKeyword("Reach")) { + if (attacker.hasKeyword(Keyword.FLYING) && !blocker.hasKeyword(Keyword.FLYING) && !blocker.hasKeyword(Keyword.REACH)) { boolean stillblock = false; for (KeywordInterface inst : blocker.getKeywords()) { String k = inst.getOriginal(); @@ -1055,15 +1056,16 @@ public class CombatUtil { } } - if (attacker.hasKeyword("Horsemanship") && !blocker.hasKeyword("Horsemanship")) { + if (attacker.hasKeyword(Keyword.HORSEMANSHIP) && !blocker.hasKeyword(Keyword.HORSEMANSHIP)) { return false; } - if (attacker.hasKeyword("Fear") && !blocker.isArtifact() && !blocker.isBlack()) { + // color is hardcoded there + if (attacker.hasKeyword(Keyword.FEAR) && !blocker.isArtifact() && !blocker.isBlack()) { return false; } - if (attacker.hasKeyword("Intimidate") && !blocker.isArtifact() && !blocker.sharesColorWith(attacker)) { + if (attacker.hasKeyword(Keyword.INTIMIDATE) && !blocker.isArtifact() && !blocker.sharesColorWith(attacker)) { return false; } diff --git a/forge-game/src/main/java/forge/game/cost/CostAdjustment.java b/forge-game/src/main/java/forge/game/cost/CostAdjustment.java index 0e65b1a2295..82bfc0608db 100644 --- a/forge-game/src/main/java/forge/game/cost/CostAdjustment.java +++ b/forge-game/src/main/java/forge/game/cost/CostAdjustment.java @@ -10,6 +10,7 @@ import forge.game.Game; import forge.game.GameObject; import forge.game.ability.AbilityUtils; import forge.game.card.*; +import forge.game.keyword.Keyword; import forge.game.keyword.KeywordInterface; import forge.game.mana.ManaCostBeingPaid; import forge.game.player.Player; @@ -210,7 +211,7 @@ public class CostAdjustment { } if (sa.isSpell()) { - if (sa.getHostCard().hasKeyword("Delve")) { + if (sa.getHostCard().hasKeyword(Keyword.DELVE)) { sa.getHostCard().clearDelved(); final CardCollection delved = new CardCollection(); @@ -235,10 +236,10 @@ public class CostAdjustment { game.getTriggerHandler().runTrigger(TriggerType.ChangesZoneAll, runParams, false); } } - if (sa.getHostCard().hasKeyword("Convoke")) { + if (sa.getHostCard().hasKeyword(Keyword.CONVOKE)) { adjustCostByConvokeOrImprovise(cost, sa, false, test); } - if (sa.getHostCard().hasKeyword("Improvise")) { + if (sa.getHostCard().hasKeyword(Keyword.IMPROVISE)) { adjustCostByConvokeOrImprovise(cost, sa, true, test); } } // isSpell diff --git a/forge-game/src/main/java/forge/game/keyword/KeywordCollection.java b/forge-game/src/main/java/forge/game/keyword/KeywordCollection.java index 80393dc2186..f00b65cf76d 100644 --- a/forge-game/src/main/java/forge/game/keyword/KeywordCollection.java +++ b/forge-game/src/main/java/forge/game/keyword/KeywordCollection.java @@ -143,6 +143,10 @@ public class KeywordCollection implements Iterable, Serializable { return map.values(); } + public Collection getValues(final Keyword keyword) { + return map.get(keyword); + } + @Override public Iterator iterator() { return new Iterator() { 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 8896f11b81a..b2a2f6c54b9 100644 --- a/forge-game/src/main/java/forge/game/player/Player.java +++ b/forge-game/src/main/java/forge/game/player/Player.java @@ -30,6 +30,7 @@ import forge.game.ability.effects.DetachedCardEffect; import forge.game.card.*; import forge.game.card.CardPredicates.Presets; import forge.game.event.*; +import forge.game.keyword.Keyword; import forge.game.keyword.KeywordCollection; import forge.game.keyword.KeywordInterface; import forge.game.keyword.KeywordCollection.KeywordCollectionView; @@ -1070,6 +1071,11 @@ public class Player extends GameEntity implements Comparable { return keywords.contains(keyword); } + @Override + public final boolean hasKeyword(final Keyword keyword) { + return keywords.contains(keyword); + } + private void updateKeywords() { keywords.clear(); @@ -2773,4 +2779,8 @@ public class Player extends GameEntity implements Comparable { this.updateZoneForView(com); } + + public final int countExaltedBonus() { + return CardLists.getAmountOfKeyword(this.getCardsIn(ZoneType.Battlefield), Keyword.EXALTED); + } } diff --git a/forge-game/src/main/java/forge/game/replacement/ReplacementHandler.java b/forge-game/src/main/java/forge/game/replacement/ReplacementHandler.java index 3c555c28624..c47dd86d932 100644 --- a/forge-game/src/main/java/forge/game/replacement/ReplacementHandler.java +++ b/forge-game/src/main/java/forge/game/replacement/ReplacementHandler.java @@ -86,7 +86,7 @@ public class ReplacementHandler { // Round up Static replacement effects game.forEachCardInGame(new Visitor() { @Override - public void visit(Card crd) { + public boolean visit(Card crd) { for (final ReplacementEffect replacementEffect : crd.getReplacementEffects()) { // Use "CheckLKIZone" parameter to test for effects that care abut where the card was last (e.g. Kalitas, Traitor of Ghet @@ -111,6 +111,7 @@ public class ReplacementHandler { possibleReplacers.add(replacementEffect); } } + return true; } }); @@ -307,7 +308,7 @@ public class ReplacementHandler { public void cleanUpTemporaryReplacements() { game.forEachCardInGame(new Visitor() { @Override - public void visit(Card c) { + public boolean visit(Card c) { for (int i = 0; i < c.getReplacementEffects().size(); i++) { ReplacementEffect rep = c.getReplacementEffects().get(i); if (rep.isTemporary()) { @@ -315,14 +316,16 @@ public class ReplacementHandler { i--; } } + return true; } }); game.forEachCardInGame(new Visitor() { @Override - public void visit(Card c) { + public boolean visit(Card c) { for (int i = 0; i < c.getReplacementEffects().size(); i++) { c.getReplacementEffects().get(i).setTemporarilySuppressed(false); } + return true; } }); } diff --git a/forge-game/src/main/java/forge/game/trigger/TriggerHandler.java b/forge-game/src/main/java/forge/game/trigger/TriggerHandler.java index e00ac10dfd6..cd25888a542 100644 --- a/forge-game/src/main/java/forge/game/trigger/TriggerHandler.java +++ b/forge-game/src/main/java/forge/game/trigger/TriggerHandler.java @@ -61,7 +61,7 @@ public class TriggerHandler { public final void cleanUpTemporaryTriggers() { game.forEachCardInGame(new Visitor() { @Override - public void visit(Card c) { + public boolean visit(Card c) { boolean changed = false; for (int i = 0; i < c.getTriggers().size(); i++) { Trigger trigger = c.getTriggers().get(i); @@ -74,11 +74,12 @@ public class TriggerHandler { if (changed) { c.updateStateForView(); } + return true; } }); game.forEachCardInGame(new Visitor() { @Override - public void visit(Card c) { + public boolean visit(Card c) { boolean changed = false; for (int i = 0; i < c.getTriggers().size(); i++) { if (c.getTriggers().get(i).isSuppressed()) { @@ -89,6 +90,7 @@ public class TriggerHandler { if (changed) { c.updateStateForView(); } + return true; } }); } @@ -226,12 +228,13 @@ public class TriggerHandler { activeTriggers.clear(); game.forEachCardInGame(new Visitor() { @Override - public void visit(Card c) { + public boolean visit(Card c) { for (final Trigger t : c.getTriggers()) { if (isTriggerActive(t)) { activeTriggers.add(t); } } + return true; } }); } diff --git a/forge-game/src/main/java/forge/game/zone/MagicStack.java b/forge-game/src/main/java/forge/game/zone/MagicStack.java index 73b06768b0d..6af1e1c31fa 100644 --- a/forge-game/src/main/java/forge/game/zone/MagicStack.java +++ b/forge-game/src/main/java/forge/game/zone/MagicStack.java @@ -51,6 +51,7 @@ import forge.game.event.GameEventSpellAbilityCast; import forge.game.event.GameEventSpellRemovedFromStack; import forge.game.event.GameEventSpellResolved; import forge.game.event.GameEventZone; +import forge.game.keyword.Keyword; import forge.game.player.Player; import forge.game.player.PlayerController.ManaPaymentPurpose; import forge.game.replacement.ReplacementEffect; @@ -119,7 +120,7 @@ public class MagicStack /* extends MyObservable */ implements Iterable