diff --git a/forge-ai/src/main/java/forge/ai/AiAttackController.java b/forge-ai/src/main/java/forge/ai/AiAttackController.java index 7faac65a862..adc58f91823 100644 --- a/forge-ai/src/main/java/forge/ai/AiAttackController.java +++ b/forge-ai/src/main/java/forge/ai/AiAttackController.java @@ -1402,8 +1402,7 @@ public class AiAttackController { boolean hasCombatEffect = attacker.getSVar("HasCombatEffect").equals("TRUE") || "Blocked".equals(attacker.getSVar("HasAttackEffect")); if (!hasCombatEffect) { - if (attacker.hasKeyword(Keyword.WITHER) || attacker.hasKeyword(Keyword.INFECT) - || attacker.hasKeyword(Keyword.LIFELINK) || attacker.hasKeyword(Keyword.AFFLICT)) { + if (attacker.isWitherDamage() || attacker.hasKeyword(Keyword.LIFELINK) || attacker.hasKeyword(Keyword.AFFLICT)) { hasCombatEffect = true; } } diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtilCombat.java b/forge-ai/src/main/java/forge/ai/ComputerUtilCombat.java index 22a80476315..4b045ddba84 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtilCombat.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtilCombat.java @@ -605,8 +605,7 @@ public class ComputerUtilCombat { } } // flanking - if (attacker.hasKeyword(Keyword.INDESTRUCTIBLE) - && !(defender.hasKeyword(Keyword.WITHER) || defender.hasKeyword(Keyword.INFECT))) { + if (attacker.hasKeyword(Keyword.INDESTRUCTIBLE) && !defender.isWitherDamage()) { return 0; } @@ -719,8 +718,7 @@ public class ComputerUtilCombat { int firstStrikeBlockerDmg = 0; for (final Card defender : blockers) { - if (!(defender.hasKeyword(Keyword.WITHER) || defender.hasKeyword(Keyword.INFECT)) - && canDestroyAttacker(ai, attacker, defender, combat, true)) { + if (!(defender.isWitherDamage()) && canDestroyAttacker(ai, attacker, defender, combat, true)) { return true; } if (defender.hasFirstStrike() || defender.hasDoubleStrike()) { @@ -901,7 +899,7 @@ public class ComputerUtilCombat { // if the attacker has first strike and wither the blocker will deal // less damage than expected if (dealsFirstStrikeDamage(attacker, withoutAbilities, null) - && (attacker.hasKeyword(Keyword.WITHER) || attacker.hasKeyword(Keyword.INFECT)) + && attacker.isWitherDamage() && !dealsFirstStrikeDamage(blocker, withoutAbilities, null) && blocker.canReceiveCounters(CounterEnumType.M1M1)) { power -= attacker.getNetCombatDamage(); @@ -1193,7 +1191,7 @@ public class ComputerUtilCombat { // less damage than expected if (null != blocker) { if (dealsFirstStrikeDamage(blocker, withoutAbilities, combat) - && (blocker.hasKeyword(Keyword.WITHER) || blocker.hasKeyword(Keyword.INFECT)) + && blocker.isWitherDamage() && !dealsFirstStrikeDamage(attacker, withoutAbilities, combat) && attacker.canReceiveCounters(CounterEnumType.M1M1)) { power -= blocker.getNetCombatDamage(); @@ -1689,7 +1687,7 @@ public class ComputerUtilCombat { } // flanking if (((attacker.hasKeyword(Keyword.INDESTRUCTIBLE) || (!withoutAbilities && ComputerUtil.canRegenerate(ai, attacker))) - && !(blocker.hasKeyword(Keyword.WITHER) || blocker.hasKeyword(Keyword.INFECT))) + && !(blocker.isWitherDamage())) || (attacker.hasKeyword(Keyword.PERSIST) && !attacker.canReceiveCounters(CounterEnumType.M1M1) && (attacker .getCounters(CounterEnumType.M1M1) == 0)) || (attacker.hasKeyword(Keyword.UNDYING) && !attacker.canReceiveCounters(CounterEnumType.P1P1) && (attacker @@ -1795,8 +1793,7 @@ public class ComputerUtilCombat { final List attackers = combat.getAttackersBlockedBy(blocker); for (Card attacker : attackers) { - if (!(attacker.hasKeyword(Keyword.WITHER) || attacker.hasKeyword(Keyword.INFECT)) - && canDestroyBlocker(ai, blocker, attacker, combat, true)) { + if (!(attacker.isWitherDamage()) && canDestroyBlocker(ai, blocker, attacker, combat, true)) { return true; } } @@ -1902,8 +1899,8 @@ public class ComputerUtilCombat { return true; } - if (((blocker.hasKeyword(Keyword.INDESTRUCTIBLE) || (!withoutAbilities && ComputerUtil.canRegenerate(ai, blocker))) && !(attacker - .hasKeyword(Keyword.WITHER) || attacker.hasKeyword(Keyword.INFECT))) + if (((blocker.hasKeyword(Keyword.INDESTRUCTIBLE) || (!withoutAbilities && ComputerUtil.canRegenerate(ai, blocker))) + && !attacker.isWitherDamage()) || (blocker.hasKeyword(Keyword.PERSIST) && !blocker.canReceiveCounters(CounterEnumType.M1M1) && blocker .getCounters(CounterEnumType.M1M1) == 0) || (blocker.hasKeyword(Keyword.UNDYING) && !blocker.canReceiveCounters(CounterEnumType.P1P1) && blocker @@ -2158,7 +2155,7 @@ public class ComputerUtilCombat { int killDamage = getDamageToKill(c, false); if (c.hasKeyword(Keyword.INDESTRUCTIBLE) || c.getCounters(CounterEnumType.SHIELD) > 0 || (c.getShieldCount() > 0 && c.canBeShielded())) { - if (!(source.hasKeyword(Keyword.WITHER) || source.hasKeyword(Keyword.INFECT))) { + if (!source.isWitherDamage()) { return maxDamage + 1; } } else if (source.hasKeyword(Keyword.DEATHTOUCH) && c.isCreature()) { diff --git a/forge-ai/src/main/java/forge/ai/SpecialCardAi.java b/forge-ai/src/main/java/forge/ai/SpecialCardAi.java index 04eae1847b4..753dec93015 100644 --- a/forge-ai/src/main/java/forge/ai/SpecialCardAi.java +++ b/forge-ai/src/main/java/forge/ai/SpecialCardAi.java @@ -498,8 +498,8 @@ public class SpecialCardAi { // Already enough to kill the blockers and survive, don't overpump return false; } - if (oppCantDie && !source.hasKeyword(Keyword.TRAMPLE) && !source.hasKeyword(Keyword.WITHER) - && !source.hasKeyword(Keyword.INFECT) && predictedPT.getLeft() <= oppT) { + if (oppCantDie && !source.hasKeyword(Keyword.TRAMPLE) && !source.isWitherDamage() + && predictedPT.getLeft() <= oppT) { // Can't kill or cripple anyone, as well as can't Trample over, so don't pump return false; } 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 568d28afe14..62dd7c9df8c 100644 --- a/forge-ai/src/main/java/forge/ai/ability/AttachAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/AttachAi.java @@ -201,8 +201,7 @@ public class AttachAi extends SpellAbilityAi { boolean dangerous = false; int totalAtkPower = 0; for (Card attacker : combat.getBlockers(attachTarget)) { - if (attacker.hasKeyword(Keyword.DEATHTOUCH) || attacker.hasKeyword(Keyword.INFECT) - || attacker.hasKeyword(Keyword.WITHER)) { + if (attacker.hasKeyword(Keyword.DEATHTOUCH) || attacker.isWitherDamage()) { dangerous = true; } totalAtkPower += attacker.getNetPower(); diff --git a/forge-ai/src/main/java/forge/ai/ability/PumpAiBase.java b/forge-ai/src/main/java/forge/ai/ability/PumpAiBase.java index 1a2e45360ce..b0702c90883 100644 --- a/forge-ai/src/main/java/forge/ai/ability/PumpAiBase.java +++ b/forge-ai/src/main/java/forge/ai/ability/PumpAiBase.java @@ -350,7 +350,7 @@ public abstract class PumpAiBase extends SpellAbilityAi { return !ph.isPlayerTurn(opp) && ((combat != null && combat.isAttacking(card)) || CombatUtil.canAttack(card, opp)) && !ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_BLOCKERS); } else if (keyword.endsWith("Wither")) { - if (newPower <= 0 || card.hasKeyword(Keyword.INFECT)) { + if (newPower <= 0 || card.isWitherDamage()) { return false; } return combat != null && (combat.isBlocking(card) || (combat.isAttacking(card) && combat.isBlocked(card))); diff --git a/forge-game/src/main/java/forge/game/GlobalRuleChange.java b/forge-game/src/main/java/forge/game/GlobalRuleChange.java index fbaff413308..ad807888522 100644 --- a/forge-game/src/main/java/forge/game/GlobalRuleChange.java +++ b/forge-game/src/main/java/forge/game/GlobalRuleChange.java @@ -22,7 +22,6 @@ package forge.game; */ public enum GlobalRuleChange { - alwaysWither ("All damage is dealt as though its source had wither."), attackerChoosesBlockers ("The attacking player chooses how each creature blocks each combat."), manaBurn ("A player losing unspent mana causes that player to lose that much life."), noNight ("It can't become night."), 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 b6510980f80..216f1ef7cf4 100644 --- a/forge-game/src/main/java/forge/game/card/Card.java +++ b/forge-game/src/main/java/forge/game/card/Card.java @@ -35,7 +35,6 @@ import forge.game.GameActionUtil; import forge.game.GameEntity; import forge.game.GameEntityCounterTable; import forge.game.GameStage; -import forge.game.GlobalRuleChange; import forge.game.IHasSVars; import forge.game.ability.AbilityFactory; import forge.game.ability.AbilityKey; @@ -5821,10 +5820,7 @@ public class Card extends GameEntity implements Comparable, IHasSVars { subtractCounter(CounterType.get(CounterEnumType.DEFENSE), damageIn, true); } if (isCreature()) { - boolean wither = game.getStaticEffects().getGlobalRuleChange(GlobalRuleChange.alwaysWither) - || source.hasKeyword(Keyword.WITHER) || source.hasKeyword(Keyword.INFECT); - - if (wither) { // 120.3d + if (source.isWitherDamage()) { // 120.3d addCounter(CounterEnumType.M1M1, damageIn, source.getController(), counterTable); damageType = DamageType.M1M1Counters; } @@ -7478,4 +7474,11 @@ public class Card extends GameEntity implements Comparable, IHasSVars { } return !StaticAbilityActivateAbilityAsIfHaste.canActivate(this); } + + public boolean isWitherDamage() { + if (this.hasKeyword(Keyword.WITHER) || this.hasKeyword(Keyword.INFECT)) { + return true; + } + return StaticAbilityWitherDamage.isWitherDamage(this); + } } diff --git a/forge-game/src/main/java/forge/game/staticability/StaticAbilityWitherDamage.java b/forge-game/src/main/java/forge/game/staticability/StaticAbilityWitherDamage.java new file mode 100644 index 00000000000..7ea9e924ad6 --- /dev/null +++ b/forge-game/src/main/java/forge/game/staticability/StaticAbilityWitherDamage.java @@ -0,0 +1,32 @@ +package forge.game.staticability; + +import forge.game.Game; +import forge.game.card.Card; +import forge.game.zone.ZoneType; + +public class StaticAbilityWitherDamage { + + static String MODE = "WitherDamage"; + + static public boolean isWitherDamage(Card source) { + final Game game = source.getGame(); + for (final Card ca : game.getCardsIn(ZoneType.STATIC_ABILITIES_SOURCE_ZONES)) { + for (final StaticAbility stAb : ca.getStaticAbilities()) { + if (!stAb.checkConditions(MODE)) { + continue; + } + if (applyWitherDamageAbility(stAb, source)) { + return true; + } + } + } + return false; + } + + static public boolean applyWitherDamageAbility(StaticAbility stAb, Card source) { + if (!stAb.matchesValidParam("ValidCard", source)) { + return false; + } + return true; + } +} diff --git a/forge-gui/res/cardsfolder/e/everlasting_torment.txt b/forge-gui/res/cardsfolder/e/everlasting_torment.txt index 9bebb75958e..a61e510041c 100644 --- a/forge-gui/res/cardsfolder/e/everlasting_torment.txt +++ b/forge-gui/res/cardsfolder/e/everlasting_torment.txt @@ -3,7 +3,7 @@ ManaCost:2 BR Types:Enchantment S:Mode$ CantGainLife | ValidPlayer$ Player | Description$ Players can't gain life. S:Mode$ CantPreventDamage | Description$ Damage can't be prevented. -S:Mode$ Continuous | GlobalRule$ All damage is dealt as though its source had wither. | Description$ All damage is dealt as though its source had wither. (A source with wither deals damage to creatures in the form of -1/-1 counters.) +S:Mode$ WitherDamage | Description$ All damage is dealt as though its source had wither. (A source with wither deals damage to creatures in the form of -1/-1 counters.) SVar:NonStackingEffect:True AI:RemoveDeck:Random Oracle:Players can't gain life.\nDamage can't be prevented.\nAll damage is dealt as though its source had wither. (A source with wither deals damage to creatures in the form of -1/-1 counters.)