From 4cfad0a001ecc7bff661c968a31b3e992e17c0f2 Mon Sep 17 00:00:00 2001 From: Hans Mackowiak Date: Mon, 6 Jun 2022 21:00:48 +0200 Subject: [PATCH 1/8] GameAction: 400.7a to keep Keyword from static ability --- .../src/main/java/forge/game/GameAction.java | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/forge-game/src/main/java/forge/game/GameAction.java b/forge-game/src/main/java/forge/game/GameAction.java index dfda9c27aa6..011c5a2a597 100644 --- a/forge-game/src/main/java/forge/game/GameAction.java +++ b/forge-game/src/main/java/forge/game/GameAction.java @@ -37,6 +37,7 @@ import com.google.common.collect.Maps; import com.google.common.collect.Multimap; import com.google.common.collect.Multimaps; import com.google.common.collect.Sets; +import com.google.common.collect.Table; import forge.GameCommand; import forge.StaticData; @@ -66,6 +67,7 @@ import forge.game.event.GameEventGameStarted; import forge.game.event.GameEventScry; import forge.game.keyword.Keyword; import forge.game.keyword.KeywordInterface; +import forge.game.keyword.KeywordsChange; import forge.game.mulligan.MulliganService; import forge.game.player.GameLossReason; import forge.game.player.Player; @@ -489,6 +491,36 @@ public class GameAction { if (!zoneTo.is(ZoneType.Exile) && !zoneTo.is(ZoneType.Stack)) { c.cleanupExiledWith(); } + + // 400.7a Effects from static abilities that give a permanent spell on the stack an ability + // that allows it to be cast for an alternative cost continue to apply to the permanent that spell becomes. + if (zoneFrom.is(ZoneType.Stack) && toBattlefield) { + List newKw = Lists.newArrayList(); + for (Table.Cell cell : c.getChangedCardKeywords().cellSet()) { + // comes from a static ability + if (cell.getColumnKey() == 0) { + continue; + } + for (KeywordInterface ki : cell.getValue().getKeywords()) { + boolean keepKeyword = false; + for (SpellAbility sa : ki.getAbilities()) { + if (!sa.isSpell()) { + continue; + } + if (sa.getAlternativeCost() != null) { + keepKeyword = true; + break; + } + } + if (keepKeyword) { + newKw.add(ki); + } + } + } + if (!newKw.isEmpty()) { + copied.addChangedCardKeywordsInternal(newKw, null, false, copied.getTimestamp(), 0, true); + } + } } // if an adventureCard is put from Stack somewhere else, need to reset to Original State From 422db8f3199c6a7d2064a21e93869f18ffee1551 Mon Sep 17 00:00:00 2001 From: Hans Mackowiak Date: Mon, 6 Jun 2022 21:43:19 +0200 Subject: [PATCH 2/8] GameActionUtil: check for Card on the Stack --- .../src/main/java/forge/game/GameAction.java | 18 ++ .../main/java/forge/game/GameActionUtil.java | 158 +++++++++++------- .../upcoming/henzie_toolbox_torre.txt | 8 + 3 files changed, 119 insertions(+), 65 deletions(-) create mode 100644 forge-gui/res/cardsfolder/upcoming/henzie_toolbox_torre.txt diff --git a/forge-game/src/main/java/forge/game/GameAction.java b/forge-game/src/main/java/forge/game/GameAction.java index 011c5a2a597..ebd976a33f4 100644 --- a/forge-game/src/main/java/forge/game/GameAction.java +++ b/forge-game/src/main/java/forge/game/GameAction.java @@ -1091,6 +1091,24 @@ public class GameAction { return holdCheckingStaticAbilities; } + // This doesn't check layers or if the ability gets removed by other effects + public boolean hasStaticAbilityAffectingZone(ZoneType zone, StaticAbilityLayer layer) { + for (final Card ca : game.getCardsIn(ZoneType.STATIC_ABILITIES_SOURCE_ZONES)) { + for (final StaticAbility stAb : ca.getStaticAbilities()) { + if (!stAb.getParam("Mode").equals("Continuous") || stAb.isSuppressed() || !stAb.checkConditions()) { + continue; + } + if (layer != null && !stAb.getLayers().contains(layer)) { + continue; + } + if (ZoneType.listValueOf(stAb.getParamOrDefault("AffectedZone", ZoneType.Battlefield.toString())).contains(zone)) { + return true; + } + } + } + return false; + } + public final void checkStaticAbilities() { checkStaticAbilities(true); } diff --git a/forge-game/src/main/java/forge/game/GameActionUtil.java b/forge-game/src/main/java/forge/game/GameActionUtil.java index addc8e311c9..4a53e6fe78f 100644 --- a/forge-game/src/main/java/forge/game/GameActionUtil.java +++ b/forge-game/src/main/java/forge/game/GameActionUtil.java @@ -50,9 +50,11 @@ import forge.game.spellability.OptionalCostValue; import forge.game.spellability.Spell; import forge.game.spellability.SpellAbility; import forge.game.spellability.SpellAbilityRestriction; +import forge.game.staticability.StaticAbilityLayer; import forge.game.trigger.Trigger; import forge.game.trigger.TriggerHandler; import forge.game.trigger.TriggerType; +import forge.game.zone.Zone; import forge.game.zone.ZoneType; import forge.util.Lang; import forge.util.TextUtil; @@ -91,7 +93,11 @@ public final class GameActionUtil { Card source = sa.getHostCard(); final Game game = source.getGame(); - if (sa.isSpell() && !source.isInPlay()) { + if (sa.isSpell() && source.isInPlay()) { + return alternatives; + } + + if (sa.isSpell()) { boolean lkicheck = false; Card newHost = ((Spell)sa).getAlternateHost(source); @@ -177,13 +183,7 @@ public final class GameActionUtil { final String[] k = keyword.split(":"); final Cost disturbCost = new Cost(k[1], true); - SpellAbility newSA; - if (source.getAlternateState().getType().hasSubtype("Aura")) { - newSA = source.getAlternateState().getFirstAbility().copyWithManaCostReplaced(activator, - disturbCost); - } else { - newSA = sa.copyWithManaCostReplaced(activator, disturbCost); - } + SpellAbility newSA = source.getAlternateState().getFirstAbility().copyWithManaCostReplaced(activator, disturbCost); newSA.setActivatingPlayer(activator); newSA.putParam("PrecostDesc", "Disturb —"); @@ -290,6 +290,35 @@ public final class GameActionUtil { foretold.putParam("AfterDescription", "(Foretold)"); alternatives.add(foretold); } + + // some needs to check after ability was put on the stack + // Currently this is only checked for Toolbox and that only cares about creature spells + if (source.isCreature() && game.getAction().hasStaticAbilityAffectingZone(ZoneType.Stack, StaticAbilityLayer.ABILITIES)) { + Zone oldZone = source.getLastKnownZone(); + Card blitzCopy = source; + if (!source.isLKI()) { + blitzCopy = CardUtil.getLKICopy(source); + } + blitzCopy.setLastKnownZone(game.getStackZone()); + lkicheck = true; + + blitzCopy.clearStaticChangedCardKeywords(false); + CardCollection preList = new CardCollection(blitzCopy); + game.getAction().checkStaticAbilities(false, Sets.newHashSet(blitzCopy), preList); + + // currently only for Keyword BLitz, but should affect Dash probably too + for (final KeywordInterface inst : blitzCopy.getKeywords(Keyword.BLITZ)) { + // TODO with mana value 4 or greater has blitz. + for (SpellAbility iSa : inst.getAbilities()) { + // do only non intrinsic + if (!iSa.isIntrinsic()) { + alternatives.add(iSa); + } + } + } + // need to reset to Old Zone, or canPlay would fail + blitzCopy.setLastKnownZone(oldZone); + } } // reset static abilities @@ -300,74 +329,73 @@ public final class GameActionUtil { // need to unfreeze tracker game.getTracker().unfreeze(); } - } + } else { + if (sa.isManaAbility() && sa.isActivatedAbility() && activator.hasKeyword("Piracy") && source.isLand() && source.isInPlay() && !activator.equals(source.getController()) && sa.getPayCosts().hasTapCost()) { + SpellAbility newSA = sa.copy(activator); + // to bypass Activator restriction, set Activator to Player + newSA.getRestrictions().setActivator("Player"); - if (sa.isManaAbility() && sa.isActivatedAbility() && activator.hasKeyword("Piracy") && source.isLand() && source.isInPlay() && !activator.equals(source.getController()) && sa.getPayCosts().hasTapCost()) { - SpellAbility newSA = sa.copy(activator); - // to bypass Activator restriction, set Activator to Player - newSA.getRestrictions().setActivator("Player"); - - // extra Mana restriction to only Spells - for (AbilityManaPart mp : newSA.getAllManaParts()) { - mp.setExtraManaRestriction("Spell"); - } - alternatives.add(newSA); - } - - // below are for some special cases of activated abilities - if (sa.isCycling() && activator.hasKeyword("CyclingForZero")) { - for (final KeywordInterface inst : source.getKeywords()) { - // need to find the correct Keyword from which this Ability is from - if (!inst.getAbilities().contains(sa)) { - continue; + // extra Mana restriction to only Spells + for (AbilityManaPart mp : newSA.getAllManaParts()) { + mp.setExtraManaRestriction("Spell"); } - - // set the cost to this directly to bypass non mana cost - final SpellAbility newSA = sa.copyWithDefinedCost("Discard<1/CARDNAME>"); - newSA.setActivatingPlayer(activator); - newSA.putParam("CostDesc", ManaCostParser.parse("0")); - - // need to build a new Keyword to get better Reminder Text - String data[] = inst.getOriginal().split(":"); - data[1] = "0"; - KeywordInterface newKi = Keyword.getInstance(StringUtils.join(data, ":")); - - // makes new SpellDescription - final StringBuilder sb = new StringBuilder(); - sb.append(newSA.getCostDescription()); - sb.append("(").append(newKi.getReminderText()).append(")"); - newSA.setDescription(sb.toString()); - alternatives.add(newSA); } - } - if (sa.isEquip() && activator.hasKeyword("You may pay 0 rather than pay equip costs.")) { - for (final KeywordInterface inst : source.getKeywords()) { - // need to find the correct Keyword from which this Ability is from - if (!inst.getAbilities().contains(sa)) { - continue; + + // below are for some special cases of activated abilities + if (sa.isCycling() && activator.hasKeyword("CyclingForZero")) { + for (final KeywordInterface inst : source.getKeywords()) { + // need to find the correct Keyword from which this Ability is from + if (!inst.getAbilities().contains(sa)) { + continue; + } + + // set the cost to this directly to bypass non mana cost + final SpellAbility newSA = sa.copyWithDefinedCost("Discard<1/CARDNAME>"); + newSA.setActivatingPlayer(activator); + newSA.putParam("CostDesc", ManaCostParser.parse("0")); + + // need to build a new Keyword to get better Reminder Text + String data[] = inst.getOriginal().split(":"); + data[1] = "0"; + KeywordInterface newKi = Keyword.getInstance(StringUtils.join(data, ":")); + + // makes new SpellDescription + final StringBuilder sb = new StringBuilder(); + sb.append(newSA.getCostDescription()); + sb.append("(").append(newKi.getReminderText()).append(")"); + newSA.setDescription(sb.toString()); + + alternatives.add(newSA); } + } + if (sa.isEquip() && activator.hasKeyword("You may pay 0 rather than pay equip costs.")) { + for (final KeywordInterface inst : source.getKeywords()) { + // need to find the correct Keyword from which this Ability is from + if (!inst.getAbilities().contains(sa)) { + continue; + } - // set the cost to this directly to bypass non mana cost - SpellAbility newSA = sa.copyWithDefinedCost("0"); - newSA.setActivatingPlayer(activator); - newSA.putParam("CostDesc", ManaCostParser.parse("0")); + // set the cost to this directly to bypass non mana cost + SpellAbility newSA = sa.copyWithDefinedCost("0"); + newSA.setActivatingPlayer(activator); + newSA.putParam("CostDesc", ManaCostParser.parse("0")); - // need to build a new Keyword to get better Reminder Text - String data[] = inst.getOriginal().split(":"); - data[1] = "0"; - KeywordInterface newKi = Keyword.getInstance(StringUtils.join(data, ":")); + // need to build a new Keyword to get better Reminder Text + String data[] = inst.getOriginal().split(":"); + data[1] = "0"; + KeywordInterface newKi = Keyword.getInstance(StringUtils.join(data, ":")); - // makes new SpellDescription - final StringBuilder sb = new StringBuilder(); - sb.append(newSA.getCostDescription()); - sb.append("(").append(newKi.getReminderText()).append(")"); - newSA.setDescription(sb.toString()); + // makes new SpellDescription + final StringBuilder sb = new StringBuilder(); + sb.append(newSA.getCostDescription()); + sb.append("(").append(newKi.getReminderText()).append(")"); + newSA.setDescription(sb.toString()); - alternatives.add(newSA); + alternatives.add(newSA); + } } } - return alternatives; } diff --git a/forge-gui/res/cardsfolder/upcoming/henzie_toolbox_torre.txt b/forge-gui/res/cardsfolder/upcoming/henzie_toolbox_torre.txt new file mode 100644 index 00000000000..93b0b647621 --- /dev/null +++ b/forge-gui/res/cardsfolder/upcoming/henzie_toolbox_torre.txt @@ -0,0 +1,8 @@ +Name:Henzie "Toolbox" Torre +ManaCost:B R G +Types: Legendary Creature Devil Rogue +PT:3/3 +S:Mode$ Continuous | Affected$ Creature.YouCtrl | AffectedZone$ Stack | AddKeyword$ Blitz:CardManaCost | Description$ Each creature spell you cast with mana value 4 or greater has blitz. The blitz cost is equal to its mana cost. (You may choose to cast that spell for its blitz cost. If you do, it gains haste and “When this creature dies, draw a card.” Sacrifice it at the beginning of the next end step.) +S:Mode$ ReduceCost | ValidCard$ Card | ValidSpell$ Spell.Blitz | Activator$ You | Amount$ X | Description$ Blitz costs you pay cost {1} less for each time you’ve cast your commander from the command zone this game. +SVar:X:Count$TotalCommanderCastFromCommandZone +Oracle:Each creature spell you cast with mana value 4 or greater has blitz. The blitz cost is equal to its mana cost. (You may choose to cast that spell for its blitz cost. If you do, it gains haste and “When this creature dies, draw a card.” Sacrifice it at the beginning of the next end step.)\nBlitz costs you pay cost {1} less for each time you’ve cast your commander from the command zone this game. From 61d8ca80d9bffc598cee1ef142592fb869de4808 Mon Sep 17 00:00:00 2001 From: Hans Mackowiak Date: Wed, 8 Jun 2022 07:35:25 +0200 Subject: [PATCH 3/8] add LegalAfterStack check --- forge-game/src/main/java/forge/game/ForgeScript.java | 2 +- .../src/main/java/forge/game/card/CardFactoryUtil.java | 4 ++++ .../main/java/forge/game/spellability/SpellAbility.java | 8 ++++++++ .../res/cardsfolder/upcoming/henzie_toolbox_torre.txt | 2 +- .../src/main/java/forge/player/HumanPlaySpellAbility.java | 1 + 5 files changed, 15 insertions(+), 2 deletions(-) diff --git a/forge-game/src/main/java/forge/game/ForgeScript.java b/forge-game/src/main/java/forge/game/ForgeScript.java index faba8b95fa0..3568ca5e9ac 100644 --- a/forge-game/src/main/java/forge/game/ForgeScript.java +++ b/forge-game/src/main/java/forge/game/ForgeScript.java @@ -252,7 +252,7 @@ public class ForgeScript { } else if (property.startsWith("cmc")) { int y = 0; // spell was on the stack - if (sa.getCardState().getCard().isInZone(ZoneType.Stack)) { + if (sa.getHostCard().isInZone(ZoneType.Stack)) { y = sa.getHostCard().getCMC(); } else { y = sa.getPayCosts().getTotalMana().getCMC(); 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 00317836bd9..828a41b8a2c 100644 --- a/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java +++ b/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java @@ -2682,6 +2682,10 @@ public class CardFactoryUtil { final SpellAbility newSA = card.getFirstSpellAbility().copyWithDefinedCost(blitzCost); + if (k.length > 2) { + newSA.getMapParams().put("ValidAfterStack", k[2]); + } + final StringBuilder desc = new StringBuilder(); desc.append("Blitz ").append(blitzCost.toSimpleString()).append(" ("); desc.append(inst.getReminderText()); 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 7940ae92aa0..a19859c37c4 100644 --- a/forge-game/src/main/java/forge/game/spellability/SpellAbility.java +++ b/forge-game/src/main/java/forge/game/spellability/SpellAbility.java @@ -2462,4 +2462,12 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit } return hidden; } + + public boolean isLegalAfterStack() { + if (!matchesValidParam("ValidAfterStack", this)) { + return false; + } + // TODO add checks for Lurrus + return true; + } } diff --git a/forge-gui/res/cardsfolder/upcoming/henzie_toolbox_torre.txt b/forge-gui/res/cardsfolder/upcoming/henzie_toolbox_torre.txt index 93b0b647621..e2c1b516b65 100644 --- a/forge-gui/res/cardsfolder/upcoming/henzie_toolbox_torre.txt +++ b/forge-gui/res/cardsfolder/upcoming/henzie_toolbox_torre.txt @@ -2,7 +2,7 @@ Name:Henzie "Toolbox" Torre ManaCost:B R G Types: Legendary Creature Devil Rogue PT:3/3 -S:Mode$ Continuous | Affected$ Creature.YouCtrl | AffectedZone$ Stack | AddKeyword$ Blitz:CardManaCost | Description$ Each creature spell you cast with mana value 4 or greater has blitz. The blitz cost is equal to its mana cost. (You may choose to cast that spell for its blitz cost. If you do, it gains haste and “When this creature dies, draw a card.” Sacrifice it at the beginning of the next end step.) +S:Mode$ Continuous | Affected$ Creature.YouCtrl | AffectedZone$ Stack | AddKeyword$ Blitz:CardManaCost:Spell.Creature+cmcGE4 | Description$ Each creature spell you cast with mana value 4 or greater has blitz. The blitz cost is equal to its mana cost. (You may choose to cast that spell for its blitz cost. If you do, it gains haste and “When this creature dies, draw a card.” Sacrifice it at the beginning of the next end step.) S:Mode$ ReduceCost | ValidCard$ Card | ValidSpell$ Spell.Blitz | Activator$ You | Amount$ X | Description$ Blitz costs you pay cost {1} less for each time you’ve cast your commander from the command zone this game. SVar:X:Count$TotalCommanderCastFromCommandZone Oracle:Each creature spell you cast with mana value 4 or greater has blitz. The blitz cost is equal to its mana cost. (You may choose to cast that spell for its blitz cost. If you do, it gains haste and “When this creature dies, draw a card.” Sacrifice it at the beginning of the next end step.)\nBlitz costs you pay cost {1} less for each time you’ve cast your commander from the command zone this game. diff --git a/forge-gui/src/main/java/forge/player/HumanPlaySpellAbility.java b/forge-gui/src/main/java/forge/player/HumanPlaySpellAbility.java index f600d640071..6e8ad86c739 100644 --- a/forge-gui/src/main/java/forge/player/HumanPlaySpellAbility.java +++ b/forge-gui/src/main/java/forge/player/HumanPlaySpellAbility.java @@ -159,6 +159,7 @@ public class HumanPlaySpellAbility { && (!mayChooseTargets || ability.setupTargets()) // if you can choose targets, then do choose them. && ability.canCastTiming(human) && ability.checkRestrictions(human) + && ability.isLegalAfterStack() && (isFree || payment.payCost(new HumanCostDecision(controller, human, ability, false, ability.getHostCard()))); if (!prerequisitesMet) { From 9de1dfa4299791c11e3d8a93b0c2baf95c29967c Mon Sep 17 00:00:00 2001 From: Hans Mackowiak Date: Wed, 8 Jun 2022 10:51:23 +0200 Subject: [PATCH 4/8] Update henzie_toolbox_torre.txt --- forge-gui/res/cardsfolder/upcoming/henzie_toolbox_torre.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/forge-gui/res/cardsfolder/upcoming/henzie_toolbox_torre.txt b/forge-gui/res/cardsfolder/upcoming/henzie_toolbox_torre.txt index e2c1b516b65..5555e44129a 100644 --- a/forge-gui/res/cardsfolder/upcoming/henzie_toolbox_torre.txt +++ b/forge-gui/res/cardsfolder/upcoming/henzie_toolbox_torre.txt @@ -2,7 +2,7 @@ Name:Henzie "Toolbox" Torre ManaCost:B R G Types: Legendary Creature Devil Rogue PT:3/3 -S:Mode$ Continuous | Affected$ Creature.YouCtrl | AffectedZone$ Stack | AddKeyword$ Blitz:CardManaCost:Spell.Creature+cmcGE4 | Description$ Each creature spell you cast with mana value 4 or greater has blitz. The blitz cost is equal to its mana cost. (You may choose to cast that spell for its blitz cost. If you do, it gains haste and “When this creature dies, draw a card.” Sacrifice it at the beginning of the next end step.) -S:Mode$ ReduceCost | ValidCard$ Card | ValidSpell$ Spell.Blitz | Activator$ You | Amount$ X | Description$ Blitz costs you pay cost {1} less for each time you’ve cast your commander from the command zone this game. +S:Mode$ Continuous | Affected$ Creature.YouCtrl | AffectedZone$ Stack | AddKeyword$ Blitz:CardManaCost:Spell.Creature+cmcGE4 | Description$ Each creature spell you cast with mana value 4 or greater has blitz. The blitz cost is equal to its mana cost. (You may choose to cast that spell for its blitz cost. If you do, it gains haste and "When this creature dies, draw a card." Sacrifice it at the beginning of the next end step.) +S:Mode$ ReduceCost | ValidCard$ Card | ValidSpell$ Spell.Blitz | Activator$ You | Amount$ X | Description$ Blitz costs you pay cost {1} less for each time you've cast your commander from the command zone this game. SVar:X:Count$TotalCommanderCastFromCommandZone -Oracle:Each creature spell you cast with mana value 4 or greater has blitz. The blitz cost is equal to its mana cost. (You may choose to cast that spell for its blitz cost. If you do, it gains haste and “When this creature dies, draw a card.” Sacrifice it at the beginning of the next end step.)\nBlitz costs you pay cost {1} less for each time you’ve cast your commander from the command zone this game. +Oracle:Each creature spell you cast with mana value 4 or greater has blitz. The blitz cost is equal to its mana cost. (You may choose to cast that spell for its blitz cost. If you do, it gains haste and "When this creature dies, draw a card." Sacrifice it at the beginning of the next end step.)\nBlitz costs you pay cost {1} less for each time you've cast your commander from the command zone this game. From 64eef69aaa132c4295fad51a1775b93ab0ff05f3 Mon Sep 17 00:00:00 2001 From: Hans Mackowiak Date: Wed, 8 Jun 2022 13:54:08 +0200 Subject: [PATCH 5/8] Update GameActionUtil.java better fix for Disturb --- .../main/java/forge/game/GameActionUtil.java | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/forge-game/src/main/java/forge/game/GameActionUtil.java b/forge-game/src/main/java/forge/game/GameActionUtil.java index 4a53e6fe78f..f5c733bb889 100644 --- a/forge-game/src/main/java/forge/game/GameActionUtil.java +++ b/forge-game/src/main/java/forge/game/GameActionUtil.java @@ -42,14 +42,7 @@ import forge.game.player.PlayerController; import forge.game.replacement.ReplacementEffect; import forge.game.replacement.ReplacementHandler; import forge.game.replacement.ReplacementLayer; -import forge.game.spellability.AbilityManaPart; -import forge.game.spellability.AbilitySub; -import forge.game.spellability.AlternativeCost; -import forge.game.spellability.OptionalCost; -import forge.game.spellability.OptionalCostValue; -import forge.game.spellability.Spell; -import forge.game.spellability.SpellAbility; -import forge.game.spellability.SpellAbilityRestriction; +import forge.game.spellability.*; import forge.game.staticability.StaticAbilityLayer; import forge.game.trigger.Trigger; import forge.game.trigger.TriggerHandler; @@ -183,7 +176,13 @@ public final class GameActionUtil { final String[] k = keyword.split(":"); final Cost disturbCost = new Cost(k[1], true); - SpellAbility newSA = source.getAlternateState().getFirstAbility().copyWithManaCostReplaced(activator, disturbCost); + SpellAbility newSA; + if (source.getAlternateState().getType().hasSubtype("Aura")) { + newSA = source.getAlternateState().getFirstAbility().copyWithManaCostReplaced(activator, disturbCost); + } else { + newSA = new SpellPermanent(source); + newSA.setCardState(source.getAlternateState()); + } newSA.setActivatingPlayer(activator); newSA.putParam("PrecostDesc", "Disturb —"); From d46168f51d19879a403853c4a43fe854aa0c4fa4 Mon Sep 17 00:00:00 2001 From: Hans Mackowiak Date: Wed, 8 Jun 2022 13:57:12 +0200 Subject: [PATCH 6/8] Update GameActionUtil.java fix cost --- forge-game/src/main/java/forge/game/GameActionUtil.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/forge-game/src/main/java/forge/game/GameActionUtil.java b/forge-game/src/main/java/forge/game/GameActionUtil.java index f5c733bb889..540b1f3408e 100644 --- a/forge-game/src/main/java/forge/game/GameActionUtil.java +++ b/forge-game/src/main/java/forge/game/GameActionUtil.java @@ -182,6 +182,8 @@ public final class GameActionUtil { } else { newSA = new SpellPermanent(source); newSA.setCardState(source.getAlternateState()); + newSA.setPayCosts(disturbCost); + newSA.setActivatingPlayer(activator); } newSA.setActivatingPlayer(activator); From d63d3202d363337c6e3bc2d1410f9645b628f00a Mon Sep 17 00:00:00 2001 From: Hans Mackowiak Date: Thu, 9 Jun 2022 06:54:26 +0200 Subject: [PATCH 7/8] AI: add flag to check for Blitz restriction --- forge-ai/src/main/java/forge/ai/AiController.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/forge-ai/src/main/java/forge/ai/AiController.java b/forge-ai/src/main/java/forge/ai/AiController.java index 9327e9666eb..fa808d5aa43 100644 --- a/forge-ai/src/main/java/forge/ai/AiController.java +++ b/forge-ai/src/main/java/forge/ai/AiController.java @@ -889,6 +889,10 @@ public class AiController { spellHost.setLastKnownZone(game.getStackZone()); // need to add to stack to make check Restrictions respect stack cmc spellHost.setCastFrom(card.getZone()); } + // TODO maybe other location for this? + if (!sa.isLegalAfterStack()) { + return AiPlayDecision.AnotherTime; + } if (!sa.checkRestrictions(spellHost, player)) { return AiPlayDecision.AnotherTime; } From 5f9a7f4521030cbdd0b8b0c58d50c556b90fd381 Mon Sep 17 00:00:00 2001 From: Hans Mackowiak Date: Thu, 9 Jun 2022 09:32:52 +0200 Subject: [PATCH 8/8] Update GameActionUtil.java --- forge-game/src/main/java/forge/game/GameActionUtil.java | 1 - 1 file changed, 1 deletion(-) diff --git a/forge-game/src/main/java/forge/game/GameActionUtil.java b/forge-game/src/main/java/forge/game/GameActionUtil.java index 540b1f3408e..4711d9fb34f 100644 --- a/forge-game/src/main/java/forge/game/GameActionUtil.java +++ b/forge-game/src/main/java/forge/game/GameActionUtil.java @@ -185,7 +185,6 @@ public final class GameActionUtil { newSA.setPayCosts(disturbCost); newSA.setActivatingPlayer(activator); } - newSA.setActivatingPlayer(activator); newSA.putParam("PrecostDesc", "Disturb —"); newSA.putParam("CostDesc", disturbCost.toString());