From 0c3536a561a947c84cdf922956ba2a6d3ccbb1eb Mon Sep 17 00:00:00 2001 From: Hans Mackowiak Date: Sun, 28 Aug 2022 16:28:55 +0200 Subject: [PATCH] Read ahead Saga Ability (#1413) --- .../src/main/java/forge/ai/GameState.java | 2 +- .../src/main/java/forge/ai/SpecialCardAi.java | 2 +- .../src/main/java/forge/game/ForgeScript.java | 2 ++ .../src/main/java/forge/game/GameEntity.java | 7 +++--- .../forge/game/GameEntityCounterTable.java | 9 +++++++- .../ability/effects/CountersPutEffect.java | 7 +++++- .../src/main/java/forge/game/card/Card.java | 18 ++++++++++++--- .../java/forge/game/card/CardFactoryUtil.java | 22 +++++++++++++++++-- .../main/java/forge/game/player/Player.java | 6 ++++- .../forge/game/spellability/SpellAbility.java | 11 ++++++++++ .../game/trigger/TriggerAbilityTriggered.java | 7 ++++-- .../game/trigger/TriggerCounterAdded.java | 13 +++++++++++ .../forge/game/trigger/WrappedAbility.java | 14 +++++++++++- .../ai/simulation/GameSimulationTest.java | 16 +++++++------- .../cardsfolder/upcoming/historians_boon.txt | 10 +++++++++ .../upcoming/the_elder_dragon_war.txt | 12 ++++++++++ .../forge/player/PlayerControllerHuman.java | 2 +- 17 files changed, 135 insertions(+), 25 deletions(-) create mode 100644 forge-gui/res/cardsfolder/upcoming/historians_boon.txt create mode 100644 forge-gui/res/cardsfolder/upcoming/the_elder_dragon_war.txt diff --git a/forge-ai/src/main/java/forge/ai/GameState.java b/forge-ai/src/main/java/forge/ai/GameState.java index 40c0d4127ec..dc9782dc05c 100644 --- a/forge-ai/src/main/java/forge/ai/GameState.java +++ b/forge-ai/src/main/java/forge/ai/GameState.java @@ -1201,7 +1201,7 @@ public abstract class GameState { String[] allCounterStrings = counterString.split(","); for (final String counterPair : allCounterStrings) { String[] pair = counterPair.split("=", 2); - entity.addCounterInternal(CounterType.getType(pair[0]), Integer.parseInt(pair[1]), null, false, null); + entity.addCounterInternal(CounterType.getType(pair[0]), Integer.parseInt(pair[1]), null, false, null, null); } } diff --git a/forge-ai/src/main/java/forge/ai/SpecialCardAi.java b/forge-ai/src/main/java/forge/ai/SpecialCardAi.java index 476fbde124e..612695cab97 100644 --- a/forge-ai/src/main/java/forge/ai/SpecialCardAi.java +++ b/forge-ai/src/main/java/forge/ai/SpecialCardAi.java @@ -218,7 +218,7 @@ public class SpecialCardAi { Card animated = AnimateAi.becomeAnimated(sa.getHostCard(), sa.getSubAbility()); if (sa.getHostCard().canReceiveCounters(CounterEnumType.P1P1)) { - animated.addCounterInternal(CounterEnumType.P1P1, 2, ai, false, null); + animated.addCounterInternal(CounterEnumType.P1P1, 2, ai, false, null, null); } boolean isOppEOT = ph.is(PhaseType.END_OF_TURN) && ph.getNextTurn() == ai; boolean isValuableAttacker = ph.is(PhaseType.MAIN1, ai) && ComputerUtilCard.doesSpecifiedCreatureAttackAI(ai, animated); diff --git a/forge-game/src/main/java/forge/game/ForgeScript.java b/forge-game/src/main/java/forge/game/ForgeScript.java index 6ac6897e72d..87b2a061002 100644 --- a/forge-game/src/main/java/forge/game/ForgeScript.java +++ b/forge-game/src/main/java/forge/game/ForgeScript.java @@ -223,6 +223,8 @@ public class ForgeScript { return sa.hasParam("Nightbound"); } else if (property.equals("paidPhyrexianMana")) { return sa.getSpendPhyrexianMana(); + } else if (property.equals("LastChapter")) { + return sa.isLastChapter(); } else if (property.startsWith("ManaSpent")) { String[] k = property.split(" ", 2); String comparator = k[1].substring(0, 2); diff --git a/forge-game/src/main/java/forge/game/GameEntity.java b/forge-game/src/main/java/forge/game/GameEntity.java index f7ba73ef30b..dd2cc5c98af 100644 --- a/forge-game/src/main/java/forge/game/GameEntity.java +++ b/forge-game/src/main/java/forge/game/GameEntity.java @@ -26,6 +26,7 @@ import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.Maps; +import forge.game.ability.AbilityKey; import forge.game.ability.AbilityUtils; import forge.game.ability.ApiType; import forge.game.card.Card; @@ -330,9 +331,9 @@ public abstract class GameEntity extends GameObject implements IIdentifiable { subtractCounter(CounterType.get(counterName), n); } - abstract public void addCounterInternal(final CounterType counterType, final int n, final Player source, final boolean fireEvents, GameEntityCounterTable table); - public void addCounterInternal(final CounterEnumType counterType, final int n, final Player source, final boolean fireEvents, GameEntityCounterTable table) { - addCounterInternal(CounterType.get(counterType), n, source, fireEvents, table); + abstract public void addCounterInternal(final CounterType counterType, final int n, final Player source, final boolean fireEvents, GameEntityCounterTable table, Map params); + public void addCounterInternal(final CounterEnumType counterType, final int n, final Player source, final boolean fireEvents, GameEntityCounterTable table, Map params) { + addCounterInternal(CounterType.get(counterType), n, source, fireEvents, table, params); } public void receiveDamage(Pair dmg) { diff --git a/forge-game/src/main/java/forge/game/GameEntityCounterTable.java b/forge-game/src/main/java/forge/game/GameEntityCounterTable.java index ef0db0e993f..c768247e03e 100644 --- a/forge-game/src/main/java/forge/game/GameEntityCounterTable.java +++ b/forge-game/src/main/java/forge/game/GameEntityCounterTable.java @@ -155,11 +155,18 @@ public class GameEntityCounterTable extends ForwardingTable, Ga continue; } + // Add ETB flag + final Map runParams = AbilityKey.newMap(); + runParams.put(AbilityKey.ETB, etb); + if (params != null) { + runParams.putAll(params); + } + // Apply counter after replacement effect for (Map.Entry, Map> e : values.entrySet()) { boolean remember = cause != null && cause.hasParam("RememberPut"); for (Map.Entry ec : e.getValue().entrySet()) { - gm.getKey().addCounterInternal(ec.getKey(), ec.getValue(), e.getKey().orNull(), true, result); + gm.getKey().addCounterInternal(ec.getKey(), ec.getValue(), e.getKey().orNull(), true, result, runParams); if (remember && ec.getValue() >= 1) { cause.getHostCard().addRemembered(gm.getKey()); } diff --git a/forge-game/src/main/java/forge/game/ability/effects/CountersPutEffect.java b/forge-game/src/main/java/forge/game/ability/effects/CountersPutEffect.java index f41b7cbdcd0..2b1d895d981 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/CountersPutEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/CountersPutEffect.java @@ -450,11 +450,12 @@ public class CountersPutEffect extends SpellAbilityEffect { 0); } if (sa.hasParam("UpTo")) { + int min = AbilityUtils.calculateAmount(card, sa.getParamOrDefault("UpToMin", "0"), sa); Map params = Maps.newHashMap(); params.put("Target", obj); params.put("CounterType", counterType); counterAmount = pc.chooseNumber(sa, - Localizer.getInstance().getMessage("lblHowManyCounters"), 0, counterAmount, params); + Localizer.getInstance().getMessage("lblHowManyCounters"), min, counterAmount, params); } if (sa.isDividedAsYouChoose() && !sa.usesTargeting()) { Map params = Maps.newHashMap(); @@ -479,6 +480,10 @@ public class CountersPutEffect extends SpellAbilityEffect { } } + if (sa.hasParam("ReadAhead")) { + gameCard.setReadAhead(counterAmount); + } + if (sa.hasParam("Tribute")) { // make a copy to check if it would be on the battlefield Card noTributeLKI = CardUtil.getLKICopy(gameCard); diff --git a/forge-game/src/main/java/forge/game/card/Card.java b/forge-game/src/main/java/forge/game/card/Card.java index d572e536935..f3323a8b814 100644 --- a/forge-game/src/main/java/forge/game/card/Card.java +++ b/forge-game/src/main/java/forge/game/card/Card.java @@ -338,6 +338,8 @@ public class Card extends GameEntity implements Comparable, IHasSVars { private ReplacementEffect shieldCounterReplaceDestroy = null; private ReplacementEffect stunCounterReplaceUntap = null; + private Integer readAhead = null; + // Enumeration for CMC request types public enum SplitCMCMode { CurrentSideCMC, @@ -1405,7 +1407,7 @@ public class Card extends GameEntity implements Comparable, IHasSVars { } @Override - public void addCounterInternal(final CounterType counterType, final int n, final Player source, final boolean fireEvents, GameEntityCounterTable table) { + public void addCounterInternal(final CounterType counterType, final int n, final Player source, final boolean fireEvents, GameEntityCounterTable table, Map params) { int addAmount = n; if (addAmount <= 0 || !canReceiveCounters(counterType)) { // As per rule 107.1b @@ -1439,6 +1441,9 @@ public class Card extends GameEntity implements Comparable, IHasSVars { final Map runParams = AbilityKey.mapFromCard(this); runParams.put(AbilityKey.Source, source); runParams.put(AbilityKey.CounterType, counterType); + if (params != null) { + runParams.putAll(params); + } for (int i = 0; i < addAmount; i++) { runParams.put(AbilityKey.CounterAmount, oldValue + i + 1); getGame().getTriggerHandler().runTrigger( @@ -2233,7 +2238,7 @@ public class Card extends GameEntity implements Comparable, IHasSVars { || keyword.startsWith("Embalm") || keyword.startsWith("Level up") || keyword.equals("Prowess") || keyword.startsWith("Eternalize") || keyword.startsWith("Reinforce") || keyword.startsWith("Champion") || keyword.startsWith("Prowl") || keyword.startsWith("Adapt") - || keyword.startsWith("Amplify") || keyword.startsWith("Ninjutsu") || keyword.startsWith("Saga") + || keyword.startsWith("Amplify") || keyword.startsWith("Ninjutsu") || keyword.startsWith("Saga") || keyword.startsWith("Read ahead") || keyword.startsWith("Transfigure") || keyword.startsWith("Aura swap") || keyword.startsWith("Cycling") || keyword.startsWith("TypeCycling") || keyword.startsWith("Encore") || keyword.startsWith("Mutate") || keyword.startsWith("Dungeon") @@ -4520,7 +4525,7 @@ public class Card extends GameEntity implements Comparable, IHasSVars { } return result; } - + public final void addKeywordForStaticAbility(KeywordInterface kw) { if (kw.getStaticId() > 0) { storedKeywords.put(kw.getStaticId(), kw.getOriginal(), kw); @@ -7132,4 +7137,11 @@ public class Card extends GameEntity implements Comparable, IHasSVars { } return StaticAbilityIgnoreLegendRule.ignoreLegendRule(this); } + + public Integer getReadAhead() { + return readAhead; + } + public void setReadAhead(int value) { + readAhead = value; + } } diff --git a/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java b/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java index 7434da24508..cb3ef24e228 100644 --- a/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java +++ b/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java @@ -1697,7 +1697,7 @@ public class CardFactoryUtil { parsedTrigger.setOverridingAbility(sa); inst.addTrigger(parsedTrigger); - } else if (keyword.startsWith("Saga")) { + } else if (keyword.startsWith("Saga") || keyword.startsWith("Read ahead")) { final String[] k = keyword.split(":"); final List abs = Arrays.asList(k[2].split(",")); if (abs.size() != Integer.valueOf(k[1])) { @@ -1724,9 +1724,10 @@ public class CardFactoryUtil { for (int i = idx; i <= skipId; i++) { SpellAbility sa = AbilityFactory.getAbility(card, ab); sa.setChapter(i); + sa.setLastChapter(idx == abs.size()); StringBuilder trigStr = new StringBuilder("Mode$ CounterAdded | ValidCard$ Card.Self | TriggerZones$ Battlefield"); - trigStr.append("| CounterType$ LORE | CounterAmount$ EQ").append(i); + trigStr.append("| Chapter$ True | CounterType$ LORE | CounterAmount$ EQ").append(i); if (i != idx) { trigStr.append(" | Secondary$ True"); } @@ -2311,6 +2312,23 @@ public class CardFactoryUtil { re.getOverridingAbility().setSVar("Sunburst", "Count$Converge"); } inst.addReplacement(re); + } else if (keyword.startsWith("Read ahead")) { + final String[] k = keyword.split(":"); + String repeffstr = "Event$ Moved | ValidCard$ Card.Self | Destination$ Battlefield | Secondary$ True | ReplacementResult$ Updated | Description$ Choose a chapter and start with that many lore counters."; + + String effStr = "DB$ PutCounter | Defined$ Self | CounterType$ LORE | ETB$ True | UpTo$ True | UpToMin$ 1 | ReadAhead$ True | CounterNum$ " + k[1]; + + SpellAbility saCounter = AbilityFactory.getAbility(effStr, card); + + if (!intrinsic) { + saCounter.setIntrinsic(false); + } + + ReplacementEffect re = ReplacementHandler.parseReplacement(repeffstr, host, intrinsic, card); + + re.setOverridingAbility(saCounter); + + inst.addReplacement(re); } else if (keyword.equals("Rebound")) { String repeffstr = "Event$ Moved | ValidLKI$ Card.Self+wasCastFromHand+YouOwn+YouCtrl " + " | Origin$ Stack | Destination$ Graveyard | Fizzle$ False " diff --git a/forge-game/src/main/java/forge/game/player/Player.java b/forge-game/src/main/java/forge/game/player/Player.java index 0dc1738b021..4fd95a2c9d8 100644 --- a/forge-game/src/main/java/forge/game/player/Player.java +++ b/forge-game/src/main/java/forge/game/player/Player.java @@ -835,7 +835,8 @@ public class Player extends GameEntity implements Comparable { return true; } - public void addCounterInternal(final CounterType counterType, final int n, final Player source, final boolean fireEvents, GameEntityCounterTable table) { + @Override + public void addCounterInternal(final CounterType counterType, final int n, final Player source, final boolean fireEvents, GameEntityCounterTable table, Map params) { int addAmount = n; if (addAmount <= 0 || !canReceiveCounters(counterType)) { // As per rule 107.1b @@ -849,6 +850,9 @@ public class Player extends GameEntity implements Comparable { final Map runParams = AbilityKey.mapFromPlayer(this); runParams.put(AbilityKey.Source, source); runParams.put(AbilityKey.CounterType, counterType); + if (params != null) { + runParams.putAll(params); + } for (int i = 0; i < addAmount; i++) { runParams.put(AbilityKey.CounterAmount, oldValue + i + 1); getGame().getTriggerHandler().runTrigger(TriggerType.CounterAdded, AbilityKey.newMap(runParams), false); diff --git a/forge-game/src/main/java/forge/game/spellability/SpellAbility.java b/forge-game/src/main/java/forge/game/spellability/SpellAbility.java index da3dd1b2383..9fa31cdd55f 100644 --- a/forge-game/src/main/java/forge/game/spellability/SpellAbility.java +++ b/forge-game/src/main/java/forge/game/spellability/SpellAbility.java @@ -135,6 +135,7 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit private boolean cumulativeupkeep = false; private boolean blessing = false; private Integer chapter = null; + private boolean lastChapter = false; /** The pay costs. */ private Cost payCosts; @@ -1066,6 +1067,13 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit chapter = val; } + public boolean isLastChapter() { + return lastChapter; + } + public boolean setLastChapter(boolean value) { + return lastChapter = value; + } + public StaticAbility getMayPlay() { return mayPlay; } @@ -1129,6 +1137,9 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit clone.paidAbilities = Lists.newArrayList(); clone.setPaidHash(Maps.newHashMap(getPaidHash())); + // copy last chapter flag for Trigger + clone.lastChapter = this.lastChapter; + if (usesTargeting()) { // the targets need to be cloned, otherwise they might be cleared clone.targetChosen = getTargets().clone(); diff --git a/forge-game/src/main/java/forge/game/trigger/TriggerAbilityTriggered.java b/forge-game/src/main/java/forge/game/trigger/TriggerAbilityTriggered.java index 45ea1fa3851..bace9cd7439 100644 --- a/forge-game/src/main/java/forge/game/trigger/TriggerAbilityTriggered.java +++ b/forge-game/src/main/java/forge/game/trigger/TriggerAbilityTriggered.java @@ -19,7 +19,6 @@ package forge.game.trigger; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; -import forge.game.Game; import forge.game.ability.AbilityKey; import forge.game.card.Card; import forge.game.card.CardZoneTable; @@ -54,8 +53,8 @@ public class TriggerAbilityTriggered extends Trigger { return false; } final Card source = spellAbility.getHostCard(); + @SuppressWarnings("unchecked") final Iterable causes = (Iterable) runParams.get(AbilityKey.Cause); - final Game game = source.getGame(); if (hasParam("ValidMode")) { List validModes = Arrays.asList(getParam("ValidMode").split(",")); @@ -73,6 +72,10 @@ public class TriggerAbilityTriggered extends Trigger { } } + if (!matchesValidParam("ValidSpellAbility", spellAbility)) { + return false; + } + if (!matchesValidParam("ValidSource", source)) { return false; } diff --git a/forge-game/src/main/java/forge/game/trigger/TriggerCounterAdded.java b/forge-game/src/main/java/forge/game/trigger/TriggerCounterAdded.java index 3cf55d0f868..bc652bc459b 100644 --- a/forge-game/src/main/java/forge/game/trigger/TriggerCounterAdded.java +++ b/forge-game/src/main/java/forge/game/trigger/TriggerCounterAdded.java @@ -89,6 +89,19 @@ public class TriggerCounterAdded extends Trigger { } } + // TODO check CR for Read Ahead when they are out + // for now assume it only is about etb counter + if (hasParam("Chapter") && runParams.containsKey(AbilityKey.ETB) && true == (boolean)runParams.get(AbilityKey.ETB)) { + Card card = (Card)runParams.get(AbilityKey.Card); + Integer readAhead = card.getReadAhead(); + if (readAhead != null) { + final int actualAmount = (Integer) runParams.get(AbilityKey.CounterAmount); + if (actualAmount < readAhead) { + return false; + } + } + } + return true; } diff --git a/forge-game/src/main/java/forge/game/trigger/WrappedAbility.java b/forge-game/src/main/java/forge/game/trigger/WrappedAbility.java index 5e512dad07b..ac6dbc662c1 100644 --- a/forge-game/src/main/java/forge/game/trigger/WrappedAbility.java +++ b/forge-game/src/main/java/forge/game/trigger/WrappedAbility.java @@ -286,19 +286,30 @@ public class WrappedAbility extends Ability { return sa.isCycling(); } - + @Override public boolean isChapter() { return sa.isChapter(); } + @Override public Integer getChapter() { return sa.getChapter(); } + @Override public void setChapter(int val) { sa.setChapter(val); } + @Override + public boolean isLastChapter() { + return sa.isLastChapter(); + } + @Override + public boolean setLastChapter(boolean value) { + return sa.setLastChapter(value); + } + @Override public boolean isFlashBackAbility() { return sa.isFlashBackAbility(); @@ -566,4 +577,5 @@ public class WrappedAbility extends Ability { public void setChosenList(List choices) { sa.setChosenList(choices); } + } \ No newline at end of file diff --git a/forge-gui-desktop/src/test/java/forge/ai/simulation/GameSimulationTest.java b/forge-gui-desktop/src/test/java/forge/ai/simulation/GameSimulationTest.java index bd4dad3a7f8..3641d7c3254 100644 --- a/forge-gui-desktop/src/test/java/forge/ai/simulation/GameSimulationTest.java +++ b/forge-gui-desktop/src/test/java/forge/ai/simulation/GameSimulationTest.java @@ -230,7 +230,7 @@ public class GameSimulationTest extends SimulationTest { Game game = initAndCreateGame(); Player p = game.getPlayers().get(1); Card sorin = addCard("Sorin, Solemn Visitor", p); - sorin.addCounterInternal(CounterEnumType.LOYALTY, 5, p, false, null); + sorin.addCounterInternal(CounterEnumType.LOYALTY, 5, p, false, null, null); game.getPhaseHandler().devModeSet(PhaseType.MAIN2, p); game.getAction().checkStateEffects(true); @@ -275,7 +275,7 @@ public class GameSimulationTest extends SimulationTest { String bearCardName = "Runeclaw Bear"; addCard(bearCardName, p); Card gideon = addCard("Gideon, Ally of Zendikar", p); - gideon.addCounterInternal(CounterEnumType.LOYALTY, 4, p, false, null); + gideon.addCounterInternal(CounterEnumType.LOYALTY, 4, p, false, null, null); game.getPhaseHandler().devModeSet(PhaseType.MAIN2, p); game.getAction().checkStateEffects(true); @@ -404,7 +404,7 @@ public class GameSimulationTest extends SimulationTest { Game game = initAndCreateGame(); Player p = game.getPlayers().get(1); Card sarkhan = addCard(sarkhanCardName, p); - sarkhan.addCounterInternal(CounterEnumType.LOYALTY, 4, p, false, null); + sarkhan.addCounterInternal(CounterEnumType.LOYALTY, 4, p, false, null, null); game.getPhaseHandler().devModeSet(PhaseType.MAIN2, p); game.getAction().checkStateEffects(true); @@ -439,7 +439,7 @@ public class GameSimulationTest extends SimulationTest { addCard(ornithoperCardName, p); addCard(bearCardName, p); Card ajani = addCard(ajaniCardName, p); - ajani.addCounterInternal(CounterEnumType.LOYALTY, 4, p, false, null); + ajani.addCounterInternal(CounterEnumType.LOYALTY, 4, p, false, null, null); game.getPhaseHandler().devModeSet(PhaseType.MAIN2, p); game.getAction().checkStateEffects(true); @@ -472,7 +472,7 @@ public class GameSimulationTest extends SimulationTest { SpellAbility boltSA = boltCard.getFirstSpellAbility(); Card ajani = addCard(ajaniCardName, p); - ajani.addCounterInternal(CounterEnumType.LOYALTY, 8, p, false, null); + ajani.addCounterInternal(CounterEnumType.LOYALTY, 8, p, false, null, null); game.getPhaseHandler().devModeSet(PhaseType.MAIN2, p); game.getAction().checkStateEffects(true); @@ -523,7 +523,7 @@ public class GameSimulationTest extends SimulationTest { addCard("Swamp", p); addCard("Swamp", p); Card depths = addCard("Dark Depths", p); - depths.addCounterInternal(CounterEnumType.ICE, 10, p, false, null); + depths.addCounterInternal(CounterEnumType.ICE, 10, p, false, null, null); Card thespian = addCard("Thespian's Stage", p); game.getPhaseHandler().devModeSet(PhaseType.MAIN2, p); game.getAction().checkStateEffects(true); @@ -2256,7 +2256,7 @@ public class GameSimulationTest extends SimulationTest { Player p = game.getPlayers().get(0); Card polukranos = addCard(polukranosCardName, p); - polukranos.addCounterInternal(CounterEnumType.P1P1, 6, p, false, null); + polukranos.addCounterInternal(CounterEnumType.P1P1, 6, p, false, null, null); addCard(hydraCardName, p); addCard(leylineCardName, p); for (int i = 0; i < 2; ++i) { @@ -2301,7 +2301,7 @@ public class GameSimulationTest extends SimulationTest { } Card nishoba = addCard(nishobaName, p1); - nishoba.addCounterInternal(CounterEnumType.P1P1, 7, p1, false, null); + nishoba.addCounterInternal(CounterEnumType.P1P1, 7, p1, false, null, null); addCard(capridorName, p1); Card pridemate = addCard(pridemateName, p1); Card indestructibility = addCard(indestructibilityName, p1); diff --git a/forge-gui/res/cardsfolder/upcoming/historians_boon.txt b/forge-gui/res/cardsfolder/upcoming/historians_boon.txt new file mode 100644 index 00000000000..dbf3831cd42 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/historians_boon.txt @@ -0,0 +1,10 @@ +Name:Historian's Boon +ManaCost:3 W +Types:Enchantment +T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self,Enchantment.nonToken+Other+YouCtrl | Execute$ TrigSmallToken | TriggerDescription$ Whenever CARDNAME or another nontoken enchantment enters the battlefield under your control, create a 1/1 white Soldier creature token. +SVar:TrigSmallToken:DB$ Token | TokenAmount$ 1 | TokenScript$ w_1_1_soldier | TokenOwner$ You +T:Mode$ AbilityTriggered | ValidMode$ CounterAdded | ValidSpellAbility$ Triggered.LastChapter | TriggerZones$ Battlefield | Execute$ TrigBigToken | TriggerDescription$ Whenever the final chapter of a Saga you control triggers, create a 4/4 white Angel creature token with flying and vigilance. +SVar:TrigBigToken:DB$ Token | TokenAmount$ 1 | TokenScript$ w_4_4_angel_flying_vigilance | TokenOwner$ You +DeckHas:Ability$Token +DeckHints:Type$Enchantment|Saga +Oracle:Whenever Historian’s Boon or another nontoken enchantment enters the battlefield under your control, create a 1/1 white Soldier creature token.\nWhenever the final chapter of a Saga you control triggers, create a 4/4 white Angel creature token with flying and vigilance. diff --git a/forge-gui/res/cardsfolder/upcoming/the_elder_dragon_war.txt b/forge-gui/res/cardsfolder/upcoming/the_elder_dragon_war.txt new file mode 100644 index 00000000000..be93ceab96a --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/the_elder_dragon_war.txt @@ -0,0 +1,12 @@ +Name:The Elder Dragon War +ManaCost:2 R R +Types:Enchantment Saga +K:Read ahead:3:DBDealDamage,DBDiscard,DBToken +SVar:DBDealDamage:DB$ DamageAll | ValidCards$ Creature | ValidPlayers$ Opponent | NumDmg$ 2 | ValidDescription$ each creature and each opponent. | SpellDescription$ CARDNAME deals 2 damage to each opponent and you gain 2 life. +SVar:DBDiscard:DB$ Discard | AnyNumber$ True | Optional$ True | Mode$ TgtChoose | RememberDiscarded$ True | SubAbility$ DBDraw | StackDescription$ {p:You} discards any number of cards, | SpellDescription$ Discard any number of cards, +SVar:DBDraw:DB$ Draw | Defined$ You | NumCards$ Y | SubAbility$ DBCleanup | StackDescription$ then draws that many cards. | SpellDescription$ then draw that many cards. +SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True +SVar:Y:Count$RememberedSize +SVar:DBToken:DB$ Token | TokenScript$ r_4_4_dragon_flying | SpellDescription$ Create a 4/4 red Dragon creature token with flying. +DeckHas:Ability$Token +Oracle:Read ahead (Choose a chapter and start with that many lore counters. Add one after your draw step. Skipped chapters don’t trigger. Sacrifice after III.)\nI — The Elder Dragon War deals 2 damage to each creature and each opponent.\nII — Discard any number of cards, then draw that many cards.\nIII — Create a 4/4 red Dragon creature token with flying. diff --git a/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java b/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java index 7e0a4b8ee01..2b3d675b671 100644 --- a/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java +++ b/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java @@ -2527,7 +2527,7 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont if (subtract) { card.subtractCounter(counter, count); } else { - card.addCounterInternal(counter, count, card.getController(), false, null); + card.addCounterInternal(counter, count, card.getController(), false, null, null); } }