From 1b655458e792af1e055879dfe8ca6db9239bfcc7 Mon Sep 17 00:00:00 2001 From: tool4ever Date: Tue, 28 Feb 2023 11:02:54 +0100 Subject: [PATCH] Fix remembering players leading to crash for AI with Shifting Shadow (#2587) Co-authored-by: tool4EvEr --- forge-ai/src/main/java/forge/ai/ComputerUtilCombat.java | 4 ++-- forge-ai/src/main/java/forge/ai/ability/ChangeZoneAi.java | 5 ++--- .../src/main/java/forge/ai/ability/CopyPermanentAi.java | 5 ++--- forge-ai/src/main/java/forge/ai/ability/DigAi.java | 5 ++--- forge-ai/src/main/java/forge/ai/ability/TokenAi.java | 5 ++--- .../src/main/java/forge/game/ability/AbilityUtils.java | 4 ++-- forge-gui/res/cardsfolder/m/mangaras_tome.txt | 4 ++-- forge-gui/res/cardsfolder/p/parallel_thoughts.txt | 4 ++-- forge-gui/res/cardsfolder/s/shifting_shadow.txt | 2 +- .../src/main/java/forge/gui/card/CardScriptParser.java | 2 +- .../src/main/java/forge/player/PlayerControllerHuman.java | 6 +++--- 11 files changed, 21 insertions(+), 25 deletions(-) diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtilCombat.java b/forge-ai/src/main/java/forge/ai/ComputerUtilCombat.java index 62519a46ce7..b43e975147b 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtilCombat.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtilCombat.java @@ -2492,12 +2492,12 @@ public class ComputerUtilCombat { return poison; } - public static GameEntity addAttackerToCombat(SpellAbility sa, Card attacker, FCollection defenders) { + public static GameEntity addAttackerToCombat(SpellAbility sa, Card attacker, Iterable defenders) { Combat combat = sa.getHostCard().getGame().getCombat(); if (combat != null) { // 1. If the card that spawned the attacker was sent at a planeswalker, attack the same. Consider improving. GameEntity def = combat.getDefenderByAttacker(sa.getHostCard()); - if (def instanceof Card && ((Card)def).isPlaneswalker() && defenders.contains(def)) { + if (def instanceof Card && ((Card)def).isPlaneswalker() && Iterables.contains(defenders, def)) { return def; } // 2. Otherwise, go through the list of options one by one, choose the first one that can't be blocked profitably. diff --git a/forge-ai/src/main/java/forge/ai/ability/ChangeZoneAi.java b/forge-ai/src/main/java/forge/ai/ability/ChangeZoneAi.java index 7756ee25776..907d6438d88 100644 --- a/forge-ai/src/main/java/forge/ai/ability/ChangeZoneAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/ChangeZoneAi.java @@ -33,7 +33,6 @@ import forge.game.staticability.StaticAbilityMustTarget; import forge.game.zone.ZoneType; import forge.util.Aggregates; import forge.util.MyRandom; -import forge.util.collect.FCollection; import org.apache.commons.lang3.StringUtils; import java.util.*; @@ -1759,7 +1758,7 @@ public class ChangeZoneAi extends SpellAbilityAi { public Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable options, Map params) { // Called when attaching Aura to player or adding creature to combat if (params != null && params.containsKey("Attacker")) { - return (Player) ComputerUtilCombat.addAttackerToCombat(sa, (Card) params.get("Attacker"), new FCollection(options)); + return (Player) ComputerUtilCombat.addAttackerToCombat(sa, (Card) params.get("Attacker"), options); } return AttachAi.attachToPlayerAIPreferences(ai, sa, true, (List)options); } @@ -1767,7 +1766,7 @@ public class ChangeZoneAi extends SpellAbilityAi { @Override protected GameEntity chooseSinglePlayerOrPlaneswalker(Player ai, SpellAbility sa, Iterable options, Map params) { if (params != null && params.containsKey("Attacker")) { - return ComputerUtilCombat.addAttackerToCombat(sa, (Card) params.get("Attacker"), new FCollection(options)); + return ComputerUtilCombat.addAttackerToCombat(sa, (Card) params.get("Attacker"), options); } // should not be reached return super.chooseSinglePlayerOrPlaneswalker(ai, sa, options, params); diff --git a/forge-ai/src/main/java/forge/ai/ability/CopyPermanentAi.java b/forge-ai/src/main/java/forge/ai/ability/CopyPermanentAi.java index ee1b5284616..53fe0518bcf 100644 --- a/forge-ai/src/main/java/forge/ai/ability/CopyPermanentAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/CopyPermanentAi.java @@ -34,7 +34,6 @@ import forge.game.player.PlayerActionConfirmMode; import forge.game.player.PlayerCollection; import forge.game.spellability.SpellAbility; import forge.game.zone.ZoneType; -import forge.util.collect.FCollection; public class CopyPermanentAi extends SpellAbilityAi { @Override @@ -254,7 +253,7 @@ public class CopyPermanentAi extends SpellAbilityAi { @Override protected Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable options, Map params) { if (params != null && params.containsKey("Attacker")) { - return (Player) ComputerUtilCombat.addAttackerToCombat(sa, (Card) params.get("Attacker"), new FCollection(options)); + return (Player) ComputerUtilCombat.addAttackerToCombat(sa, (Card) params.get("Attacker"), options); } final List cards = new PlayerCollection(options).getCreaturesInPlay(); Card chosen = ComputerUtilCard.getBestCreatureAI(cards); @@ -264,7 +263,7 @@ public class CopyPermanentAi extends SpellAbilityAi { @Override protected GameEntity chooseSinglePlayerOrPlaneswalker(Player ai, SpellAbility sa, Iterable options, Map params) { if (params != null && params.containsKey("Attacker")) { - return ComputerUtilCombat.addAttackerToCombat(sa, (Card) params.get("Attacker"), new FCollection(options)); + return ComputerUtilCombat.addAttackerToCombat(sa, (Card) params.get("Attacker"), options); } // should not be reached return super.chooseSinglePlayerOrPlaneswalker(ai, sa, options, params); diff --git a/forge-ai/src/main/java/forge/ai/ability/DigAi.java b/forge-ai/src/main/java/forge/ai/ability/DigAi.java index 33b8dac06a2..cfd4214d834 100644 --- a/forge-ai/src/main/java/forge/ai/ability/DigAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/DigAi.java @@ -27,7 +27,6 @@ import forge.game.player.PlayerPredicates; import forge.game.spellability.SpellAbility; import forge.game.zone.ZoneType; import forge.util.TextUtil; -import forge.util.collect.FCollection; public class DigAi extends SpellAbilityAi { @@ -190,7 +189,7 @@ public class DigAi extends SpellAbilityAi { @Override public Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable options, Map params) { if (params != null && params.containsKey("Attacker")) { - return (Player) ComputerUtilCombat.addAttackerToCombat(sa, (Card) params.get("Attacker"), new FCollection(options)); + return (Player) ComputerUtilCombat.addAttackerToCombat(sa, (Card) params.get("Attacker"), options); } // an opponent choose a card from return Iterables.getFirst(options, null); @@ -199,7 +198,7 @@ public class DigAi extends SpellAbilityAi { @Override protected GameEntity chooseSinglePlayerOrPlaneswalker(Player ai, SpellAbility sa, Iterable options, Map params) { if (params != null && params.containsKey("Attacker")) { - return ComputerUtilCombat.addAttackerToCombat(sa, (Card) params.get("Attacker"), new FCollection(options)); + return ComputerUtilCombat.addAttackerToCombat(sa, (Card) params.get("Attacker"), options); } // should not be reached return super.chooseSinglePlayerOrPlaneswalker(ai, sa, options, params); diff --git a/forge-ai/src/main/java/forge/ai/ability/TokenAi.java b/forge-ai/src/main/java/forge/ai/ability/TokenAi.java index 45789029150..06a7ce2f7dc 100644 --- a/forge-ai/src/main/java/forge/ai/ability/TokenAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/TokenAi.java @@ -40,7 +40,6 @@ import forge.game.spellability.SpellAbility; import forge.game.spellability.TargetRestrictions; import forge.game.zone.ZoneType; import forge.util.MyRandom; -import forge.util.collect.FCollection; /** *

@@ -321,7 +320,7 @@ public class TokenAi extends SpellAbilityAi { @Override protected Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable options, Map params) { if (params != null && params.containsKey("Attacker")) { - return (Player) ComputerUtilCombat.addAttackerToCombat(sa, (Card) params.get("Attacker"), new FCollection(options)); + return (Player) ComputerUtilCombat.addAttackerToCombat(sa, (Card) params.get("Attacker"), options); } return Iterables.getFirst(options, null); } @@ -332,7 +331,7 @@ public class TokenAi extends SpellAbilityAi { @Override protected GameEntity chooseSinglePlayerOrPlaneswalker(Player ai, SpellAbility sa, Iterable options, Map params) { if (params != null && params.containsKey("Attacker")) { - return ComputerUtilCombat.addAttackerToCombat(sa, (Card) params.get("Attacker"), new FCollection(options)); + return ComputerUtilCombat.addAttackerToCombat(sa, (Card) params.get("Attacker"), options); } // should not be reached return super.chooseSinglePlayerOrPlaneswalker(ai, sa, options, params); 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 9d811b8fa3f..7f2ffe06653 100644 --- a/forge-game/src/main/java/forge/game/ability/AbilityUtils.java +++ b/forge-game/src/main/java/forge/game/ability/AbilityUtils.java @@ -288,12 +288,12 @@ public class AbilityUtils { } else { System.err.println("Warning: couldn't find trigger SA in the chain of SpellAbility " + sa); } - } else if (defined.equals("FirstRemembered")) { + } else if (defined.equals("RememberedFirst")) { Object o = hostCard.getFirstRemembered(); if (o instanceof Card) { cards.add(game.getCardState((Card) o)); } - } else if (defined.equals("LastRemembered")) { + } else if (defined.equals("RememberedLast")) { Object o = Iterables.getLast(hostCard.getRemembered(), null); if (o instanceof Card) { cards.add(game.getCardState((Card) o)); diff --git a/forge-gui/res/cardsfolder/m/mangaras_tome.txt b/forge-gui/res/cardsfolder/m/mangaras_tome.txt index 00445043bc0..8b0f68e8e20 100644 --- a/forge-gui/res/cardsfolder/m/mangaras_tome.txt +++ b/forge-gui/res/cardsfolder/m/mangaras_tome.txt @@ -2,12 +2,12 @@ Name:Mangara's Tome ManaCost:5 Types:Artifact T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigSearch | TriggerDescription$ When CARDNAME enters the battlefield, search your library for five cards, exile them in a face-down pile, and shuffle that pile. Then shuffle your library. -SVar:TrigSearch:DB$ ChangeZone | ChangeNum$ 5 | ChangeType$ Card | Origin$ Library | Destination$ Exile | ShuffleChangedPile$ True | ExileFaceDown$ True | RememberChanged$ True +SVar:TrigSearch:DB$ ChangeZone | ChangeNum$ 5 | Mandatory$ True | ChangeType$ Card | Origin$ Library | Destination$ Exile | ShuffleChangedPile$ True | ExileFaceDown$ True | RememberChanged$ True T:Mode$ ChangesZone | ValidCard$ Card.IsRemembered | Origin$ Exile | Destination$ Any | Static$ True | Execute$ TrigForget SVar:TrigForget:DB$ Pump | ForgetObjects$ TriggeredCard T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Any | ValidCard$ Card.Self | Execute$ TrigReset | Static$ True SVar:TrigReset:DB$ Cleanup | ClearRemembered$ True -A:AB$ Effect | Cost$ 2 | RememberObjects$ FirstRemembered | Triggers$ TrigLeavePlay | ReplacementEffects$ DrawReplace | ImprintCards$ Self | SpellDescription$ The next time you would draw a card this turn, instead put the top card of the exiled pile into its owner's hand. +A:AB$ Effect | Cost$ 2 | RememberObjects$ RememberedFirst | Triggers$ TrigLeavePlay | ReplacementEffects$ DrawReplace | ImprintCards$ Self | SpellDescription$ The next time you would draw a card this turn, instead put the top card of the exiled pile into its owner's hand. SVar:DrawReplace:Event$ Draw | ValidPlayer$ You | ReplaceWith$ RepMangarasTome | Description$ The next time you would draw a card this turn, instead put the top card of the exiled pile into its owner's hand. SVar:TrigLeavePlay:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Any | ValidCard$ Card.IsImprinted | Execute$ ExileEffect | Static$ True SVar:RepMangarasTome:DB$ ChangeZone | Defined$ Remembered | Origin$ Exile | Destination$ Hand | SubAbility$ ExileEffect diff --git a/forge-gui/res/cardsfolder/p/parallel_thoughts.txt b/forge-gui/res/cardsfolder/p/parallel_thoughts.txt index b46e08bff06..d7eaa72047b 100644 --- a/forge-gui/res/cardsfolder/p/parallel_thoughts.txt +++ b/forge-gui/res/cardsfolder/p/parallel_thoughts.txt @@ -2,13 +2,13 @@ Name:Parallel Thoughts ManaCost:3 U U Types:Enchantment T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigSearch | TriggerDescription$ When CARDNAME enters the battlefield, search your library for seven cards, exile them in a face-down pile, and shuffle that pile. Then shuffle your library. -SVar:TrigSearch:DB$ ChangeZone | ChangeNum$ 7 | ChangeType$ Card | Origin$ Library | Destination$ Exile | ShuffleChangedPile$ True | ExileFaceDown$ True | RememberChanged$ True +SVar:TrigSearch:DB$ ChangeZone | ChangeNum$ 7 | ChangeType$ Card | Mandatory$ True | Origin$ Library | Destination$ Exile | ShuffleChangedPile$ True | ExileFaceDown$ True | RememberChanged$ True T:Mode$ ChangesZone | ValidCard$ Card.IsRemembered | Origin$ Exile | Destination$ Any | Static$ True | Execute$ TrigForget SVar:TrigForget:DB$ Pump | ForgetObjects$ TriggeredCard T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Any | ValidCard$ Card.Self | Execute$ TrigReset | Static$ True SVar:TrigReset:DB$ Cleanup | ClearRemembered$ True R:Event$ Draw | ActiveZones$ Battlefield | ValidPlayer$ You | ReplaceWith$ RepParallelThoughts | Optional$ True | OptionalDecider$ You | Description$ If you would draw a card, you may instead put the top card of the pile you exiled into your hand. -SVar:RepParallelThoughts:DB$ ChangeZone | Defined$ FirstRemembered | Origin$ Exile | Destination$ Hand +SVar:RepParallelThoughts:DB$ ChangeZone | Defined$ RememberedFirst | Origin$ Exile | Destination$ Hand AI:RemoveDeck:All AI:RemoveDeck:Random Oracle:When Parallel Thoughts enters the battlefield, search your library for seven cards, exile them in a face-down pile, and shuffle that pile. Then shuffle your library.\nIf you would draw a card, you may instead put the top card of the pile you exiled into your hand. diff --git a/forge-gui/res/cardsfolder/s/shifting_shadow.txt b/forge-gui/res/cardsfolder/s/shifting_shadow.txt index 747e145eef9..a157a0a2839 100644 --- a/forge-gui/res/cardsfolder/s/shifting_shadow.txt +++ b/forge-gui/res/cardsfolder/s/shifting_shadow.txt @@ -10,6 +10,6 @@ SVar:ShadowUpkeepTrig:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | TriggerZo SVar:ShadowRememberSelf:DB$ Pump | ImprintCards$ OriginalHost | SubAbility$ ShadowDestroyEnchanted SVar:ShadowDestroyEnchanted:DB$ Destroy | Defined$ Self | SubAbility$ ShadowRevealCards SVar:ShadowRevealCards:DB$ DigUntil | Valid$ Creature | ValidDescription$ creature | FoundDestination$ Battlefield | RevealedDestination$ Library | RevealedLibraryPosition$ -1 | RevealRandomOrder$ True | RememberFound$ True | SubAbility$ ShadowReattach -SVar:ShadowReattach:DB$ Attach | Defined$ LastRemembered | Object$ Imprinted | SubAbility$ DBCleanup +SVar:ShadowReattach:DB$ Attach | Defined$ RememberedLast | Object$ Imprinted | SubAbility$ DBCleanup SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True | ClearImprinted$ True Oracle:Enchant creature\nEnchanted creature has haste and "At the beginning of your upkeep, destroy this creature. Reveal cards from the top of your library until you reveal a creature card. Put that card onto the battlefield and attach Shifting Shadow to it, then put all other cards revealed this way on the bottom of your library in a random order." diff --git a/forge-gui/src/main/java/forge/gui/card/CardScriptParser.java b/forge-gui/src/main/java/forge/gui/card/CardScriptParser.java index 5370e3a8be6..f2e94dfcdfc 100644 --- a/forge-gui/src/main/java/forge/gui/card/CardScriptParser.java +++ b/forge-gui/src/main/java/forge/gui/card/CardScriptParser.java @@ -359,7 +359,7 @@ public final class CardScriptParser { "Self", "OriginalHost", "EffectSource", "Equipped", "Enchanted", "TopOfLibrary", "BottomOfLibrary", "Targeted", "ThisTargetedCard", "ParentTarget", "Remembered", "DirectRemembered", - "DelayTriggerRemembered", "FirstRemembered", "Clones", "Imprinted", + "DelayTriggerRemembered", "RememberedFirst", "Clones", "Imprinted", "ChosenCard", "SacrificedCards", "Sacrificed", "DiscardedCards", "Discarded", "ExiledCards", "Exiled", "TappedCards", "Tapped", "UntappedCards", "Untapped", "Parent", "SourceFirstSpell"); diff --git a/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java b/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java index a3b840fb3c3..0389e02ccaf 100644 --- a/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java +++ b/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java @@ -743,14 +743,14 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont Card show = null; Object o = null; switch (sa.getParam("ShowCardInPrompt")) { - case "FirstRemembered": + case "RememberedFirst": o = sa.getHostCard().getFirstRemembered(); if (o instanceof Card) { show = (Card) o; } break; - case "LastRemembered": - o = sa.getHostCard().getFirstRemembered(); + case "RememberedLast": + o = Iterables.getLast(sa.getHostCard().getRemembered(), null); if (o instanceof Card) { show = (Card) o; }