From 956910fbe944b96f8b37ab5909b101f4c53407be Mon Sep 17 00:00:00 2001 From: excessum Date: Sat, 23 Jul 2016 04:24:47 +0000 Subject: [PATCH] - Updated CharmAi to use new canPlay() --- .../main/java/forge/ai/ability/CharmAi.java | 111 +++++++++--------- .../res/cardsfolder/t/triskaidekaphobia.txt | 2 +- 2 files changed, 55 insertions(+), 58 deletions(-) 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 86547cca082..cf76743727b 100644 --- a/forge-ai/src/main/java/forge/ai/ability/CharmAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/CharmAi.java @@ -18,40 +18,10 @@ import java.util.Random; public class CharmAi extends SpellAbilityAi { @Override - 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(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())) { + 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(); AbilitySub gain = choices.get(0); AbilitySub lose = choices.get(1); FCollection opponents = ai.getOpponents(); @@ -118,21 +88,50 @@ public class CharmAi extends SpellAbilityAi { } chosenList.add(aiLife == 12 || oppCritical ? lose : gain); } else { - // 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); + // For cases not handled by the above, try to kill everyone + chosenList.add(lose); } - return chosenList; + 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()); } AiController aic = ((PlayerControllerAi) ai.getController()).getAi(); @@ -175,31 +174,29 @@ public class CharmAi extends SpellAbilityAi { return chosenList; } - //Extension of chooseOptionsAi specific to multi-option charms (eg. Cryptic Command, DTK commands) - private List chooseMultipleOptionsAi(SpellAbility sa, final Player ai, int min) { - if (sa.getChosenList() != null) { - return sa.getChosenList(); - } - List choices = CharmEffect.makePossibleOptions(sa); + // Choice selection for charms that require multiple choices (eg. Cryptic Command, DTK commands) + 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); + // Assign generic good choice to fill up choices if necessary if ("Good".equals(sub.getParam("AILogic")) && aic.doTrigger(sub, false)) { goodChoice = sub; } else { + // Standard canPlayAi() if (AiPlayDecision.WillPlay == aic.canPlaySa(sub)) { chosenList.add(sub); if (chosenList.size() == min) { - break; + break; // enough choices } } } } + // Add generic good choice if one more choice is needed if (chosenList.size() == min - 1 && goodChoice != null) { - chosenList.add(0, goodChoice); //hack to make Dromoka's Charm fight targets work + chosenList.add(0, goodChoice); // hack to make Dromoka's Command fight targets work return chosenList; } if (chosenList.size() != min) { diff --git a/forge-gui/res/cardsfolder/t/triskaidekaphobia.txt b/forge-gui/res/cardsfolder/t/triskaidekaphobia.txt index af51d02285d..51948628e8a 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 | Choices$ DBLoseGame1,DBLoseGame2 | CharmNum$ 1 +SVar:TrigCharm:AB$ Charm | Cost$ 0 | AILogic$ Triskaidekaphobia | 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.