mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-15 18:28:00 +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.
|
* @return a int.
|
||||||
*/
|
*/
|
||||||
public final static int getDamageToKill(final Card c) {
|
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")) {
|
&& c.hasSVar("DestroyWhenDamaged")) {
|
||||||
killDamage = 1 + c.getPreventNextDamageTotalShields();
|
killDamage = 1 + damageShield;
|
||||||
}
|
}
|
||||||
|
|
||||||
return killDamage;
|
return killDamage;
|
||||||
|
|||||||
@@ -88,7 +88,6 @@ public class GameCopier {
|
|||||||
newPlayer.setCounters(Maps.newHashMap(origPlayer.getCounters()));
|
newPlayer.setCounters(Maps.newHashMap(origPlayer.getCounters()));
|
||||||
newPlayer.setLifeLostLastTurn(origPlayer.getLifeLostLastTurn());
|
newPlayer.setLifeLostLastTurn(origPlayer.getLifeLostLastTurn());
|
||||||
newPlayer.setLifeLostThisTurn(origPlayer.getLifeLostThisTurn());
|
newPlayer.setLifeLostThisTurn(origPlayer.getLifeLostThisTurn());
|
||||||
newPlayer.setPreventNextDamage(origPlayer.getPreventNextDamage());
|
|
||||||
newPlayer.getManaPool().add(origPlayer.getManaPool());
|
newPlayer.getManaPool().add(origPlayer.getManaPool());
|
||||||
newPlayer.setCommanders(origPlayer.getCommanders()); // will be fixed up below
|
newPlayer.setCommanders(origPlayer.getCommanders()); // will be fixed up below
|
||||||
playerMap.put(origPlayer, newPlayer);
|
playerMap.put(origPlayer, newPlayer);
|
||||||
|
|||||||
@@ -45,9 +45,7 @@ import forge.game.zone.ZoneType;
|
|||||||
public abstract class GameEntity extends GameObject implements IIdentifiable {
|
public abstract class GameEntity extends GameObject implements IIdentifiable {
|
||||||
protected final int id;
|
protected final int id;
|
||||||
private String name = "";
|
private String name = "";
|
||||||
private int preventNextDamage = 0;
|
|
||||||
protected CardCollection attachedCards = new CardCollection();
|
protected CardCollection attachedCards = new CardCollection();
|
||||||
private Map<Card, Map<String, String>> preventionShieldsWithEffects = Maps.newTreeMap();
|
|
||||||
protected Map<CounterType, Integer> counters = Maps.newHashMap();
|
protected Map<CounterType, Integer> counters = Maps.newHashMap();
|
||||||
|
|
||||||
protected GameEntity(int id0) {
|
protected GameEntity(int id0) {
|
||||||
@@ -192,20 +190,6 @@ public abstract class GameEntity extends GameObject implements IIdentifiable {
|
|||||||
// then apply static Damage Prevention effects
|
// then apply static Damage Prevention effects
|
||||||
restDamage = staticDamagePrevention(restDamage, source, isCombat, false);
|
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 is greater than restDamage, damage was prevented
|
||||||
if (damage > restDamage) {
|
if (damage > restDamage) {
|
||||||
int prevent = damage - restDamage;
|
int prevent = damage - restDamage;
|
||||||
@@ -223,60 +207,8 @@ public abstract class GameEntity extends GameObject implements IIdentifiable {
|
|||||||
return restDamage;
|
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() {
|
public int getPreventNextDamageTotalShields() {
|
||||||
int shields = preventNextDamage;
|
return getGame().getReplacementHandler().getTotalPreventionShieldAmount(this);
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract boolean hasKeyword(final String keyword);
|
public abstract boolean hasKeyword(final String keyword);
|
||||||
|
|||||||
@@ -2,14 +2,13 @@ package forge.game.ability.effects;
|
|||||||
|
|
||||||
import forge.game.Game;
|
import forge.game.Game;
|
||||||
import forge.game.ability.AbilityUtils;
|
import forge.game.ability.AbilityUtils;
|
||||||
import forge.game.ability.SpellAbilityEffect;
|
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
import forge.game.card.CardCollectionView;
|
import forge.game.card.CardCollectionView;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
import forge.game.zone.ZoneType;
|
import forge.game.zone.ZoneType;
|
||||||
|
|
||||||
public class DamagePreventAllEffect extends SpellAbilityEffect {
|
public class DamagePreventAllEffect extends DamagePreventEffectBase {
|
||||||
@Override
|
@Override
|
||||||
public void resolve(SpellAbility sa) {
|
public void resolve(SpellAbility sa) {
|
||||||
final Card source = sa.getHostCard();
|
final Card source = sa.getHostCard();
|
||||||
@@ -26,14 +25,14 @@ public class DamagePreventAllEffect extends SpellAbilityEffect {
|
|||||||
CardCollectionView list = game.getCardsIn(ZoneType.Battlefield);
|
CardCollectionView list = game.getCardsIn(ZoneType.Battlefield);
|
||||||
list = AbilityUtils.filterListByType(list, sa.getParam("ValidCards"), sa);
|
list = AbilityUtils.filterListByType(list, sa.getParam("ValidCards"), sa);
|
||||||
for (final Card c : list) {
|
for (final Card c : list) {
|
||||||
c.addPreventNextDamage(numDam);
|
addPreventNextDamage(sa, c, numDam);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!players.equals("")) {
|
if (!players.equals("")) {
|
||||||
for (final Player p : game.getPlayers()) {
|
for (final Player p : game.getPlayers()) {
|
||||||
if (p.isValid(players, source.getController(), source, sa)) {
|
if (p.isValid(players, source.getController(), source, sa)) {
|
||||||
p.addPreventNextDamage(numDam);
|
addPreventNextDamage(sa, p, numDam);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,20 +1,16 @@
|
|||||||
package forge.game.ability.effects;
|
package forge.game.ability.effects;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.TreeMap;
|
|
||||||
|
|
||||||
import forge.game.GameObject;
|
import forge.game.GameObject;
|
||||||
import forge.game.ability.AbilityUtils;
|
import forge.game.ability.AbilityUtils;
|
||||||
import forge.game.ability.SpellAbilityEffect;
|
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
import forge.game.card.CardCollection;
|
import forge.game.card.CardCollection;
|
||||||
import forge.game.card.CardUtil;
|
import forge.game.card.CardUtil;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
|
|
||||||
public class DamagePreventEffect extends SpellAbilityEffect {
|
public class DamagePreventEffect extends DamagePreventEffectBase {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String getStackDescription(SpellAbility sa) {
|
protected String getStackDescription(SpellAbility sa) {
|
||||||
@@ -73,69 +69,25 @@ public class DamagePreventEffect extends SpellAbilityEffect {
|
|||||||
final CardCollection untargetedCards = CardUtil.getRadiance(sa);
|
final CardCollection untargetedCards = CardUtil.getRadiance(sa);
|
||||||
|
|
||||||
final boolean targeted = (sa.usesTargeting());
|
final boolean targeted = (sa.usesTargeting());
|
||||||
final boolean preventionWithEffect = sa.hasParam("PreventionSubAbility");
|
|
||||||
|
|
||||||
for (final GameObject o : tgts) {
|
for (final GameObject o : tgts) {
|
||||||
numDam = (sa.usesTargeting() && sa.isDividedAsYouChoose()) ? sa.getDividedValue(o) : numDam;
|
numDam = (sa.usesTargeting() && sa.isDividedAsYouChoose()) ? sa.getDividedValue(o) : numDam;
|
||||||
if (o instanceof Card) {
|
if (o instanceof Card) {
|
||||||
final Card c = (Card) o;
|
final Card c = (Card) o;
|
||||||
if (c.isInPlay() && (!targeted || c.canBeTargetedBy(sa))) {
|
if (c.isInPlay() && (!targeted || c.canBeTargetedBy(sa))) {
|
||||||
if (preventionWithEffect) {
|
addPreventNextDamage(sa, o, numDam);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if (o instanceof Player) {
|
} else if (o instanceof Player) {
|
||||||
final Player p = (Player) o;
|
final Player p = (Player) o;
|
||||||
if (!targeted || p.canBeTargetedBy(sa)) {
|
if (!targeted || p.canBeTargetedBy(sa)) {
|
||||||
if (preventionWithEffect) {
|
addPreventNextDamage(sa, o, numDam);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (final Card c : untargetedCards) {
|
for (final Card c : untargetedCards) {
|
||||||
if (c.isInPlay()) {
|
if (c.isInPlay()) {
|
||||||
c.addPreventNextDamage(numDam);
|
addPreventNextDamage(sa, c, numDam);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} // preventDamageResolve
|
} // 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();
|
final ReplacementType event = sa.getReplacementEffect().getMode();
|
||||||
|
|
||||||
String varValue = sa.getParamOrDefault("VarName", "1");
|
String varValue = sa.getParamOrDefault("Amount", "1");
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
Map<AbilityKey, Object> originalParams = (Map<AbilityKey, Object>) sa.getReplacingObject(AbilityKey.OriginalParams);
|
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)) {
|
} else if (!StringUtils.isNumeric(varValue)) {
|
||||||
card.setSVar(varValue, "Number$" + prevent);
|
card.setSVar(varValue, "Number$" + prevent);
|
||||||
}
|
}
|
||||||
|
// Set PreventedDamage SVar for PreventionSubAbility
|
||||||
|
card.setSVar("PreventedDamage", "Number$" + n);
|
||||||
}
|
}
|
||||||
|
|
||||||
// no damage for original target anymore
|
// no damage for original target anymore
|
||||||
|
|||||||
@@ -5231,83 +5231,6 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
|
|||||||
return restDamage > 0 ? restDamage : 0;
|
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)
|
// This is used by the AI to forecast an effect (so it must not change the game state)
|
||||||
@Override
|
@Override
|
||||||
public final int staticReplaceDamage(final int damage, final Card source, final boolean isCombat) {
|
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) {
|
public void onCleanupPhase(final Player turn) {
|
||||||
setDamage(0);
|
setDamage(0);
|
||||||
setHasBeenDealtDeathtouchDamage(false);
|
setHasBeenDealtDeathtouchDamage(false);
|
||||||
resetPreventNextDamage();
|
|
||||||
resetPreventNextDamageWithEffect();
|
|
||||||
resetReceivedDamageFromThisTurn();
|
resetReceivedDamageFromThisTurn();
|
||||||
resetDealtDamageToThisTurn();
|
resetDealtDamageToThisTurn();
|
||||||
resetDealtDamageToPlayerThisTurn();
|
resetDealtDamageToPlayerThisTurn();
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ import java.util.Map.Entry;
|
|||||||
import java.util.NavigableMap;
|
import java.util.NavigableMap;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.SortedSet;
|
import java.util.SortedSet;
|
||||||
import java.util.TreeMap;
|
|
||||||
import java.util.concurrent.ConcurrentSkipListMap;
|
import java.util.concurrent.ConcurrentSkipListMap;
|
||||||
|
|
||||||
import org.apache.commons.lang3.tuple.ImmutablePair;
|
import org.apache.commons.lang3.tuple.ImmutablePair;
|
||||||
@@ -840,83 +839,6 @@ public class Player extends GameEntity implements Comparable<Player> {
|
|||||||
return restDamage;
|
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() {
|
public final void clearAssignedDamage() {
|
||||||
assignedDamage.clear();
|
assignedDamage.clear();
|
||||||
assignedCombatDamage.clear();
|
assignedCombatDamage.clear();
|
||||||
@@ -2597,8 +2519,6 @@ public class Player extends GameEntity implements Comparable<Player> {
|
|||||||
for (final PlayerZone pz : zones.values()) {
|
for (final PlayerZone pz : zones.values()) {
|
||||||
pz.resetCardsAddedThisTurn();
|
pz.resetCardsAddedThisTurn();
|
||||||
}
|
}
|
||||||
resetPreventNextDamage();
|
|
||||||
resetPreventNextDamageWithEffect();
|
|
||||||
resetNumDrawnThisTurn();
|
resetNumDrawnThisTurn();
|
||||||
resetNumDiscardedThisTurn();
|
resetNumDiscardedThisTurn();
|
||||||
resetNumForetoldThisTurn();
|
resetNumForetoldThisTurn();
|
||||||
|
|||||||
@@ -29,11 +29,13 @@ import com.google.common.collect.Sets;
|
|||||||
|
|
||||||
import forge.game.CardTraitBase;
|
import forge.game.CardTraitBase;
|
||||||
import forge.game.Game;
|
import forge.game.Game;
|
||||||
|
import forge.game.GameEntity;
|
||||||
import forge.game.GameLogEntryType;
|
import forge.game.GameLogEntryType;
|
||||||
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;
|
||||||
import forge.game.ability.AbilityUtils;
|
import forge.game.ability.AbilityUtils;
|
||||||
|
import forge.game.ability.ApiType;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
import forge.game.card.CardCollection;
|
import forge.game.card.CardCollection;
|
||||||
import forge.game.card.CardState;
|
import forge.game.card.CardState;
|
||||||
@@ -436,4 +438,39 @@ public class ReplacementHandler {
|
|||||||
}
|
}
|
||||||
return true;
|
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
|
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.
|
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: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: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 | VarName$ X
|
SVar:GraceDmg:DB$ ReplaceDamage | Amount$ ShieldAmount
|
||||||
SVar:DBGainLife:DB$ GainLife | LifeAmount$ 3 | SubAbility$ DBCleanup
|
SVar:DBGainLife:DB$ GainLife | LifeAmount$ 3 | SubAbility$ DBCleanup
|
||||||
SVar:DBCleanup:DB$ Cleanup | ClearChosenCard$ True
|
SVar:DBCleanup:DB$ Cleanup | ClearChosenCard$ True
|
||||||
SVar:X:Number$3
|
SVar:ShieldAmount:Number$3
|
||||||
AI:RemoveDeck:All
|
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.
|
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