From daff36b64ee39c4574779415965f74841b36e77d Mon Sep 17 00:00:00 2001 From: Agetian Date: Mon, 13 Nov 2023 19:56:15 +0300 Subject: [PATCH] AI logic for Veil of Summer (#4149) * - AI logic for Veil of Summer. * - Simplify implementation. --- .../src/main/java/forge/ai/SpecialCardAi.java | 35 +++++++++++++++++++ .../main/java/forge/ai/ability/DrawAi.java | 24 ++++++------- .../res/cardsfolder/v/veil_of_summer.txt | 2 +- 3 files changed, 47 insertions(+), 14 deletions(-) diff --git a/forge-ai/src/main/java/forge/ai/SpecialCardAi.java b/forge-ai/src/main/java/forge/ai/SpecialCardAi.java index 09a06bf1122..6366da9e194 100644 --- a/forge-ai/src/main/java/forge/ai/SpecialCardAi.java +++ b/forge-ai/src/main/java/forge/ai/SpecialCardAi.java @@ -1683,6 +1683,41 @@ public class SpecialCardAi { } } + // Veil of Summer + public static class VeilOfSummer { + public static boolean consider(final Player ai, final SpellAbility sa) { + // check the top ability on stack if it's (a) an opponent's counterspell targeting the AI's spell; + // (b) a black or a blue spell targeting something that belongs to the AI + Game game = ai.getGame(); + if (game.getStack().isEmpty()) { + return false; + } + + SpellAbility topSA = game.getStack().peekAbility(); + if (topSA.usesTargeting() && topSA.getActivatingPlayer().isOpponentOf(ai)) { + if (topSA.getApi() == ApiType.Counter) { + SpellAbility tgtSpell = topSA.getTargets().getFirstTargetedSpell(); + if (tgtSpell != null && tgtSpell.getActivatingPlayer().equals(ai)) { + return true; + } + } else if (topSA.getHostCard().isBlack() || topSA.getHostCard().isBlue()) { + for (Player tgtP : topSA.getTargets().getTargetPlayers()) { + if (tgtP.equals(ai)) { + return true; + } + } + for (Card tgtC : topSA.getTargets().getTargetCards()) { + if (tgtC.getController().equals(ai)) { + return true; + } + } + } + } + return false; + } + } + + // Volrath's Shapeshifter public static class VolrathsShapeshifter { public static boolean consider(final Player ai, final SpellAbility sa) { diff --git a/forge-ai/src/main/java/forge/ai/ability/DrawAi.java b/forge-ai/src/main/java/forge/ai/ability/DrawAi.java index c871935ae68..072125b3340 100644 --- a/forge-ai/src/main/java/forge/ai/ability/DrawAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/DrawAi.java @@ -132,18 +132,6 @@ public class DrawAi extends SpellAbilityAi { */ @Override protected boolean checkPhaseRestrictions(Player ai, SpellAbility sa, PhaseHandler ph) { - String logic = sa.getParamOrDefault("AILogic", ""); - - if (logic.startsWith("LifeLessThan.")) { - // LifeLessThan logic presupposes activation as soon as possible in an - // attempt to save the AI from dying - return true; - } else if (logic.equals("AtOppEOT")) { - return ph.is(PhaseType.END_OF_TURN) && ph.getNextTurn().equals(ai); - } else if (logic.equals("RespondToOwnActivation")) { - return !ai.getGame().getStack().isEmpty() && ai.getGame().getStack().peekAbility().getHostCard().equals(sa.getHostCard()); - } - // Sacrificing a creature in response to something dangerous is generally good in any phase boolean isSacCost = false; if (sa.getPayCosts() != null && sa.getPayCosts().hasSpecificCostType(CostSacrifice.class)) { @@ -169,7 +157,17 @@ public class DrawAi extends SpellAbilityAi { */ @Override protected boolean checkPhaseRestrictions(Player ai, SpellAbility sa, PhaseHandler ph, String logic) { - if ((!ph.getNextTurn().equals(ai) || ph.getPhase().isBefore(PhaseType.END_OF_TURN)) + if (logic.equals("VeilOfSummer")) { + return SpecialCardAi.VeilOfSummer.consider(ai, sa); // this is more of a counterspell than a true draw card, so it's timed by the card-specific logic + } else if (logic.startsWith("LifeLessThan.")) { + // LifeLessThan logic presupposes activation as soon as possible in an + // attempt to save the AI from dying + return true; + } else if (logic.equals("AtOppEOT")) { + return ph.is(PhaseType.END_OF_TURN) && ph.getNextTurn().equals(ai); + } else if (logic.equals("RespondToOwnActivation")) { + return !ai.getGame().getStack().isEmpty() && ai.getGame().getStack().peekAbility().getHostCard().equals(sa.getHostCard()); + } else if ((!ph.getNextTurn().equals(ai) || ph.getPhase().isBefore(PhaseType.END_OF_TURN)) && !sa.hasParam("PlayerTurn") && !isSorcerySpeed(sa, ai) && ai.getCardsIn(ZoneType.Hand).size() > 1 && !ComputerUtil.activateForCost(sa, ai) && !"YawgmothsBargain".equals(logic)) { diff --git a/forge-gui/res/cardsfolder/v/veil_of_summer.txt b/forge-gui/res/cardsfolder/v/veil_of_summer.txt index f0a1f5e1181..eaafd9a06fe 100644 --- a/forge-gui/res/cardsfolder/v/veil_of_summer.txt +++ b/forge-gui/res/cardsfolder/v/veil_of_summer.txt @@ -1,7 +1,7 @@ Name:Veil of Summer ManaCost:G Types:Instant -A:SP$ Draw | Cost$ G | Defined$ You | ConditionCheckSVar$ X | ConditionSVarCompare$ GE1 | SubAbility$ DBEffect | StackDescription$ SpellDescription | SpellDescription$ Draw a card if an opponent has cast a blue or black spell this turn. Spells you control can't be countered this turn. You and permanents you control gain hexproof from blue and from black until end of turn. (You and they can't be the targets of blue or black spells or abilities your opponents control.) +A:SP$ Draw | Cost$ G | Defined$ You | ConditionCheckSVar$ X | ConditionSVarCompare$ GE1 | SubAbility$ DBEffect | AILogic$ VeilOfSummer | StackDescription$ SpellDescription | SpellDescription$ Draw a card if an opponent has cast a blue or black spell this turn. Spells you control can't be countered this turn. You and permanents you control gain hexproof from blue and from black until end of turn. (You and they can't be the targets of blue or black spells or abilities your opponents control.) SVar:DBEffect:DB$ Effect | StaticAbilities$ AntiMagic | SubAbility$ DBPump SVar:AntiMagic:Mode$ Continuous | Affected$ Card.YouCtrl | AffectedZone$ Stack | EffectZone$ Command | AddHiddenKeyword$ CARDNAME can't be countered. | Description$ Spells you control can't be countered this turn. SVar:DBPump:DB$ Pump | Defined$ You | KW$ Hexproof:Card.Black:black & Hexproof:Card.Blue:blue | SubAbility$ DBPumpAll | StackDescription$ None