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"));
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;
}
}

View File

@@ -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<Card> 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()) {

View File

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

View File

@@ -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();

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))
&& !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)));

View File

@@ -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."),

View File

@@ -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<Card>, 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<Card>, IHasSVars {
}
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
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.)