diff --git a/forge-ai/src/main/java/forge/ai/ability/CharmAi.java b/forge-ai/src/main/java/forge/ai/ability/CharmAi.java index cf76743727b..e99ce18e597 100644 --- a/forge-ai/src/main/java/forge/ai/ability/CharmAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/CharmAi.java @@ -18,10 +18,41 @@ import java.util.Random; public class CharmAi extends SpellAbilityAi { @Override - protected boolean checkAiLogic(final Player ai, final SpellAbility sa, final String aiLogic) { - if (aiLogic.equals("Triskaidekaphobia")) { - List choices = CharmEffect.makePossibleOptions(sa); - List chosenList = new ArrayList(); + protected boolean canPlayAI(Player ai, SpellAbility sa) { + final Random r = MyRandom.getRandom(); + + final int num = Integer.parseInt(sa.hasParam("CharmNum") ? sa.getParam("CharmNum") : "1"); + final int min = sa.hasParam("MinCharmNum") ? Integer.parseInt(sa.getParam("MinCharmNum")) : num; + boolean timingRight = sa.isTrigger(); //is there a reason to play the charm now? + + // reset the chosen list. Otherwise it will be locked in forever + sa.setChosenList(null); + List chosenList = min > 1 ? chooseMultipleOptionsAi(CharmEffect.makePossibleOptions(sa), ai, min) + : chooseOptionsAi(sa, ai, timingRight, num, min, sa.hasParam("CanRepeatModes"), false); + + if (chosenList.isEmpty()) { + return false; + } else { + sa.setChosenList(chosenList); + } + + // prevent run-away activations - first time will always return true + return r.nextFloat() <= Math.pow(.6667, sa.getActivationsThisTurn()); + } + + public static List chooseOptionsAi(SpellAbility sa, final Player ai, boolean playNow, int num, int min, boolean allowRepeat, boolean opponentChoser) { + if (sa.getChosenList() != null) { + return sa.getChosenList(); + } + List choices = CharmEffect.makePossibleOptions(sa); + List chosenList = new ArrayList(); + + if (opponentChoser) { + // This branch is for "An Opponent chooses" Charm spells from Alliances + // Current just choose the first available spell, which seem generally less disastrous for the AI. + //return choices.subList(0, 1); + return choices.subList(1, choices.size()); + } else if ("Triskaidekaphobia".equals(sa.getHostCard().getName())) { AbilitySub gain = choices.get(0); AbilitySub lose = choices.get(1); FCollection opponents = ai.getOpponents(); @@ -88,50 +119,21 @@ public class CharmAi extends SpellAbilityAi { } chosenList.add(aiLife == 12 || oppCritical ? lose : gain); } else { - // For cases not handled by the above, try to kill everyone - chosenList.add(lose); + // normal logic, try to gain life if its critical + boolean oppCritical = false; + // an oppoent is Critical = 12, and can gain life, try to gain life instead + // but only if ai doesn't kill itself with that. + if (aiLife != 12) { + for (Player p : opponents) { + if (p.getLife() == 12 && p.canGainLife()) { + oppCritical = true; + break; + } + } + } + chosenList.add(aiLife == 14 || aiLife <= 10 || oppCritical ? gain : lose); } - sa.setChosenList(chosenList); - } - return true; - } - - @Override - protected boolean checkApiLogic(Player ai, SpellAbility sa) { - final Random r = MyRandom.getRandom(); - - final int num = Integer.parseInt(sa.hasParam("CharmNum") ? sa.getParam("CharmNum") : "1"); - final int min = sa.hasParam("MinCharmNum") ? Integer.parseInt(sa.getParam("MinCharmNum")) : num; - boolean timingRight = sa.isTrigger(); //is there a reason to play the charm now? - - // reset the chosen list. Otherwise it will be locked in forever - sa.setChosenList(null); - List chosenList = min > 1 ? chooseMultipleOptionsAi(CharmEffect.makePossibleOptions(sa), ai, min) - : chooseOptionsAi(sa, ai, timingRight, num, min, sa.hasParam("CanRepeatModes"), false); - - if (chosenList.isEmpty()) { - return false; - } else { - sa.setChosenList(chosenList); - } - - // prevent run-away activations - first time will always return true - return r.nextFloat() <= Math.pow(.6667, sa.getActivationsThisTurn()); - } - - public static List chooseOptionsAi(SpellAbility sa, final Player ai, boolean playNow, int num, int min, - boolean allowRepeat, boolean opponentChoser) { - if (sa.getChosenList() != null) { - return sa.getChosenList(); - } - List choices = CharmEffect.makePossibleOptions(sa); - List chosenList = new ArrayList(); - - if (opponentChoser) { - // This branch is for "An Opponent chooses" Charm spells from Alliances - // Current just choose the first available spell, which seem generally less disastrous for the AI. - //return choices.subList(0, 1); - return choices.subList(1, choices.size()); + return chosenList; } AiController aic = ((PlayerControllerAi) ai.getController()).getAi(); @@ -178,6 +180,7 @@ public class CharmAi extends SpellAbilityAi { private List chooseMultipleOptionsAi(List choices, final Player ai, int min) { AbilitySub goodChoice = null; List chosenList = new ArrayList(); + // select first n playable options AiController aic = ((PlayerControllerAi) ai.getController()).getAi(); for (AbilitySub sub : choices) { sub.setActivatingPlayer(ai); diff --git a/forge-gui/res/cardsfolder/t/triskaidekaphobia.txt b/forge-gui/res/cardsfolder/t/triskaidekaphobia.txt index 51948628e8a..af51d02285d 100644 --- a/forge-gui/res/cardsfolder/t/triskaidekaphobia.txt +++ b/forge-gui/res/cardsfolder/t/triskaidekaphobia.txt @@ -2,7 +2,7 @@ Name:Triskaidekaphobia ManaCost:3 B Types:Enchantment T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | Execute$ TrigCharm | TriggerZones$ Battlefield | TriggerDescription$ At the beginning of your upkeep, choose one - Each player with exactly 13 life loses the game, then each player gains 1 life; Each player with exactly 13 life loses the game, then each player loses 1 life. -SVar:TrigCharm:AB$ Charm | Cost$ 0 | AILogic$ Triskaidekaphobia | Choices$ DBLoseGame1,DBLoseGame2 | CharmNum$ 1 +SVar:TrigCharm:AB$ Charm | Cost$ 0 | Choices$ DBLoseGame1,DBLoseGame2 | CharmNum$ 1 SVar:DBLoseGame1:DB$ LosesGame | Cost$ 0 | Defined$ Player.LifeEquals_13 | SubAbility$ DBGainLife | SpellDescription$ Each player with exactly 13 life loses the game, then each player gains 1 life. SVar:DBGainLife:DB$ GainLife | Defined$ Player | LifeAmount$ 1 SVar:DBLoseGame2:DB$ LosesGame | Cost$ 0 | Defined$ Player.LifeEquals_13 | SubAbility$ DBLoseLife | SpellDescription$ Each player with exactly 13 life loses the game, then each player loses 1 life.