diff --git a/forge-ai/src/main/java/forge/ai/AiController.java b/forge-ai/src/main/java/forge/ai/AiController.java index 959e3f871aa..e12cb8be41b 100644 --- a/forge-ai/src/main/java/forge/ai/AiController.java +++ b/forge-ai/src/main/java/forge/ai/AiController.java @@ -60,11 +60,7 @@ import forge.game.card.CardPredicates.Presets; import forge.game.card.CounterType; import forge.game.combat.Combat; import forge.game.combat.CombatUtil; -import forge.game.cost.Cost; -import forge.game.cost.CostDiscard; -import forge.game.cost.CostPart; -import forge.game.cost.CostPutCounter; -import forge.game.cost.CostRemoveCounter; +import forge.game.cost.*; import forge.game.mana.ManaCostBeingPaid; import forge.game.player.Player; import forge.game.player.PlayerActionConfirmMode; @@ -729,7 +725,26 @@ public class AiController { } else if (ApiType.RollPlanarDice == b.getApi() && b.getHostCard().hasSVar("AIRollPlanarDieParams") && b.getHostCard().getSVar("AIRollPlanarDieParams").toLowerCase().matches(".*lowpriority\\$\\s*true.*")) { return -1; } - + + // deprioritize pump spells with pure energy cost (can be activated last, + // since energy is generally scarce, plus can benefit e.g. Electrostatic Pummeler) + int a2 = 0, b2 = 0; + if (a.getApi() == ApiType.Pump && a.getPayCosts() != null && a.getPayCosts().getCostEnergy() != null) { + if (a.getPayCosts().hasOnlySpecificCostType(CostPayEnergy.class)) { + a2 = a.getPayCosts().getCostEnergy().convertAmount(); + } + } + if (b.getApi() == ApiType.Pump && b.getPayCosts() != null && b.getPayCosts().getCostEnergy() != null) { + if (b.getPayCosts().hasOnlySpecificCostType(CostPayEnergy.class)) { + b2 = b.getPayCosts().getCostEnergy().convertAmount(); + } + } + if (a2 == 0 && b2 > 0) { + return -1; + } else if (b2 == 0 && a2 > 0) { + return 1; + } + // cast 0 mana cost spells first (might be a Mox) if (a1 == 0 && b1 > 0 && ApiType.Mana != a.getApi()) { return -1; diff --git a/forge-ai/src/main/java/forge/ai/CreatureEvaluator.java b/forge-ai/src/main/java/forge/ai/CreatureEvaluator.java index 090c5ce22f3..a9f43291f19 100644 --- a/forge-ai/src/main/java/forge/ai/CreatureEvaluator.java +++ b/forge-ai/src/main/java/forge/ai/CreatureEvaluator.java @@ -2,7 +2,10 @@ package forge.ai; import com.google.common.base.Function; +import forge.game.ability.ApiType; import forge.game.card.Card; +import forge.game.card.CounterType; +import forge.game.cost.CostPayEnergy; import forge.game.spellability.SpellAbility; public class CreatureEvaluator implements Function { @@ -184,7 +187,7 @@ public class CreatureEvaluator implements Function { for (final SpellAbility sa : c.getSpellAbilities()) { if (sa.isAbility()) { - value += addValue(10, "sa: " + sa); + value += addValue(evaluateSpecialAbility(sa), "sa: " + sa); } } if (!c.getManaAbilities().isEmpty()) { @@ -203,9 +206,38 @@ public class CreatureEvaluator implements Function { if (!c.getEncodedCards().isEmpty()) { value += addValue(24, "encoded"); } + return value; } - + + private int evaluateSpecialAbility(SpellAbility sa) { + // Pump abilities + if (sa.getApi() == ApiType.Pump) { + if ("+X".equals(sa.getParam("NumAtt")) + && "+X".equals(sa.getParam("NumDef")) + && !sa.usesTargeting() + && (!sa.hasParam("Defined") || "Self".equals(sa.getParam("Defined")))) { + // Pump abilities that grant +X/+X to the card + if (sa.getPayCosts() != null && sa.getPayCosts().hasOnlySpecificCostType(CostPayEnergy.class)) { + // Electrostatic Pummeler + int initPower = getEffectivePower(sa.getHostCard()); + int pumpedPower = initPower; + int energy = sa.getHostCard().getController().getCounters(CounterType.ENERGY); + if (energy > 0) { + int numActivations = energy / 3; + for (int i = 0; i < numActivations; i++) { + pumpedPower *= 2; + } + return (pumpedPower - initPower) * 15; + } + } + } + } + + // default value + return 10; + } + protected int addValue(int value, String text) { return value; }