diff --git a/forge-game/src/main/java/forge/game/CardTraitBase.java b/forge-game/src/main/java/forge/game/CardTraitBase.java index 65cce4f0519..20be553599c 100644 --- a/forge-game/src/main/java/forge/game/CardTraitBase.java +++ b/forge-game/src/main/java/forge/game/CardTraitBase.java @@ -258,7 +258,7 @@ public abstract class CardTraitBase extends GameObject implements IHasCardView, // intervening if check, make sure to use right controller if (game.getStack().isResolving(getHostCard())) { - SpellAbility sa = game.getStack().peekAbility(); + SpellAbility sa = game.getStack().peek().getSpellAbility(false); if (sa.isTrigger()) { hostController = sa.getActivatingPlayer(); } diff --git a/forge-game/src/main/java/forge/game/ability/AbilityUtils.java b/forge-game/src/main/java/forge/game/ability/AbilityUtils.java index 06c5ec3691d..66446269598 100644 --- a/forge-game/src/main/java/forge/game/ability/AbilityUtils.java +++ b/forge-game/src/main/java/forge/game/ability/AbilityUtils.java @@ -1723,12 +1723,12 @@ public class AbilityUtils { && ZoneType.Battlefield.name().equals(t.getParam("Destination"))) { return doXMath(c.getXManaCostPaid(), expr, c, ctb); } else if (TriggerType.SpellCast.equals(t.getMode())) { - // Cast Trigger like Hydroid Krasis - SpellAbility castSA = (SpellAbility) root.getTriggeringObject(AbilityKey.SpellAbility); - if (castSA == null || castSA.getXManaCostPaid() == null) { + // Cast Trigger like Hydroid Krasis, use SI because Unbound Flourishing might change X + SpellAbilityStackInstance castSI = (SpellAbilityStackInstance) root.getTriggeringObject(AbilityKey.StackInstance); + if (castSI == null) { return doXMath(0, expr, c, ctb); } - return doXMath(castSA.getXManaCostPaid(), expr, c, ctb); + return doXMath(castSI.getXManaPaid(), expr, c, ctb); } else if (TriggerType.Cycled.equals(t.getMode())) { SpellAbility cycleSA = (SpellAbility) sa.getTriggeringObject(AbilityKey.Cause); if (cycleSA == null || cycleSA.getXManaCostPaid() == null) { diff --git a/forge-game/src/main/java/forge/game/ability/effects/ChangeXEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ChangeXEffect.java index 1c50edcc7e5..9ba46ba079d 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ChangeXEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ChangeXEffect.java @@ -25,7 +25,7 @@ public class ChangeXEffect extends SpellAbilityEffect { for (final SpellAbility tgtSA : sas) { // for Unbound Flourishing, can't go over SpellAbilityStackInstances because the x is in cast SA copy SpellAbility castSA = tgtSA.getHostCard().getCastSA(); - if (castSA != null && tgtSA.equals(castSA)) { + if (castSA != null && tgtSA.equals(castSA) && castSA.getXManaCostPaid() != null) { castSA.setXManaCostPaid(castSA.getXManaCostPaid() * 2); } // fall back to other potential cards diff --git a/forge-game/src/main/java/forge/game/card/CardUtil.java b/forge-game/src/main/java/forge/game/card/CardUtil.java index 77abf9b1f43..701eeefe278 100644 --- a/forge-game/src/main/java/forge/game/card/CardUtil.java +++ b/forge-game/src/main/java/forge/game/card/CardUtil.java @@ -269,6 +269,7 @@ public final class CardUtil { } newCopy.addRemembered(in.getRemembered()); newCopy.addImprintedCards(in.getImprintedCards()); + newCopy.setChosenCards(new CardCollection(in.getChosenCards())); for (Table.Cell cl : in.getEtbCounters()) { newCopy.addEtbCounter(cl.getColumnKey(), cl.getValue(), cl.getRowKey()); diff --git a/forge-game/src/main/java/forge/game/spellability/SpellAbilityStackInstance.java b/forge-game/src/main/java/forge/game/spellability/SpellAbilityStackInstance.java index c3920199e4c..297f541acf1 100644 --- a/forge-game/src/main/java/forge/game/spellability/SpellAbilityStackInstance.java +++ b/forge-game/src/main/java/forge/game/spellability/SpellAbilityStackInstance.java @@ -212,7 +212,7 @@ public class SpellAbilityStackInstance implements IIdentifiable, IHasCardView { } public final int getXManaPaid() { - return xManaPaid; + return xManaPaid == null ? 0 : xManaPaid; } public final void setXManaPaid(int x) { xManaPaid = x; diff --git a/forge-game/src/main/java/forge/game/trigger/TriggerSpellAbilityCastOrCopy.java b/forge-game/src/main/java/forge/game/trigger/TriggerSpellAbilityCastOrCopy.java index 7c2a7c85189..d4fcda3b19d 100644 --- a/forge-game/src/main/java/forge/game/trigger/TriggerSpellAbilityCastOrCopy.java +++ b/forge-game/src/main/java/forge/game/trigger/TriggerSpellAbilityCastOrCopy.java @@ -192,11 +192,13 @@ public class TriggerSpellAbilityCastOrCopy extends Trigger { } if (hasParam("HasXManaCost")) { - final Cost cost = (Cost) (runParams.get(AbilityKey.Cost)); - if (cost.hasNoManaCost()) { - return false; + final int numX; + if (spellAbility.isActivatedAbility()) { + numX = spellAbility.getPayCosts().hasManaCost() ? spellAbility.getPayCosts().getCostMana().getAmountOfX() : 0; + } else { + numX = cast.getManaCost().countX(); } - if (cost.getCostMana().getAmountOfX() <= 0) { + if (numX == 0) { return false; } } diff --git a/forge-gui/res/cardsfolder/a/azor_the_lawbringer.txt b/forge-gui/res/cardsfolder/a/azor_the_lawbringer.txt index 5cb8e8f4cd0..6906a26e037 100644 --- a/forge-gui/res/cardsfolder/a/azor_the_lawbringer.txt +++ b/forge-gui/res/cardsfolder/a/azor_the_lawbringer.txt @@ -7,7 +7,7 @@ T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.S SVar:TrigChange:DB$ RepeatEach | RepeatPlayers$ Player.Opponent | NextTurnForEachPlayer$ True | RepeatSubAbility$ DBEffect | SpellDescription$ Each opponent can't cast instant or sorcery spells during that player's next turn. SVar:DBEffect:DB$ Effect | Name$ Azor, the Lawbringer's Effect | StaticAbilities$ STCantBeCast | EffectOwner$ Remembered SVar:STCantBeCast:Mode$ CantBeCast | ValidCard$ Instant,Sorcery | Caster$ You | EffectZone$ Command | Description$ You can't cast instant or sorcery spells. -T:Mode$ Attacks | ValidCard$ Card.Self | Execute$ TrigDraw | TriggerDescription$ Whenever Azor attacks, you may pay {X}{W}{U}{U}. If you do, you gain X life and draw X cards. +T:Mode$ Attacks | ValidCard$ Card.Self | Execute$ TrigDraw | TriggerDescription$ Whenever NICKNAME attacks, you may pay {X}{W}{U}{U}. If you do, you gain X life and draw X cards. SVar:TrigDraw:AB$ GainLife | Cost$ X W U U | Defined$ You | LifeAmount$ X | SubAbility$ DBDraw | SpellDescription$ You gain X life and draw X cards. SVar:DBDraw:DB$ Draw | NumCards$ X SVar:X:Count$xPaid diff --git a/forge-gui/res/cardsfolder/d/dimensional_breach.txt b/forge-gui/res/cardsfolder/d/dimensional_breach.txt index 52d12c030f3..3e2a990237a 100644 --- a/forge-gui/res/cardsfolder/d/dimensional_breach.txt +++ b/forge-gui/res/cardsfolder/d/dimensional_breach.txt @@ -4,7 +4,7 @@ Types:Sorcery A:SP$ ChangeZoneAll | Cost$ 5 W W | ChangeType$ Permanent | Origin$ Battlefield | Destination$ Exile | RememberChanged$ True | SubAbility$ DBEffect | SpellDescription$ Exile all permanents. For as long as any of those cards remain exiled, at the beginning of each player's upkeep, that player returns one of the exiled cards they own to the battlefield. SVar:DBEffect:DB$ Effect | Triggers$ TrigUpkeep | RememberObjects$ Remembered | Duration$ Permanent | ForgetOnMoved$ Exile | SubAbility$ DBCleanup SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True -SVar:TrigUpkeep:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ Player | Execute$ BreachReturn | TriggerZones$ Command | TriggerController$ TriggeredPlayer | CheckSVar$ BreachX | SVarCompare$ GE1 | TriggerDescription$ At the beginning of each player's upkeep, that player returns one of the exiled cards they own to the battlefield. +SVar:TrigUpkeep:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ Player | Execute$ BreachReturn | TriggerZones$ Command | TriggerController$ TriggeredPlayer | TriggerDescription$ At the beginning of each player's upkeep, that player returns one of the exiled cards they own to the battlefield. SVar:BreachReturn:DB$ ChooseCard | Defined$ TriggeredPlayer | Amount$ 1 | Mandatory$ True | ChoiceTitle$ Choose a card to return to the battlefield | Choices$ Card.IsRemembered+ActivePlayerCtrl | ChoiceZone$ Exile | SubAbility$ MoveChosen SVar:MoveChosen:DB$ ChangeZone | Origin$ Exile | Destination$ Battlefield | Defined$ ChosenCard | ForgetChanged$ True Oracle:Exile all permanents. For as long as any of those cards remain exiled, at the beginning of each player's upkeep, that player returns one of the exiled cards they own to the battlefield. diff --git a/forge-gui/res/cardsfolder/t/titans_nest.txt b/forge-gui/res/cardsfolder/t/titans_nest.txt index a6262bc1600..ef111c90629 100644 --- a/forge-gui/res/cardsfolder/t/titans_nest.txt +++ b/forge-gui/res/cardsfolder/t/titans_nest.txt @@ -3,6 +3,6 @@ ManaCost:1 B G U Types:Enchantment T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigDig | TriggerDescription$ At the beginning of your upkeep, look at the top card of your library. You may put that card into your graveyard. SVar:TrigDig:DB$ Dig | DigNum$ 1 | ChangeNum$ 1 | DestinationZone$ Graveyard | Optional$ True | LibraryPosition2$ 0 -A:AB$ Mana | Cost$ ExileFromGrave<1/Card> | Produced$ C | RestrictValid$ Spell.nonColorless+withoutXCost | SpellDescription$ Add {C}. Spend this mana only to cast a spell that's one or more colors without {X} in its mana cost. +A:AB$ Mana | Cost$ ExileFromGrave<1/Card> | Produced$ C | RestrictValid$ Spell.nonColorless+!hasXCost | SpellDescription$ Add {C}. Spend this mana only to cast a spell that's one or more colors without {X} in its mana cost. AI:RemoveDeck:All Oracle:At the beginning of your upkeep, look at the top card of your library. You may put that card into your graveyard.\nExile a card from your graveyard: Add {C}. Spend this mana only to cast a spell that's one or more colors without {X} in its mana cost. diff --git a/forge-gui/res/cardsfolder/upcoming/celestine_the_living_saint.txt b/forge-gui/res/cardsfolder/upcoming/celestine_the_living_saint.txt index 9ee5860d892..e7198fe7a3b 100644 --- a/forge-gui/res/cardsfolder/upcoming/celestine_the_living_saint.txt +++ b/forge-gui/res/cardsfolder/upcoming/celestine_the_living_saint.txt @@ -4,7 +4,7 @@ Types:Legendary Creature Human Warrior PT:3/4 K:Flying K:Lifelink -T:Mode$ Phase | Phase$ End of Turn | ValidPlayer$ You | Execute$ TrigReturn | TriggerDescription$ Healing Tears — At the beginning of your end step, return target creature card with mana value X or less from your graveyard to the battlefield, where X is the amount of life you gained this turn. +T:Mode$ Phase | Phase$ End of Turn | ValidPlayer$ You | Execute$ TrigReturn | TriggerZones$ Battlefield | TriggerDescription$ Healing Tears — At the beginning of your end step, return target creature card with mana value X or less from your graveyard to the battlefield, where X is the amount of life you gained this turn. SVar:TrigReturn:DB$ ChangeZone | ValidTgts$ Creature.cmcLEX+YouOwn | TgtPrompt$ Select target creature card with mana value X or less | Origin$ Graveyard | Destination$ Battlefield SVar:X:Count$LifeYouGainedThisTurn DeckHas:Ability$LifeGain|Graveyard diff --git a/forge-gui/res/cardsfolder/upcoming/epistolary_librarian.txt b/forge-gui/res/cardsfolder/upcoming/epistolary_librarian.txt index 844d47222f4..b8dfdd1e8d2 100644 --- a/forge-gui/res/cardsfolder/upcoming/epistolary_librarian.txt +++ b/forge-gui/res/cardsfolder/upcoming/epistolary_librarian.txt @@ -3,7 +3,7 @@ ManaCost:2 W U Types:Creature Astartes Wizard PT:3/4 T:Mode$ Attacks | ValidCard$ Card.Self | Execute$ TrigCast | TriggerDescription$ Veil of Time — Whenever CARDNAME attacks, you may cast a spell with mana value X or less from your hand without paying its mana cost, where X is the number of attacking creatures. -SVar:TrigCast:DB$ Play | ValidZone$ Hand | Valid$ Card.cmcLEX+YouOwn | ValidSA$ Spell | Optional$ True | WithoutManaCost$ True | -SVar:Z:Count$Valid Creature.attacking+YouCtrl +SVar:TrigCast:DB$ Play | ValidZone$ Hand | Valid$ Card.cmcLEX+YouOwn | ValidSA$ Spell | Optional$ True | WithoutManaCost$ True +SVar:X:Count$Valid Creature.attacking+YouCtrl SVar:HasAttackEffect:TRUE Oracle:Veil of Time — Whenever Epistolary Librarian attacks, you may cast a spell with mana value X or less from your hand without paying its mana cost, where X is the number of attacking creatures. diff --git a/forge-gui/res/cardsfolder/upcoming/tyrannical_pitlord.txt b/forge-gui/res/cardsfolder/upcoming/tyrannical_pitlord.txt index eaf43137c45..0f6831fefb2 100644 --- a/forge-gui/res/cardsfolder/upcoming/tyrannical_pitlord.txt +++ b/forge-gui/res/cardsfolder/upcoming/tyrannical_pitlord.txt @@ -5,10 +5,9 @@ PT:6/6 K:Flying K:Trample K:ETBReplacement:Other:DBChoose -SVar:DBChoose:DB$ ChooseCard | Choices$ Creature.Other+YouCtrl |TgtPrompt$ Select another target creature you control | Description$ As CARDNAME enters the battlefield, choose another creature you control. +SVar:DBChoose:DB$ ChooseCard | Choices$ Creature.Other+YouCtrl | Mandatory$ True | SpellDescription$ As CARDNAME enters the battlefield, choose another creature you control. S:Mode$ Continuous | Affected$ Card.ChosenCardStrict | AddKeyword$ Flying | AddToughness$ 3 | AddPower$ 3 | SpellDescription$ The chosen creature gets +3/+3 and has flying. -T:Mode$ ChangesZone | ValidCard$ Card.Self | Origin$ Battlefield | Destination$ Any | Execute$ TrigSacrifice | SubAbility$ DBCleanup | SpellDescription$ When CARDNAME leaves the battlefield, sacrifice the chosen creature. -SVar:TrigSacrifice:DB$ SacrificeAll | Defined$ ChosenCard -SVar:DBCleanup:DB$ Cleanup | ClearChosen$ True +T:Mode$ ChangesZone | ValidCard$ Card.Self | Origin$ Battlefield | Destination$ Any | Execute$ TrigSacrifice | SubAbility$ TrigSacrifice | TriggerDescription$ When CARDNAME leaves the battlefield, sacrifice the chosen creature. +SVar:TrigSacrifice:DB$ SacrificeAll | ValidCards$ Card.ChosenCardStrict DeckHas:Ability$Sacrifice Oracle:Flying, trample\nAs Tyrannical Pitlord enters the battlefield, choose another creature you control.\nThe chosen creature gets +3/+3 and has flying.\nWhen Tyrannical Pitlord leaves the battlefield, sacrifice the chosen creature.