diff --git a/forge-ai/src/main/java/forge/ai/simulation/SpellAbilityPicker.java b/forge-ai/src/main/java/forge/ai/simulation/SpellAbilityPicker.java index 9cec7654bf2..16717055564 100644 --- a/forge-ai/src/main/java/forge/ai/simulation/SpellAbilityPicker.java +++ b/forge-ai/src/main/java/forge/ai/simulation/SpellAbilityPicker.java @@ -444,7 +444,7 @@ public Card chooseCardToHiddenOriginChangeZone(ZoneType destination, List= 2) { if (interceptor != null) { diff --git a/forge-game/src/main/java/forge/game/CardTraitBase.java b/forge-game/src/main/java/forge/game/CardTraitBase.java index 426e9b32f73..d1f82854e03 100644 --- a/forge-game/src/main/java/forge/game/CardTraitBase.java +++ b/forge-game/src/main/java/forge/game/CardTraitBase.java @@ -63,7 +63,7 @@ public abstract class CardTraitBase extends GameObject implements IHasCardView, * Keys that should not changed */ private static final ImmutableList noChangeKeys = ImmutableList.builder() - .add("TokenScript", "LegacyImage", "TokenImage", "NewName", "ChooseFromList") + .add("TokenScript", "TokenImage", "NewName", "ChooseFromList") .add("AddAbility").build(); /** 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 c88712bad2e..a3c21ab74a1 100644 --- a/forge-game/src/main/java/forge/game/ability/AbilityUtils.java +++ b/forge-game/src/main/java/forge/game/ability/AbilityUtils.java @@ -718,11 +718,7 @@ else if (calcX[0].startsWith("ReplaceCount")) { list = sa.getRootAbility().getPaidList("Sacrificed", true); } else if (calcX[0].startsWith("Discarded")) { - final SpellAbility root = sa.getRootAbility(); - list = root.getPaidList("Discarded", true); - if (null == list && root.isTrigger()) { - list = root.getHostCard().getSpellPermanent().getPaidList("Discarded", true); - } + list = sa.getRootAbility().getPaidList("Discarded", true); } else if (calcX[0].startsWith("Exiled")) { list = sa.getRootAbility().getPaidList("Exiled", true); diff --git a/forge-game/src/main/java/forge/game/ability/effects/CountersPutAllEffect.java b/forge-game/src/main/java/forge/game/ability/effects/CountersPutAllEffect.java index e67ccabea7b..5d8b12f6e5d 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/CountersPutAllEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/CountersPutAllEffect.java @@ -69,7 +69,7 @@ public void resolve(SpellAbility sa) { if (pstr.contains("Controller")) { placerPerCard = true; } else { - placer = AbilityUtils.getDefinedPlayers(host, pstr, sa).get(0); + placer = AbilityUtils.getDefinedPlayers(host, pstr, sa).get(0); } } diff --git a/forge-game/src/main/java/forge/game/cost/Cost.java b/forge-game/src/main/java/forge/game/cost/Cost.java index e22adae7d45..147cfa123df 100644 --- a/forge-game/src/main/java/forge/game/cost/Cost.java +++ b/forge-game/src/main/java/forge/game/cost/Cost.java @@ -938,26 +938,23 @@ public Cost add(Cost cost1) { public Cost add(Cost cost1, boolean mergeAdditional) { return add(cost1, mergeAdditional, null); } - public Cost add(Cost cost1, boolean mergeAdditional, final SpellAbility sa) { - CostPartMana costPart2 = this.getCostMana(); + public Cost add(Cost cost, boolean mergeAdditional, final SpellAbility sa) { + CostPartMana mPartOld = this.getCostMana(); List toRemove = Lists.newArrayList(); - for (final CostPart part : cost1.getCostParts()) { + for (final CostPart part : cost.getCostParts()) { if (part instanceof CostPartMana && ((CostPartMana) part).getMana().isZero()) { continue; // do not add Zero Mana - } else if (part instanceof CostPartMana && costPart2 != null) { + } else if (part instanceof CostPartMana && mPartOld != null) { CostPartMana mPart = (CostPartMana) part; - ManaCostBeingPaid oldManaCost = new ManaCostBeingPaid(mPart.getMana()); - oldManaCost.addManaCost(costPart2.getMana()); - costParts.remove(costPart2); - int xMin = mPart.getXMin(); - if (costPart2.getXMin() > xMin) xMin = costPart2.getXMin(); - if (mPart.isExiledCreatureCost() || mPart.isEnchantedCreatureCost() || xMin > 0) { - // FIXME: something was amiss when trying to add the cost since the mana cost is either \EnchantedCost or \Exiled but the - // restriction no longer marks it as such. Therefore, we need to explicitly copy the ExiledCreatureCost/EnchantedCreatureCost - // to make cards like Merseine or Back from the Brink work. - costParts.add(0, new CostPartMana(oldManaCost.toManaCost(), mPart.isExiledCreatureCost(), mPart.isEnchantedCreatureCost(), xMin)); + ManaCostBeingPaid manaCost = new ManaCostBeingPaid(mPart.getMana()); + costParts.remove(mPartOld); + int xMin = Math.max(mPart.getXMin(), mPartOld.getXMin()); + manaCost.addManaCost(mPartOld.getMana()); + if (mPartOld.isExiledCreatureCost() || mPartOld.isEnchantedCreatureCost() || xMin > 0) { + // need to explicitly copy the ExiledCreatureCost/EnchantedCreatureCost + costParts.add(0, new CostPartMana(manaCost.toManaCost(), mPartOld.isExiledCreatureCost(), mPartOld.isEnchantedCreatureCost(), xMin)); } else { - costParts.add(0, new CostPartMana(oldManaCost.toManaCost(), null)); + costParts.add(0, new CostPartMana(manaCost.toManaCost(), null)); } } else if (part instanceof CostPutCounter || (mergeAdditional && // below usually not desired because they're from different causes (part instanceof CostDiscard || part instanceof CostDraw || diff --git a/forge-game/src/main/java/forge/game/cost/CostAdjustment.java b/forge-game/src/main/java/forge/game/cost/CostAdjustment.java index c0ce1840b51..a3bed20e557 100644 --- a/forge-game/src/main/java/forge/game/cost/CostAdjustment.java +++ b/forge-game/src/main/java/forge/game/cost/CostAdjustment.java @@ -65,7 +65,7 @@ public static Cost adjust(final Cost cost, final SpellAbility sa) { result.add(new Cost(ManaCost.get(n), false)); } } - } // isSpell + } CardCollection cardsOnBattlefield = new CardCollection(game.getCardsIn(ZoneType.Battlefield)); cardsOnBattlefield.addAll(game.getCardsIn(ZoneType.Stack)); @@ -223,7 +223,7 @@ else if (stAb.checkMode("SetCost")) { // need to reduce generic extra because of 2 hybrid mana cost.decreaseGenericMana(sumGeneric); - if (sa.isSpell() && !sa.getPipsToReduce().isEmpty()) { + if (sa.isSpell()) { for (String pip : sa.getPipsToReduce()) { cost.decreaseShard(ManaCostShard.parseNonGeneric(pip), 1); } diff --git a/forge-game/src/main/java/forge/game/cost/CostPartMana.java b/forge-game/src/main/java/forge/game/cost/CostPartMana.java index 0dcf0ede007..49a4275db96 100644 --- a/forge-game/src/main/java/forge/game/cost/CostPartMana.java +++ b/forge-game/src/main/java/forge/game/cost/CostPartMana.java @@ -118,12 +118,22 @@ public T accept(ICostVisitor visitor) { public ManaCost getManaCostFor(SpellAbility sa) { if (isExiledCreatureCost() && sa.getPaidList(CostExile.HashLKIListKey, true) != null && !sa.getPaidList(CostExile.HashLKIListKey, true).isEmpty()) { - // back from the brink - return sa.getPaidList(CostExile.HashLKIListKey, true).get(0).getManaCost(); + ManaCost mod = sa.getPaidList(CostExile.HashLKIListKey, true).get(0).getManaCost(); + if (mod.isNoCost()) { + return mod; + } + ManaCostBeingPaid manaCostNew = new ManaCostBeingPaid(getMana()); + manaCostNew.addManaCost(mod); + return manaCostNew.toManaCost(); } if (isEnchantedCreatureCost() && sa.getHostCard().isEnchantingCard()) { - // TODO human can still activate on TDFC backside - return sa.getHostCard().getEnchantingCard().getManaCost(); + ManaCost mod = sa.getHostCard().getEnchantingCard().getManaCost(); + if (mod.isNoCost()) { + return mod; + } + ManaCostBeingPaid manaCostNew = new ManaCostBeingPaid(getMana()); + manaCostNew.addManaCost(mod); + return manaCostNew.toManaCost(); } if (isCostPayAnyNumberOfTimes) { int timesToPay = AbilityUtils.calculateAmount(sa.getHostCard(), sa.getSVar("NumTimes"), sa); 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 fe5e524a721..38d0568b6d0 100644 --- a/forge-game/src/main/java/forge/game/player/Player.java +++ b/forge-game/src/main/java/forge/game/player/Player.java @@ -1724,7 +1724,7 @@ public final boolean canPlayLand(final Card land, final boolean ignoreZoneAndTim if (!game.getPhaseHandler().isPlayerTurn(this)) { return false; } - if (!canCastSorcery() && !landSa.withFlash(land, this)) { + if (!canCastSorcery() && (landSa == null || !landSa.withFlash(land, this))) { return false; } } diff --git a/forge-game/src/main/java/forge/game/spellability/AbilityActivated.java b/forge-game/src/main/java/forge/game/spellability/AbilityActivated.java index 60112d3784c..1eb494eb31c 100644 --- a/forge-game/src/main/java/forge/game/spellability/AbilityActivated.java +++ b/forge-game/src/main/java/forge/game/spellability/AbilityActivated.java @@ -70,6 +70,11 @@ public AbilityActivated(final Card sourceCard, final Cost abCost, final TargetRe /** {@inheritDoc} */ @Override public boolean canPlay() { + // CR 118.6 cost is unpayable + if (getPayCosts().hasManaCost() && getPayCosts().getCostMana().getManaCostFor(this).isNoCost()) { + return false; + } + Player player = getActivatingPlayer(); if (player == null) { player = this.getHostCard().getController(); diff --git a/forge-game/src/main/java/forge/game/spellability/SpellAbilityCondition.java b/forge-game/src/main/java/forge/game/spellability/SpellAbilityCondition.java index a94c33008bb..5b22d743fa5 100644 --- a/forge-game/src/main/java/forge/game/spellability/SpellAbilityCondition.java +++ b/forge-game/src/main/java/forge/game/spellability/SpellAbilityCondition.java @@ -550,7 +550,6 @@ public final boolean areMet(final SpellAbility sa) { if (!Expressions.compare(svarValue, this.getsVarOperator(), operandValue) && !secondCheck) { return false; } - } return true; diff --git a/forge-gui/res/cardsfolder/b/back_from_the_brink.txt b/forge-gui/res/cardsfolder/b/back_from_the_brink.txt index 5aab0df5a94..f4166e674fb 100644 --- a/forge-gui/res/cardsfolder/b/back_from_the_brink.txt +++ b/forge-gui/res/cardsfolder/b/back_from_the_brink.txt @@ -1,6 +1,6 @@ Name:Back from the Brink ManaCost:4 U U Types:Enchantment -A:AB$ CopyPermanent | Cost$ ExileFromGrave<1/Creature> Mana<20\Exiled> | CostDesc$ Exile a creature card from your graveyard and pay its mana cost: | Defined$ Exiled | SorcerySpeed$ True | SpellDescription$ Create a token that's a copy of that card. Activate only as a sorcery. +A:AB$ CopyPermanent | Cost$ ExileFromGrave<1/Creature> Mana<0\Exiled> | CostDesc$ Exile a creature card from your graveyard and pay its mana cost: | Defined$ Exiled | SorcerySpeed$ True | SpellDescription$ Create a token that's a copy of that card. Activate only as a sorcery. SVar:NonStackingEffect:True Oracle:Exile a creature card from your graveyard and pay its mana cost: Create a token that's a copy of that card. Activate only as a sorcery. diff --git a/forge-gui/res/cardsfolder/c/consecrate_consume.txt b/forge-gui/res/cardsfolder/c/consecrate_consume.txt index f2bde9d26a2..a82aebc40dd 100644 --- a/forge-gui/res/cardsfolder/c/consecrate_consume.txt +++ b/forge-gui/res/cardsfolder/c/consecrate_consume.txt @@ -11,7 +11,7 @@ ALTERNATE Name:Consume ManaCost:2 W B Types:Sorcery -A:SP$ Sacrifice | Cost$ 2 W B | ValidTgts$ Player | SacValid$ Creature.greatestPowerControlledByTargered | SubAbility$ DBGainLife | SacMessage$ the creature with the highest power | RememberSacrificed$ True | SpellDescription$ Target player sacrifices a creature with the greatest power among creatures they control. You gain life equal to its power. +A:SP$ Sacrifice | Cost$ 2 W B | ValidTgts$ Player | SacValid$ Creature.greatestPowerControlledByTargeted | SubAbility$ DBGainLife | SacMessage$ the creature with the highest power | RememberSacrificed$ True | SpellDescription$ Target player sacrifices a creature with the greatest power among creatures they control. You gain life equal to its power. SVar:DBGainLife:DB$ GainLife | Defined$ You | LifeAmount$ X | SubAbility$ DBCleanup SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True SVar:X:RememberedLKI$CardPower diff --git a/forge-gui/res/cardsfolder/l/leafkin_avenger.txt b/forge-gui/res/cardsfolder/l/leafkin_avenger.txt index 465a05d21da..7682740d53b 100644 --- a/forge-gui/res/cardsfolder/l/leafkin_avenger.txt +++ b/forge-gui/res/cardsfolder/l/leafkin_avenger.txt @@ -2,7 +2,7 @@ Name:Leafkin Avenger ManaCost:2 R G Types:Creature Elemental Druid PT:4/3 -A:AB$ Mana | Cost$ T | Produced$ G | Amount$ X | SpellDescription$ Add {G} for each Creature you control with power 4 or greater. +A:AB$ Mana | Cost$ T | Produced$ G | Amount$ X | SpellDescription$ Add {G} for each creature you control with power 4 or greater. SVar:X:Count$Valid Creature.YouCtrl+powerGE4 A:AB$ DealDamage | Cost$ 7 R | ValidTgts$ Player,Planeswalker | TgtPrompt$ Select target player or planeswalker | NumDmg$ Y | SpellDescription$ CARDNAME deals damage equal to its power to target player or planeswalker. SVar:Y:Count$CardPower diff --git a/forge-gui/res/cardsfolder/m/merseine.txt b/forge-gui/res/cardsfolder/m/merseine.txt index 324d61de096..dcd714f23e6 100644 --- a/forge-gui/res/cardsfolder/m/merseine.txt +++ b/forge-gui/res/cardsfolder/m/merseine.txt @@ -5,6 +5,6 @@ K:Enchant creature A:SP$ Attach | Cost$ 2 U U | ValidTgts$ Creature | AILogic$ KeepTapped | AITgts$ Card.cmcGE2 K:etbCounter:NET:3 S:Mode$ Continuous | Affected$ Creature.EnchantedBy | AddHiddenKeyword$ CARDNAME doesn't untap during your untap step. | CheckSVar$ X | Description$ Enchanted creature doesn't untap during its controller's untap step if CARDNAME has a net counter on it. -A:AB$ RemoveCounter | Activator$ Player.EnchantedController | Cost$ Mana<1\EnchantedCost> | CostDesc$ Pay enchanted creature's mana cost: | CounterType$ NET | CounterNum$ 1 | SpellDescription$ Remove a net counter from CARDNAME. Only the controller of the enchanted creature may activate this ability. +A:AB$ RemoveCounter | Activator$ Player.EnchantedController | Cost$ Mana<0\EnchantedCost> | CostDesc$ Pay enchanted creature's mana cost: | CounterType$ NET | CounterNum$ 1 | SpellDescription$ Remove a net counter from CARDNAME. Only the controller of the enchanted creature may activate this ability. SVar:X:Count$CardCounters.NET Oracle:Enchant creature\nMerseine enters the battlefield with three net counters on it.\nEnchanted creature doesn't untap during its controller's untap step if Merseine has a net counter on it.\nPay enchanted creature's mana cost: Remove a net counter from Merseine. Only the controller of the enchanted creature may activate this ability. diff --git a/forge-gui/res/cardsfolder/t/tegwyll_duke_of_splendor.txt b/forge-gui/res/cardsfolder/t/tegwyll_duke_of_splendor.txt index 3cbb3d417ae..bf6216dd69a 100644 --- a/forge-gui/res/cardsfolder/t/tegwyll_duke_of_splendor.txt +++ b/forge-gui/res/cardsfolder/t/tegwyll_duke_of_splendor.txt @@ -1,9 +1,12 @@ -Name:Tegwyll's Scouring -ManaCost:4 B B -Types:Sorcery -K:MayFlashCost:tapXType<3/Creature.withFlying/creatures with flying> -A:SP$ DestroyAll | ValidCards$ Creature | SubAbility$ DBToken | SpellDescription$ Destroy all creatures. Create three 1/1 black Faerie Rogue creature tokens with flying. -SVar:DBToken:DB$ Token | TokenAmount$ 3 | TokenScript$ b_1_1_faerie_rogue_flying | TokenOwner$ You +Name:Tegwyll, Duke of Splendor +ManaCost:1 U B +Types:Legendary Creature Faerie Noble +PT:2/3 +K:Flying +K:Deathtouch +S:Mode$ Continuous | Affected$ Creature.Faerie+Other+YouCtrl | AddPower$ 1 | AddToughness$ 1 | Description$ Other Faerie creatures you control get +1/+1. +T:Mode$ ChangesZone | ValidCard$ Faerie.Other+YouCtrl | Origin$ Battlefield | Destination$ Graveyard | Execute$ TrigDraw | TriggerZones$ Battlefield | TriggerDescription$ Whenever another Faerie you control dies, you draw a card and you lose 1 life. +SVar:TrigDraw:DB$ Draw | Defined$ You | NumCards$ 1 | SubAbility$ DBLoseLife +SVar:DBLoseLife:DB$ LoseLife | Defined$ You | LifeAmount$ 1 DeckHints:Type$Faerie -DeckHas:Ability$Token & Type$Faerie -Oracle:You may cast Tegwyll's Scouring as though it had flash by tapping three untapped creatures you control with flying in addition to paying its other costs.\nDestroy all creatures. Create three 1/1 black Faerie Rogue creature tokens with flying. \ No newline at end of file +Oracle:Flying, deathtouch\nOther Faeries you control get +1/+1.\nWhenever another Faerie you control dies, you draw a card and you lose 1 life. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/t/tegwylls_scouring.txt b/forge-gui/res/cardsfolder/t/tegwylls_scouring.txt index bf6216dd69a..3cbb3d417ae 100644 --- a/forge-gui/res/cardsfolder/t/tegwylls_scouring.txt +++ b/forge-gui/res/cardsfolder/t/tegwylls_scouring.txt @@ -1,12 +1,9 @@ -Name:Tegwyll, Duke of Splendor -ManaCost:1 U B -Types:Legendary Creature Faerie Noble -PT:2/3 -K:Flying -K:Deathtouch -S:Mode$ Continuous | Affected$ Creature.Faerie+Other+YouCtrl | AddPower$ 1 | AddToughness$ 1 | Description$ Other Faerie creatures you control get +1/+1. -T:Mode$ ChangesZone | ValidCard$ Faerie.Other+YouCtrl | Origin$ Battlefield | Destination$ Graveyard | Execute$ TrigDraw | TriggerZones$ Battlefield | TriggerDescription$ Whenever another Faerie you control dies, you draw a card and you lose 1 life. -SVar:TrigDraw:DB$ Draw | Defined$ You | NumCards$ 1 | SubAbility$ DBLoseLife -SVar:DBLoseLife:DB$ LoseLife | Defined$ You | LifeAmount$ 1 +Name:Tegwyll's Scouring +ManaCost:4 B B +Types:Sorcery +K:MayFlashCost:tapXType<3/Creature.withFlying/creatures with flying> +A:SP$ DestroyAll | ValidCards$ Creature | SubAbility$ DBToken | SpellDescription$ Destroy all creatures. Create three 1/1 black Faerie Rogue creature tokens with flying. +SVar:DBToken:DB$ Token | TokenAmount$ 3 | TokenScript$ b_1_1_faerie_rogue_flying | TokenOwner$ You DeckHints:Type$Faerie -Oracle:Flying, deathtouch\nOther Faeries you control get +1/+1.\nWhenever another Faerie you control dies, you draw a card and you lose 1 life. \ No newline at end of file +DeckHas:Ability$Token & Type$Faerie +Oracle:You may cast Tegwyll's Scouring as though it had flash by tapping three untapped creatures you control with flying in addition to paying its other costs.\nDestroy all creatures. Create three 1/1 black Faerie Rogue creature tokens with flying. \ No newline at end of file diff --git a/forge-gui/res/cardsfolder/t/tempt_with_glory.txt b/forge-gui/res/cardsfolder/t/tempt_with_glory.txt index f99ffc1e8bf..aaa9deb21cc 100644 --- a/forge-gui/res/cardsfolder/t/tempt_with_glory.txt +++ b/forge-gui/res/cardsfolder/t/tempt_with_glory.txt @@ -3,7 +3,7 @@ ManaCost:5 W Types:Sorcery A:SP$ PutCounterAll | Cost$ 5 W | ValidCards$ Creature.YouCtrl | CounterType$ P1P1 | CounterNum$ 1 | SubAbility$ DBRepeat | StackDescription$ SpellDescription | SpellDescription$ Tempting offer — Put a +1/+1 counter on each creature you control. Each opponent may put a +1/+1 counter on each creature they control. For each opponent who does, put a +1/+1 counter on each creature you control. SVar:DBRepeat:DB$ RepeatEach | RepeatSubAbility$ DBPutCounter | RepeatPlayers$ Player.Opponent | SubAbility$ PutCounterAgain | RepeatOptionalForEachPlayer$ True | RepeatOptionalMessage$ Do you want to put a +1/+1 counter on each creature you control? -SVar:DBPutCounter:DB$ PutCounterAll | ValidCards$ Creature.RememberedPlayerCtrl | CounterType$ P1P1 | CounterNum$ 1 | SubAbility$ DBCount +SVar:DBPutCounter:DB$ PutCounterAll | ValidCards$ Creature.RememberedPlayerCtrl | Placer$ Controller | CounterType$ P1P1 | CounterNum$ 1 | SubAbility$ DBCount SVar:DBCount:DB$ StoreSVar | SVar$ X | Type$ CountSVar | Expression$ X/Plus.1 SVar:PutCounterAgain:DB$ PutCounterAll | ValidCards$ Creature.YouCtrl | CounterType$ P1P1 | CounterNum$ X | SubAbility$ DBReset | StackDescription$ None SVar:DBReset:DB$ StoreSVar | SVar$ X | Type$ Number | Expression$ 0