From 4b71ea18b8be872136881ca5116a97c1f0881848 Mon Sep 17 00:00:00 2001 From: moomarc Date: Fri, 28 Jun 2013 16:56:56 +0000 Subject: [PATCH] - Added support for damage prevention shields with effects. - Added: Candles' Glow, Temper, Test of Faith and Vengeful Archon --- .gitattributes | 4 + res/cardsfolder/a/vengeful_archon.txt | 13 ++++ res/cardsfolder/c/candles_glow.txt | 8 ++ res/cardsfolder/t/temper.txt | 8 ++ res/cardsfolder/t/test_of_faith.txt | 7 ++ src/main/java/forge/Card.java | 62 +++++++++++++++ src/main/java/forge/GameEntity.java | 75 +++++++++++++++++++ .../forge/card/ability/AbilityFactory.java | 4 + .../java/forge/card/ability/AbilityUtils.java | 15 ++++ .../ability/effects/DamagePreventEffect.java | 48 +++++++++++- .../card/replacement/ReplacementHandler.java | 7 +- .../forge/game/ai/ComputerUtilCombat.java | 6 +- src/main/java/forge/game/player/Player.java | 62 +++++++++++++++ .../forge/game/player/PlayerController.java | 2 + .../forge/game/player/PlayerControllerAi.java | 11 +++ .../game/player/PlayerControllerHuman.java | 11 +++ src/main/java/forge/gui/CardDetailPanel.java | 2 +- .../java/forge/gui/match/views/VPlayers.java | 2 +- 18 files changed, 334 insertions(+), 13 deletions(-) create mode 100644 res/cardsfolder/a/vengeful_archon.txt create mode 100644 res/cardsfolder/c/candles_glow.txt create mode 100644 res/cardsfolder/t/temper.txt create mode 100644 res/cardsfolder/t/test_of_faith.txt diff --git a/.gitattributes b/.gitattributes index e573d655f4e..dc747379ed3 100644 --- a/.gitattributes +++ b/.gitattributes @@ -707,6 +707,7 @@ res/cardsfolder/a/azors_elocutors.txt -text res/cardsfolder/a/azure_drake.txt svneol=native#text/plain res/cardsfolder/a/azure_mage.txt svneol=native#text/plain res/cardsfolder/a/azusa_lost_but_seeking.txt svneol=native#text/plain +res/cardsfolder/a/vengeful_archon.txt -text res/cardsfolder/b/back_from_the_brink.txt -text res/cardsfolder/b/back_to_basics.txt svneol=native#text/plain res/cardsfolder/b/back_to_nature.txt svneol=native#text/plain @@ -1496,6 +1497,7 @@ res/cardsfolder/c/caltrops.txt svneol=native#text/plain res/cardsfolder/c/camel.txt -text res/cardsfolder/c/cancel.txt svneol=native#text/plain res/cardsfolder/c/candelabra_of_tawnos.txt svneol=native#text/plain +res/cardsfolder/c/candles_glow.txt -text res/cardsfolder/c/candles_of_leng.txt -text svneol=unset#text/plain res/cardsfolder/c/canker_abomination.txt svneol=native#text/plain res/cardsfolder/c/cankerous_thirst.txt -text @@ -11142,6 +11144,7 @@ res/cardsfolder/t/telimtors_edict.txt svneol=native#text/plain res/cardsfolder/t/teller_of_tales.txt svneol=native#text/plain res/cardsfolder/t/telling_time.txt svneol=native#text/plain res/cardsfolder/t/tember_city.txt -text +res/cardsfolder/t/temper.txt -text res/cardsfolder/t/tempered_steel.txt svneol=native#text/plain res/cardsfolder/t/tempest_drake.txt svneol=native#text/plain res/cardsfolder/t/tempest_of_light.txt svneol=native#text/plain @@ -11197,6 +11200,7 @@ res/cardsfolder/t/territorial_dispute.txt -text res/cardsfolder/t/terror.txt svneol=native#text/plain res/cardsfolder/t/terrus_wurm.txt -text res/cardsfolder/t/test_of_endurance.txt svneol=native#text/plain +res/cardsfolder/t/test_of_faith.txt -text res/cardsfolder/t/testament_of_faith.txt svneol=native#text/plain res/cardsfolder/t/tethered_griffin.txt svneol=native#text/plain res/cardsfolder/t/tethered_skirge.txt svneol=native#text/plain diff --git a/res/cardsfolder/a/vengeful_archon.txt b/res/cardsfolder/a/vengeful_archon.txt new file mode 100644 index 00000000000..709005cab48 --- /dev/null +++ b/res/cardsfolder/a/vengeful_archon.txt @@ -0,0 +1,13 @@ +Name:Vengeful Archon +ManaCost:4 W W W +Types:Creature Archon +PT:7/7 +K:Flying +A:AB$ Pump | Cost$ X | ValidTgts$ Player | TgtPrompt$ Select target player damaged by shield effect | IsCurse$ True | References$ X | RememberObjects$ Targeted | SubAbility$ ArchonPrevention | StackDescription$ none | SpellDescription$ Prevent the next X damage that would be dealt to you this turn. If damage is prevented this way, CARDNAME deals that much damage to target player. +SVar:ArchonPrevention:DB$ PreventDamage | Defined$ You | Amount$ X | References$ X | PreventionSubAbility$ ArchonsVengeance | ShieldEffectTarget$ Remembered | SubAbility$ DBCleanup +SVar:ArchonsVengeance:AB$ DealDamage | Cost$ 0 | Defined$ ShieldEffectTarget | NumDmg$ PreventedDamage | SpellDescription$ CARDNAME deals damage to target player for each damage prevented this way. +SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True +SVar:X:Count$xPaid +SVar:RemAIDeck:True +SVar:Picture:http://www.wizards.com/global/images/magic/general/vengeful_archon.jpg +Oracle:Flying\n{X}: Prevent the next X damage that would be dealt to you this turn. If damage is prevented this way, Vengeful Archon deals that much damage to target player. diff --git a/res/cardsfolder/c/candles_glow.txt b/res/cardsfolder/c/candles_glow.txt new file mode 100644 index 00000000000..352574f8c87 --- /dev/null +++ b/res/cardsfolder/c/candles_glow.txt @@ -0,0 +1,8 @@ +Name:Candles' Glow +ManaCost:1 W +Types:Instant Arcane +A:SP$ PreventDamage | Cost$ 1 W | ValidTgts$ Player,Creature | Amount$ 3 | PreventionSubAbility$ GlowOfLife | References$ GlowOfLife | ShieldEffectTarget$ You | TgtPrompt$ Select target creature or player | SpellDescription$ Prevent the next 3 damage that would be dealt to target creature or player this turn. You gain life equal to the damage prevented this way. +SVar:GlowOfLife:AB$ GainLife | Cost$ 0 | Defined$ ShieldEffectTarget | LifeAmount$ PreventedDamage | SpellDescription$ You gain life equal to the damage prevented this way. +K:Splice onto Arcane 1 W +SVar:Picture:http://www.wizards.com/global/images/magic/general/candles_glow.jpg +Oracle:Prevent the next 3 damage that would be dealt to target creature or player this turn. You gain life equal to the damage prevented this way.\nSplice onto Arcane {1}{W} (As you cast an Arcane spell, you may reveal this card from your hand and pay its splice cost. If you do, add this card's effects to that spell.) diff --git a/res/cardsfolder/t/temper.txt b/res/cardsfolder/t/temper.txt new file mode 100644 index 00000000000..d4094b23fb9 --- /dev/null +++ b/res/cardsfolder/t/temper.txt @@ -0,0 +1,8 @@ +Name:Temper +ManaCost:X 1 W +Types:Instant +A:SP$ PreventDamage | Cost$ X 1 W | ValidTgts$ Creature | Amount$ X | References$ X | PreventionSubAbility$ EvenTemper | ShieldEffectTarget$ Targeted | TgtPrompt$ Select target creature | SpellDescription$ Prevent the next X damage that would be dealt to target creature this turn. For each 1 damage prevented this way, put a +1/+1 counter on that creature. +SVar:EvenTemper:AB$ PutCounter | Cost$ 0 | Defined$ ShieldEffectTarget | CounterType$ P1P1 | CounterNum$ PreventedDamage | SpellDescription$ Put a +1/+1 counter on this creature for each 1 damage prevented this way. +SVar:X:Count$xPaid +SVar:Picture:http://www.wizards.com/global/images/magic/general/temper.jpg +Oracle:Prevent the next X damage that would be dealt to target creature this turn. For each 1 damage prevented this way, put a +1/+1 counter on that creature. diff --git a/res/cardsfolder/t/test_of_faith.txt b/res/cardsfolder/t/test_of_faith.txt new file mode 100644 index 00000000000..bb340c199c1 --- /dev/null +++ b/res/cardsfolder/t/test_of_faith.txt @@ -0,0 +1,7 @@ +Name:Test of Faith +ManaCost:1 W +Types:Instant +A:SP$ PreventDamage | Cost$ 1 W | ValidTgts$ Creature | Amount$ 3 | PreventionSubAbility$ FaithsReward | ShieldEffectTarget$ Targeted | TgtPrompt$ Select target creature | SpellDescription$ Prevent the next 3 damage that would be dealt to target creature this turn, and put a +1/+1 counter on that creature for each 1 damage prevented this way. +SVar:FaithsReward:AB$ PutCounter | Cost$ 0 | Defined$ ShieldEffectTarget | CounterType$ P1P1 | CounterNum$ PreventedDamage | SpellDescription$ Put a +1/+1 counter on this creature for each 1 damage prevented this way. +SVar:Picture:http://www.wizards.com/global/images/magic/general/test_of_faith.jpg +Oracle:Prevent the next 3 damage that would be dealt to target creature this turn, and put a +1/+1 counter on that creature for each 1 damage prevented this way. diff --git a/src/main/java/forge/Card.java b/src/main/java/forge/Card.java index 68b18be79b4..48596c37af3 100644 --- a/src/main/java/forge/Card.java +++ b/src/main/java/forge/Card.java @@ -48,6 +48,7 @@ import forge.card.CardRarity; import forge.card.CardRules; import forge.card.ColorSet; import forge.card.MagicColor; +import forge.card.ability.AbilityFactory; import forge.card.ability.AbilityUtils; import forge.card.ability.ApiType; import forge.card.cardfactory.CardFactoryUtil; @@ -7341,6 +7342,66 @@ public class Card extends GameEntity implements Comparable { int restDamage = damage; + boolean DEBUGShieldsWithEffects = false; + while (!this.getPreventNextDamageWithEffect().isEmpty() && restDamage != 0) { + TreeMap> shieldMap = this.getPreventNextDamageWithEffect(); + List preventionEffectSources = new ArrayList(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(key).get("ShieldAmount") + + " shields - " + effDesc; + choices.add(shieldDescription); + choiceMap.put(shieldDescription, key); + } + shieldSource = this.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 = null; + String effectAbString = shieldMap.get(shieldSource).get("EffectString"); + effectAbString = effectAbString.replace("PreventedDamage", Integer.toString(dmgToBePrevented)); + effectAbString = effectAbString.replace("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"); + } + + if (restDamage >= shieldAmount) { + this.getController().getController().playSpellAbilityNoStack(this.getController(), shieldSA); + this.subtractPreventNextDamageWithEffect(shieldSource, restDamage); + restDamage = restDamage - shieldAmount; + } else { + this.subtractPreventNextDamageWithEffect(shieldSource, restDamage); + this.getController().getController().playSpellAbilityNoStack(this.getController(), shieldSA); + restDamage = 0; + } + if (DEBUGShieldsWithEffects) { + System.out.println("Remaining shields: " + + (shieldMap.containsKey(shieldSource) ? shieldMap.get(shieldSource).get("ShieldAmount") : "all shields used")); + System.out.println("Remaining damage: " + restDamage); + } + } + if (this.getName().equals("Swans of Bryn Argoll")) { source.getController().drawCards(restDamage); return 0; @@ -8228,6 +8289,7 @@ public class Card extends GameEntity implements Comparable { public void onCleanupPhase(final Player turn) { setDamage(0); resetPreventNextDamage(); + resetPreventNextDamageWithEffect(); resetReceivedDamageFromThisTurn(); resetDealtDamageToThisTurn(); resetDealtDamageToPlayerThisTurn(); diff --git a/src/main/java/forge/GameEntity.java b/src/main/java/forge/GameEntity.java index c2e6043f9a8..0b67c5f5786 100644 --- a/src/main/java/forge/GameEntity.java +++ b/src/main/java/forge/GameEntity.java @@ -18,6 +18,8 @@ package forge; import java.util.ArrayList; +import java.util.Map; +import java.util.TreeMap; import forge.game.Game; import forge.game.player.Player; @@ -34,6 +36,7 @@ import forge.util.MyObservable; public abstract class GameEntity extends MyObservable implements ITargetable { private String name = ""; private int preventNextDamage = 0; + private TreeMap> preventionShieldsWithEffects = new TreeMap>(); /** The enchanted by. */ private ArrayList enchantedBy = new ArrayList(); @@ -255,6 +258,78 @@ public abstract class GameEntity extends MyObservable implements ITargetable { this.preventNextDamage = 0; } + // PreventNextDamageWithEffect + /** + *

