From bf3f0aed064d2da74de2a6f0acc986936add354b Mon Sep 17 00:00:00 2001 From: tool4EvEr Date: Wed, 12 Apr 2023 09:11:09 +0200 Subject: [PATCH] Cleanup & Fixes --- .../src/main/java/forge/ai/AiController.java | 36 ++++++++----------- .../main/java/forge/ai/ComputerUtilCard.java | 3 +- .../main/java/forge/ai/ability/AttachAi.java | 3 +- .../forge/ai/ability/CopySpellAbilityAi.java | 3 ++ .../main/java/forge/ai/ability/PoisonAi.java | 3 +- .../java/forge/game/cost/CostAdjustment.java | 19 ---------- .../forge/game/trigger/TriggerHandler.java | 7 ++-- .../res/cardsfolder/k/kaerveks_torch.txt | 2 +- .../res/cardsfolder/r/rousing_refrain.txt | 2 +- .../res/cardsfolder/t/the_royal_scions.txt | 5 +-- 10 files changed, 28 insertions(+), 55 deletions(-) diff --git a/forge-ai/src/main/java/forge/ai/AiController.java b/forge-ai/src/main/java/forge/ai/AiController.java index 8136f0d5ac9..1806bcf7146 100644 --- a/forge-ai/src/main/java/forge/ai/AiController.java +++ b/forge-ai/src/main/java/forge/ai/AiController.java @@ -969,21 +969,16 @@ public class AiController { } private boolean canPlaySpellWithoutBuyback(Card card, SpellAbility sa) { - boolean wasteBuybackAllowed = false; - - // About to lose game : allow - if (ComputerUtil.aiLifeInDanger(player, true, 0)) { - wasteBuybackAllowed = true; - } - int copies = CardLists.count(player.getCardsIn(ZoneType.Hand), CardPredicates.nameEquals(card.getName())); // Have two copies : allow if (copies >= 2) { - wasteBuybackAllowed = true; + return true; } - int neededMana = 0; - boolean dangerousRecurringCost = false; + // About to lose game : allow + if (ComputerUtil.aiLifeInDanger(player, true, 0)) { + return true; + } Cost costWithBuyback = sa.getPayCosts().copy(); for (OptionalCostValue opt : GameActionUtil.getOptionalCostValues(sa)) { @@ -991,22 +986,19 @@ public class AiController { costWithBuyback.add(opt.getCost()); } } - CostAdjustment.adjust(costWithBuyback, sa); - if (costWithBuyback.getCostMana() != null) { - neededMana = costWithBuyback.getCostMana().getMana().getCMC(); - } + costWithBuyback = CostAdjustment.adjust(costWithBuyback, sa); if (costWithBuyback.hasSpecificCostType(CostPayLife.class) || costWithBuyback.hasSpecificCostType(CostDiscard.class) || costWithBuyback.hasSpecificCostType(CostSacrifice.class)) { - dangerousRecurringCost = true; + // won't be able to afford buyback any time soon + // if Buyback cost includes sacrifice, life, discard + return true; } - // won't be able to afford buyback any time soon - // if Buyback cost includes sacrifice, life, discard - if (dangerousRecurringCost) { - wasteBuybackAllowed = true; + int neededMana = 0; + if (costWithBuyback.getCostMana() != null) { + neededMana = costWithBuyback.getCostMana().getMana().getCMC(); } - // Memory Crystal-like effects need special handling for (Card c : game.getCardsIn(ZoneType.Battlefield)) { for (StaticAbility s : c.getStaticAbilities()) { @@ -1022,10 +1014,10 @@ public class AiController { int hasMana = ComputerUtilMana.getAvailableManaEstimate(player, false); if (hasMana < neededMana - 1) { - wasteBuybackAllowed = true; + return true; } - return wasteBuybackAllowed; + return false; } // not sure "playing biggest spell" matters? diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java b/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java index ac9f829abf6..02f19fc12f2 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java @@ -1871,10 +1871,9 @@ public class ComputerUtilCard { public static int getMaxSAEnergyCostOnBattlefield(final Player ai) { // returns the maximum energy cost of an ability that permanents on the battlefield under AI's control have - CardCollectionView otb = ai.getCardsIn(ZoneType.Battlefield); int maxEnergyCost = 0; - for (Card c : otb) { + for (Card c : ai.getCardsIn(ZoneType.Battlefield)) { for (SpellAbility sa : c.getSpellAbilities()) { CostPayEnergy energyCost = sa.getPayCosts().getCostEnergy(); if (energyCost != null) { diff --git a/forge-ai/src/main/java/forge/ai/ability/AttachAi.java b/forge-ai/src/main/java/forge/ai/ability/AttachAi.java index 06a4b539863..d842ce39305 100644 --- a/forge-ai/src/main/java/forge/ai/ability/AttachAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/AttachAi.java @@ -669,8 +669,7 @@ public class AttachAi extends SpellAbilityAi { // Prefer "tap to deal damage" // TODO : Skip this one if triggers on combat damage only? for (SpellAbility sa2 : card.getSpellAbilities()) { - if (ApiType.DealDamage.equals(sa2.getApi()) - && (sa2.getTargetRestrictions().canTgtPlayer())) { + if (ApiType.DealDamage.equals(sa2.getApi()) && sa2.usesTargeting() && sa2.getTargetRestrictions().canTgtPlayer()) { cardPriority += 300; } } diff --git a/forge-ai/src/main/java/forge/ai/ability/CopySpellAbilityAi.java b/forge-ai/src/main/java/forge/ai/ability/CopySpellAbilityAi.java index 534ad4e5b79..00dcf7ce1b0 100644 --- a/forge-ai/src/main/java/forge/ai/ability/CopySpellAbilityAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/CopySpellAbilityAi.java @@ -71,6 +71,9 @@ public class CopySpellAbilityAi extends SpellAbilityAi { } else if (top.getApi() == ApiType.CopySpellAbility) { // Don't try to copy a copy ability, too complex for the AI to handle return false; + } else if (top.getApi() == ApiType.Mana) { + // would lead to Stack Overflow by trying to play this again + return false; } else if (top.getApi() == ApiType.DestroyAll || top.getApi() == ApiType.SacrificeAll || top.getApi() == ApiType.ChangeZoneAll || top.getApi() == ApiType.TapAll || top.getApi() == ApiType.UnattachAll) { if (!top.usesTargeting() || top.getActivatingPlayer().equals(aiPlayer)) { // If we activated a mass removal / mass tap / mass bounce / etc. spell, or if the opponent activated it but diff --git a/forge-ai/src/main/java/forge/ai/ability/PoisonAi.java b/forge-ai/src/main/java/forge/ai/ability/PoisonAi.java index d6a65513f4e..ef1ef5fbab1 100644 --- a/forge-ai/src/main/java/forge/ai/ability/PoisonAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/PoisonAi.java @@ -64,8 +64,7 @@ public class PoisonAi extends SpellAbilityAi { return true; } else { // currently there are no optional Trigger - final PlayerCollection players = AbilityUtils.getDefinedPlayers(sa.getHostCard(), sa.getParam("Defined"), - sa); + final PlayerCollection players = AbilityUtils.getDefinedPlayers(sa.getHostCard(), sa.getParam("Defined"), sa); if (players.isEmpty()) { return false; } 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 98e03914cd9..03cba699bb4 100644 --- a/forge-game/src/main/java/forge/game/cost/CostAdjustment.java +++ b/forge-game/src/main/java/forge/game/cost/CostAdjustment.java @@ -553,25 +553,6 @@ public class CostAdjustment { return false; } } - if (st.hasParam("ValidSpellTarget")) { - SpellAbility curSa = sa; - boolean targetValid = false; - outer: while (curSa != null) { - if (!curSa.usesTargeting()) { - curSa = curSa.getSubAbility(); - continue; - } - for (SpellAbility target : curSa.getTargets().getTargetSpells()) { - Card targetCard = target.getHostCard(); - if (targetCard.isValid(st.getParam("ValidSpellTarget").split(","), controller, hostCard, curSa)) { - targetValid = true; - break outer; - } - } - curSa = curSa.getSubAbility(); - } - return targetValid; - } return true; } } diff --git a/forge-game/src/main/java/forge/game/trigger/TriggerHandler.java b/forge-game/src/main/java/forge/game/trigger/TriggerHandler.java index 067f4165bd2..8a54c840cf0 100644 --- a/forge-game/src/main/java/forge/game/trigger/TriggerHandler.java +++ b/forge-game/src/main/java/forge/game/trigger/TriggerHandler.java @@ -207,7 +207,6 @@ public class TriggerHandler { public final void resetActiveTriggers() { resetActiveTriggers(true); } - public final void resetActiveTriggers(boolean collect) { if (collect) { collectTriggerForWaiting(); @@ -281,11 +280,11 @@ public class TriggerHandler { } public final boolean runWaitingTriggers() { - final List waiting = new ArrayList<>(waitingTriggers); - waitingTriggers.clear(); - if (waiting.isEmpty()) { + if (waitingTriggers.isEmpty()) { return false; } + final List waiting = new ArrayList<>(waitingTriggers); + waitingTriggers.clear(); boolean haveWaiting = false; for (final TriggerWaiting wt : waiting) { diff --git a/forge-gui/res/cardsfolder/k/kaerveks_torch.txt b/forge-gui/res/cardsfolder/k/kaerveks_torch.txt index e2662a81368..94d064f7eb9 100644 --- a/forge-gui/res/cardsfolder/k/kaerveks_torch.txt +++ b/forge-gui/res/cardsfolder/k/kaerveks_torch.txt @@ -2,6 +2,6 @@ Name:Kaervek's Torch ManaCost:X R Types:Sorcery A:SP$ DealDamage | Cost$ X R | ValidTgts$ Creature,Player,Planeswalker | TgtPrompt$ Select any target | NumDmg$ X | SpellDescription$ CARDNAME deals X damage to any target. -S:Mode$ RaiseCost | ValidSpellTarget$ Card.Self | Activator$ Player | Type$ Spell | Amount$ 2 | EffectZone$ Stack | Description$ As long as CARDNAME is on the stack, spells that target it cost {2} more to cast. +S:Mode$ RaiseCost | ValidTarget$ Spell.Self | Activator$ Player | Type$ Spell | Amount$ 2 | EffectZone$ Stack | Description$ As long as CARDNAME is on the stack, spells that target it cost {2} more to cast. SVar:X:Count$xPaid Oracle:As long as Kaervek's Torch is on the stack, spells that target it cost {2} more to cast.\nKaervek's Torch deals X damage to any target. diff --git a/forge-gui/res/cardsfolder/r/rousing_refrain.txt b/forge-gui/res/cardsfolder/r/rousing_refrain.txt index 145306015f5..0d908d8297b 100644 --- a/forge-gui/res/cardsfolder/r/rousing_refrain.txt +++ b/forge-gui/res/cardsfolder/r/rousing_refrain.txt @@ -2,7 +2,7 @@ Name:Rousing Refrain ManaCost:3 R R Types:Sorcery K:Suspend:3:1 R -A:SP$ Mana | Cost$ 3 R R | ValidTgts$ Opponent | TgtPrompt$ Select target opponent | SubAbility$ DBMana | AILogic$ ManaRitual | Produced$ R | Amount$ Z | PersistentMana$ True | Defined$ You | SubAbility$ DBChange | StackDescription$ SpellDescription | SpellDescription$ Until end of turn, you don't lose this mana as steps and phases end. +A:SP$ Mana | Cost$ 3 R R | ValidTgts$ Opponent | TgtPrompt$ Select target opponent | SubAbility$ DBMana | AILogic$ ManaRitual | Produced$ R | Amount$ Z | PersistentMana$ True | Defined$ You | SubAbility$ DBChange | StackDescription$ SpellDescription | SpellDescription$ Until end of turn, you don't lose this mana as steps and phases end. SVar:Z:TargetedPlayer$CardsInHand SVar:DBChange:DB$ ChangeZone | Origin$ Stack | Destination$ Exile | WithCountersType$ TIME | WithCountersAmount$ 3 | SpellDescription$ Exile CARDNAME with three time counters on it. Oracle:Add {R} for each card in target opponent's hand. Until end of turn, you don't lose this mana as steps and phases end. Exile Rousing Refrain with three time counters on it.\nSuspend 3—{1}{R} (Rather than cast this card from your hand, you may pay {1}{R} and exile it with three time counters on it. At the beginning of your upkeep, remove a time counter. When the last is removed, cast it without paying its mana cost.) diff --git a/forge-gui/res/cardsfolder/t/the_royal_scions.txt b/forge-gui/res/cardsfolder/t/the_royal_scions.txt index b387373661c..76cf63b57ff 100644 --- a/forge-gui/res/cardsfolder/t/the_royal_scions.txt +++ b/forge-gui/res/cardsfolder/t/the_royal_scions.txt @@ -6,8 +6,9 @@ A:AB$ Draw | Cost$ AddCounter<1/LOYALTY> | Planeswalker$ True | NumCards$ 1 | Sp SVar:DBDiscard:DB$ Discard | Defined$ You | NumCards$ 1 | Mode$ TgtChoose AI:RemoveDeck:All A:AB$ Pump | Cost$ AddCounter<1/LOYALTY> | Planeswalker$ True | ValidTgts$ Creature | TgtPrompt$ Select target creature | NumAtt$ +2 | KW$ First Strike & Trample | SpellDescription$ Target creature gets +2/+0 and gains first strike and trample until end of turn. -A:AB$ Draw | Cost$ SubCounter<8/LOYALTY> | Planeswalker$ True | Ultimate$ True | NumCards$ 4 | SubAbility$ DBTrigger | SpellDescription$ Draw four cards. When you do, CARDNAME deals damage to any target equal to the number of cards in your hand. -SVar:DBTrigger:DB$ ImmediateTrigger | Execute$ DBDamage | TriggerDescription$ When you do, CARDNAME deals damage to any target equal to the number of cards in your hand. +A:AB$ Draw | Cost$ SubCounter<8/LOYALTY> | Planeswalker$ True | Ultimate$ True | NumCards$ 4 | RememberDrawn$ True | SubAbility$ DBTrigger | SpellDescription$ Draw four cards. When you do, CARDNAME deals damage to any target equal to the number of cards in your hand. +SVar:DBTrigger:DB$ ImmediateTrigger | ConditionDefined$ Remembered | ConditionPresent$ Card | ConditionCompare$ GE4 | Execute$ DBDamage | TriggerDescription$ When you do, CARDNAME deals damage to any target equal to the number of cards in your hand. | SubAbility$ DBCleanup SVar:DBDamage:DB$ DealDamage | ValidTgts$ Creature,Player,Planeswalker | TgtPrompt$ Select any target | NumDmg$ X +SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True SVar:X:Count$InYourHand Oracle:[+1]: Draw a card, then discard a card.\n[+1]: Target creature gets +2/+0 and gains first strike and trample until end of turn.\n[-8]: Draw four cards. When you do, The Royal Scions deals damage to any target equal to the number of cards in your hand.