diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtilCombat.java b/forge-ai/src/main/java/forge/ai/ComputerUtilCombat.java index ebcde781c0e..d40ce7cf5df 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtilCombat.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtilCombat.java @@ -2256,11 +2256,12 @@ public class ComputerUtilCombat { * @return a int. */ public final static int getDamageToKill(final Card c) { - int killDamage = c.getLethalDamage() + c.getPreventNextDamageTotalShields(); + int damageShield = c.getPreventNextDamageTotalShields(); + int killDamage = c.getLethalDamage() + damageShield; - if ((killDamage > c.getPreventNextDamageTotalShields()) + if ((killDamage > damageShield) && c.hasSVar("DestroyWhenDamaged")) { - killDamage = 1 + c.getPreventNextDamageTotalShields(); + killDamage = 1 + damageShield; } return killDamage; 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 eccdeb48f16..3e9d300fe7b 100644 --- a/forge-ai/src/main/java/forge/ai/simulation/GameCopier.java +++ b/forge-ai/src/main/java/forge/ai/simulation/GameCopier.java @@ -88,7 +88,6 @@ public class GameCopier { newPlayer.setCounters(Maps.newHashMap(origPlayer.getCounters())); newPlayer.setLifeLostLastTurn(origPlayer.getLifeLostLastTurn()); newPlayer.setLifeLostThisTurn(origPlayer.getLifeLostThisTurn()); - newPlayer.setPreventNextDamage(origPlayer.getPreventNextDamage()); newPlayer.getManaPool().add(origPlayer.getManaPool()); newPlayer.setCommanders(origPlayer.getCommanders()); // will be fixed up below playerMap.put(origPlayer, newPlayer); diff --git a/forge-game/src/main/java/forge/game/GameEntity.java b/forge-game/src/main/java/forge/game/GameEntity.java index 285e3e15a0b..df87537339f 100644 --- a/forge-game/src/main/java/forge/game/GameEntity.java +++ b/forge-game/src/main/java/forge/game/GameEntity.java @@ -45,9 +45,7 @@ import forge.game.zone.ZoneType; public abstract class GameEntity extends GameObject implements IIdentifiable { protected final int id; private String name = ""; - private int preventNextDamage = 0; protected CardCollection attachedCards = new CardCollection(); - private Map> preventionShieldsWithEffects = Maps.newTreeMap(); protected Map counters = Maps.newHashMap(); protected GameEntity(int id0) { @@ -192,20 +190,6 @@ public abstract class GameEntity extends GameObject implements IIdentifiable { // then apply static Damage Prevention effects restDamage = staticDamagePrevention(restDamage, source, isCombat, false); - // then apply ShieldEffects with Special Effect - restDamage = preventShieldEffect(restDamage); - - // then do Shield with only number - if (restDamage <= 0) { - restDamage = 0; - } else if (restDamage >= getPreventNextDamage()) { - restDamage = restDamage - getPreventNextDamage(); - setPreventNextDamage(0); - } else { - setPreventNextDamage(getPreventNextDamage() - restDamage); - restDamage = 0; - } - // if damage is greater than restDamage, damage was prevented if (damage > restDamage) { int prevent = damage - restDamage; @@ -223,60 +207,8 @@ public abstract class GameEntity extends GameObject implements IIdentifiable { return restDamage; } - protected abstract int preventShieldEffect(final int damage); - - public int getPreventNextDamage() { - return preventNextDamage; - } - public void setPreventNextDamage(final int n) { - preventNextDamage = n; - } - public void addPreventNextDamage(final int n) { - preventNextDamage += n; - } - public void subtractPreventNextDamage(final int n) { - preventNextDamage -= n; - } - public void resetPreventNextDamage() { - preventNextDamage = 0; - } - - // PreventNextDamageWithEffect - public Map> getPreventNextDamageWithEffect() { - return preventionShieldsWithEffects; - } public int getPreventNextDamageTotalShields() { - int shields = preventNextDamage; - for (final Map value : preventionShieldsWithEffects.values()) { - shields += Integer.valueOf(value.get("ShieldAmount")); - } - return shields; - } - /** - * Adds a damage prevention shield with an effect that happens at time of prevention. - * @param shieldSource - The source card which generated the shield - * @param effectMap - A map of the effect occurring with the damage prevention - */ - public void addPreventNextDamageWithEffect(final Card shieldSource, Map effectMap) { - if (preventionShieldsWithEffects.containsKey(shieldSource)) { - int currentShields = Integer.valueOf(preventionShieldsWithEffects.get(shieldSource).get("ShieldAmount")); - currentShields += Integer.valueOf(effectMap.get("ShieldAmount")); - effectMap.put("ShieldAmount", Integer.toString(currentShields)); - preventionShieldsWithEffects.put(shieldSource, effectMap); - } else { - preventionShieldsWithEffects.put(shieldSource, effectMap); - } - } - public void subtractPreventNextDamageWithEffect(final Card shieldSource, final int n) { - int currentShields = Integer.valueOf(preventionShieldsWithEffects.get(shieldSource).get("ShieldAmount")); - if (currentShields > n) { - preventionShieldsWithEffects.get(shieldSource).put("ShieldAmount", String.valueOf(currentShields - n)); - } else { - preventionShieldsWithEffects.remove(shieldSource); - } - } - public void resetPreventNextDamageWithEffect() { - preventionShieldsWithEffects.clear(); + return getGame().getReplacementHandler().getTotalPreventionShieldAmount(this); } public abstract boolean hasKeyword(final String keyword); diff --git a/forge-game/src/main/java/forge/game/ability/effects/DamagePreventAllEffect.java b/forge-game/src/main/java/forge/game/ability/effects/DamagePreventAllEffect.java index e7bb3ae0fc4..a419612d638 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/DamagePreventAllEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/DamagePreventAllEffect.java @@ -2,14 +2,13 @@ package forge.game.ability.effects; import forge.game.Game; import forge.game.ability.AbilityUtils; -import forge.game.ability.SpellAbilityEffect; import forge.game.card.Card; import forge.game.card.CardCollectionView; import forge.game.player.Player; import forge.game.spellability.SpellAbility; import forge.game.zone.ZoneType; -public class DamagePreventAllEffect extends SpellAbilityEffect { +public class DamagePreventAllEffect extends DamagePreventEffectBase { @Override public void resolve(SpellAbility sa) { final Card source = sa.getHostCard(); @@ -26,14 +25,14 @@ public class DamagePreventAllEffect extends SpellAbilityEffect { CardCollectionView list = game.getCardsIn(ZoneType.Battlefield); list = AbilityUtils.filterListByType(list, sa.getParam("ValidCards"), sa); for (final Card c : list) { - c.addPreventNextDamage(numDam); + addPreventNextDamage(sa, c, numDam); } } if (!players.equals("")) { for (final Player p : game.getPlayers()) { if (p.isValid(players, source.getController(), source, sa)) { - p.addPreventNextDamage(numDam); + addPreventNextDamage(sa, p, numDam); } } } diff --git a/forge-game/src/main/java/forge/game/ability/effects/DamagePreventEffect.java b/forge-game/src/main/java/forge/game/ability/effects/DamagePreventEffect.java index 54e0395a34e..173d1318758 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/DamagePreventEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/DamagePreventEffect.java @@ -1,20 +1,16 @@ package forge.game.ability.effects; -import java.util.ArrayList; import java.util.List; -import java.util.Map; -import java.util.TreeMap; import forge.game.GameObject; import forge.game.ability.AbilityUtils; -import forge.game.ability.SpellAbilityEffect; import forge.game.card.Card; import forge.game.card.CardCollection; import forge.game.card.CardUtil; import forge.game.player.Player; import forge.game.spellability.SpellAbility; -public class DamagePreventEffect extends SpellAbilityEffect { +public class DamagePreventEffect extends DamagePreventEffectBase { @Override protected String getStackDescription(SpellAbility sa) { @@ -73,69 +69,25 @@ public class DamagePreventEffect extends SpellAbilityEffect { final CardCollection untargetedCards = CardUtil.getRadiance(sa); final boolean targeted = (sa.usesTargeting()); - final boolean preventionWithEffect = sa.hasParam("PreventionSubAbility"); for (final GameObject o : tgts) { numDam = (sa.usesTargeting() && sa.isDividedAsYouChoose()) ? sa.getDividedValue(o) : numDam; if (o instanceof Card) { final Card c = (Card) o; if (c.isInPlay() && (!targeted || c.canBeTargetedBy(sa))) { - if (preventionWithEffect) { - Map effectMap = new TreeMap<>(); - effectMap.put("EffectString", sa.getSVar(sa.getParam("PreventionSubAbility"))); - effectMap.put("ShieldAmount", String.valueOf(numDam)); - if (sa.hasParam("ShieldEffectTarget")) { - String effTgtString = ""; - List effTgts = new ArrayList<>(); - effTgts = AbilityUtils.getDefinedObjects(host, sa.getParam("ShieldEffectTarget"), sa); - for (final Object effTgt : effTgts) { - if (effTgt instanceof Card) { - effTgtString = String.valueOf(((Card) effTgt).getId()); - effectMap.put("ShieldEffectTarget", "CardUID_" + effTgtString); - } else if (effTgt instanceof Player) { - effTgtString = ((Player) effTgt).getName(); - effectMap.put("ShieldEffectTarget", "PlayerNamed_" + effTgtString); - } - } - } - c.addPreventNextDamageWithEffect(host, effectMap); - } else { - c.addPreventNextDamage(numDam); - } + addPreventNextDamage(sa, o, numDam); } - } else if (o instanceof Player) { final Player p = (Player) o; if (!targeted || p.canBeTargetedBy(sa)) { - if (preventionWithEffect) { - Map effectMap = new TreeMap<>(); - effectMap.put("EffectString", sa.getSVar(sa.getParam("PreventionSubAbility"))); - effectMap.put("ShieldAmount", String.valueOf(numDam)); - if (sa.hasParam("ShieldEffectTarget")) { - String effTgtString = ""; - List effTgts = new ArrayList<>(); - effTgts = AbilityUtils.getDefinedObjects(host, sa.getParam("ShieldEffectTarget"), sa); - for (final Object effTgt : effTgts) { - if (effTgt instanceof Card) { - effTgtString = String.valueOf(((Card) effTgt).getId()); - effectMap.put("ShieldEffectTarget", "CardUID_" + effTgtString); - } else if (effTgt instanceof Player) { - effTgtString = ((Player) effTgt).getName(); - effectMap.put("ShieldEffectTarget", "PlayerNamed_" + effTgtString); - } - } - } - p.addPreventNextDamageWithEffect(host, effectMap); - } else { - p.addPreventNextDamage(numDam); - } + addPreventNextDamage(sa, o, numDam); } } } for (final Card c : untargetedCards) { if (c.isInPlay()) { - c.addPreventNextDamage(numDam); + addPreventNextDamage(sa, c, numDam); } } } // preventDamageResolve diff --git a/forge-game/src/main/java/forge/game/ability/effects/DamagePreventEffectBase.java b/forge-game/src/main/java/forge/game/ability/effects/DamagePreventEffectBase.java new file mode 100644 index 00000000000..99446569b3c --- /dev/null +++ b/forge-game/src/main/java/forge/game/ability/effects/DamagePreventEffectBase.java @@ -0,0 +1,76 @@ +package forge.game.ability.effects; + +import java.util.List; + +import forge.GameCommand; +import forge.game.Game; +import forge.game.GameObject; +import forge.game.ability.AbilityFactory; +import forge.game.ability.AbilityUtils; +import forge.game.ability.SpellAbilityEffect; +import forge.game.card.Card; +import forge.game.player.Player; +import forge.game.replacement.ReplacementEffect; +import forge.game.replacement.ReplacementHandler; +import forge.game.spellability.AbilitySub; +import forge.game.spellability.SpellAbility; +import forge.game.trigger.TriggerType; +import forge.game.zone.ZoneType; +import forge.util.TextUtil; + +public abstract class DamagePreventEffectBase extends SpellAbilityEffect { + public static void addPreventNextDamage(SpellAbility sa, GameObject o, int numDam) { + final Card hostCard = sa.getHostCard(); + final Game game = hostCard.getGame(); + final Player player = hostCard.getController(); + final String name = hostCard.getName() + "'s Effect"; + final String image = hostCard.getImageKey(); + StringBuilder sb = new StringBuilder("Event$ DamageDone | ActiveZones$ Command | ValidTarget$ "); + sb.append((o instanceof Card ? "Card.IsRemembered" : "Player.IsRemembered")); + sb.append(" | PreventionEffect$ True | Description$ Prevent the next ").append(numDam).append(" damage."); + String repeffstr = sb.toString(); + String effect = "DB$ ReplaceDamage | Amount$ ShieldAmount"; + + final Card eff = createEffect(sa, player, name, image); + eff.setSVar("ShieldAmount", "Number$" + numDam); + eff.setSVar("PreventedDamage", "Number$0"); + eff.addRemembered(o); + + SpellAbility replaceDamage = AbilityFactory.getAbility(effect, eff); + if (sa.hasParam("PreventionSubAbility")) { + String subAbString = sa.getSVar(sa.getParam("PreventionSubAbility")); + if (sa.hasParam("ShieldEffectTarget")) { + List effTgts = AbilityUtils.getDefinedObjects(hostCard, sa.getParam("ShieldEffectTarget"), sa); + String effTgtString = ""; + for (final GameObject effTgt : effTgts) { + if (effTgt instanceof Card) { + effTgtString = "CardUID_" + String.valueOf(((Card) effTgt).getId()); + } else if (effTgt instanceof Player) { + effTgtString = "PlayerNamed_" + ((Player) effTgt).getName(); + } + } + subAbString = TextUtil.fastReplace(subAbString, "ShieldEffectTarget", effTgtString); + } + replaceDamage.setSubAbility((AbilitySub) AbilityFactory.getAbility(subAbString, eff)); + } + + ReplacementEffect re = ReplacementHandler.parseReplacement(repeffstr, eff, true); + re.setOverridingAbility(replaceDamage); + eff.addReplacementEffect(re); + if (o instanceof Card) { + addForgetOnMovedTrigger(eff, "Battlefield"); + } + eff.updateStateForView(); + + game.getTriggerHandler().suppressMode(TriggerType.ChangesZone); + game.getAction().moveTo(ZoneType.Command, eff, sa); + game.getTriggerHandler().clearSuppression(TriggerType.ChangesZone); + + game.getEndOfTurn().addUntil(new GameCommand() { + @Override + public void run() { + game.getAction().exile(eff, null); + } + }); + } +} diff --git a/forge-game/src/main/java/forge/game/ability/effects/ReplaceDamageEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ReplaceDamageEffect.java index 51ba01cbbad..a1e2fa1d76e 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ReplaceDamageEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ReplaceDamageEffect.java @@ -29,7 +29,7 @@ public class ReplaceDamageEffect extends SpellAbilityEffect { final ReplacementType event = sa.getReplacementEffect().getMode(); - String varValue = sa.getParamOrDefault("VarName", "1"); + String varValue = sa.getParamOrDefault("Amount", "1"); @SuppressWarnings("unchecked") Map originalParams = (Map) sa.getReplacingObject(AbilityKey.OriginalParams); @@ -51,6 +51,8 @@ public class ReplaceDamageEffect extends SpellAbilityEffect { } else if (!StringUtils.isNumeric(varValue)) { card.setSVar(varValue, "Number$" + prevent); } + // Set PreventedDamage SVar for PreventionSubAbility + card.setSVar("PreventedDamage", "Number$" + n); } // no damage for original target anymore 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 43eb7c32f20..38911562cf2 100644 --- a/forge-game/src/main/java/forge/game/card/Card.java +++ b/forge-game/src/main/java/forge/game/card/Card.java @@ -5231,83 +5231,6 @@ public class Card extends GameEntity implements Comparable, IHasSVars { return restDamage > 0 ? restDamage : 0; } - protected int preventShieldEffect(final int damage) { - if (damage <= 0) { - return 0; - } - - int restDamage = damage; - - boolean DEBUGShieldsWithEffects = false; - while (!getPreventNextDamageWithEffect().isEmpty() && restDamage != 0) { - Map> shieldMap = getPreventNextDamageWithEffect(); - CardCollectionView preventionEffectSources = new CardCollection(shieldMap.keySet()); - Card shieldSource = preventionEffectSources.get(0); - if (preventionEffectSources.size() > 1) { - Map choiceMap = Maps.newTreeMap(); - List choices = Lists.newArrayList(); - for (final Card key : preventionEffectSources) { - String effDesc = shieldMap.get(key).get("EffectString"); - int descIndex = effDesc.indexOf("SpellDescription"); - effDesc = effDesc.substring(descIndex + 18); - String shieldDescription = key.toString() + " - " + shieldMap.get(key).get("ShieldAmount") - + " shields - " + effDesc; - choices.add(shieldDescription); - choiceMap.put(shieldDescription, key); - } - shieldSource = getController().getController().chooseProtectionShield(this, choices, choiceMap); - } - if (DEBUGShieldsWithEffects) { - System.out.println("Prevention shield source: " + shieldSource); - } - - int shieldAmount = Integer.valueOf(shieldMap.get(shieldSource).get("ShieldAmount")); - int dmgToBePrevented = Math.min(restDamage, shieldAmount); - if (DEBUGShieldsWithEffects) { - System.out.println("Selected source initial shield amount: " + shieldAmount); - System.out.println("Incoming damage: " + restDamage); - System.out.println("Damage to be prevented: " + dmgToBePrevented); - } - - //Set up ability - SpellAbility shieldSA; - String effectAbString = shieldMap.get(shieldSource).get("EffectString"); - effectAbString = TextUtil.fastReplace(effectAbString, "PreventedDamage", Integer.toString(dmgToBePrevented)); - effectAbString = TextUtil.fastReplace(effectAbString, "ShieldEffectTarget", shieldMap.get(shieldSource).get("ShieldEffectTarget")); - if (DEBUGShieldsWithEffects) { - System.out.println("Final shield ability string: " + effectAbString); - } - shieldSA = AbilityFactory.getAbility(effectAbString, shieldSource); - if (shieldSA.usesTargeting()) { - System.err.println(shieldSource + " - Targeting for prevention shield's effect should be done with initial spell"); - } - - boolean apiIsEffect = (shieldSA.getApi() == ApiType.Effect); - CardCollectionView cardsInCommand = null; - if (apiIsEffect) { - cardsInCommand = getGame().getCardsIn(ZoneType.Command); - } - - getController().getController().playSpellAbilityNoStack(shieldSA, true); - if (apiIsEffect) { - CardCollection newCardsInCommand = (CardCollection)getGame().getCardsIn(ZoneType.Command); - newCardsInCommand.removeAll(cardsInCommand); - if (!newCardsInCommand.isEmpty()) { - newCardsInCommand.get(0).setSVar("PreventedDamage", "Number$" + dmgToBePrevented); - } - } - subtractPreventNextDamageWithEffect(shieldSource, restDamage); - restDamage = restDamage - dmgToBePrevented; - - if (DEBUGShieldsWithEffects) { - System.out.println("Remaining shields: " - + (shieldMap.containsKey(shieldSource) ? shieldMap.get(shieldSource).get("ShieldAmount") : "all shields used")); - System.out.println("Remaining damage: " + restDamage); - } - } - return restDamage; - } - // This is used by the AI to forecast an effect (so it must not change the game state) @Override public final int staticReplaceDamage(final int damage, final Card source, final boolean isCombat) { @@ -6205,8 +6128,6 @@ public class Card extends GameEntity implements Comparable, IHasSVars { public void onCleanupPhase(final Player turn) { setDamage(0); setHasBeenDealtDeathtouchDamage(false); - resetPreventNextDamage(); - resetPreventNextDamageWithEffect(); resetReceivedDamageFromThisTurn(); resetDealtDamageToThisTurn(); resetDealtDamageToPlayerThisTurn(); 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 161f38409bd..fe041141a72 100644 --- a/forge-game/src/main/java/forge/game/player/Player.java +++ b/forge-game/src/main/java/forge/game/player/Player.java @@ -31,7 +31,6 @@ import java.util.Map.Entry; import java.util.NavigableMap; import java.util.Set; import java.util.SortedSet; -import java.util.TreeMap; import java.util.concurrent.ConcurrentSkipListMap; import org.apache.commons.lang3.tuple.ImmutablePair; @@ -840,83 +839,6 @@ public class Player extends GameEntity implements Comparable { return restDamage; } - protected int preventShieldEffect(final int damage) { - if (damage <= 0) { - return 0; - } - - int restDamage = damage; - - boolean DEBUGShieldsWithEffects = false; - while (!getPreventNextDamageWithEffect().isEmpty() && restDamage != 0) { - Map> shieldMap = getPreventNextDamageWithEffect(); - CardCollection preventionEffectSources = new CardCollection(shieldMap.keySet()); - Card shieldSource = preventionEffectSources.get(0); - if (preventionEffectSources.size() > 1) { - Map choiceMap = new TreeMap<>(); - List choices = new ArrayList<>(); - for (final Card key : preventionEffectSources) { - String effDesc = shieldMap.get(key).get("EffectString"); - int descIndex = effDesc.indexOf("SpellDescription"); - effDesc = effDesc.substring(descIndex + 18); - String shieldDescription = key.toString() + " - " + shieldMap.get(shieldSource).get("ShieldAmount") - + " shields - " + effDesc; - choices.add(shieldDescription); - choiceMap.put(shieldDescription, key); - } - shieldSource = getController().chooseProtectionShield(this, choices, choiceMap); - } - if (DEBUGShieldsWithEffects) { - System.out.println("Prevention shield source: " + shieldSource); - } - - int shieldAmount = Integer.valueOf(shieldMap.get(shieldSource).get("ShieldAmount")); - int dmgToBePrevented = Math.min(restDamage, shieldAmount); - if (DEBUGShieldsWithEffects) { - System.out.println("Selected source initial shield amount: " + shieldAmount); - System.out.println("Incoming damage: " + restDamage); - System.out.println("Damage to be prevented: " + dmgToBePrevented); - } - - //Set up ability - SpellAbility shieldSA = null; - String effectAbString = shieldMap.get(shieldSource).get("EffectString"); - effectAbString = TextUtil.fastReplace(effectAbString, "PreventedDamage", Integer.toString(dmgToBePrevented)); - effectAbString = TextUtil.fastReplace(effectAbString, "ShieldEffectTarget", shieldMap.get(shieldSource).get("ShieldEffectTarget")); - if (DEBUGShieldsWithEffects) { - System.out.println("Final shield ability string: " + effectAbString); - } - shieldSA = AbilityFactory.getAbility(effectAbString, shieldSource); - if (shieldSA.usesTargeting()) { - System.err.println(shieldSource + " - Targeting for prevention shield's effect should be done with initial spell"); - } - - boolean apiIsEffect = (shieldSA.getApi() == ApiType.Effect); - CardCollectionView cardsInCommand = null; - if (apiIsEffect) { - cardsInCommand = getGame().getCardsIn(ZoneType.Command); - } - - getController().playSpellAbilityNoStack(shieldSA, true); - if (apiIsEffect) { - CardCollection newCardsInCommand = new CardCollection(getGame().getCardsIn(ZoneType.Command)); - newCardsInCommand.removeAll(cardsInCommand); - if (!newCardsInCommand.isEmpty()) { - newCardsInCommand.get(0).setSVar("PreventedDamage", "Number$" + dmgToBePrevented); - } - } - subtractPreventNextDamageWithEffect(shieldSource, restDamage); - restDamage = restDamage - dmgToBePrevented; - - if (DEBUGShieldsWithEffects) { - System.out.println("Remaining shields: " - + (shieldMap.containsKey(shieldSource) ? shieldMap.get(shieldSource).get("ShieldAmount") : "all shields used")); - System.out.println("Remaining damage: " + restDamage); - } - } - return restDamage; - } - public final void clearAssignedDamage() { assignedDamage.clear(); assignedCombatDamage.clear(); @@ -2597,8 +2519,6 @@ public class Player extends GameEntity implements Comparable { for (final PlayerZone pz : zones.values()) { pz.resetCardsAddedThisTurn(); } - resetPreventNextDamage(); - resetPreventNextDamageWithEffect(); resetNumDrawnThisTurn(); resetNumDiscardedThisTurn(); resetNumForetoldThisTurn(); 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 3ae0c8029e7..2a18dbf3c8c 100644 --- a/forge-game/src/main/java/forge/game/replacement/ReplacementHandler.java +++ b/forge-game/src/main/java/forge/game/replacement/ReplacementHandler.java @@ -29,11 +29,13 @@ import com.google.common.collect.Sets; import forge.game.CardTraitBase; import forge.game.Game; +import forge.game.GameEntity; import forge.game.GameLogEntryType; import forge.game.IHasSVars; import forge.game.ability.AbilityFactory; import forge.game.ability.AbilityKey; import forge.game.ability.AbilityUtils; +import forge.game.ability.ApiType; import forge.game.card.Card; import forge.game.card.CardCollection; import forge.game.card.CardState; @@ -436,4 +438,39 @@ public class ReplacementHandler { } return true; } + + /** + * Helper function to get total prevention shield amount (limited to "prevent next N damage effects") + * @param o Affected game entity object + * @return total shield amount + */ + public int getTotalPreventionShieldAmount(GameEntity o) { + final Map repParams = AbilityKey.mapFromAffected(o); + repParams.put(AbilityKey.Prevention, true); + repParams.put(AbilityKey.DamageAmount, 1); + List list = getReplacementList(ReplacementType.DamageDone, repParams, ReplacementLayer.Other); + if (list.isEmpty()) { + return 0; + } + int totalAmount = 0; + for (ReplacementEffect re : list) { + if (re.getOverridingAbility() != null) { + SpellAbility sa = re.getOverridingAbility(); + if (ApiType.ReplaceDamage == sa.getApi() && sa.hasParam("Amount")) { + String varValue = sa.getParam("Amount"); + if (StringUtils.isNumeric(varValue)) { + totalAmount += Integer.parseInt(varValue); + } else { + varValue = sa.getSVar(varValue); + if (StringUtils.isNumeric(varValue)) { + totalAmount += Integer.parseInt(varValue); + } else if (varValue.startsWith("Number$")) { + totalAmount += Integer.parseInt(varValue.substring(7)); + } + } + } + } + } + return totalAmount; + } } diff --git a/forge-gui/res/cardsfolder/h/healing_grace.txt b/forge-gui/res/cardsfolder/h/healing_grace.txt index e6bb01c3cb9..40d9669dc1e 100644 --- a/forge-gui/res/cardsfolder/h/healing_grace.txt +++ b/forge-gui/res/cardsfolder/h/healing_grace.txt @@ -3,10 +3,10 @@ ManaCost:W Types:Instant A:SP$ ChooseSource | Cost$ W | Choices$ Card,Emblem | SubAbility$ DBEffect | StackDescription$ SpellDescription | SpellDescription$ Prevent the next 3 damage that would be dealt to any target this turn by a source of your choice. You gain 3 life. SVar:DBEffect:DB$ Effect | ValidTgts$ Creature,Player,Planeswalker | TgtPrompt$ Select any target to prevent damage to | ReplacementEffects$ GraceDamage | ForgetOnMoved$ Battlefield | RememberObjects$ Targeted | SubAbility$ DBGainLife -SVar:GraceDamage:Event$ DamageDone | ValidTarget$ Creature.IsRemembered,Player.IsRemembered | ValidSource$ Card.ChosenCard,Emblem.ChosenCard | ReplaceWith$ GraceDmg | PreventionEffect$ True | Description$ Prevent the next 3 damage that would be dealt to any target this turn by a source of your choice. -SVar:GraceDmg:DB$ ReplaceDamage | VarName$ X +SVar:GraceDamage:Event$ DamageDone | ValidTarget$ Card.IsRemembered,Player.IsRemembered | ValidSource$ Card.ChosenCard,Emblem.ChosenCard | ReplaceWith$ GraceDmg | PreventionEffect$ True | Description$ Prevent the next 3 damage that would be dealt to any target this turn by a source of your choice. +SVar:GraceDmg:DB$ ReplaceDamage | Amount$ ShieldAmount SVar:DBGainLife:DB$ GainLife | LifeAmount$ 3 | SubAbility$ DBCleanup SVar:DBCleanup:DB$ Cleanup | ClearChosenCard$ True -SVar:X:Number$3 +SVar:ShieldAmount:Number$3 AI:RemoveDeck:All Oracle:Prevent the next 3 damage that would be dealt to any target this turn by a source of your choice. You gain 3 life.