Card: Wither damage as property, use Static for Everlasting Torment (#3502)

* Card: Wither damage as property, use Static for Everlasting Torment

* Update Card.java
This commit is contained in:
Hans Mackowiak
2023-07-21 08:57:25 +02:00
committed by GitHub
parent c743a2ebd3
commit c96af2b590
9 changed files with 55 additions and 26 deletions

View File

@@ -1402,8 +1402,7 @@ public class AiAttackController {
boolean hasCombatEffect = attacker.getSVar("HasCombatEffect").equals("TRUE") || "Blocked".equals(attacker.getSVar("HasAttackEffect")); boolean hasCombatEffect = attacker.getSVar("HasCombatEffect").equals("TRUE") || "Blocked".equals(attacker.getSVar("HasAttackEffect"));
if (!hasCombatEffect) { if (!hasCombatEffect) {
if (attacker.hasKeyword(Keyword.WITHER) || attacker.hasKeyword(Keyword.INFECT) if (attacker.isWitherDamage() || attacker.hasKeyword(Keyword.LIFELINK) || attacker.hasKeyword(Keyword.AFFLICT)) {
|| attacker.hasKeyword(Keyword.LIFELINK) || attacker.hasKeyword(Keyword.AFFLICT)) {
hasCombatEffect = true; hasCombatEffect = true;
} }
} }

View File

@@ -605,8 +605,7 @@ public class ComputerUtilCombat {
} }
} // flanking } // flanking
if (attacker.hasKeyword(Keyword.INDESTRUCTIBLE) if (attacker.hasKeyword(Keyword.INDESTRUCTIBLE) && !defender.isWitherDamage()) {
&& !(defender.hasKeyword(Keyword.WITHER) || defender.hasKeyword(Keyword.INFECT))) {
return 0; return 0;
} }
@@ -719,8 +718,7 @@ public class ComputerUtilCombat {
int firstStrikeBlockerDmg = 0; int firstStrikeBlockerDmg = 0;
for (final Card defender : blockers) { for (final Card defender : blockers) {
if (!(defender.hasKeyword(Keyword.WITHER) || defender.hasKeyword(Keyword.INFECT)) if (!(defender.isWitherDamage()) && canDestroyAttacker(ai, attacker, defender, combat, true)) {
&& canDestroyAttacker(ai, attacker, defender, combat, true)) {
return true; return true;
} }
if (defender.hasFirstStrike() || defender.hasDoubleStrike()) { if (defender.hasFirstStrike() || defender.hasDoubleStrike()) {
@@ -901,7 +899,7 @@ public class ComputerUtilCombat {
// if the attacker has first strike and wither the blocker will deal // if the attacker has first strike and wither the blocker will deal
// less damage than expected // less damage than expected
if (dealsFirstStrikeDamage(attacker, withoutAbilities, null) if (dealsFirstStrikeDamage(attacker, withoutAbilities, null)
&& (attacker.hasKeyword(Keyword.WITHER) || attacker.hasKeyword(Keyword.INFECT)) && attacker.isWitherDamage()
&& !dealsFirstStrikeDamage(blocker, withoutAbilities, null) && !dealsFirstStrikeDamage(blocker, withoutAbilities, null)
&& blocker.canReceiveCounters(CounterEnumType.M1M1)) { && blocker.canReceiveCounters(CounterEnumType.M1M1)) {
power -= attacker.getNetCombatDamage(); power -= attacker.getNetCombatDamage();
@@ -1193,7 +1191,7 @@ public class ComputerUtilCombat {
// less damage than expected // less damage than expected
if (null != blocker) { if (null != blocker) {
if (dealsFirstStrikeDamage(blocker, withoutAbilities, combat) if (dealsFirstStrikeDamage(blocker, withoutAbilities, combat)
&& (blocker.hasKeyword(Keyword.WITHER) || blocker.hasKeyword(Keyword.INFECT)) && blocker.isWitherDamage()
&& !dealsFirstStrikeDamage(attacker, withoutAbilities, combat) && !dealsFirstStrikeDamage(attacker, withoutAbilities, combat)
&& attacker.canReceiveCounters(CounterEnumType.M1M1)) { && attacker.canReceiveCounters(CounterEnumType.M1M1)) {
power -= blocker.getNetCombatDamage(); power -= blocker.getNetCombatDamage();
@@ -1689,7 +1687,7 @@ public class ComputerUtilCombat {
} // flanking } // flanking
if (((attacker.hasKeyword(Keyword.INDESTRUCTIBLE) || (!withoutAbilities && ComputerUtil.canRegenerate(ai, attacker))) 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 || (attacker.hasKeyword(Keyword.PERSIST) && !attacker.canReceiveCounters(CounterEnumType.M1M1) && (attacker
.getCounters(CounterEnumType.M1M1) == 0)) .getCounters(CounterEnumType.M1M1) == 0))
|| (attacker.hasKeyword(Keyword.UNDYING) && !attacker.canReceiveCounters(CounterEnumType.P1P1) && (attacker || (attacker.hasKeyword(Keyword.UNDYING) && !attacker.canReceiveCounters(CounterEnumType.P1P1) && (attacker
@@ -1795,8 +1793,7 @@ public class ComputerUtilCombat {
final List<Card> attackers = combat.getAttackersBlockedBy(blocker); final List<Card> attackers = combat.getAttackersBlockedBy(blocker);
for (Card attacker : attackers) { for (Card attacker : attackers) {
if (!(attacker.hasKeyword(Keyword.WITHER) || attacker.hasKeyword(Keyword.INFECT)) if (!(attacker.isWitherDamage()) && canDestroyBlocker(ai, blocker, attacker, combat, true)) {
&& canDestroyBlocker(ai, blocker, attacker, combat, true)) {
return true; return true;
} }
} }
@@ -1902,8 +1899,8 @@ public class ComputerUtilCombat {
return true; return true;
} }
if (((blocker.hasKeyword(Keyword.INDESTRUCTIBLE) || (!withoutAbilities && ComputerUtil.canRegenerate(ai, blocker))) && !(attacker if (((blocker.hasKeyword(Keyword.INDESTRUCTIBLE) || (!withoutAbilities && ComputerUtil.canRegenerate(ai, blocker)))
.hasKeyword(Keyword.WITHER) || attacker.hasKeyword(Keyword.INFECT))) && !attacker.isWitherDamage())
|| (blocker.hasKeyword(Keyword.PERSIST) && !blocker.canReceiveCounters(CounterEnumType.M1M1) && blocker || (blocker.hasKeyword(Keyword.PERSIST) && !blocker.canReceiveCounters(CounterEnumType.M1M1) && blocker
.getCounters(CounterEnumType.M1M1) == 0) .getCounters(CounterEnumType.M1M1) == 0)
|| (blocker.hasKeyword(Keyword.UNDYING) && !blocker.canReceiveCounters(CounterEnumType.P1P1) && blocker || (blocker.hasKeyword(Keyword.UNDYING) && !blocker.canReceiveCounters(CounterEnumType.P1P1) && blocker
@@ -2158,7 +2155,7 @@ public class ComputerUtilCombat {
int killDamage = getDamageToKill(c, false); int killDamage = getDamageToKill(c, false);
if (c.hasKeyword(Keyword.INDESTRUCTIBLE) || c.getCounters(CounterEnumType.SHIELD) > 0 || (c.getShieldCount() > 0 && c.canBeShielded())) { 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; return maxDamage + 1;
} }
} else if (source.hasKeyword(Keyword.DEATHTOUCH) && c.isCreature()) { } else if (source.hasKeyword(Keyword.DEATHTOUCH) && c.isCreature()) {

View File

@@ -498,8 +498,8 @@ public class SpecialCardAi {
// Already enough to kill the blockers and survive, don't overpump // Already enough to kill the blockers and survive, don't overpump
return false; return false;
} }
if (oppCantDie && !source.hasKeyword(Keyword.TRAMPLE) && !source.hasKeyword(Keyword.WITHER) if (oppCantDie && !source.hasKeyword(Keyword.TRAMPLE) && !source.isWitherDamage()
&& !source.hasKeyword(Keyword.INFECT) && predictedPT.getLeft() <= oppT) { && predictedPT.getLeft() <= oppT) {
// Can't kill or cripple anyone, as well as can't Trample over, so don't pump // Can't kill or cripple anyone, as well as can't Trample over, so don't pump
return false; return false;
} }

View File

@@ -201,8 +201,7 @@ public class AttachAi extends SpellAbilityAi {
boolean dangerous = false; boolean dangerous = false;
int totalAtkPower = 0; int totalAtkPower = 0;
for (Card attacker : combat.getBlockers(attachTarget)) { for (Card attacker : combat.getBlockers(attachTarget)) {
if (attacker.hasKeyword(Keyword.DEATHTOUCH) || attacker.hasKeyword(Keyword.INFECT) if (attacker.hasKeyword(Keyword.DEATHTOUCH) || attacker.isWitherDamage()) {
|| attacker.hasKeyword(Keyword.WITHER)) {
dangerous = true; dangerous = true;
} }
totalAtkPower += attacker.getNetPower(); totalAtkPower += attacker.getNetPower();

View File

@@ -350,7 +350,7 @@ public abstract class PumpAiBase extends SpellAbilityAi {
return !ph.isPlayerTurn(opp) && ((combat != null && combat.isAttacking(card)) || CombatUtil.canAttack(card, opp)) return !ph.isPlayerTurn(opp) && ((combat != null && combat.isAttacking(card)) || CombatUtil.canAttack(card, opp))
&& !ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_BLOCKERS); && !ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_BLOCKERS);
} else if (keyword.endsWith("Wither")) { } else if (keyword.endsWith("Wither")) {
if (newPower <= 0 || card.hasKeyword(Keyword.INFECT)) { if (newPower <= 0 || card.isWitherDamage()) {
return false; return false;
} }
return combat != null && (combat.isBlocking(card) || (combat.isAttacking(card) && combat.isBlocked(card))); return combat != null && (combat.isBlocking(card) || (combat.isAttacking(card) && combat.isBlocked(card)));

View File

@@ -22,7 +22,6 @@ package forge.game;
*/ */
public enum GlobalRuleChange { 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."), 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."), manaBurn ("A player losing unspent mana causes that player to lose that much life."),
noNight ("It can't become night."), noNight ("It can't become night."),

View File

@@ -35,7 +35,6 @@ import forge.game.GameActionUtil;
import forge.game.GameEntity; import forge.game.GameEntity;
import forge.game.GameEntityCounterTable; import forge.game.GameEntityCounterTable;
import forge.game.GameStage; import forge.game.GameStage;
import forge.game.GlobalRuleChange;
import forge.game.IHasSVars; import forge.game.IHasSVars;
import forge.game.ability.AbilityFactory; import forge.game.ability.AbilityFactory;
import forge.game.ability.AbilityKey; import forge.game.ability.AbilityKey;
@@ -5821,10 +5820,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
subtractCounter(CounterType.get(CounterEnumType.DEFENSE), damageIn, true); subtractCounter(CounterType.get(CounterEnumType.DEFENSE), damageIn, true);
} }
if (isCreature()) { if (isCreature()) {
boolean wither = game.getStaticEffects().getGlobalRuleChange(GlobalRuleChange.alwaysWither) if (source.isWitherDamage()) { // 120.3d
|| source.hasKeyword(Keyword.WITHER) || source.hasKeyword(Keyword.INFECT);
if (wither) { // 120.3d
addCounter(CounterEnumType.M1M1, damageIn, source.getController(), counterTable); addCounter(CounterEnumType.M1M1, damageIn, source.getController(), counterTable);
damageType = DamageType.M1M1Counters; damageType = DamageType.M1M1Counters;
} }
@@ -7478,4 +7474,11 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
} }
return !StaticAbilityActivateAbilityAsIfHaste.canActivate(this); return !StaticAbilityActivateAbilityAsIfHaste.canActivate(this);
} }
public boolean isWitherDamage() {
if (this.hasKeyword(Keyword.WITHER) || this.hasKeyword(Keyword.INFECT)) {
return true;
}
return StaticAbilityWitherDamage.isWitherDamage(this);
}
} }

View File

@@ -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;
}
}

View File

@@ -3,7 +3,7 @@ ManaCost:2 BR
Types:Enchantment Types:Enchantment
S:Mode$ CantGainLife | ValidPlayer$ Player | Description$ Players can't gain life. S:Mode$ CantGainLife | ValidPlayer$ Player | Description$ Players can't gain life.
S:Mode$ CantPreventDamage | Description$ Damage can't be prevented. 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 SVar:NonStackingEffect:True
AI:RemoveDeck:Random 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.) 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.)