mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-14 17:58:01 +00:00
Merge branch 'refactor_prevention' into 'master'
Refactor Damage Prevention - Step 1 See merge request core-developers/forge!4675
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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<Card, Map<String, String>> preventionShieldsWithEffects = Maps.newTreeMap();
|
||||
protected Map<CounterType, Integer> 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<Card, Map<String, String>> getPreventNextDamageWithEffect() {
|
||||
return preventionShieldsWithEffects;
|
||||
}
|
||||
public int getPreventNextDamageTotalShields() {
|
||||
int shields = preventNextDamage;
|
||||
for (final Map<String, String> 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<String, String> 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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<String, String> effectMap = new TreeMap<>();
|
||||
effectMap.put("EffectString", sa.getSVar(sa.getParam("PreventionSubAbility")));
|
||||
effectMap.put("ShieldAmount", String.valueOf(numDam));
|
||||
if (sa.hasParam("ShieldEffectTarget")) {
|
||||
String effTgtString = "";
|
||||
List<GameObject> 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<String, String> effectMap = new TreeMap<>();
|
||||
effectMap.put("EffectString", sa.getSVar(sa.getParam("PreventionSubAbility")));
|
||||
effectMap.put("ShieldAmount", String.valueOf(numDam));
|
||||
if (sa.hasParam("ShieldEffectTarget")) {
|
||||
String effTgtString = "";
|
||||
List<GameObject> 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
|
||||
|
||||
@@ -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<GameObject> 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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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<AbilityKey, Object> originalParams = (Map<AbilityKey, Object>) 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
|
||||
|
||||
@@ -5231,83 +5231,6 @@ public class Card extends GameEntity implements Comparable<Card>, 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<Card, Map<String, String>> shieldMap = getPreventNextDamageWithEffect();
|
||||
CardCollectionView preventionEffectSources = new CardCollection(shieldMap.keySet());
|
||||
Card shieldSource = preventionEffectSources.get(0);
|
||||
if (preventionEffectSources.size() > 1) {
|
||||
Map<String, Card> choiceMap = Maps.newTreeMap();
|
||||
List<String> 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<Card>, IHasSVars {
|
||||
public void onCleanupPhase(final Player turn) {
|
||||
setDamage(0);
|
||||
setHasBeenDealtDeathtouchDamage(false);
|
||||
resetPreventNextDamage();
|
||||
resetPreventNextDamageWithEffect();
|
||||
resetReceivedDamageFromThisTurn();
|
||||
resetDealtDamageToThisTurn();
|
||||
resetDealtDamageToPlayerThisTurn();
|
||||
|
||||
@@ -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<Player> {
|
||||
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<Card, Map<String, String>> shieldMap = getPreventNextDamageWithEffect();
|
||||
CardCollection preventionEffectSources = new CardCollection(shieldMap.keySet());
|
||||
Card shieldSource = preventionEffectSources.get(0);
|
||||
if (preventionEffectSources.size() > 1) {
|
||||
Map<String, Card> choiceMap = new TreeMap<>();
|
||||
List<String> 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<Player> {
|
||||
for (final PlayerZone pz : zones.values()) {
|
||||
pz.resetCardsAddedThisTurn();
|
||||
}
|
||||
resetPreventNextDamage();
|
||||
resetPreventNextDamageWithEffect();
|
||||
resetNumDrawnThisTurn();
|
||||
resetNumDiscardedThisTurn();
|
||||
resetNumForetoldThisTurn();
|
||||
|
||||
@@ -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<AbilityKey, Object> repParams = AbilityKey.mapFromAffected(o);
|
||||
repParams.put(AbilityKey.Prevention, true);
|
||||
repParams.put(AbilityKey.DamageAmount, 1);
|
||||
List<ReplacementEffect> 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user