+ * Gets the map of damage prevention shields with effects. + *

+ * + * @return the map of damage prevention shields with effects. + */ + public TreeMap> getPreventNextDamageWithEffect() { + return this.preventionShieldsWithEffects; + } + + /** + *

+ * 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, TreeMap effectMap) { + if (this.preventionShieldsWithEffects.containsKey(shieldSource)) { + int currentShields = Integer.valueOf(this.preventionShieldsWithEffects.get(shieldSource).get("ShieldAmount")); + currentShields += Integer.valueOf(effectMap.get("ShieldAmount")); + effectMap.put("ShieldAmount", Integer.toString(currentShields)); + this.preventionShieldsWithEffects.put(shieldSource, effectMap); + } else { + this.preventionShieldsWithEffects.put(shieldSource, effectMap); + } + } + + /** + *

+ * subtractPreventNextDamageWithEffect. + *

+ * + * @param shieldSource The source card which generated the shield + * @param n The number of shields to remove originating from shieldSource + */ + public void subtractPreventNextDamageWithEffect(final Card shieldSource, final int n) { + int currentShields = Integer.valueOf(this.preventionShieldsWithEffects.get(shieldSource).get("ShieldAmount")); + if (currentShields > n) { + this.preventionShieldsWithEffects.get(shieldSource).put("ShieldAmount", String.valueOf(currentShields - n)); + } else { + this.preventionShieldsWithEffects.remove(shieldSource); + } + } + + /** + *

+ * resetPreventNextDamageWithEffect. + *

+ */ + public void resetPreventNextDamageWithEffect() { + this.preventionShieldsWithEffects = new TreeMap>(); + } + + /** + *

+ * Gets the total amount of damage prevention shields. + *

+ * + * @return the number of damage prevention shields with and without effects. + */ + public int getPreventNextDamageTotalShields() { + int shields = this.preventNextDamage; + for (final Map value : this.preventionShieldsWithEffects.values()) { + shields += Integer.valueOf(value.get("ShieldAmount")); + } + return shields; + } + /** * Checks for keyword. * diff --git a/src/main/java/forge/card/ability/AbilityFactory.java b/src/main/java/forge/card/ability/AbilityFactory.java index d2d5357a022..9181521fcd3 100644 --- a/src/main/java/forge/card/ability/AbilityFactory.java +++ b/src/main/java/forge/card/ability/AbilityFactory.java @@ -166,6 +166,10 @@ public final class AbilityFactory { } } + if (mapParams.containsKey("PreventionSubAbility")) { + spellAbility.setSVar(mapParams.get("PreventionSubAbility"), hostCard.getSVar(mapParams.get("PreventionSubAbility"))); + } + if (mapParams.containsKey("SubAbility")) { spellAbility.setSubAbility(getSubAbility(hostCard, hostCard.getSVar(mapParams.get("SubAbility")))); } diff --git a/src/main/java/forge/card/ability/AbilityUtils.java b/src/main/java/forge/card/ability/AbilityUtils.java index eb4ae7458aa..9b17ad4e29a 100644 --- a/src/main/java/forge/card/ability/AbilityUtils.java +++ b/src/main/java/forge/card/ability/AbilityUtils.java @@ -204,6 +204,14 @@ public class AbilityUtils { for (final Card chosen : hostCard.getChosenCard()) { cards.add(game.getCardState(chosen)); } + } + else if (defined.startsWith("CardUID_")) { + String idString = defined.substring(8); + for (final Card cardByID : game.getCardsInGame()) { + if (cardByID.getUniqueNumber() == Integer.valueOf(idString)) { + cards.add(game.getCardState(cardByID)); + } + } } else { List list = null; if (defined.startsWith("Sacrificed")) { @@ -905,6 +913,13 @@ public class AbilityUtils { if (!players.contains(p)) { players.add(p); } + } else if (defined.startsWith("PlayerNamed_")) { + for (Player p : game.getPlayers()) { + System.out.println("Named player " + defined.substring(12)); + if (p.getName().equals(defined.substring(12))) { + players.add(p); + } + } } else if (defined.startsWith("Flipped")) { for (Player p : game.getPlayers()) { if (null != sa.getSourceCard().getFlipResult(p)) { diff --git a/src/main/java/forge/card/ability/effects/DamagePreventEffect.java b/src/main/java/forge/card/ability/effects/DamagePreventEffect.java index 4cc66d78dc3..67e9196b02c 100644 --- a/src/main/java/forge/card/ability/effects/DamagePreventEffect.java +++ b/src/main/java/forge/card/ability/effects/DamagePreventEffect.java @@ -2,6 +2,7 @@ package forge.card.ability.effects; import java.util.ArrayList; import java.util.List; +import java.util.TreeMap; import forge.Card; import forge.CardUtil; @@ -86,19 +87,62 @@ public class DamagePreventEffect extends SpellAbilityEffect { } final boolean targeted = (sa.usesTargeting()); + final boolean preventionWithEffect = sa.hasParam("PreventionSubAbility"); for (final Object o : tgts) { numDam = (sa.usesTargeting() && sa.hasParam("DividedAsYouChoose")) ? sa.getTargetRestrictions().getDividedValue(o) : numDam; if (o instanceof Card) { final Card c = (Card) o; if (c.isInPlay() && (!targeted || c.canBeTargetedBy(sa))) { - c.addPreventNextDamage(numDam); + if (preventionWithEffect) { + TreeMap 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).getUniqueNumber()); + 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) { final Player p = (Player) o; if (!targeted || p.canBeTargetedBy(sa)) { - p.addPreventNextDamage(numDam); + if (preventionWithEffect) { + TreeMap 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).getUniqueNumber()); + 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); + } } } } diff --git a/src/main/java/forge/card/replacement/ReplacementHandler.java b/src/main/java/forge/card/replacement/ReplacementHandler.java index 5329459f34d..51ae439435c 100644 --- a/src/main/java/forge/card/replacement/ReplacementHandler.java +++ b/src/main/java/forge/card/replacement/ReplacementHandler.java @@ -240,12 +240,7 @@ public class ReplacementHandler { } Player player = replacementEffect.getHostCard().getController(); - //player.getController().playNoStack() - if (player.isHuman()) { - HumanPlay.playSpellAbilityNoStack(player, effectSA); - } else { - ComputerUtil.playNoStack(player, effectSA, game); - } + player.getController().playSpellAbilityNoStack(player, effectSA); return ReplacementResult.Replaced; } diff --git a/src/main/java/forge/game/ai/ComputerUtilCombat.java b/src/main/java/forge/game/ai/ComputerUtilCombat.java index 9d32d23f2ff..d7846921abb 100644 --- a/src/main/java/forge/game/ai/ComputerUtilCombat.java +++ b/src/main/java/forge/game/ai/ComputerUtilCombat.java @@ -1828,10 +1828,10 @@ public class ComputerUtilCombat { * @return a int. */ public final static int getDamageToKill(final Card c) { - int killDamage = c.getLethalDamage() + c.getPreventNextDamage(); - if ((killDamage > c.getPreventNextDamage()) + int killDamage = c.getLethalDamage() + c.getPreventNextDamageTotalShields(); + if ((killDamage > c.getPreventNextDamageTotalShields()) && c.hasStartOfKeyword("When CARDNAME is dealt damage, destroy it.")) { - killDamage = 1 + c.getPreventNextDamage(); + killDamage = 1 + c.getPreventNextDamageTotalShields(); } return killDamage; diff --git a/src/main/java/forge/game/player/Player.java b/src/main/java/forge/game/player/Player.java index 9d534dfce68..4e108da14e9 100644 --- a/src/main/java/forge/game/player/Player.java +++ b/src/main/java/forge/game/player/Player.java @@ -25,6 +25,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Random; +import java.util.TreeMap; import com.google.common.base.Function; import com.google.common.base.Predicate; @@ -887,6 +888,66 @@ public class Player extends GameEntity implements Comparable { int restDamage = damage; + boolean DEBUGShieldsWithEffects = false; + while (!this.getPreventNextDamageWithEffect().isEmpty() && restDamage != 0) { + TreeMap> shieldMap = this.getPreventNextDamageWithEffect(); + List preventionEffectSources = new ArrayList(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 = this.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 = effectAbString.replace("PreventedDamage", Integer.toString(dmgToBePrevented)); + effectAbString = effectAbString.replace("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"); + } + + if (restDamage >= shieldAmount) { + this.getController().playSpellAbilityNoStack(this, shieldSA); + this.subtractPreventNextDamageWithEffect(shieldSource, restDamage); + restDamage = restDamage - shieldAmount; + } else { + this.subtractPreventNextDamageWithEffect(shieldSource, restDamage); + this.getController().playSpellAbilityNoStack(this, shieldSA); + restDamage = 0; + } + if (DEBUGShieldsWithEffects) { + System.out.println("Remaining shields: " + + (shieldMap.containsKey(shieldSource) ? shieldMap.get(shieldSource).get("ShieldAmount") : "all shields used")); + System.out.println("Remaining damage: " + restDamage); + } + } + final HashMap repParams = new HashMap(); repParams.put("Event", "DamageDone"); repParams.put("Affected", this); @@ -2757,6 +2818,7 @@ public class Player extends GameEntity implements Comparable { c.setDrawnThisTurn(false); } resetPreventNextDamage(); + resetPreventNextDamageWithEffect(); resetNumDrawnThisTurn(); resetNumDiscardedThisTurn(); setAttackedWithCreatureThisTurn(false); diff --git a/src/main/java/forge/game/player/PlayerController.java b/src/main/java/forge/game/player/PlayerController.java index c643715fc3c..00477eaff66 100644 --- a/src/main/java/forge/game/player/PlayerController.java +++ b/src/main/java/forge/game/player/PlayerController.java @@ -96,6 +96,7 @@ public abstract class PlayerController { //public abstract void playFromSuspend(Card c); public abstract boolean playCascade(Card cascadedCard, Card sourceCard); public abstract void playSpellAbilityForFree(SpellAbility copySA, boolean mayChoseNewTargets); + public abstract void playSpellAbilityNoStack(Player player, SpellAbility effectSA); public abstract Deck sideboard(final Deck deck, GameType gameType); @@ -157,5 +158,6 @@ public abstract class PlayerController { public abstract boolean chooseBinary(SpellAbility sa, String question, boolean isCoin); public abstract boolean chooseFilpResult(SpellAbility sa, Player flipper, boolean[] results, boolean call); + public abstract Card chooseProtectionShield(GameEntity entityBeingDamaged, List options, Map choiceMap); } diff --git a/src/main/java/forge/game/player/PlayerControllerAi.java b/src/main/java/forge/game/player/PlayerControllerAi.java index 241ad34783d..9409c6f78b2 100644 --- a/src/main/java/forge/game/player/PlayerControllerAi.java +++ b/src/main/java/forge/game/player/PlayerControllerAi.java @@ -248,6 +248,11 @@ public class PlayerControllerAi extends PlayerController { ComputerUtil.playSpellAbilityForFree(player, copySA); } + @Override + public void playSpellAbilityNoStack(Player player, SpellAbility effectSA) { + ComputerUtil.playNoStack(player, effectSA, game); + } + @Override public void playMiracle(SpellAbility miracle, Card card) { getAi().chooseAndPlaySa(false, false, miracle); @@ -412,4 +417,10 @@ public class PlayerControllerAi extends PlayerController { public boolean chooseBinary(SpellAbility sa, String question, boolean isCoin) { return MyRandom.getRandom().nextBoolean(); } + + @Override + public Card chooseProtectionShield(GameEntity entityBeingDamaged, List options, Map choiceMap) { + int i = MyRandom.getRandom().nextInt(options.size()); + return choiceMap.get(options.get(i)); + } } diff --git a/src/main/java/forge/game/player/PlayerControllerHuman.java b/src/main/java/forge/game/player/PlayerControllerHuman.java index 9faba09eb9e..2d4136a61df 100644 --- a/src/main/java/forge/game/player/PlayerControllerHuman.java +++ b/src/main/java/forge/game/player/PlayerControllerHuman.java @@ -121,6 +121,11 @@ public class PlayerControllerHuman extends PlayerController { HumanPlay.playSaWithoutPayingManaCost(player.getGame(), copySA, mayChoseNewTargets); } + @Override + public void playSpellAbilityNoStack(Player player, SpellAbility effectSA) { + HumanPlay.playSpellAbilityNoStack(player, effectSA); + } + /* (non-Javadoc) * @see forge.game.player.PlayerController#sideboard(forge.deck.Deck) */ @@ -645,6 +650,12 @@ public class PlayerControllerHuman extends PlayerController { return GuiChoose.one(sa.getSourceCard().getName() + " - Choose a result", strResults) == labelsSrc[0]; } + @Override + public Card chooseProtectionShield(GameEntity entityBeingDamaged, List options, Map choiceMap) { + String title = entityBeingDamaged + " - select which prevention shield to use"; + return choiceMap.get(GuiChoose.one(title, options)); + } + @Override public Pair chooseTarget(SpellAbility saSpellskite, List> allTargets) { diff --git a/src/main/java/forge/gui/CardDetailPanel.java b/src/main/java/forge/gui/CardDetailPanel.java index dad8b5305d9..cf957853f34 100644 --- a/src/main/java/forge/gui/CardDetailPanel.java +++ b/src/main/java/forge/gui/CardDetailPanel.java @@ -400,7 +400,7 @@ public class CardDetailPanel extends FPanel { } // Damage Prevention - final int preventNextDamage = card.getPreventNextDamage(); + final int preventNextDamage = card.getPreventNextDamageTotalShields(); if (preventNextDamage > 0) { area.append("\n"); area.append("Prevent the next ").append(preventNextDamage).append(" damage that would be dealt to "); diff --git a/src/main/java/forge/gui/match/views/VPlayers.java b/src/main/java/forge/gui/match/views/VPlayers.java index 3e850be2f3e..000ebf7f7ac 100644 --- a/src/main/java/forge/gui/match/views/VPlayers.java +++ b/src/main/java/forge/gui/match/views/VPlayers.java @@ -152,7 +152,7 @@ public enum VPlayers implements IVDoc { + String.valueOf(p0.getPoisonCounters())); temp[2].setText("Maximum hand size: " + String.valueOf(p0.getMaxHandSize())); temp[3].setText("Cards drawn this turn: " + String.valueOf(p0.getNumDrawnThisTurn())); - temp[4].setText("Damage Prevention: " + String.valueOf(p0.getPreventNextDamage())); + temp[4].setText("Damage Prevention: " + String.valueOf(p0.getPreventNextDamageTotalShields())); if (!p0.getKeywords().isEmpty()) { temp[5].setText(p0.getKeywords().toString()); } else {