diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java b/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java index 91ace06fe5b..d0578669b07 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java @@ -1151,7 +1151,7 @@ public class ComputerUtilCard { if (c.isEquipped()) { valueTempo *= 2; } - if (SpellAbilityAi.isSorcerySpeed(sa)) { + if (SpellAbilityAi.isSorcerySpeed(sa, ai)) { valueTempo *= 2; //sorceries have less usage opportunities } if (!c.canBeDestroyed()) { @@ -1329,7 +1329,7 @@ public class ComputerUtilCard { // will the creature attack (only relevant for sorcery speed)? if (phase.getPhase().isBefore(PhaseType.COMBAT_DECLARE_ATTACKERS) && phase.isPlayerTurn(ai) - && (SpellAbilityAi.isSorcerySpeed(sa) || main1Preferred) + && (SpellAbilityAi.isSorcerySpeed(sa, ai) || main1Preferred) && power > 0 && doesCreatureAttackAI(ai, c)) { return true; diff --git a/forge-ai/src/main/java/forge/ai/SpellAbilityAi.java b/forge-ai/src/main/java/forge/ai/SpellAbilityAi.java index 7fb54cde205..e96dc0dcbb3 100644 --- a/forge-ai/src/main/java/forge/ai/SpellAbilityAi.java +++ b/forge-ai/src/main/java/forge/ai/SpellAbilityAi.java @@ -251,11 +251,11 @@ public abstract class SpellAbilityAi { * a {@link forge.game.spellability.SpellAbility} object. * @return a boolean. */ - protected static boolean isSorcerySpeed(final SpellAbility sa) { + protected static boolean isSorcerySpeed(final SpellAbility sa, Player ai) { return (sa.getRootAbility().isSpell() && sa.getHostCard().isSorcery()) || (sa.getRootAbility().isActivatedAbility() && sa.getRestrictions().isSorcerySpeed()) || (sa.getRootAbility().isAdventure() && sa.getHostCard().getState(CardStateName.Adventure).getType().isSorcery()) - || (sa.isPwAbility() && !sa.getHostCard().hasKeyword("CARDNAME's loyalty abilities can be activated at instant speed.")); + || (sa.isPwAbility() && !sa.withFlash(sa.getHostCard(), ai)); } /** diff --git a/forge-ai/src/main/java/forge/ai/ability/AnimateAi.java b/forge-ai/src/main/java/forge/ai/ability/AnimateAi.java index 94c0f0e1bb3..49876c64fb4 100644 --- a/forge-ai/src/main/java/forge/ai/ability/AnimateAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/AnimateAi.java @@ -87,7 +87,7 @@ public class AnimateAi extends SpellAbilityAi { } } // Don't use instant speed animate abilities before AI's COMBAT_BEGIN - if (!ph.is(PhaseType.COMBAT_BEGIN) && ph.isPlayerTurn(ai) && !SpellAbilityAi.isSorcerySpeed(sa) + if (!ph.is(PhaseType.COMBAT_BEGIN) && ph.isPlayerTurn(ai) && !SpellAbilityAi.isSorcerySpeed(sa, ai) && !sa.hasParam("ActivationPhases") && !"Permanent".equals(sa.getParam("Duration"))) { return false; } @@ -175,7 +175,7 @@ public class AnimateAi extends SpellAbilityAi { } } - if (!SpellAbilityAi.isSorcerySpeed(sa) && !"Permanent".equals(sa.getParam("Duration"))) { + if (!SpellAbilityAi.isSorcerySpeed(sa, aiPlayer) && !"Permanent".equals(sa.getParam("Duration"))) { if (sa.hasParam("Crew") && c.isCreature()) { // Do not try to crew a vehicle which is already a creature return false; 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 7237fa2eae3..e965e71e833 100644 --- a/forge-ai/src/main/java/forge/ai/ability/ChangeZoneAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/ChangeZoneAi.java @@ -1059,7 +1059,7 @@ public class ChangeZoneAi extends SpellAbilityAi { } if (!immediately && (!game.getPhaseHandler().getNextTurn().equals(ai) || game.getPhaseHandler().getPhase().isBefore(PhaseType.END_OF_TURN)) - && !sa.hasParam("PlayerTurn") && !SpellAbilityAi.isSorcerySpeed(sa) + && !sa.hasParam("PlayerTurn") && !SpellAbilityAi.isSorcerySpeed(sa, ai) && !ComputerUtil.activateForCost(sa, ai)) { return false; } diff --git a/forge-ai/src/main/java/forge/ai/ability/CloneAi.java b/forge-ai/src/main/java/forge/ai/ability/CloneAi.java index 9128682d2f5..f0cfe7c4a87 100644 --- a/forge-ai/src/main/java/forge/ai/ability/CloneAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/CloneAi.java @@ -243,7 +243,7 @@ public class CloneAi extends SpellAbilityAi { // don't use instant speed clone abilities outside computers // Combat_Begin step if (!ph.is(PhaseType.COMBAT_BEGIN) - && ph.isPlayerTurn(ai) && !SpellAbilityAi.isSorcerySpeed(sa) + && ph.isPlayerTurn(ai) && !SpellAbilityAi.isSorcerySpeed(sa, ai) && !sa.hasParam("ActivationPhases") && sa.hasParam("Duration")) { return false; } diff --git a/forge-ai/src/main/java/forge/ai/ability/CountersMultiplyAi.java b/forge-ai/src/main/java/forge/ai/ability/CountersMultiplyAi.java index 0f72bee70c6..af44764663a 100644 --- a/forge-ai/src/main/java/forge/ai/ability/CountersMultiplyAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/CountersMultiplyAi.java @@ -83,7 +83,7 @@ public class CountersMultiplyAi extends SpellAbilityAi { if (ph.getPhase().isBefore(PhaseType.MAIN2) && !ComputerUtil.castSpellInMain1(ai, sa)) { return false; } - if (ph.isPlayerTurn(ai) && !isSorcerySpeed(sa)) { + if (ph.isPlayerTurn(ai) && !isSorcerySpeed(sa, ai)) { return false; } } diff --git a/forge-ai/src/main/java/forge/ai/ability/CountersPutAi.java b/forge-ai/src/main/java/forge/ai/ability/CountersPutAi.java index ff4a0b0d8d4..28393be6aa7 100644 --- a/forge-ai/src/main/java/forge/ai/ability/CountersPutAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/CountersPutAi.java @@ -448,7 +448,7 @@ public class CountersPutAi extends CountersAi { } } - if (!ai.getGame().getStack().isEmpty() && !SpellAbilityAi.isSorcerySpeed(sa)) { + if (!ai.getGame().getStack().isEmpty() && !SpellAbilityAi.isSorcerySpeed(sa, ai)) { // only evaluates case where all tokens are placed on a single target if (sa.usesTargeting() && sa.getMinTargets() < 2) { if (ComputerUtilCard.canPumpAgainstRemoval(ai, sa)) { @@ -557,7 +557,7 @@ public class CountersPutAi extends CountersAi { if (sa.isCurse()) { choice = chooseCursedTarget(list, type, amount, ai); } else { - if (type.equals("P1P1") && !SpellAbilityAi.isSorcerySpeed(sa)) { + if (type.equals("P1P1") && !SpellAbilityAi.isSorcerySpeed(sa, ai)) { for (Card c : list) { if (ComputerUtilCard.shouldPumpCard(ai, sa, c, amount, amount, Lists.newArrayList())) { @@ -619,7 +619,7 @@ public class CountersPutAi extends CountersAi { return false; } // Instant +1/+1 - if (type.equals("P1P1") && !SpellAbilityAi.isSorcerySpeed(sa)) { + if (type.equals("P1P1") && !SpellAbilityAi.isSorcerySpeed(sa, ai)) { if (!(ph.getNextTurn() == ai && ph.is(PhaseType.END_OF_TURN) && abCost.isReusuableResource())) { return false; // only if next turn and cost is reusable } @@ -641,7 +641,7 @@ public class CountersPutAi extends CountersAi { if (ph.getPhase().isBefore(PhaseType.MAIN2) && !ComputerUtil.castSpellInMain1(ai, sa)) { return false; } - if (ph.isPlayerTurn(ai) && !isSorcerySpeed(sa)) { + if (ph.isPlayerTurn(ai) && !isSorcerySpeed(sa, ai)) { return false; } } diff --git a/forge-ai/src/main/java/forge/ai/ability/DamageAiBase.java b/forge-ai/src/main/java/forge/ai/ability/DamageAiBase.java index 649432f670f..9cfe684e49e 100644 --- a/forge-ai/src/main/java/forge/ai/ability/DamageAiBase.java +++ b/forge-ai/src/main/java/forge/ai/ability/DamageAiBase.java @@ -126,7 +126,7 @@ public abstract class DamageAiBase extends SpellAbilityAi { // chance to burn player based on current hand size if (hand.size() > 2) { float value = 0; - if (SpellAbilityAi.isSorcerySpeed(sa)) { + if (SpellAbilityAi.isSorcerySpeed(sa, comp)) { //lower chance for sorcery as other spells may be cast in main2 if (phase.isPlayerTurn(comp) && phase.is(PhaseType.MAIN2)) { value = 1.0f * restDamage / enemy.getLife(); diff --git a/forge-ai/src/main/java/forge/ai/ability/DamageDealAi.java b/forge-ai/src/main/java/forge/ai/ability/DamageDealAi.java index 8e9229745fc..a41b4c42dab 100644 --- a/forge-ai/src/main/java/forge/ai/ability/DamageDealAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/DamageDealAi.java @@ -761,7 +761,7 @@ public class DamageDealAi extends DamageAiBase { // TODO: Improve Damage, we shouldn't just target the player just because we can if (sa.canTarget(enemy) && tcs.size() < tgt.getMaxTargets(source, sa)) { if (((phase.is(PhaseType.END_OF_TURN) && phase.getNextTurn().equals(ai)) - || (SpellAbilityAi.isSorcerySpeed(sa) && phase.is(PhaseType.MAIN2)) + || (SpellAbilityAi.isSorcerySpeed(sa, ai) && phase.is(PhaseType.MAIN2)) || ("PingAfterAttack".equals(logic) && phase.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS) && phase.isPlayerTurn(ai)) || immediately || shouldTgtP(ai, sa, dmg, noPrevention)) && (!avoidTargetP(ai, sa))) { diff --git a/forge-ai/src/main/java/forge/ai/ability/DayTimeAi.java b/forge-ai/src/main/java/forge/ai/ability/DayTimeAi.java index bf37bb99e03..a51e6745b6d 100644 --- a/forge-ai/src/main/java/forge/ai/ability/DayTimeAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/DayTimeAi.java @@ -14,7 +14,7 @@ public class DayTimeAi extends SpellAbilityAi { if ((sa.getHostCard().isCreature() && sa.getPayCosts().hasTapCost()) || sa.getPayCosts().hasManaCost()) { // If it involves a cost that may put us at a disadvantage, better activate before own turn if possible - if (!SpellAbilityAi.isSorcerySpeed(sa)) { + if (!SpellAbilityAi.isSorcerySpeed(sa, aiPlayer)) { return ph.is(PhaseType.END_OF_TURN) && ph.getNextTurn() == aiPlayer; } else { return ph.is(PhaseType.MAIN2, aiPlayer); // Give other things a chance to be cast (e.g. Celestus) diff --git a/forge-ai/src/main/java/forge/ai/ability/DebuffAi.java b/forge-ai/src/main/java/forge/ai/ability/DebuffAi.java index 832c1313dce..64b553adb90 100644 --- a/forge-ai/src/main/java/forge/ai/ability/DebuffAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/DebuffAi.java @@ -60,7 +60,7 @@ public class DebuffAi extends SpellAbilityAi { || !game.getStack().isEmpty()) { // Instant-speed pumps should not be cast outside of combat when the // stack is empty - if (!SpellAbilityAi.isSorcerySpeed(sa)) { + if (!SpellAbilityAi.isSorcerySpeed(sa, ai)) { return false; } } 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 367c435ca2a..9f223ca34ae 100644 --- a/forge-ai/src/main/java/forge/ai/ability/DigAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/DigAi.java @@ -108,7 +108,7 @@ public class DigAi extends SpellAbilityAi { if ((!game.getPhaseHandler().getNextTurn().equals(ai) || game.getPhaseHandler().getPhase().isBefore(PhaseType.END_OF_TURN)) - && !sa.hasParam("PlayerTurn") && !SpellAbilityAi.isSorcerySpeed(sa) + && !sa.hasParam("PlayerTurn") && !SpellAbilityAi.isSorcerySpeed(sa, ai) && (ai.getCardsIn(ZoneType.Hand).size() > 1 || game.getPhaseHandler().getPhase().isBefore(PhaseType.DRAW)) && !ComputerUtil.activateForCost(sa, ai)) { return false; diff --git a/forge-ai/src/main/java/forge/ai/ability/DigMultipleAi.java b/forge-ai/src/main/java/forge/ai/ability/DigMultipleAi.java index 08a6c192914..1f838d9172d 100644 --- a/forge-ai/src/main/java/forge/ai/ability/DigMultipleAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/DigMultipleAi.java @@ -66,7 +66,7 @@ public class DigMultipleAi extends SpellAbilityAi { if ((!game.getPhaseHandler().getNextTurn().equals(ai) || game.getPhaseHandler().getPhase().isBefore(PhaseType.END_OF_TURN)) - && !sa.hasParam("PlayerTurn") && !SpellAbilityAi.isSorcerySpeed(sa) + && !sa.hasParam("PlayerTurn") && !SpellAbilityAi.isSorcerySpeed(sa, ai) && (ai.getCardsIn(ZoneType.Hand).size() > 1 || game.getPhaseHandler().getPhase().isBefore(PhaseType.DRAW)) && !ComputerUtil.activateForCost(sa, ai)) { return false; diff --git a/forge-ai/src/main/java/forge/ai/ability/DigUntilAi.java b/forge-ai/src/main/java/forge/ai/ability/DigUntilAi.java index 93c4559952a..8d09f66e886 100644 --- a/forge-ai/src/main/java/forge/ai/ability/DigUntilAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/DigUntilAi.java @@ -24,7 +24,7 @@ public class DigUntilAi extends SpellAbilityAi { Card source = sa.getHostCard(); final String logic = sa.getParamOrDefault("AILogic", ""); double chance = .4; // 40 percent chance with instant speed stuff - if (SpellAbilityAi.isSorcerySpeed(sa)) { + if (SpellAbilityAi.isSorcerySpeed(sa, ai)) { chance = .667; // 66.7% chance for sorcery speed (since it will // never activate EOT) } 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 a93d368805f..eec736c93e6 100644 --- a/forge-ai/src/main/java/forge/ai/ability/DrawAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/DrawAi.java @@ -166,7 +166,7 @@ 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)) - && !sa.hasParam("PlayerTurn") && !SpellAbilityAi.isSorcerySpeed(sa) + && !sa.hasParam("PlayerTurn") && !SpellAbilityAi.isSorcerySpeed(sa, ai) && ai.getCardsIn(ZoneType.Hand).size() > 1 && !ComputerUtil.activateForCost(sa, ai) && !"YawgmothsBargain".equals(logic)) { return false; diff --git a/forge-ai/src/main/java/forge/ai/ability/FlipOntoBattlefieldAi.java b/forge-ai/src/main/java/forge/ai/ability/FlipOntoBattlefieldAi.java index dfce114c0a0..ca37e15340a 100644 --- a/forge-ai/src/main/java/forge/ai/ability/FlipOntoBattlefieldAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/FlipOntoBattlefieldAi.java @@ -18,7 +18,7 @@ public class FlipOntoBattlefieldAi extends SpellAbilityAi { PhaseHandler ph = sa.getHostCard().getGame().getPhaseHandler(); String logic = sa.getParamOrDefault("AILogic", ""); - if (!SpellAbilityAi.isSorcerySpeed(sa) && sa.getPayCosts().hasManaCost()) { + if (!SpellAbilityAi.isSorcerySpeed(sa, aiPlayer) && sa.getPayCosts().hasManaCost()) { return ph.is(PhaseType.END_OF_TURN); } diff --git a/forge-ai/src/main/java/forge/ai/ability/LifeGainAi.java b/forge-ai/src/main/java/forge/ai/ability/LifeGainAi.java index a47db66e293..caf1efa54e6 100644 --- a/forge-ai/src/main/java/forge/ai/ability/LifeGainAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/LifeGainAi.java @@ -111,7 +111,7 @@ public class LifeGainAi extends SpellAbilityAi { return lifeCritical || activateForCost || (ph.getNextTurn().equals(ai) && !ph.getPhase().isBefore(PhaseType.END_OF_TURN)) - || sa.hasParam("PlayerTurn") || SpellAbilityAi.isSorcerySpeed(sa); + || sa.hasParam("PlayerTurn") || SpellAbilityAi.isSorcerySpeed(sa, ai); } /* @@ -180,7 +180,7 @@ public class LifeGainAi extends SpellAbilityAi { return true; } - if (SpellAbilityAi.isSorcerySpeed(sa) + if (SpellAbilityAi.isSorcerySpeed(sa, ai) || sa.getSubAbility() != null || SpellAbilityAi.playReusable(ai, sa)) { return true; } diff --git a/forge-ai/src/main/java/forge/ai/ability/LifeLoseAi.java b/forge-ai/src/main/java/forge/ai/ability/LifeLoseAi.java index 95f9a1d3d0c..c9ce8e01d1d 100644 --- a/forge-ai/src/main/java/forge/ai/ability/LifeLoseAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/LifeLoseAi.java @@ -144,7 +144,7 @@ public class LifeLoseAi extends SpellAbilityAi { return false; } - if (SpellAbilityAi.isSorcerySpeed(sa) || sa.hasParam("ActivationPhases") || SpellAbilityAi.playReusable(ai, sa) + if (SpellAbilityAi.isSorcerySpeed(sa, ai) || sa.hasParam("ActivationPhases") || SpellAbilityAi.playReusable(ai, sa) || ComputerUtil.activateForCost(sa, ai)) { return true; } 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 6062a19bb84..1f337d1cd0a 100644 --- a/forge-ai/src/main/java/forge/ai/ability/ManifestAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/ManifestAi.java @@ -66,7 +66,7 @@ public class ManifestAi extends SpellAbilityAi { if (!buff) { return false; } - } else if (!SpellAbilityAi.isSorcerySpeed(sa)) { + } else if (!SpellAbilityAi.isSorcerySpeed(sa, ai)) { return false; } } else { diff --git a/forge-ai/src/main/java/forge/ai/ability/MillAi.java b/forge-ai/src/main/java/forge/ai/ability/MillAi.java index ab6a00ce796..c673882dc3e 100644 --- a/forge-ai/src/main/java/forge/ai/ability/MillAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/MillAi.java @@ -53,7 +53,7 @@ public class MillAi extends SpellAbilityAi { return (ph.is(PhaseType.MAIN1) || ph.is(PhaseType.MAIN2)) && ph.isPlayerTurn(ai); // Chandra, Torch of Defiance and similar } if (!sa.isPwAbility()) { // Planeswalker abilities are only activated at sorcery speed - if ("You".equals(sa.getParam("Defined")) && !(!SpellAbilityAi.isSorcerySpeed(sa) && ph.is(PhaseType.END_OF_TURN) + if ("You".equals(sa.getParam("Defined")) && !(!SpellAbilityAi.isSorcerySpeed(sa, ai) && ph.is(PhaseType.END_OF_TURN) && ph.getNextTurn().equals(ai))) { return false; // only self-mill at opponent EOT } diff --git a/forge-ai/src/main/java/forge/ai/ability/ProtectAi.java b/forge-ai/src/main/java/forge/ai/ability/ProtectAi.java index 2094239437c..47b37126a7d 100644 --- a/forge-ai/src/main/java/forge/ai/ability/ProtectAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/ProtectAi.java @@ -164,7 +164,7 @@ public class ProtectAi extends SpellAbilityAi { protected boolean checkPhaseRestrictions(final Player ai, final SpellAbility sa, final PhaseHandler ph) { final boolean notAiMain1 = !(ph.getPlayerTurn() == ai && ph.getPhase() == PhaseType.MAIN1); // sorceries can only give protection in order to create an unblockable attacker - return !SpellAbilityAi.isSorcerySpeed(sa) || !notAiMain1; + return !SpellAbilityAi.isSorcerySpeed(sa, ai) || !notAiMain1; } @Override diff --git a/forge-ai/src/main/java/forge/ai/ability/PumpAi.java b/forge-ai/src/main/java/forge/ai/ability/PumpAi.java index 73971e59feb..0312faf38a1 100644 --- a/forge-ai/src/main/java/forge/ai/ability/PumpAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/PumpAi.java @@ -81,7 +81,7 @@ public class PumpAi extends PumpAiBase { return true; } - return SpellAbilityAi.isSorcerySpeed(sa) || (ph.getNextTurn().equals(ai) && !ph.getPhase().isBefore(PhaseType.END_OF_TURN)); + return SpellAbilityAi.isSorcerySpeed(sa, ai) || (ph.getNextTurn().equals(ai) && !ph.getPhase().isBefore(PhaseType.END_OF_TURN)); } else if (logic.equals("Aristocrat")) { final boolean isThreatened = ComputerUtil.predictThreatenedObjects(ai, null, true).contains(sa.getHostCard()); if (!ph.is(PhaseType.COMBAT_DECLARE_BLOCKERS) && !isThreatened) { @@ -112,7 +112,7 @@ public class PumpAi extends PumpAiBase { || ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_BLOCKERS))) { // Instant-speed pumps should not be cast outside of combat when the // stack is empty - return sa.isCurse() || SpellAbilityAi.isSorcerySpeed(sa) || main1Preferred; + return sa.isCurse() || SpellAbilityAi.isSorcerySpeed(sa, ai) || main1Preferred; } return true; } @@ -364,7 +364,7 @@ public class PumpAi extends PumpAiBase { if (ComputerUtilCard.shouldPumpCard(ai, sa, card, defense, attack, keywords, false)) { return true; } else if (containsUsefulKeyword(ai, keywords, card, sa, attack)) { - if (game.getPhaseHandler().isPreCombatMain() && SpellAbilityAi.isSorcerySpeed(sa) || + if (game.getPhaseHandler().isPreCombatMain() && SpellAbilityAi.isSorcerySpeed(sa, ai) || game.getPhaseHandler().is(PhaseType.COMBAT_DECLARE_ATTACKERS, ai) || game.getPhaseHandler().is(PhaseType.COMBAT_BEGIN, ai)) { Card pumped = ComputerUtilCard.getPumpedCreature(ai, sa, card, 0, 0, keywords); diff --git a/forge-ai/src/main/java/forge/ai/ability/ScryAi.java b/forge-ai/src/main/java/forge/ai/ability/ScryAi.java index f3b160a1f86..e65b97f8e38 100644 --- a/forge-ai/src/main/java/forge/ai/ability/ScryAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/ScryAi.java @@ -55,7 +55,7 @@ public class ScryAi extends SpellAbilityAi { // even if there's no mana cost. if (sa.getPayCosts().hasTapCost() && (sa.getPayCosts().hasManaCost() || (sa.getHostCard() != null && sa.getHostCard().isCreature())) - && !SpellAbilityAi.isSorcerySpeed(sa)) { + && !SpellAbilityAi.isSorcerySpeed(sa, ai)) { return ph.getNextTurn() == ai && ph.is(PhaseType.END_OF_TURN); } @@ -67,7 +67,7 @@ public class ScryAi extends SpellAbilityAi { // in the playerturn Scry should only be done in Main1 or in upkeep if able if (ph.isPlayerTurn(ai)) { - if (SpellAbilityAi.isSorcerySpeed(sa)) { + if (SpellAbilityAi.isSorcerySpeed(sa, ai)) { return ph.is(PhaseType.MAIN1) || sa.isPwAbility(); } else { return ph.is(PhaseType.UPKEEP); @@ -119,7 +119,7 @@ public class ScryAi extends SpellAbilityAi { } double chance = .4; // 40 percent chance of milling with instant speed stuff - if (SpellAbilityAi.isSorcerySpeed(sa)) { + if (SpellAbilityAi.isSorcerySpeed(sa, ai)) { chance = .667; // 66.7% chance for sorcery speed (since it will never activate EOT) } boolean randomReturn = MyRandom.getRandom().nextFloat() <= Math.pow(chance, sa.getActivationsThisTurn() + 1); diff --git a/forge-ai/src/main/java/forge/ai/ability/SurveilAi.java b/forge-ai/src/main/java/forge/ai/ability/SurveilAi.java index 573ab9e993e..45bc7c9f67c 100644 --- a/forge-ai/src/main/java/forge/ai/ability/SurveilAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/SurveilAi.java @@ -52,13 +52,13 @@ public class SurveilAi extends SpellAbilityAi { // even if there's no mana cost. if (sa.getPayCosts().hasTapCost() && (sa.getPayCosts().hasManaCost() || (sa.getHostCard() != null && sa.getHostCard().isCreature())) - && !SpellAbilityAi.isSorcerySpeed(sa)) { + && !SpellAbilityAi.isSorcerySpeed(sa, ai)) { return ph.getNextTurn() == ai && ph.is(PhaseType.END_OF_TURN); } // in the player's turn Surveil should only be done in Main1 or in Upkeep if able if (ph.isPlayerTurn(ai)) { - if (SpellAbilityAi.isSorcerySpeed(sa)) { + if (SpellAbilityAi.isSorcerySpeed(sa, ai)) { return ph.is(PhaseType.MAIN1) || sa.isPwAbility(); } else { return ph.is(PhaseType.UPKEEP); @@ -102,7 +102,7 @@ public class SurveilAi extends SpellAbilityAi { } double chance = .4; // 40 percent chance for instant speed - if (SpellAbilityAi.isSorcerySpeed(sa)) { + if (SpellAbilityAi.isSorcerySpeed(sa, ai)) { chance = .667; // 66.7% chance for sorcery speed (since it will never activate EOT) } diff --git a/forge-ai/src/main/java/forge/ai/ability/TapAi.java b/forge-ai/src/main/java/forge/ai/ability/TapAi.java index 6d23c619f4c..e9a703e5086 100644 --- a/forge-ai/src/main/java/forge/ai/ability/TapAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/TapAi.java @@ -18,7 +18,7 @@ public class TapAi extends TapAiBase { if (turn.isOpponentOf(ai) && phase.getPhase().isBefore(PhaseType.COMBAT_DECLARE_ATTACKERS)) { // Tap things down if it's Human's turn } else if (turn.equals(ai)) { - if (SpellAbilityAi.isSorcerySpeed(sa) && phase.getPhase().isBefore(PhaseType.COMBAT_BEGIN)) { + if (SpellAbilityAi.isSorcerySpeed(sa, ai) && phase.getPhase().isBefore(PhaseType.COMBAT_BEGIN)) { // Cast it if it's a sorcery. } else if (phase.getPhase().isBefore(PhaseType.COMBAT_DECLARE_BLOCKERS)) { // Aggro Brains are willing to use TapEffects aggressively instead of defensively 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 e7a17fb2c9c..4605604cabc 100644 --- a/forge-ai/src/main/java/forge/ai/ability/TokenAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/TokenAi.java @@ -134,7 +134,7 @@ public class TokenAi extends SpellAbilityAi { } } if ((ph.isPlayerTurn(ai) || ph.getPhase().isBefore(PhaseType.COMBAT_DECLARE_ATTACKERS)) - && !sa.hasParam("ActivationPhases") && !sa.hasParam("PlayerTurn") && !SpellAbilityAi.isSorcerySpeed(sa) + && !sa.hasParam("ActivationPhases") && !sa.hasParam("PlayerTurn") && !SpellAbilityAi.isSorcerySpeed(sa, ai) && !haste && !pwMinus) { return false; } diff --git a/forge-ai/src/main/java/forge/ai/ability/VentureAi.java b/forge-ai/src/main/java/forge/ai/ability/VentureAi.java index 5d4b3fe95c5..0a3197fac13 100644 --- a/forge-ai/src/main/java/forge/ai/ability/VentureAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/VentureAi.java @@ -20,7 +20,7 @@ public class VentureAi extends SpellAbilityAi { // If this has a mana cost, do it at opponent's EOT if able to prevent spending mana early; if sorcery, do it in Main2 PhaseHandler ph = aiPlayer.getGame().getPhaseHandler(); if (sa.getPayCosts().hasManaCost() || sa.getPayCosts().hasTapCost()) { - if (SpellAbilityAi.isSorcerySpeed(sa)) { + if (SpellAbilityAi.isSorcerySpeed(sa, aiPlayer)) { return ph.is(PhaseType.MAIN2, aiPlayer); } else { return ph.is(PhaseType.END_OF_TURN) && ph.getNextTurn() == aiPlayer; diff --git a/forge-ai/src/main/java/forge/ai/simulation/SpellAbilityPicker.java b/forge-ai/src/main/java/forge/ai/simulation/SpellAbilityPicker.java index 36157977d19..11921d1c5c9 100644 --- a/forge-ai/src/main/java/forge/ai/simulation/SpellAbilityPicker.java +++ b/forge-ai/src/main/java/forge/ai/simulation/SpellAbilityPicker.java @@ -154,7 +154,7 @@ public class SpellAbilityPicker { return !sa.withFlash(sa.getHostCard(), player); } if (sa.isPwAbility()) { - return !sa.getHostCard().hasKeyword("CARDNAME's loyalty abilities can be activated at instant speed."); + return !sa.withFlash(sa.getHostCard(), player); } return sa.isActivatedAbility() && sa.getRestrictions().isSorcerySpeed(); } 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 598623e4d9b..8468a87d3e0 100644 --- a/forge-game/src/main/java/forge/game/spellability/SpellAbility.java +++ b/forge-game/src/main/java/forge/game/spellability/SpellAbility.java @@ -2393,8 +2393,8 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit } if (isActivatedAbility()) { - // Activated Abillties are instant speed per default - return !getRestrictions().isSorcerySpeed(); + // Activated Abilities are instant speed per default, except Planeswalker abilities + return !isPwAbility() && !getRestrictions().isSorcerySpeed(); } return true; } @@ -2403,10 +2403,8 @@ public abstract class SpellAbility extends CardTraitBase implements ISpellAbilit if (getRestrictions().isInstantSpeed()) { return true; } - if (isSpell()) { - if (hasSVar("IsCastFromPlayEffect") || host.isInstant() || host.hasKeyword(Keyword.FLASH)) { - return true; - } + if (isSpell() && (hasSVar("IsCastFromPlayEffect") || host.isInstant() || host.hasKeyword(Keyword.FLASH))) { + return true; } return StaticAbilityCastWithFlash.anyWithFlash(this, host, activator); diff --git a/forge-game/src/main/java/forge/game/spellability/SpellAbilityRestriction.java b/forge-game/src/main/java/forge/game/spellability/SpellAbilityRestriction.java index 968dc022c3d..e9f296f5c7e 100644 --- a/forge-game/src/main/java/forge/game/spellability/SpellAbilityRestriction.java +++ b/forge-game/src/main/java/forge/game/spellability/SpellAbilityRestriction.java @@ -479,11 +479,6 @@ public class SpellAbilityRestriction extends SpellAbilityVariables { } if (sa.isPwAbility()) { - if (!c.hasKeyword("CARDNAME's loyalty abilities can be activated at instant speed.") - && !activator.canCastSorcery()) { - return false; - } - final int initialLimit = c.hasKeyword("CARDNAME's loyalty abilities can be activated twice each turn rather than only once") ? 1 : 0; final int limits = c.getAmountOfKeyword("May activate CARDNAME's loyalty abilities once") + initialLimit; diff --git a/forge-game/src/main/java/forge/game/staticability/StaticAbilityCastWithFlash.java b/forge-game/src/main/java/forge/game/staticability/StaticAbilityCastWithFlash.java index c1b51e90d34..4c1ea86c7a1 100644 --- a/forge-game/src/main/java/forge/game/staticability/StaticAbilityCastWithFlash.java +++ b/forge-game/src/main/java/forge/game/staticability/StaticAbilityCastWithFlash.java @@ -1,9 +1,6 @@ package forge.game.staticability; -import com.google.common.collect.Iterables; - import forge.game.Game; -import forge.game.GameObjectPredicates; import forge.game.card.Card; import forge.game.card.CardCollection; import forge.game.player.Player; @@ -72,7 +69,6 @@ public class StaticAbilityCastWithFlash { } public static boolean applyWithFlashAbility(final StaticAbility stAb, final SpellAbility sa, final Card card, final Player activator) { - final Card hostCard = stAb.getHostCard(); if (!commonParts(stAb, sa, card, activator)) { return false; @@ -83,8 +79,7 @@ public class StaticAbilityCastWithFlash { return false; } - String[] valids = stAb.getParam("Targeting").split(","); - if (!Iterables.any(sa.getTargets(), GameObjectPredicates.restriction(valids, hostCard.getController(), hostCard, stAb))) { + if (!stAb.matchesValidParam("Targeting", sa.getTargets())) { return false; } } diff --git a/forge-gui/res/cardsfolder/t/teferi_master_of_time.txt b/forge-gui/res/cardsfolder/t/teferi_master_of_time.txt index 83b7579a443..541c6231dcc 100755 --- a/forge-gui/res/cardsfolder/t/teferi_master_of_time.txt +++ b/forge-gui/res/cardsfolder/t/teferi_master_of_time.txt @@ -2,7 +2,7 @@ Name:Teferi, Master of Time ManaCost:2 U U Types:Legendary Planeswalker Teferi Loyalty:3 -S:Mode$ Continuous | Affected$ Card.Self | AddHiddenKeyword$ CARDNAME's loyalty abilities can be activated at instant speed. | Description$ You may activate loyalty abilities of CARDNAME on any player's turn any time you could cast an instant. +S:Mode$ CastWithFlash | ValidCard$ Card.Self | ValidSA$ Activated.Loyalty | Caster$ You | Description$ You may activate loyalty abilities of CARDNAME on any player's turn any time you could cast an instant. A:AB$ Draw | Cost$ AddCounter<1/LOYALTY> | Planeswalker$ True | NumCards$ 1 | SubAbility$ DBDiscard | SpellDescription$ Draw a card, then discard a card. SVar:DBDiscard:DB$ Discard | Defined$ You | NumCards$ 1 | Mode$ TgtChoose A:AB$ Phases | Cost$ SubCounter<3/LOYALTY> | Planeswalker$ True | ValidTgts$ Creature.YouDontCtrl | TgtPrompt$ Select target creature you don't control | IsCurse$ True | SpellDescription$ Target creature you don't control phases out. (Treat it and anything attached to it as though they don't exist until its controllers's next turn.) diff --git a/forge-gui/res/cardsfolder/t/teferi_temporal_archmage.txt b/forge-gui/res/cardsfolder/t/teferi_temporal_archmage.txt index 52904c6da03..440163e32a3 100644 --- a/forge-gui/res/cardsfolder/t/teferi_temporal_archmage.txt +++ b/forge-gui/res/cardsfolder/t/teferi_temporal_archmage.txt @@ -6,6 +6,5 @@ Text:CARDNAME can be your commander. A:AB$ Dig | Cost$ AddCounter<1/LOYALTY> | Planeswalker$ True | DigNum$ 2 | SpellDescription$ Look at the top two cards of your library. Put one of them into your hand and the other on the bottom of your library. A:AB$ Untap | Cost$ SubCounter<1/LOYALTY> | Planeswalker$ True | ValidTgts$ Permanent | TargetMin$ 0 | TargetMax$ 4 | SpellDescription$ Untap up to four target permanents. A:AB$ Effect | Cost$ SubCounter<10/LOYALTY> | Planeswalker$ True | Ultimate$ True | Name$ Emblem - Teferi, Temporal Archmage | Image$ emblem_teferi_temporal_archmage | StaticAbilities$ InstantPlaneswalkers | Stackable$ False | Duration$ Permanent | AILogic$ Always | SpellDescription$ You get an emblem with "You may activate loyalty abilities of planeswalkers you control on any player's turn any time you could cast an instant." -SVar:InstantPlaneswalkers:Mode$ Continuous | EffectZone$ Command | Affected$ Planeswalker.YouCtrl | AddHiddenKeyword$ CARDNAME's loyalty abilities can be activated at instant speed. | Description$ You may activate loyalty abilities of planeswalkers you control on any player's turn any time you could cast an instant. -SVar:Picture:http://www.wizards.com/global/images/magic/general/teferi_temporal_archmage.jpg +SVar:InstantPlaneswalkers:Mode$ CastWithFlash | ValidCard$ Planeswalker.YouCtrl | ValidSA$ Activated.Loyalty | Caster$ You | Description$ You may activate loyalty abilities of planeswalkers you control on any player's turn any time you could cast an instant. Oracle:[+1]: Look at the top two cards of your library. Put one of them into your hand and the other on the bottom of your library.\n[−1]: Untap up to four target permanents.\n[−10]: You get an emblem with "You may activate loyalty abilities of planeswalkers you control on any player's turn any time you could cast an instant."\nTeferi, Temporal Archmage can be your commander.