From f8761d234932e8bbe3e52783de319166dacae06b Mon Sep 17 00:00:00 2001 From: tool4ever Date: Sat, 4 Feb 2023 05:43:12 +0100 Subject: [PATCH] Card fixes (#2389) * Fix AI check * Fix cards * Clean up --------- Co-authored-by: tool4EvEr --- forge-ai/src/main/java/forge/ai/ComputerUtil.java | 10 ++++++++++ .../main/java/forge/ai/ability/ChangeZoneAi.java | 8 +++++--- .../src/main/java/forge/ai/ability/ManifestAi.java | 13 +------------ .../java/forge/game/player/PlayerProperty.java | 14 ++++++++++++++ forge-gui/res/cardsfolder/p/phytotitan.txt | 2 +- forge-gui/res/cardsfolder/p/plague_reaver.txt | 2 +- .../cardsfolder/u/uro_titan_of_natures_wrath.txt | 6 +++--- 7 files changed, 35 insertions(+), 20 deletions(-) diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtil.java b/forge-ai/src/main/java/forge/ai/ComputerUtil.java index c5c5720241d..19aa1bd8a4d 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtil.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtil.java @@ -3161,4 +3161,14 @@ public class ComputerUtil { return remainingLife; } + public static boolean isETBprevented(Card c) { + final Map repParams = AbilityKey.mapFromAffected(c); + // don't need to bother with real LKI since this is a passive check and the card isn't going anywhere + repParams.put(AbilityKey.CardLKI, c); + repParams.put(AbilityKey.Origin, c.getZone().getZoneType()); + repParams.put(AbilityKey.Destination, ZoneType.Battlefield); + List list = c.getGame().getReplacementHandler().getReplacementList(ReplacementType.Moved, repParams, ReplacementLayer.CantHappen); + return !list.isEmpty(); + } + } 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 c02636e2df3..ab4c7003e65 100644 --- a/forge-ai/src/main/java/forge/ai/ability/ChangeZoneAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/ChangeZoneAi.java @@ -673,10 +673,8 @@ public class ChangeZoneAi extends SpellAbilityAi { return false; } - // if (origin.equals("Graveyard")) { // return this card from graveyard: cards like Hammer of Bogardan - // in general this is cool, but we should add some type of - // restrictions + // in general this is cool, but we should add some type of restrictions // return this card from battlefield: cards like Blinking Spirit // in general this should only be used to protect from Imminent Harm @@ -713,6 +711,10 @@ public class ChangeZoneAi extends SpellAbilityAi { } if (destination == ZoneType.Battlefield) { + if (ComputerUtil.isETBprevented(retrieval.get(0))) { + return false; + } + // predict whether something may put a ETBing creature below zero toughness // (e.g. Reassembing Skeleton + Elesh Norn, Grand Cenobite) for (final Card c : retrieval) { diff --git a/forge-ai/src/main/java/forge/ai/ability/ManifestAi.java b/forge-ai/src/main/java/forge/ai/ability/ManifestAi.java index 891fabb44ba..c53f7ab8c7c 100644 --- a/forge-ai/src/main/java/forge/ai/ability/ManifestAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/ManifestAi.java @@ -1,6 +1,5 @@ package forge.ai.ability; -import java.util.List; import java.util.Map; import com.google.common.base.Predicate; @@ -11,7 +10,6 @@ import forge.ai.ComputerUtilCard; import forge.ai.ComputerUtilCost; import forge.ai.SpellAbilityAi; import forge.game.Game; -import forge.game.ability.AbilityKey; import forge.game.card.Card; import forge.game.card.CardCollection; import forge.game.card.CardCollectionView; @@ -21,9 +19,6 @@ import forge.game.phase.PhaseHandler; import forge.game.phase.PhaseType; import forge.game.player.Player; import forge.game.player.PlayerActionConfirmMode; -import forge.game.replacement.ReplacementEffect; -import forge.game.replacement.ReplacementLayer; -import forge.game.replacement.ReplacementType; import forge.game.spellability.SpellAbility; import forge.game.zone.ZoneType; import forge.util.MyRandom; @@ -90,19 +85,13 @@ public class ManifestAi extends SpellAbilityAi { } static boolean shouldManyfest(final Card card, final Player ai, final SpellAbility sa) { - final Game game = ai.getGame(); // check to ensure that there are no replacement effects that prevent creatures ETBing from library // (e.g. Grafdigger's Cage) Card topCopy = CardUtil.getLKICopy(card); topCopy.turnFaceDownNoUpdate(); topCopy.setManifested(true); - final Map repParams = AbilityKey.mapFromAffected(topCopy); - repParams.put(AbilityKey.Origin, card.getZone().getZoneType()); - repParams.put(AbilityKey.Destination, ZoneType.Battlefield); - repParams.put(AbilityKey.Source, sa.getHostCard()); - List list = game.getReplacementHandler().getReplacementList(ReplacementType.Moved, repParams, ReplacementLayer.CantHappen); - if (!list.isEmpty()) { + if (ComputerUtil.isETBprevented(topCopy)) { return false; } diff --git a/forge-game/src/main/java/forge/game/player/PlayerProperty.java b/forge-game/src/main/java/forge/game/player/PlayerProperty.java index 9fd1a94ba5b..63911512d4b 100644 --- a/forge-game/src/main/java/forge/game/player/PlayerProperty.java +++ b/forge-game/src/main/java/forge/game/player/PlayerProperty.java @@ -194,6 +194,20 @@ public class PlayerProperty { if (source.isRemembered(player)) { return false; } + } else if (property.equals("IsTriggerRemembered")) { + boolean found = false; + for (Object o : spellAbility.getTriggerRemembered()) { + if (o instanceof Player) { + Player trigRem = (Player) o; + if (trigRem.equals(player)) { + found = true; + break; + } + } + } + if (!found) { + return false; + } } else if (property.equals("EnchantedBy")) { if (!player.isEnchantedBy(source)) { return false; diff --git a/forge-gui/res/cardsfolder/p/phytotitan.txt b/forge-gui/res/cardsfolder/p/phytotitan.txt index fad13d22bdf..8aeb155aa54 100644 --- a/forge-gui/res/cardsfolder/p/phytotitan.txt +++ b/forge-gui/res/cardsfolder/p/phytotitan.txt @@ -3,7 +3,7 @@ ManaCost:4 G G Types:Creature Plant Elemental PT:7/2 T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Graveyard | ValidCard$ Card.Self | Execute$ DelTrig | TriggerDescription$ When CARDNAME dies, return it to the battlefield tapped under its owner's control at the beginning of their next upkeep. -SVar:DelTrig:DB$ DelayedTrigger | DelayedTriggerDefinedPlayer$ TriggeredCardOwner | Mode$ Phase | Phase$ Upkeep | Execute$ TrigChange | RememberObjects$ TriggeredNewCardLKICopy | TriggerDescription$ Return CARDNAME to the battlefield tapped under its owner's control at the beginning of their next upkeep. +SVar:DelTrig:DB$ DelayedTrigger | ValidPlayer$ Player.IsTriggerRemembered | Mode$ Phase | Phase$ Upkeep | Execute$ TrigChange | RememberObjects$ TriggeredNewCardLKICopy,TriggeredCardOwner | TriggerDescription$ Return CARDNAME to the battlefield tapped under its owner's control at the beginning of their next upkeep. SVar:TrigChange:DB$ ChangeZone | Origin$ Graveyard | Destination$ Battlefield | Defined$ DelayTriggerRememberedLKI | Tapped$ True SVar:SacMe:1 Oracle:When Phytotitan dies, return it to the battlefield tapped under its owner's control at the beginning of their next upkeep. diff --git a/forge-gui/res/cardsfolder/p/plague_reaver.txt b/forge-gui/res/cardsfolder/p/plague_reaver.txt index ab85bab1c91..04a3025b01a 100644 --- a/forge-gui/res/cardsfolder/p/plague_reaver.txt +++ b/forge-gui/res/cardsfolder/p/plague_reaver.txt @@ -5,7 +5,7 @@ PT:6/5 T:Mode$ Phase | Phase$ End of Turn | ValidPlayer$ You | TriggerZones$ Battlefield | Execute$ SacAllOthers | TriggerDescription$ At the beginning of your end step, sacrifice each other creature you control. SVar:SacAllOthers:DB$ SacrificeAll | ValidCards$ Creature.Other+YouCtrl A:AB$ Pump | Cost$ Discard<2/Card> Sac<1/CARDNAME> | ValidTgts$ Opponent | TgtPrompt$ Select target opponent | RememberTargets$ True | SubAbility$ DBDelayTrig | StackDescription$ Return CARDNAME to the battlefield under {p:Targeted}'s control at the beginning of their next upkeep. | SpellDescription$ Choose target opponent. Return CARDNAME to the battlefield under that player's control at the beginning of their next upkeep. -SVar:DBDelayTrig:DB$ DelayedTrigger | TriggerZones$ Graveyard | Mode$ Phase | Phase$ Upkeep | DelayedTriggerDefinedPlayer$ Remembered | RememberObjects$ Remembered,SacrificedCards | Execute$ DBChange | StackDescription$ None | TriggerDescription$ Return CARDNAME to the battlefield under that player's control at the beginning of their next upkeep. +SVar:DBDelayTrig:DB$ DelayedTrigger | TriggerZones$ Graveyard | Mode$ Phase | Phase$ Upkeep | ValidPlayer$ Player.IsTriggerRemembered | RememberObjects$ Remembered,SacrificedCards | Execute$ DBChange | StackDescription$ None | TriggerDescription$ Return CARDNAME to the battlefield under that player's control at the beginning of their next upkeep. SVar:DBChange:DB$ ChangeZone | Defined$ DelayTriggerRememberedLKI | Origin$ Graveyard | Destination$ Battlefield | GainControl$ DelayTriggerRemembered AI:RemoveDeck:All DeckHas:Ability$Discard|Sacrifice diff --git a/forge-gui/res/cardsfolder/u/uro_titan_of_natures_wrath.txt b/forge-gui/res/cardsfolder/u/uro_titan_of_natures_wrath.txt index 26e8dc52888..2ec961aa0b0 100644 --- a/forge-gui/res/cardsfolder/u/uro_titan_of_natures_wrath.txt +++ b/forge-gui/res/cardsfolder/u/uro_titan_of_natures_wrath.txt @@ -2,10 +2,10 @@ Name:Uro, Titan of Nature's Wrath ManaCost:1 G U Types:Legendary Creature Elder Giant PT:6/6 -T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigSac | TriggerDescription$ When CARDNAME enters the battlefield, sacrifice it unless it escaped. +T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigSac | TriggerDescription$ When NICKNAME enters the battlefield, sacrifice it unless it escaped. SVar:TrigSac:DB$ Sacrifice | SacValid$ Self | ConditionNotPresent$ Card.Self+escaped -T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigGainLife | TriggerDescription$ When CARDNAME enters the battlefield or attacks, you gain 3 life and draw a card, then you may put a land card from your hand onto the battlefield. -T:Mode$ Attacks | ValidCard$ Card.Self | Execute$ TrigGainLife | TriggerZones$ Battlefield | OptionalDecider$ You | Secondary$ True | TriggerDescription$ When CARDNAME enters the battlefield or attacks, you gain 3 life and draw a card, then you may put a land card from your hand onto the battlefield. +T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigGainLife | TriggerDescription$ When NICKNAME enters the battlefield or attacks, you gain 3 life and draw a card, then you may put a land card from your hand onto the battlefield. +T:Mode$ Attacks | ValidCard$ Card.Self | Execute$ TrigGainLife | TriggerZones$ Battlefield | OptionalDecider$ You | Secondary$ True | TriggerDescription$ When NICKNAME enters the battlefield or attacks, you gain 3 life and draw a card, then you may put a land card from your hand onto the battlefield. SVar:TrigGainLife:DB$ GainLife | LifeAmount$ 3 | SubAbility$ DBDraw SVar:DBDraw:DB$ Draw | Defined$ You | SubAbility$ DBLand SVar:DBLand:DB$ ChangeZone | Origin$ Hand | Destination$ Battlefield | ChangeType$ Land | ChangeNum$ 1 | OptionalDecider$ You