diff --git a/forge-ai/src/main/java/forge/ai/AiAttackController.java b/forge-ai/src/main/java/forge/ai/AiAttackController.java index 63b58c2ff25..0920da8b4d8 100644 --- a/forge-ai/src/main/java/forge/ai/AiAttackController.java +++ b/forge-ai/src/main/java/forge/ai/AiAttackController.java @@ -23,7 +23,6 @@ import com.google.common.collect.Lists; import forge.ai.ability.AnimateAi; import forge.card.CardTypeView; import forge.game.GameEntity; -import forge.game.ability.AbilityFactory; import forge.game.ability.AbilityUtils; import forge.game.ability.ApiType; import forge.game.ability.effects.ProtectEffect; @@ -196,12 +195,12 @@ public class AiAttackController { for (Card c : ai.getOpponents().getCardsIn(ZoneType.Battlefield)) { for (Trigger t : c.getTriggers()) { if (t.getMode() == TriggerType.Attacks) { - SpellAbility sa = t.getOverridingAbility(); - if (sa == null && t.hasParam("Execute")) { - sa = AbilityFactory.getAbility(c, t.getParam("Execute")); + SpellAbility sa = t.ensureAbility(); + if (sa == null) { + continue; } - if (sa != null && sa.getApi() == ApiType.EachDamage && "TriggeredAttacker".equals(sa.getParam("DefinedPlayers"))) { + if (sa.getApi() == ApiType.EachDamage && "TriggeredAttacker".equals(sa.getParam("DefinedPlayers"))) { List valid = CardLists.getValidCards(c.getController().getCreaturesInPlay(), sa.getParam("ValidCards"), c.getController(), c, sa); // TODO: this assumes that 1 damage is dealt per creature. Improve this to check the parameter/X to determine // how much damage is dealt by each of the creatures in the valid list. @@ -1349,9 +1348,9 @@ public class AiAttackController { if (!TriggerType.Exerted.equals(t.getMode())) { continue; } - SpellAbility sa = t.getOverridingAbility(); + SpellAbility sa = t.ensureAbility(); if (sa == null) { - sa = AbilityFactory.getAbility(c, t.getParam("Execute")); + continue; } if (sa.usesTargeting()) { sa.setActivatingPlayer(c.getController()); diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtil.java b/forge-ai/src/main/java/forge/ai/ComputerUtil.java index aa061068677..e51e0d847a4 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtil.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtil.java @@ -28,7 +28,6 @@ import forge.card.ColorSet; import forge.card.MagicColor; import forge.card.mana.ManaCostShard; import forge.game.*; -import forge.game.ability.AbilityFactory; import forge.game.ability.AbilityKey; import forge.game.ability.AbilityUtils; import forge.game.ability.ApiType; @@ -1468,15 +1467,13 @@ public class ComputerUtil { // Triggered abilities if (c.isCreature() && c.isInZone(ZoneType.Battlefield) && CombatUtil.canAttack(c)) { for (final Trigger t : c.getTriggers()) { - if ("Attacks".equals(t.getParam("Mode")) && t.hasParam("Execute")) { - String exec = c.getSVar(t.getParam("Execute")); - if (!exec.isEmpty()) { - SpellAbility trigSa = AbilityFactory.getAbility(exec, c); - if (trigSa != null && trigSa.getApi() == ApiType.LoseLife - && trigSa.getParamOrDefault("Defined", "").contains("Opponent")) { - trigSa.setHostCard(c); - damage += AbilityUtils.calculateAmount(trigSa.getHostCard(), trigSa.getParam("LifeAmount"), trigSa); - } + if (TriggerType.Attacks.equals(t.getMode())) { + SpellAbility sa = t.ensureAbility(); + if (sa == null) { + continue; + } + if (sa.getApi() == ApiType.LoseLife && sa.getParamOrDefault("Defined", "").contains("Opponent")) { + damage += AbilityUtils.calculateAmount(sa.getHostCard(), sa.getParam("LifeAmount"), sa); } } } @@ -2612,7 +2609,6 @@ public class ComputerUtil { theTriggers.addAll(c.getTriggers()); } for (Trigger trigger : theTriggers) { - Map trigParams = trigger.getMapParams(); final Card source = trigger.getHostCard(); @@ -2622,73 +2618,43 @@ public class ComputerUtil { if (!trigger.requirementsCheck(game)) { continue; } - TriggerType mode = trigger.getMode(); - if (mode != TriggerType.SpellCast) { + if (trigger.getMode() != TriggerType.SpellCast) { continue; } - if (trigParams.containsKey("ValidCard")) { - if (!card.isValid(trigParams.get("ValidCard"), source.getController(), source, sa)) { + if (trigger.hasParam("ValidCard")) { + if (!card.isValid(trigger.getParam("ValidCard"), source.getController(), source, sa)) { continue; } } - if (trigParams.containsKey("ValidActivatingPlayer")) { - if (!player.isValid(trigParams.get("ValidActivatingPlayer"), source.getController(), source, sa)) { + if (trigger.hasParam("ValidActivatingPlayer")) { + if (!player.isValid(trigger.getParam("ValidActivatingPlayer"), source.getController(), source, sa)) { continue; } } - if (!trigParams.containsKey("Execute")) { - // fall back for OverridingAbility - SpellAbility trigSa = trigger.getOverridingAbility(); - if (trigSa == null) { + // fall back for OverridingAbility + SpellAbility trigSa = trigger.ensureAbility(); + if (trigSa == null) { + continue; + } + if (trigSa.getApi() == ApiType.DealDamage) { + if (!"TriggeredActivator".equals(trigSa.getParam("Defined"))) { continue; } - if (trigSa.getApi() == ApiType.DealDamage) { - if (!"TriggeredActivator".equals(trigSa.getParam("Defined"))) { - continue; - } - if (!trigSa.hasParam("NumDmg")) { - continue; - } - damage += ComputerUtilCombat.predictDamageTo(player, - AbilityUtils.calculateAmount(source, trigSa.getParam("NumDmg"), trigSa), source, false); - } else if (trigSa.getApi() == ApiType.LoseLife) { - if (!"TriggeredActivator".equals(trigSa.getParam("Defined"))) { - continue; - } - if (!trigSa.hasParam("LifeAmount")) { - continue; - } - damage += AbilityUtils.calculateAmount(source, trigSa.getParam("LifeAmount"), trigSa); - } - } else { - String ability = source.getSVar(trigParams.get("Execute")); - if (ability.isEmpty()) { + if (!trigSa.hasParam("NumDmg")) { continue; } - - final Map abilityParams = AbilityFactory.getMapParams(ability); - if ((abilityParams.containsKey("AB") && abilityParams.get("AB").equals("DealDamage")) - || (abilityParams.containsKey("DB") && abilityParams.get("DB").equals("DealDamage"))) { - if (!"TriggeredActivator".equals(abilityParams.get("Defined"))) { - continue; - } - if (!abilityParams.containsKey("NumDmg")) { - continue; - } - damage += ComputerUtilCombat.predictDamageTo(player, - AbilityUtils.calculateAmount(source, abilityParams.get("NumDmg"), null), source, false); - } else if ((abilityParams.containsKey("AB") && abilityParams.get("AB").equals("LoseLife")) - || (abilityParams.containsKey("DB") && abilityParams.get("DB").equals("LoseLife"))) { - if (!"TriggeredActivator".equals(abilityParams.get("Defined"))) { - continue; - } - if (!abilityParams.containsKey("LifeAmount")) { - continue; - } - damage += AbilityUtils.calculateAmount(source, abilityParams.get("LifeAmount"), null); + damage += ComputerUtilCombat.predictDamageTo(player, + AbilityUtils.calculateAmount(source, trigSa.getParam("NumDmg"), trigSa), source, false); + } else if (trigSa.getApi() == ApiType.LoseLife) { + if (!"TriggeredActivator".equals(trigSa.getParam("Defined"))) { + continue; } + if (!trigSa.hasParam("LifeAmount")) { + continue; + } + damage += AbilityUtils.calculateAmount(source, trigSa.getParam("LifeAmount"), trigSa); } } @@ -2704,7 +2670,6 @@ public class ComputerUtil { theTriggers.addAll(card.getTriggers()); } for (Trigger trigger : theTriggers) { - Map trigParams = trigger.getMapParams(); final Card source = trigger.getHostCard(); @@ -2714,74 +2679,43 @@ public class ComputerUtil { if (!trigger.requirementsCheck(game)) { continue; } - if (trigParams.containsKey("CheckOnTriggeredCard") - && AbilityUtils.getDefinedCards(permanent, source.getSVar(trigParams.get("CheckOnTriggeredCard").split(" ")[0]), null).isEmpty()) { + if (trigger.hasParam("CheckOnTriggeredCard") + && AbilityUtils.getDefinedCards(permanent, source.getSVar(trigger.getParam("CheckOnTriggeredCard").split(" ")[0]), null).isEmpty()) { continue; } - TriggerType mode = trigger.getMode(); - if (mode != TriggerType.ChangesZone) { + if (trigger.getMode() != TriggerType.ChangesZone) { continue; } - if (!"Battlefield".equals(trigParams.get("Destination"))) { + if (!"Battlefield".equals(trigger.getParam("Destination"))) { continue; } - if (trigParams.containsKey("ValidCard")) { - if (!permanent.isValid(trigParams.get("ValidCard"), source.getController(), source, null)) { + if (trigger.hasParam("ValidCard")) { + if (!permanent.isValid(trigger.getParam("ValidCard"), source.getController(), source, null)) { continue; } } - if (!trigParams.containsKey("Execute")) { - // fall back for OverridingAbility - SpellAbility trigSa = trigger.getOverridingAbility(); - if (trigSa == null) { + // fall back for OverridingAbility + SpellAbility trigSa = trigger.ensureAbility(); + if (trigSa == null) { + continue; + } + if (trigSa.getApi() == ApiType.DealDamage) { + if (!"TriggeredCardController".equals(trigSa.getParam("Defined"))) { continue; } - if (trigSa.getApi() == ApiType.DealDamage) { - if (!"TriggeredCardController".equals(trigSa.getParam("Defined"))) { - continue; - } - if (!trigSa.hasParam("NumDmg")) { - continue; - } - damage += ComputerUtilCombat.predictDamageTo(player, - AbilityUtils.calculateAmount(source, trigSa.getParam("NumDmg"), trigSa), source, false); - } else if (trigSa.getApi() == ApiType.LoseLife) { - if (!"TriggeredCardController".equals(trigSa.getParam("Defined"))) { - continue; - } - if (!trigSa.hasParam("LifeAmount")) { - continue; - } - damage += AbilityUtils.calculateAmount(source, trigSa.getParam("LifeAmount"), trigSa); - } - } else { - String ability = source.getSVar(trigParams.get("Execute")); - if (ability.isEmpty()) { + if (!trigSa.hasParam("NumDmg")) { continue; } - - final Map abilityParams = AbilityFactory.getMapParams(ability); - // Destroy triggers - if ((abilityParams.containsKey("AB") && abilityParams.get("AB").equals("DealDamage")) - || (abilityParams.containsKey("DB") && abilityParams.get("DB").equals("DealDamage"))) { - if (!"TriggeredCardController".equals(abilityParams.get("Defined"))) { - continue; - } - if (!abilityParams.containsKey("NumDmg")) { - continue; - } - damage += ComputerUtilCombat.predictDamageTo(player, - AbilityUtils.calculateAmount(source, abilityParams.get("NumDmg"), null), source, false); - } else if ((abilityParams.containsKey("AB") && abilityParams.get("AB").equals("LoseLife")) - || (abilityParams.containsKey("DB") && abilityParams.get("DB").equals("LoseLife"))) { - if (!"TriggeredCardController".equals(abilityParams.get("Defined"))) { - continue; - } - if (!abilityParams.containsKey("LifeAmount")) { - continue; - } - damage += AbilityUtils.calculateAmount(source, abilityParams.get("LifeAmount"), null); + damage += ComputerUtilCombat.predictDamageTo(player, + AbilityUtils.calculateAmount(source, trigSa.getParam("NumDmg"), trigSa), source, false); + } else if (trigSa.getApi() == ApiType.LoseLife) { + if (!"TriggeredCardController".equals(trigSa.getParam("Defined"))) { + continue; } + if (!trigSa.hasParam("LifeAmount")) { + continue; + } + damage += AbilityUtils.calculateAmount(source, trigSa.getParam("LifeAmount"), trigSa); } } return damage; diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java b/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java index 69b9191e6b4..109c5dfc423 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java @@ -14,7 +14,6 @@ import forge.deck.Deck; import forge.deck.DeckSection; import forge.game.Game; import forge.game.GameObject; -import forge.game.ability.AbilityFactory; import forge.game.ability.AbilityUtils; import forge.game.ability.ApiType; import forge.game.card.*; @@ -697,39 +696,21 @@ public class ComputerUtilCard { } // same for Trigger that does make Tokens for(Trigger t:c.getTriggers()){ - SpellAbility sa = t.getOverridingAbility(); - String sTokenTypes = null; + SpellAbility sa = t.ensureAbility(); if (sa != null) { if (sa.getApi() != ApiType.Token || !sa.hasParam("TokenTypes")) { continue; } - sTokenTypes = sa.getParam("TokenTypes"); - } else if (t.hasParam("Execute")) { - String name = t.getParam("Execute"); - if (!c.hasSVar(name)) { - continue; + for (String var : sa.getParam("TokenTypes").split(",")) { + if (!CardType.isACreatureType(var)) { + continue; + } + Integer count = typesInDeck.get(var); + if (count == null) { + count = 0; + } + typesInDeck.put(var, count + 1); } - - Map params = AbilityFactory.getMapParams(c.getSVar(name)); - if (!params.containsKey("TokenTypes")) { - continue; - } - sTokenTypes = params.get("TokenTypes"); - } - - if (sTokenTypes == null) { - continue; - } - - for (String var : sTokenTypes.split(",")) { - if (!CardType.isACreatureType(var)) { - continue; - } - Integer count = typesInDeck.get(var); - if (count == null) { - count = 0; - } - typesInDeck.put(var, count + 1); } } // special rule for Fabricate and Servo diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtilCombat.java b/forge-ai/src/main/java/forge/ai/ComputerUtilCombat.java index 753aaf17989..fe240b141db 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtilCombat.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtilCombat.java @@ -28,7 +28,6 @@ import com.google.common.collect.Maps; import forge.game.CardTraitBase; import forge.game.Game; import forge.game.GameEntity; -import forge.game.ability.AbilityFactory; import forge.game.ability.AbilityKey; import forge.game.ability.AbilityUtils; import forge.game.ability.ApiType; @@ -46,7 +45,6 @@ import forge.game.replacement.ReplacementType; import forge.game.spellability.SpellAbility; import forge.game.staticability.StaticAbility; import forge.game.trigger.Trigger; -import forge.game.trigger.TriggerHandler; import forge.game.trigger.TriggerType; import forge.game.zone.ZoneType; import forge.util.MyRandom; @@ -1274,44 +1272,26 @@ public class ComputerUtilCombat { } for (final Trigger trigger : theTriggers) { - final Map trigParams = trigger.getMapParams(); final Card source = trigger.getHostCard(); if (!ComputerUtilCombat.combatTriggerWillTrigger(attacker, blocker, trigger, combat)) { continue; } - Map abilityParams = null; - if (trigger.getOverridingAbility() != null) { - abilityParams = trigger.getOverridingAbility().getMapParams(); - } else if (trigParams.containsKey("Execute")) { - final String ability = source.getSVar(trigParams.get("Execute")); - abilityParams = AbilityFactory.getMapParams(ability); - } else { + SpellAbility sa = trigger.ensureAbility(); + if (sa == null) { continue; } - if (abilityParams.containsKey("ValidTgts") || abilityParams.containsKey("Tgt")) { + if (sa.usesTargeting()) { continue; // targeted pumping not supported } - if (abilityParams.containsKey("AB") && !abilityParams.get("AB").equals("Pump") - && !abilityParams.get("AB").equals("PumpAll")) { - continue; - } - if (abilityParams.containsKey("DB") && !abilityParams.get("DB").equals("Pump") - && !abilityParams.get("DB").equals("PumpAll")) { + + if (!ApiType.Pump.equals(sa.getApi()) && !ApiType.PumpAll.equals(sa.getApi())) { continue; } - if (abilityParams.containsKey("Cost")) { - SpellAbility sa = null; - if (trigger.getOverridingAbility() != null) { - sa = trigger.getOverridingAbility(); - } else { - final String ability = source.getSVar(trigParams.get("Execute")); - sa = AbilityFactory.getAbility(ability, source); - } - + if (sa.hasParam("Cost")) { sa.setActivatingPlayer(source.getController()); if (!CostPayment.canPayAdditionalCosts(sa.getPayCosts(), sa)) { continue; @@ -1319,15 +1299,15 @@ public class ComputerUtilCombat { } List list = Lists.newArrayList(); - if (!abilityParams.containsKey("ValidCards")) { - list = AbilityUtils.getDefinedCards(source, abilityParams.get("Defined"), null); + if (!sa.hasParam("ValidCards")) { + list = AbilityUtils.getDefinedCards(source, sa.getParam("Defined"), null); } - if (abilityParams.containsKey("Defined") && abilityParams.get("Defined").equals("TriggeredAttacker")) { + if (sa.hasParam("Defined") && sa.getParam("Defined").equals("TriggeredAttacker")) { list.add(attacker); } - if (abilityParams.containsKey("ValidCards")) { - if (attacker.isValid(abilityParams.get("ValidCards").split(","), source.getController(), source, null) - || attacker.isValid(abilityParams.get("ValidCards").replace("attacking+", "").split(","), + if (sa.hasParam("ValidCards")) { + if (attacker.isValid(sa.getParam("ValidCards").split(","), source.getController(), source, null) + || attacker.isValid(sa.getParam("ValidCards").replace("attacking+", "").split(","), source.getController(), source, null)) { list.add(attacker); } @@ -1338,11 +1318,11 @@ public class ComputerUtilCombat { if (!list.contains(attacker)) { continue; } - if (!abilityParams.containsKey("NumAtt")) { + if (!sa.hasParam("NumAtt")) { continue; } - String att = abilityParams.get("NumAtt"); + String att = sa.getParam("NumAtt"); if (att.startsWith("+")) { att = att.substring(1); } @@ -1657,35 +1637,26 @@ public class ComputerUtilCombat { theTriggers.addAll(card.getTriggers()); } for (Trigger trigger : theTriggers) { - Map trigParams = trigger.getMapParams(); final Card source = trigger.getHostCard(); if (!ComputerUtilCombat.combatTriggerWillTrigger(attacker, blocker, trigger, null)) { continue; } - //consider delayed triggers - if (trigParams.containsKey("DelayedTrigger")) { - String sVarName = trigParams.get("DelayedTrigger"); - trigger = TriggerHandler.parseTrigger(source.getSVar(sVarName), trigger.getHostCard(), true); - trigParams = trigger.getMapParams(); - } - if (!trigParams.containsKey("Execute")) { + SpellAbility sa = trigger.ensureAbility(); + if (sa == null) { continue; } - String ability = source.getSVar(trigParams.get("Execute")); - final Map abilityParams = AbilityFactory.getMapParams(ability); - if ((abilityParams.containsKey("AB") && abilityParams.get("AB").equals("Destroy")) - || (abilityParams.containsKey("DB") && abilityParams.get("DB").equals("Destroy"))) { - if (!abilityParams.containsKey("Defined")) { + if (ApiType.Destroy.equals(sa.getApi())) { + if (!sa.hasParam("Defined")) { continue; } - if (abilityParams.get("Defined").equals("TriggeredAttacker")) { + if (sa.getParam("Defined").equals("TriggeredAttacker")) { return true; } - if (abilityParams.get("Defined").equals("Self") && source.equals(attacker)) { + if (sa.getParam("Defined").equals("Self") && source.equals(attacker)) { return true; } - if (abilityParams.get("Defined").equals("TriggeredTarget") && source.equals(blocker)) { + if (sa.getParam("Defined").equals("TriggeredTarget") && source.equals(blocker)) { return true; } } @@ -1935,36 +1906,27 @@ public class ComputerUtilCombat { theTriggers.addAll(card.getTriggers()); } for (Trigger trigger : theTriggers) { - Map trigParams = trigger.getMapParams(); final Card source = trigger.getHostCard(); if (!ComputerUtilCombat.combatTriggerWillTrigger(attacker, blocker, trigger, null)) { continue; } - //consider delayed triggers - if (trigParams.containsKey("DelayedTrigger")) { - String sVarName = trigParams.get("DelayedTrigger"); - trigger = TriggerHandler.parseTrigger(source.getSVar(sVarName), trigger.getHostCard(), true); - trigParams = trigger.getMapParams(); - } - if (!trigParams.containsKey("Execute")) { + SpellAbility sa = trigger.ensureAbility(); + if (sa == null) { continue; } - String ability = source.getSVar(trigParams.get("Execute")); - final Map abilityParams = AbilityFactory.getMapParams(ability); // Destroy triggers - if ((abilityParams.containsKey("AB") && abilityParams.get("AB").equals("Destroy")) - || (abilityParams.containsKey("DB") && abilityParams.get("DB").equals("Destroy"))) { - if (!abilityParams.containsKey("Defined")) { + if (ApiType.Destroy.equals(sa.getApi())) { + if (!sa.hasParam("Defined")) { continue; } - if (abilityParams.get("Defined").equals("TriggeredBlocker")) { + if (sa.getParam("Defined").equals("TriggeredBlocker")) { return true; } - if (abilityParams.get("Defined").equals("Self") && source.equals(blocker)) { + if (sa.getParam("Defined").equals("Self") && source.equals(blocker)) { return true; } - if (abilityParams.get("Defined").equals("TriggeredTarget") && source.equals(attacker)) { + if (sa.getParam("Defined").equals("TriggeredTarget") && source.equals(attacker)) { return true; } } @@ -2578,26 +2540,24 @@ public class ComputerUtilCombat { // Test for some special triggers that can change the creature in combat for (Trigger t : attacker.getTriggers()) { - if (t.getMode() == TriggerType.Attacks && t.hasParam("Execute")) { - if (!attacker.hasSVar(t.getParam("Execute"))) { + if (t.getMode() == TriggerType.Attacks) { + SpellAbility exec = t.ensureAbility(); + if (exec == null) { continue; } - SpellAbility exec = AbilityFactory.getAbility(attacker, t.getParam("Execute")); - if (exec != null) { - if (exec.getApi() == ApiType.Clone && "Self".equals(exec.getParam("CloneTarget")) - && exec.hasParam("ValidTgts") && exec.getParam("ValidTgts").contains("Creature") - && exec.getParam("ValidTgts").contains("attacking")) { - // Tilonalli's Skinshifter and potentially other similar cards that can clone other stuff - // while attacking - if (exec.getParam("ValidTgts").contains("nonLegendary") && attacker.getType().isLegendary()) { - continue; - } - int maxPwr = 0; - for (Card c : attacker.getController().getCreaturesInPlay()) { - if (c.getNetPower() > maxPwr || (c.getNetPower() == maxPwr && ComputerUtilCard.evaluateCreature(c) > ComputerUtilCard.evaluateCreature(attackerAfterTrigs))) { - maxPwr = c.getNetPower(); - attackerAfterTrigs = c; - } + if (exec.getApi() == ApiType.Clone && "Self".equals(exec.getParam("CloneTarget")) + && exec.hasParam("ValidTgts") && exec.getParam("ValidTgts").contains("Creature") + && exec.getParam("ValidTgts").contains("attacking")) { + // Tilonalli's Skinshifter and potentially other similar cards that can clone other stuff + // while attacking + if (exec.getParam("ValidTgts").contains("nonLegendary") && attacker.getType().isLegendary()) { + continue; + } + int maxPwr = 0; + for (Card c : attacker.getController().getCreaturesInPlay()) { + if (c.getNetPower() > maxPwr || (c.getNetPower() == maxPwr && ComputerUtilCard.evaluateCreature(c) > ComputerUtilCard.evaluateCreature(attackerAfterTrigs))) { + maxPwr = c.getNetPower(); + attackerAfterTrigs = c; } } } diff --git a/forge-ai/src/main/java/forge/ai/SpecialCardAi.java b/forge-ai/src/main/java/forge/ai/SpecialCardAi.java index 2173f53bb8f..ecbd71f2fe4 100644 --- a/forge-ai/src/main/java/forge/ai/SpecialCardAi.java +++ b/forge-ai/src/main/java/forge/ai/SpecialCardAi.java @@ -26,7 +26,6 @@ import forge.card.MagicColor; import forge.card.mana.ManaCost; import forge.game.Game; import forge.game.GameType; -import forge.game.ability.AbilityFactory; import forge.game.ability.AbilityUtils; import forge.game.ability.ApiType; import forge.game.card.*; @@ -636,10 +635,7 @@ public class SpecialCardAi { boolean canRetFromGrave = false; String name = c.getName().replace(',', ';'); for (Trigger t : c.getTriggers()) { - SpellAbility ab = null; - if (t.hasParam("Execute")) { - ab = AbilityFactory.getAbility(c.getSVar(t.getParam("Execute")), c); - } + SpellAbility ab = t.ensureAbility(); if (ab == null) { continue; } if (ab.getApi() == ApiType.ChangeZone 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 b3ae3638b6f..ed7d26cc043 100644 --- a/forge-ai/src/main/java/forge/ai/ability/AttachAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/AttachAi.java @@ -1086,12 +1086,7 @@ public class AttachAi extends SpellAbilityAi { final Map params = t.getMapParams(); if ("Card.Self".equals(params.get("ValidCard")) && "Battlefield".equals(params.get("Destination"))) { - SpellAbility trigSa = null; - if (t.hasParam("Execute") && attachSource.hasSVar(t.getParam("Execute"))) { - trigSa = AbilityFactory.getAbility(attachSource.getSVar(params.get("Execute")), attachSource); - } else if (t.getOverridingAbility() != null) { - trigSa = t.getOverridingAbility(); - } + SpellAbility trigSa = t.ensureAbility(); if (trigSa != null && trigSa.getApi() == ApiType.DealDamage && "Enchanted".equals(trigSa.getParam("Defined"))) { for (Card target : list) { if (!target.getController().isOpponentOf(ai)) { 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 1923fb877ac..c364996f774 100644 --- a/forge-ai/src/main/java/forge/ai/ability/ChangeZoneAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/ChangeZoneAi.java @@ -1811,7 +1811,7 @@ public class ChangeZoneAi extends SpellAbilityAi { } else // This is an intrinsic effect that blinks the card (e.g. Obzedat, Ghost Council), no need to // return the commander to the Command zone. if (subApi == ApiType.DelayedTrigger) { - SpellAbility exec = causeSub.getAdditionalAbility("Execute"); + SpellAbility exec = causeSub.getAdditionalAbility("Execute"); if (exec != null && exec.getApi() == ApiType.ChangeZone) { // A blink effect implemented using a delayed trigger return !"Exile".equals(exec.getParam("Origin")) || !"Battlefield".equals(exec.getParam("Destination")); diff --git a/forge-ai/src/main/java/forge/ai/ability/DelayedTriggerAi.java b/forge-ai/src/main/java/forge/ai/ability/DelayedTriggerAi.java index bd22d902785..bede875289d 100644 --- a/forge-ai/src/main/java/forge/ai/ability/DelayedTriggerAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/DelayedTriggerAi.java @@ -3,7 +3,6 @@ package forge.ai.ability; import com.google.common.base.Predicate; import forge.ai.*; import forge.card.mana.ManaCost; -import forge.game.ability.AbilityFactory; import forge.game.ability.ApiType; import forge.game.card.Card; import forge.game.card.CardLists; @@ -22,11 +21,9 @@ public class DelayedTriggerAi extends SpellAbilityAi { // TODO: improve ai return true; } - SpellAbility trigsa = null; - if (sa.hasAdditionalAbility("Execute")) { - trigsa = sa.getAdditionalAbility("Execute"); - } else { - trigsa = AbilityFactory.getAbility(sa.getHostCard(), sa.getParam("Execute")); + SpellAbility trigsa = sa.getAdditionalAbility("Execute"); + if (trigsa == null) { + return false; } trigsa.setActivatingPlayer(ai); @@ -39,12 +36,11 @@ public class DelayedTriggerAi extends SpellAbilityAi { @Override protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) { - SpellAbility trigsa = null; - if (sa.hasAdditionalAbility("Execute")) { - trigsa = sa.getAdditionalAbility("Execute"); - } else { - trigsa = AbilityFactory.getAbility(sa.getHostCard(), sa.getParam("Execute")); + SpellAbility trigsa = sa.getAdditionalAbility("Execute"); + if (trigsa == null) { + return false; } + AiController aic = ((PlayerControllerAi)ai.getController()).getAi(); trigsa.setActivatingPlayer(ai); @@ -143,11 +139,9 @@ public class DelayedTriggerAi extends SpellAbilityAi { } // Generic logic - SpellAbility trigsa = null; - if (sa.hasAdditionalAbility("Execute")) { - trigsa = sa.getAdditionalAbility("Execute"); - } else { - trigsa = AbilityFactory.getAbility(sa.getHostCard(), sa.getParam("Execute")); + SpellAbility trigsa = sa.getAdditionalAbility("Execute"); + if (trigsa == null) { + return false; } trigsa.setActivatingPlayer(ai); return AiPlayDecision.WillPlay == ((PlayerControllerAi)ai.getController()).getAi().canPlaySa(trigsa); diff --git a/forge-ai/src/main/java/forge/ai/ability/FightAi.java b/forge-ai/src/main/java/forge/ai/ability/FightAi.java index 1b95b09c1ca..714376bb3a8 100644 --- a/forge-ai/src/main/java/forge/ai/ability/FightAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/FightAi.java @@ -3,6 +3,7 @@ package forge.ai.ability; import forge.ai.*; import forge.game.ability.AbilityFactory; import forge.game.ability.AbilityUtils; +import forge.game.ability.ApiType; import forge.game.card.Card; import forge.game.card.CardCollection; import forge.game.card.CardCollectionView; @@ -193,7 +194,7 @@ public class FightAi extends SpellAbilityAi { for (Card humanCreature : humCreatures) { for (Card aiCreature : aiCreatures) { if (source.isSpell()) { // heroic triggers adding counters and prowess - final int bonus = getSpellBonus(aiCreature); + final int bonus = getSpellBonus(aiCreature); power += bonus; toughness += bonus; } @@ -247,28 +248,32 @@ public class FightAi extends SpellAbilityAi { return false; } - /** - * Compute the bonus from Heroic +1/+1 counters or Prowess - */ - private static int getSpellBonus(final Card aiCreature) { - for (Trigger t : aiCreature.getTriggers()) { - if (t.getMode() == TriggerType.SpellCast) { - final Map params = t.getMapParams(); - if ("Card.Self".equals(params.get("TargetsValid")) && "You".equals(params.get("ValidActivatingPlayer")) - && params.containsKey("Execute")) { - SpellAbility heroic = AbilityFactory.getAbility(aiCreature.getSVar(params.get("Execute")),aiCreature); - if ("Self".equals(heroic.getParam("Defined")) && "P1P1".equals(heroic.getParam("CounterType"))) { - return AbilityUtils.calculateAmount(aiCreature, heroic.getParam("CounterNum"), heroic); - } - break; - } - if ("ProwessPump".equals(params.get("Execute"))) { - return 1; - } - } - } - return 0; - } + /** + * Compute the bonus from Heroic +1/+1 counters or Prowess + */ + private static int getSpellBonus(final Card aiCreature) { + for (Trigger t : aiCreature.getTriggers()) { + if (t.getMode() == TriggerType.SpellCast) { + SpellAbility sa = t.ensureAbility(); + final Map params = t.getMapParams(); + if (sa == null) { + continue; + } + if (ApiType.PutCounter.equals(sa.getApi())) { + if ("Card.Self".equals(params.get("TargetsValid")) && "You".equals(params.get("ValidActivatingPlayer"))) { + SpellAbility heroic = AbilityFactory.getAbility(aiCreature.getSVar(params.get("Execute")),aiCreature); + if ("Self".equals(heroic.getParam("Defined")) && "P1P1".equals(heroic.getParam("CounterType"))) { + return AbilityUtils.calculateAmount(aiCreature, heroic.getParam("CounterNum"), heroic); + } + break; + } + } else if (ApiType.Pump.equals(sa.getApi())) { + // TODO add prowess boost + } + } + } + return 0; + } private static boolean shouldFight(Card fighter, Card opponent, int pumpAttack, int pumpDefense) { if (canKill(fighter, opponent, pumpAttack)) { diff --git a/forge-ai/src/main/java/forge/ai/ability/ImmediateTriggerAi.java b/forge-ai/src/main/java/forge/ai/ability/ImmediateTriggerAi.java index 2619018d4bc..b241dd8f7bf 100644 --- a/forge-ai/src/main/java/forge/ai/ability/ImmediateTriggerAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/ImmediateTriggerAi.java @@ -1,7 +1,6 @@ package forge.ai.ability; import forge.ai.*; -import forge.game.ability.AbilityFactory; import forge.game.player.Player; import forge.game.spellability.AbilitySub; import forge.game.spellability.SpellAbility; @@ -16,12 +15,11 @@ public class ImmediateTriggerAi extends SpellAbilityAi { return true; } - SpellAbility trigsa = null; - if (sa.hasAdditionalAbility("Execute")) { - trigsa = sa.getAdditionalAbility("Execute"); - } else { - trigsa = AbilityFactory.getAbility(sa.getHostCard(), sa.getParam("Execute")); + SpellAbility trigsa = sa.getAdditionalAbility("Execute"); + if (trigsa == null) { + return false; } + trigsa.setActivatingPlayer(ai); if (trigsa instanceof AbilitySub) { @@ -33,12 +31,11 @@ public class ImmediateTriggerAi extends SpellAbilityAi { @Override protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) { - SpellAbility trigsa = null; - if (sa.hasAdditionalAbility("Execute")) { - trigsa = sa.getAdditionalAbility("Execute"); - } else { - trigsa = AbilityFactory.getAbility(sa.getHostCard(), sa.getParam("Execute")); + SpellAbility trigsa = sa.getAdditionalAbility("Execute"); + if (trigsa == null) { + return false; } + AiController aic = ((PlayerControllerAi)ai.getController()).getAi(); trigsa.setActivatingPlayer(ai); @@ -56,12 +53,11 @@ public class ImmediateTriggerAi extends SpellAbilityAi { return true; } - SpellAbility trigsa = null; - if (sa.hasAdditionalAbility("Execute")) { - trigsa = sa.getAdditionalAbility("Execute"); - } else { - trigsa = AbilityFactory.getAbility(sa.getHostCard(), sa.getParam("Execute")); + SpellAbility trigsa = sa.getAdditionalAbility("Execute"); + if (trigsa == null) { + return false; } + trigsa.setActivatingPlayer(ai); return AiPlayDecision.WillPlay == ((PlayerControllerAi)ai.getController()).getAi().canPlaySa(trigsa); } diff --git a/forge-game/src/main/java/forge/game/ability/AbilityFactory.java b/forge-game/src/main/java/forge/game/ability/AbilityFactory.java index 752dfbbfdc7..9ccdb5315a2 100644 --- a/forge-game/src/main/java/forge/game/ability/AbilityFactory.java +++ b/forge-game/src/main/java/forge/game/ability/AbilityFactory.java @@ -249,7 +249,7 @@ public final class AbilityFactory { } } - if (api == ApiType.DelayedTrigger && mapParams.containsKey("Execute")) { + if ((api == ApiType.DelayedTrigger || api == ApiType.ImmediateTrigger) && mapParams.containsKey("Execute")) { spellAbility.setSVar(mapParams.get("Execute"), sVarHolder.getSVar(mapParams.get("Execute"))); } diff --git a/forge-game/src/main/java/forge/game/ability/SpellAbilityEffect.java b/forge-game/src/main/java/forge/game/ability/SpellAbilityEffect.java index 381ef080e5a..4f492f439ff 100644 --- a/forge-game/src/main/java/forge/game/ability/SpellAbilityEffect.java +++ b/forge-game/src/main/java/forge/game/ability/SpellAbilityEffect.java @@ -429,7 +429,7 @@ public abstract class SpellAbilityEffect { + " exile it instead of putting it anywhere else."; String effect = "DB$ ChangeZone | Defined$ ReplacedCard | Origin$ Battlefield | Destination$ " + zone; - ReplacementEffect re = ReplacementHandler.parseReplacement(repeffstr, eff, true); + ReplacementEffect re = ReplacementHandler.parseReplacement(repeffstr, eff, true, null); re.setLayer(ReplacementLayer.Other); re.setOverridingAbility(AbilityFactory.getAbility(effect, eff)); diff --git a/forge-game/src/main/java/forge/game/ability/effects/AnimateEffectBase.java b/forge-game/src/main/java/forge/game/ability/effects/AnimateEffectBase.java index 68b50dfae39..86ad24f6249 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/AnimateEffectBase.java +++ b/forge-game/src/main/java/forge/game/ability/effects/AnimateEffectBase.java @@ -159,8 +159,7 @@ public abstract class AnimateEffectBase extends SpellAbilityEffect { // Grant triggers final List addedTriggers = Lists.newArrayList(); for (final String s : triggers) { - final Trigger parsedTrigger = TriggerHandler.parseTrigger(AbilityUtils.getSVar(sa, s), c, false); - parsedTrigger.setOverridingAbility(AbilityFactory.getAbility(c, parsedTrigger.getParam("Execute"), sa)); + final Trigger parsedTrigger = TriggerHandler.parseTrigger(AbilityUtils.getSVar(sa, s), c, false, sa); parsedTrigger.setOriginalHost(source); addedTriggers.add(parsedTrigger); } @@ -168,7 +167,7 @@ public abstract class AnimateEffectBase extends SpellAbilityEffect { // give replacement effects final List addedReplacements = Lists.newArrayList(); for (final String s : replacements) { - addedReplacements.add(ReplacementHandler.parseReplacement(AbilityUtils.getSVar(sa, s), c, false)); + addedReplacements.add(ReplacementHandler.parseReplacement(AbilityUtils.getSVar(sa, s), c, false, sa)); } // give static abilities (should only be used by cards to give diff --git a/forge-game/src/main/java/forge/game/ability/effects/DelayedTriggerEffect.java b/forge-game/src/main/java/forge/game/ability/effects/DelayedTriggerEffect.java index b8172eaad7f..bb2e7afc4d4 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/DelayedTriggerEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/DelayedTriggerEffect.java @@ -58,7 +58,7 @@ public class DelayedTriggerEffect extends SpellAbilityEffect { Card lki = CardUtil.getLKICopy(gameCard); lki.clearControllers(); lki.setOwner(sa.getActivatingPlayer()); - final Trigger delTrig = TriggerHandler.parseTrigger(mapParams, lki, sa.isIntrinsic()); + final Trigger delTrig = TriggerHandler.parseTrigger(mapParams, lki, sa.isIntrinsic(), null); delTrig.setSpawningAbility(sa.copy(lki, sa.getActivatingPlayer(), true)); if (triggerRemembered != null) { @@ -81,7 +81,7 @@ public class DelayedTriggerEffect extends SpellAbilityEffect { } } - if (mapParams.containsKey("Execute") || sa.hasAdditionalAbility("Execute")) { + if (sa.hasAdditionalAbility("Execute")) { AbilitySub overridingSA = (AbilitySub)sa.getAdditionalAbility("Execute").copy(lki, sa.getActivatingPlayer(), false); // need to reset the parent, additionalAbility does set it to this overridingSA.setParent(null); @@ -96,7 +96,7 @@ public class DelayedTriggerEffect extends SpellAbilityEffect { delTrig.setOverridingAbility(overridingSA); } - final TriggerHandler trigHandler = sa.getActivatingPlayer().getGame().getTriggerHandler(); + final TriggerHandler trigHandler = game.getTriggerHandler(); if (mapParams.containsKey("DelayedTriggerDefinedPlayer")) { // on sb's next turn Player p = Iterables.getFirst(AbilityUtils.getDefinedPlayers(sa.getHostCard(), mapParams.get("DelayedTriggerDefinedPlayer"), sa), null); trigHandler.registerPlayerDefinedDelayedTrigger(p, delTrig); diff --git a/forge-game/src/main/java/forge/game/ability/effects/EffectEffect.java b/forge-game/src/main/java/forge/game/ability/effects/EffectEffect.java index d7e1e7fec74..20723feba63 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/EffectEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/EffectEffect.java @@ -189,7 +189,7 @@ public class EffectEffect extends SpellAbilityEffect { for (final String s : effectReplacementEffects) { final String actualReplacement = AbilityUtils.getSVar(sa, s); - final ReplacementEffect parsedReplacement = ReplacementHandler.parseReplacement(actualReplacement, eff, true); + final ReplacementEffect parsedReplacement = ReplacementHandler.parseReplacement(actualReplacement, eff, true, sa); parsedReplacement.setActiveZone(EnumSet.of(ZoneType.Command)); parsedReplacement.setIntrinsic(true); eff.addReplacementEffect(parsedReplacement); diff --git a/forge-game/src/main/java/forge/game/ability/effects/ImmediateTriggerEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ImmediateTriggerEffect.java index 959190819d2..b44158828dd 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ImmediateTriggerEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ImmediateTriggerEffect.java @@ -57,7 +57,7 @@ public class ImmediateTriggerEffect extends SpellAbilityEffect { Card lki = CardUtil.getLKICopy(gameCard); lki.clearControllers(); lki.setOwner(sa.getActivatingPlayer()); - final Trigger immediateTrig = TriggerHandler.parseTrigger(mapParams, lki, sa.isIntrinsic()); + final Trigger immediateTrig = TriggerHandler.parseTrigger(mapParams, lki, sa.isIntrinsic(), null); immediateTrig.setSpawningAbility(sa.copy(lki, sa.getActivatingPlayer(), true)); // Need to copy paid costs @@ -74,7 +74,7 @@ public class ImmediateTriggerEffect extends SpellAbilityEffect { } } - if (mapParams.containsKey("Execute") || sa.hasAdditionalAbility("Execute")) { + if (sa.hasAdditionalAbility("Execute")) { AbilitySub overridingSA = (AbilitySub)sa.getAdditionalAbility("Execute").copy(lki, sa.getActivatingPlayer(), false); // need to set Parent to null, otherwise it might have wrong root ability overridingSA.setParent(null); @@ -85,9 +85,8 @@ public class ImmediateTriggerEffect extends SpellAbilityEffect { immediateTrig.setOverridingAbility(overridingSA); } - final TriggerHandler trigHandler = sa.getActivatingPlayer().getGame().getTriggerHandler(); // Instead of registering this, add to the delayed triggers as an immediate trigger type? Which means it'll fire as soon as possible - trigHandler.registerDelayedTrigger(immediateTrig); + game.getTriggerHandler().registerDelayedTrigger(immediateTrig); } } diff --git a/forge-game/src/main/java/forge/game/card/Card.java b/forge-game/src/main/java/forge/game/card/Card.java index 5417bfbf63c..ea3455d8f6f 100644 --- a/forge-game/src/main/java/forge/game/card/Card.java +++ b/forge-game/src/main/java/forge/game/card/Card.java @@ -5932,21 +5932,20 @@ public class Card extends GameEntity implements Comparable, IHasSVars { public boolean hasETBTrigger(final boolean drawbackOnly) { for (final Trigger tr : getTriggers()) { - final Map params = tr.getMapParams(); if (tr.getMode() != TriggerType.ChangesZone) { continue; } - if (!params.get("Destination").equals(ZoneType.Battlefield.toString())) { + if (!tr.getParam("Destination").equals(ZoneType.Battlefield.toString())) { continue; } - if (params.containsKey("ValidCard") && !params.get("ValidCard").contains("Self")) { + if (tr.hasParam("ValidCard") && !tr.getParam("ValidCard").contains("Self")) { continue; } - if (drawbackOnly && params.containsKey("Execute")){ - String exec = this.getSVar(params.get("Execute")); - if (exec.contains("AB$")) { + if (drawbackOnly) { + SpellAbility sa = tr.ensureAbility(); + if (sa == null || sa.isActivatedAbility()) { continue; } } diff --git a/forge-game/src/main/java/forge/game/card/CardFactory.java b/forge-game/src/main/java/forge/game/card/CardFactory.java index 422876c4428..b968f054d1a 100644 --- a/forge-game/src/main/java/forge/game/card/CardFactory.java +++ b/forge-game/src/main/java/forge/game/card/CardFactory.java @@ -664,6 +664,7 @@ public class CardFactory { if (origSVars.containsKey(s)) { final String actualTrigger = origSVars.get(s); final Trigger parsedTrigger = TriggerHandler.parseTrigger(actualTrigger, out, true); + parsedTrigger.setOriginalHost(host); state.addTrigger(parsedTrigger); } } @@ -687,6 +688,7 @@ public class CardFactory { if (origSVars.containsKey(s)) { final String actualAbility = origSVars.get(s); final SpellAbility grantedAbility = AbilityFactory.getAbility(actualAbility, out); + grantedAbility.setOriginalHost(host); grantedAbility.setIntrinsic(true); state.addSpellAbility(grantedAbility); } @@ -700,6 +702,7 @@ public class CardFactory { if (origSVars.containsKey(s)) { final String actualStatic = origSVars.get(s); final StaticAbility grantedStatic = new StaticAbility(actualStatic, out); + grantedStatic.setOriginalHost(host); grantedStatic.setIntrinsic(true); state.addStaticAbility(grantedStatic); } diff --git a/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java b/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java index bcf466f0886..56630eeda2f 100644 --- a/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java +++ b/forge-game/src/main/java/forge/game/card/CardFactoryUtil.java @@ -2227,7 +2227,7 @@ public class CardFactoryUtil { final String abStringAfflict = "DB$ LoseLife | Defined$ TriggeredDefendingPlayer" + " | LifeAmount$ " + n; - final Trigger afflictTrigger = TriggerHandler.parseTrigger(trigStr, card, intrinsic); + final Trigger afflictTrigger = TriggerHandler.parseTrigger(trigStr, card, intrinsic, null); afflictTrigger.setOverridingAbility(AbilityFactory.getAbility(abStringAfflict, card)); inst.addTrigger(afflictTrigger); diff --git a/forge-game/src/main/java/forge/game/replacement/ReplacementHandler.java b/forge-game/src/main/java/forge/game/replacement/ReplacementHandler.java index 8215153e57e..e042aace629 100644 --- a/forge-game/src/main/java/forge/game/replacement/ReplacementHandler.java +++ b/forge-game/src/main/java/forge/game/replacement/ReplacementHandler.java @@ -19,6 +19,7 @@ package forge.game.replacement; import forge.game.Game; import forge.game.GameLogEntryType; +import forge.game.IHasSVars; import forge.game.ability.AbilityFactory; import forge.game.ability.AbilityKey; import forge.game.ability.AbilityUtils; @@ -370,7 +371,10 @@ public class ReplacementHandler { * @return A finished instance */ public static ReplacementEffect parseReplacement(final String repParse, final Card host, final boolean intrinsic) { - return ReplacementHandler.parseReplacement(parseParams(repParse), host, intrinsic); + return parseReplacement(repParse, host, intrinsic, host); + } + public static ReplacementEffect parseReplacement(final String repParse, final Card host, final boolean intrinsic, final IHasSVars sVarHolder) { + return ReplacementHandler.parseReplacement(parseParams(repParse), host, intrinsic, sVarHolder); } public static Map parseParams(final String repParse) { @@ -388,7 +392,7 @@ public class ReplacementHandler { * The card that hosts the replacement effect * @return The finished instance */ - private static ReplacementEffect parseReplacement(final Map mapParams, final Card host, final boolean intrinsic) { + private static ReplacementEffect parseReplacement(final Map mapParams, final Card host, final boolean intrinsic, final IHasSVars sVarHolder) { final ReplacementType rt = ReplacementType.smartValueOf(mapParams.get("Event")); ReplacementEffect ret = rt.createReplacement(mapParams, host, intrinsic); @@ -397,8 +401,8 @@ public class ReplacementHandler { ret.setActiveZone(EnumSet.copyOf(ZoneType.listValueOf(activeZones))); } - if (mapParams.containsKey("ReplaceWith")) { - ret.setOverridingAbility(AbilityFactory.getAbility(host, mapParams.get("ReplaceWith"), ret)); + if (mapParams.containsKey("ReplaceWith") && sVarHolder != null) { + ret.setOverridingAbility(AbilityFactory.getAbility(host, mapParams.get("ReplaceWith"), sVarHolder)); } return ret; diff --git a/forge-game/src/main/java/forge/game/staticability/StaticAbilityContinuous.java b/forge-game/src/main/java/forge/game/staticability/StaticAbilityContinuous.java index b0ae7dfe94a..c0066417085 100644 --- a/forge-game/src/main/java/forge/game/staticability/StaticAbilityContinuous.java +++ b/forge-game/src/main/java/forge/game/staticability/StaticAbilityContinuous.java @@ -781,8 +781,7 @@ public final class StaticAbilityContinuous { // add Replacement effects if (addReplacements != null) { for (String rep : addReplacements) { - final ReplacementEffect actualRep = ReplacementHandler.parseReplacement(rep, affectedCard, false); - actualRep.setIntrinsic(false); + final ReplacementEffect actualRep = ReplacementHandler.parseReplacement(rep, affectedCard, false, stAb); addedReplacementEffects.add(actualRep); } } @@ -790,17 +789,12 @@ public final class StaticAbilityContinuous { // add triggers if (addTriggers != null) { for (final String trigger : addTriggers) { - final Trigger actualTrigger = TriggerHandler.parseTrigger(trigger, affectedCard, false); + final Trigger actualTrigger = TriggerHandler.parseTrigger(trigger, affectedCard, false, stAb); // if the trigger has Execute param, which most trigger gained by Static Abilties should have // turn them into SpellAbility object before adding to card // with that the TargetedCard does not need the Svars added to them anymore // but only do it if the trigger doesn't already have a overriding ability - if (actualTrigger.hasParam("Execute") && actualTrigger.getOverridingAbility() == null) { - // set overriding ability to the trigger - actualTrigger.setOverridingAbility(AbilityFactory.getAbility(affectedCard, actualTrigger.getParam("Execute"), stAb)); - } actualTrigger.setOriginalHost(hostCard); - actualTrigger.setIntrinsic(false); addedTrigger.add(actualTrigger); } } diff --git a/forge-game/src/main/java/forge/game/trigger/Trigger.java b/forge-game/src/main/java/forge/game/trigger/Trigger.java index 6b27441b38a..811f2aa7371 100644 --- a/forge-game/src/main/java/forge/game/trigger/Trigger.java +++ b/forge-game/src/main/java/forge/game/trigger/Trigger.java @@ -19,6 +19,7 @@ package forge.game.trigger; import forge.game.Game; import forge.game.GameEntity; +import forge.game.IHasSVars; import forge.game.TriggerReplacementBase; import forge.game.ability.AbilityFactory; import forge.game.ability.AbilityKey; @@ -562,12 +563,16 @@ public abstract class Trigger extends TriggerReplacementBase { } } - public SpellAbility ensureAbility() { + public SpellAbility ensureAbility(final IHasSVars sVarHolder) { SpellAbility sa = getOverridingAbility(); if (sa == null && hasParam("Execute")) { - sa = AbilityFactory.getAbility(getHostCard(), getParam("Execute")); + sa = AbilityFactory.getAbility(getHostCard(), getParam("Execute"), sVarHolder); setOverridingAbility(sa); } return sa; } + + public SpellAbility ensureAbility() { + return ensureAbility(this); + } } 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 ab6f39712d2..015f1958360 100644 --- a/forge-game/src/main/java/forge/game/trigger/TriggerHandler.java +++ b/forge-game/src/main/java/forge/game/trigger/TriggerHandler.java @@ -19,6 +19,7 @@ package forge.game.trigger; import forge.game.Game; import forge.game.GlobalRuleChange; +import forge.game.IHasSVars; import forge.game.ability.AbilityFactory; import forge.game.ability.AbilityUtils; import forge.game.ability.ApiType; @@ -123,9 +124,13 @@ public class TriggerHandler { } public static Trigger parseTrigger(final String trigParse, final Card host, final boolean intrinsic) { + return parseTrigger(trigParse, host, intrinsic, host); + } + + public static Trigger parseTrigger(final String trigParse, final Card host, final boolean intrinsic, final IHasSVars sVarHolder) { try { final Map mapParams = TriggerHandler.parseParams(trigParse); - return TriggerHandler.parseTrigger(mapParams, host, intrinsic); + return TriggerHandler.parseTrigger(mapParams, host, intrinsic, sVarHolder); } catch (Exception e) { String msg = "TriggerHandler:parseTrigger failed to parse"; Sentry.getContext().recordBreadcrumb( @@ -137,12 +142,15 @@ public class TriggerHandler { } } - public static Trigger parseTrigger(final Map mapParams, final Card host, final boolean intrinsic) { + public static Trigger parseTrigger(final Map mapParams, final Card host, final boolean intrinsic, final IHasSVars sVarHolder) { Trigger ret = null; try { final TriggerType type = TriggerType.smartValueOf(mapParams.get("Mode")); ret = type.createTrigger(mapParams, host, intrinsic); + if (sVarHolder != null) { + ret.ensureAbility(sVarHolder); + } } catch (Exception e) { String msg = "TriggerHandler:parseTrigger failed to parse"; Sentry.getContext().recordBreadcrumb( diff --git a/forge-gui/src/main/java/forge/deck/CardRelationMatrixGenerator.java b/forge-gui/src/main/java/forge/deck/CardRelationMatrixGenerator.java index 6c9c7e74687..0898e16d672 100644 --- a/forge-gui/src/main/java/forge/deck/CardRelationMatrixGenerator.java +++ b/forge-gui/src/main/java/forge/deck/CardRelationMatrixGenerator.java @@ -26,7 +26,7 @@ public final class CardRelationMatrixGenerator { public static HashMap>>> cardPools = new HashMap<>(); - public static Map>>> ldaPools = new HashMap(); + public static Map>>> ldaPools = new HashMap<>(); /** To ensure that only cards with at least 14 connections (as 14*4+4=60) are included in the card based deck generation pools