diff --git a/forge-ai/src/main/java/forge/ai/AiAttackController.java b/forge-ai/src/main/java/forge/ai/AiAttackController.java index b49c5d11ee4..a3c6dde0856 100644 --- a/forge-ai/src/main/java/forge/ai/AiAttackController.java +++ b/forge-ai/src/main/java/forge/ai/AiAttackController.java @@ -393,7 +393,7 @@ public class AiAttackController { if (ai.getController().isAI()) { PlayerControllerAi aic = ((PlayerControllerAi) ai.getController()); pilotsNonAggroDeck = aic.pilotsNonAggroDeck(); - playAggro = !pilotsNonAggroDeck || aic.getAi().getBooleanProperty(AiProps.PLAY_AGGRO); + playAggro = !pilotsNonAggroDeck || aic.getAi().getBoolProperty(AiProps.PLAY_AGGRO); } // TODO make switchable via AI property int thresholdMod = 0; @@ -614,13 +614,7 @@ public class AiAttackController { // if true, the AI will attempt to identify which blockers will already be taken, // thus attempting to predict how many creatures with evasion can actively block - boolean predictEvasion = false; - if (ai.getController().isAI()) { - AiController aic = ((PlayerControllerAi) ai.getController()).getAi(); - if (aic.getBooleanProperty(AiProps.COMBAT_ASSAULT_ATTACK_EVASION_PREDICTION)) { - predictEvasion = true; - } - } + boolean predictEvasion = AiProfileUtil.getBoolProperty(ai, AiProps.COMBAT_ASSAULT_ATTACK_EVASION_PREDICTION); CardCollection accountedBlockers = new CardCollection(this.blockers); CardCollection categorizedAttackers = new CardCollection(); @@ -860,12 +854,12 @@ public class AiAttackController { AiController aic = ((PlayerControllerAi) ai.getController()).getAi(); simAI = aic.usesSimulation(); if (!simAI) { - playAggro = aic.getBooleanProperty(AiProps.PLAY_AGGRO); + playAggro = aic.getBoolProperty(AiProps.PLAY_AGGRO); chanceToAttackToTrade = aic.getIntProperty(AiProps.CHANCE_TO_ATTACK_INTO_TRADE); - tradeIfTappedOut = aic.getBooleanProperty(AiProps.ATTACK_INTO_TRADE_WHEN_TAPPED_OUT); + tradeIfTappedOut = aic.getBoolProperty(AiProps.ATTACK_INTO_TRADE_WHEN_TAPPED_OUT); extraChanceIfOppHasMana = aic.getIntProperty(AiProps.CHANCE_TO_ATKTRADE_WHEN_OPP_HAS_MANA); - tradeIfLowerLifePressure = aic.getBooleanProperty(AiProps.RANDOMLY_ATKTRADE_ONLY_ON_LOWER_LIFE_PRESSURE); - predictEvasion = aic.getBooleanProperty(AiProps.COMBAT_ATTRITION_ATTACK_EVASION_PREDICTION); + tradeIfLowerLifePressure = aic.getBoolProperty(AiProps.RANDOMLY_ATKTRADE_ONLY_ON_LOWER_LIFE_PRESSURE); + predictEvasion = aic.getBoolProperty(AiProps.COMBAT_ATTRITION_ATTACK_EVASION_PREDICTION); } } @@ -1423,7 +1417,7 @@ public class AiAttackController { // Check if maybe we are too reckless in adding this attacker if (canKillAllDangerous) { boolean avoidAttackingIntoBlock = ai.getController().isAI() - && ((PlayerControllerAi) ai.getController()).getAi().getBooleanProperty(AiProps.TRY_TO_AVOID_ATTACKING_INTO_CERTAIN_BLOCK); + && ((PlayerControllerAi) ai.getController()).getAi().getBoolProperty(AiProps.TRY_TO_AVOID_ATTACKING_INTO_CERTAIN_BLOCK); boolean attackerWillDie = defPower >= attacker.getNetToughness(); boolean uselessAttack = !hasCombatEffect && !hasAttackEffect; boolean noContributionToAttack = attackers.size() <= defenders.size() || attacker.getNetPower() <= 0; diff --git a/forge-ai/src/main/java/forge/ai/AiBlockController.java b/forge-ai/src/main/java/forge/ai/AiBlockController.java index e8a3933dac1..d8a8ada92e6 100644 --- a/forge-ai/src/main/java/forge/ai/AiBlockController.java +++ b/forge-ai/src/main/java/forge/ai/AiBlockController.java @@ -861,10 +861,9 @@ public class AiBlockController { return; } - AiController aic = ((PlayerControllerAi) ai.getController()).getAi(); - final int evalThresholdToken = aic.getIntProperty(AiProps.THRESHOLD_TOKEN_CHUMP_TO_SAVE_PLANESWALKER); - final int evalThresholdNonToken = aic.getIntProperty(AiProps.THRESHOLD_NONTOKEN_CHUMP_TO_SAVE_PLANESWALKER); - final boolean onlyIfLethal = aic.getBooleanProperty(AiProps.CHUMP_TO_SAVE_PLANESWALKER_ONLY_ON_LETHAL); + final int evalThresholdToken = AiProfileUtil.getIntProperty(ai, AiProps.THRESHOLD_TOKEN_CHUMP_TO_SAVE_PLANESWALKER); + final int evalThresholdNonToken = AiProfileUtil.getIntProperty(ai, AiProps.THRESHOLD_NONTOKEN_CHUMP_TO_SAVE_PLANESWALKER); + final boolean onlyIfLethal = AiProfileUtil.getBoolProperty(ai, AiProps.CHUMP_TO_SAVE_PLANESWALKER_ONLY_ON_LETHAL); if (evalThresholdToken > 0 || evalThresholdNonToken > 0) { // detect how much damage is threatened to each of the planeswalkers, see which ones would be @@ -1047,7 +1046,7 @@ public class AiBlockController { clearBlockers(combat, possibleBlockers); diff = (ai.getLife() * 2) - 5; // This is the minimal gain for an unnecessary trade - if (ai.getController().isAI() && diff > 0 && ((PlayerControllerAi) ai.getController()).getAi().getBooleanProperty(AiProps.PLAY_AGGRO)) { + if (diff > 0 && AiProfileUtil.getBoolProperty(ai, AiProps.PLAY_AGGRO)) { diff = 0; } @@ -1284,9 +1283,9 @@ public class AiBlockController { AiController aic = ((PlayerControllerAi) ai.getController()).getAi(); // simulation must get same results or it may crash if (!aic.usesSimulation()) { - enableRandomTrades = aic.getBooleanProperty(AiProps.ENABLE_RANDOM_FAVORABLE_TRADES_ON_BLOCK); - randomTradeIfBehindOnBoard = aic.getBooleanProperty(AiProps.RANDOMLY_TRADE_EVEN_WHEN_HAVE_LESS_CREATS); - randomTradeIfCreatInHand = aic.getBooleanProperty(AiProps.ALSO_TRADE_WHEN_HAVE_A_REPLACEMENT_CREAT); + enableRandomTrades = aic.getBoolProperty(AiProps.ENABLE_RANDOM_FAVORABLE_TRADES_ON_BLOCK); + randomTradeIfBehindOnBoard = aic.getBoolProperty(AiProps.RANDOMLY_TRADE_EVEN_WHEN_HAVE_LESS_CREATS); + randomTradeIfCreatInHand = aic.getBoolProperty(AiProps.ALSO_TRADE_WHEN_HAVE_A_REPLACEMENT_CREAT); minRandomTradeChance = aic.getIntProperty(AiProps.MIN_CHANCE_TO_RANDOMLY_TRADE_ON_BLOCK); maxRandomTradeChance = aic.getIntProperty(AiProps.MAX_CHANCE_TO_RANDOMLY_TRADE_ON_BLOCK); chanceModForEmbalm = aic.getIntProperty(AiProps.CHANCE_DECREASE_TO_TRADE_VS_EMBALM); diff --git a/forge-ai/src/main/java/forge/ai/AiController.java b/forge-ai/src/main/java/forge/ai/AiController.java index 1430d8285bf..1d8acfcbc68 100644 --- a/forge-ai/src/main/java/forge/ai/AiController.java +++ b/forge-ai/src/main/java/forge/ai/AiController.java @@ -764,7 +764,7 @@ public class AiController { return predictSpellToCastInMain2(exceptSA, true); } private SpellAbility predictSpellToCastInMain2(ApiType exceptSA, boolean handOnly) { - if (!getBooleanProperty(AiProps.PREDICT_SPELLS_FOR_MAIN2)) { + if (!getBoolProperty(AiProps.PREDICT_SPELLS_FOR_MAIN2)) { return null; } @@ -930,7 +930,7 @@ public class AiController { final Card card = sa.getHostCard(); // Trying to play a card that has Buyback without a Buyback cost, look for possible additional considerations - if (getBooleanProperty(AiProps.TRY_TO_PRESERVE_BUYBACK_SPELLS)) { + if (getBoolProperty(AiProps.TRY_TO_PRESERVE_BUYBACK_SPELLS)) { if (card.hasKeyword(Keyword.BUYBACK) && !sa.isBuyback() && !canPlaySpellWithoutBuyback(card, sa)) { return AiPlayDecision.NeedsToPlayCriteriaNotMet; } @@ -1299,27 +1299,13 @@ public class AiController { } public String getProperty(AiProps propName) { - return AiProfileUtil.getAIProp(getPlayer().getLobbyPlayer(), propName); + return AiProfileUtil.getProperty(getPlayer(), propName); } - public int getIntProperty(AiProps propName) { - String prop = AiProfileUtil.getAIProp(getPlayer().getLobbyPlayer(), propName); - - if (prop == null || prop.isEmpty()) { - return Integer.parseInt(propName.getDefault()); - } - - return Integer.parseInt(prop); + return AiProfileUtil.getIntProperty(getPlayer(), propName); } - - public boolean getBooleanProperty(AiProps propName) { - String prop = AiProfileUtil.getAIProp(getPlayer().getLobbyPlayer(), propName); - - if (prop == null || prop.isEmpty()) { - return Boolean.parseBoolean(propName.getDefault()); - } - - return Boolean.parseBoolean(prop); + public boolean getBoolProperty(AiProps propName) { + return AiProfileUtil.getBoolProperty(getPlayer(), propName); } public AiPlayDecision canPlayFromEffectAI(Spell spell, boolean mandatory, boolean withoutPayingManaCost) { @@ -1330,7 +1316,7 @@ public class AiController { final Card card = spell.getHostCard(); if (spell instanceof SpellApiBased) { - boolean chance = false; + boolean chance; if (withoutPayingManaCost) { chance = SpellApiToAi.Converter.get(spell).doTriggerNoCostWithSubs(player, spell, mandatory).willingToPlay(); } else { @@ -1468,7 +1454,7 @@ public class AiController { CardCollection inHand = CardLists.filter(player.getCardsIn(ZoneType.Hand), CardPredicates.NON_LANDS); CardCollectionView otb = player.getCardsIn(ZoneType.Battlefield); - if (getBooleanProperty(AiProps.HOLD_LAND_DROP_ONLY_IF_HAVE_OTHER_PERMS)) { + if (getBoolProperty(AiProps.HOLD_LAND_DROP_ONLY_IF_HAVE_OTHER_PERMS)) { if (!otb.anyMatch(CardPredicates.NON_LANDS)) { return false; } diff --git a/forge-ai/src/main/java/forge/ai/AiProfileUtil.java b/forge-ai/src/main/java/forge/ai/AiProfileUtil.java index 5fe94c6e22c..03ab37f9654 100644 --- a/forge-ai/src/main/java/forge/ai/AiProfileUtil.java +++ b/forge-ai/src/main/java/forge/ai/AiProfileUtil.java @@ -18,6 +18,7 @@ package forge.ai; import forge.LobbyPlayer; +import forge.game.player.Player; import forge.util.Aggregates; import forge.util.FileUtil; import forge.util.TextUtil; @@ -113,6 +114,23 @@ public class AiProfileUtil { return profileMap; } + public static String getProperty(final Player p, final AiProps propName) { + String prop = AiProfileUtil.getAIProp(p.getLobbyPlayer(), propName); + + if (prop == null || prop.isEmpty()) { + // TODO if p is human try to predict some values from previous plays or something + return propName.getDefault(); + } + + return prop; + } + public static int getIntProperty(final Player p, final AiProps propName) { + return Integer.parseInt(getProperty(p, propName)); + } + public static boolean getBoolProperty(final Player p, final AiProps propName) { + return Boolean.parseBoolean(getProperty(p, propName)); + } + /** * Returns an AI property value for the current profile. * diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtil.java b/forge-ai/src/main/java/forge/ai/ComputerUtil.java index bad4b3c3bbc..c88cc23f1a1 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtil.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtil.java @@ -345,27 +345,24 @@ public class ComputerUtil { return sacMeList.getFirst(); } else { // empty sacMeList, so get some viable average preference if the option is enabled - if (ai.getController().isAI()) { - AiController aic = ((PlayerControllerAi) ai.getController()).getAi(); - boolean enableDefaultPref = aic.getBooleanProperty(AiProps.SACRIFICE_DEFAULT_PREF_ENABLE); - if (enableDefaultPref) { - int minCMC = aic.getIntProperty(AiProps.SACRIFICE_DEFAULT_PREF_MIN_CMC); - int maxCMC = aic.getIntProperty(AiProps.SACRIFICE_DEFAULT_PREF_MAX_CMC); - int maxCreatureEval = aic.getIntProperty(AiProps.SACRIFICE_DEFAULT_PREF_MAX_CREATURE_EVAL); - boolean allowTokens = aic.getBooleanProperty(AiProps.SACRIFICE_DEFAULT_PREF_ALLOW_TOKENS); - List dontSac = Arrays.asList("Black Lotus", "Mox Pearl", "Mox Jet", "Mox Emerald", "Mox Ruby", "Mox Sapphire", "Lotus Petal"); - CardCollection allowList = CardLists.filter(typeList, card -> { - if (card.isCreature() && ComputerUtilCard.evaluateCreature(card) > maxCreatureEval) { - return false; - } - - return (allowTokens && card.isToken()) - || (card.getCMC() >= minCMC && card.getCMC() <= maxCMC && !dontSac.contains(card.getName())); - }); - if (!allowList.isEmpty()) { - CardLists.sortByCmcDesc(allowList); - return allowList.getLast(); + boolean enableDefaultPref = AiProfileUtil.getBoolProperty(ai, AiProps.SACRIFICE_DEFAULT_PREF_ENABLE); + if (enableDefaultPref) { + int minCMC = AiProfileUtil.getIntProperty(ai, AiProps.SACRIFICE_DEFAULT_PREF_MIN_CMC); + int maxCMC = AiProfileUtil.getIntProperty(ai, AiProps.SACRIFICE_DEFAULT_PREF_MAX_CMC); + int maxCreatureEval = AiProfileUtil.getIntProperty(ai, AiProps.SACRIFICE_DEFAULT_PREF_MAX_CREATURE_EVAL); + boolean allowTokens = AiProfileUtil.getBoolProperty(ai, AiProps.SACRIFICE_DEFAULT_PREF_ALLOW_TOKENS); + List dontSac = Arrays.asList("Black Lotus", "Mox Pearl", "Mox Jet", "Mox Emerald", "Mox Ruby", "Mox Sapphire", "Lotus Petal"); + CardCollection allowList = CardLists.filter(typeList, card -> { + if (card.isCreature() && ComputerUtilCard.evaluateCreature(card) > maxCreatureEval) { + return false; } + + return (allowTokens && card.isToken()) + || (card.getCMC() >= minCMC && card.getCMC() <= maxCMC && !dontSac.contains(card.getName())); + }); + if (!allowList.isEmpty()) { + CardLists.sortByCmcDesc(allowList); + return allowList.getLast(); } } } @@ -1999,8 +1996,7 @@ public class ComputerUtil { else if ((threatApi == ApiType.Attach && (topStack.isCurse() || "Curse".equals(topStack.getParam("AILogic")))) && (saviourApi == ApiType.Pump || saviourApi == ApiType.PumpAll || saviourApi == ApiType.Protection || saviourApi == null)) { - AiController aic = aiPlayer.isAI() ? ((PlayerControllerAi)aiPlayer.getController()).getAi() : null; - boolean enableCurseAuraRemoval = aic != null ? aic.getBooleanProperty(AiProps.ACTIVELY_DESTROY_IMMEDIATELY_UNBLOCKABLE) : false; + boolean enableCurseAuraRemoval = AiProfileUtil.getBoolProperty(aiPlayer, AiProps.ACTIVELY_DESTROY_IMMEDIATELY_UNBLOCKABLE); if (enableCurseAuraRemoval) { for (final Object o : objects) { if (o instanceof Card c) { @@ -2041,17 +2037,14 @@ public class ComputerUtil { // a creature will [hopefully] die from a spell on stack boolean willDieFromSpell = false; boolean noStackCheck = false; - if (ai.getController().isAI()) { - AiController aic = ((PlayerControllerAi) ai.getController()).getAi(); - if (aic.getBooleanProperty(AiProps.DONT_EVAL_KILLSPELLS_ON_STACK_WITH_PERMISSION)) { - // See if permission is on stack and ignore this check if there is and the relevant AI flag is set - // TODO: improve this so that this flag is not needed and the AI can properly evaluate spells in presence of counterspells. - for (SpellAbilityStackInstance si : game.getStack()) { - SpellAbility sa = si.getSpellAbility(); - if (sa.getApi() == ApiType.Counter) { - noStackCheck = true; - break; - } + if (AiProfileUtil.getBoolProperty(ai, AiProps.DONT_EVAL_KILLSPELLS_ON_STACK_WITH_PERMISSION)) { + // See if permission is on stack and ignore this check if there is and the relevant AI flag is set + // TODO: improve this so that this flag is not needed and the AI can properly evaluate spells in presence of counterspells. + for (SpellAbilityStackInstance si : game.getStack()) { + SpellAbility sa = si.getSpellAbility(); + if (sa.getApi() == ApiType.Counter) { + noStackCheck = true; + break; } } } @@ -2080,8 +2073,7 @@ public class ComputerUtil { * @return a filtered list with no dying creatures in it */ public static CardCollection filterCreaturesThatWillDieThisTurn(final Player ai, final CardCollection list, final SpellAbility excludeSa) { - AiController aic = ((PlayerControllerAi)ai.getController()).getAi(); - if (aic.getBooleanProperty(AiProps.AVOID_TARGETING_CREATS_THAT_WILL_DIE)) { + if (AiProfileUtil.getBoolProperty(ai, AiProps.AVOID_TARGETING_CREATS_THAT_WILL_DIE)) { // Try to avoid targeting creatures that are dead on board List willBeKilled = CardLists.filter(list, card -> card.isCreature() && predictCreatureWillDieThisTurn(ai, card, excludeSa)); list.removeAll(willBeKilled); @@ -2254,25 +2246,14 @@ public class ComputerUtil { boolean bottom = false; // AI profile-based toggles - int maxLandsToScryLandsToTop = 3; - int minLandsToScryLandsAway = 8; - int minCreatsToScryCreatsAway = 5; - int minCreatEvalThreshold = 160; // just a bit higher than a baseline 2/2 creature or a 1/1 mana dork - int lowCMCThreshold = 3; - int maxCreatsToScryLowCMCAway = 3; - boolean uncastablesToBottom = false; - int uncastableCMCThreshold = 1; - if (player.getController().isAI()) { - AiController aic = ((PlayerControllerAi)player.getController()).getAi(); - maxLandsToScryLandsToTop = aic.getIntProperty(AiProps.SCRY_NUM_LANDS_TO_STILL_NEED_MORE); - minLandsToScryLandsAway = aic.getIntProperty(AiProps.SCRY_NUM_LANDS_TO_NOT_NEED_MORE); - minCreatsToScryCreatsAway = aic.getIntProperty(AiProps.SCRY_NUM_CREATURES_TO_NOT_NEED_SUBPAR_ONES); - minCreatEvalThreshold = aic.getIntProperty(AiProps.SCRY_EVALTHR_TO_SCRY_AWAY_LOWCMC_CREATURE); - lowCMCThreshold = aic.getIntProperty(AiProps.SCRY_EVALTHR_CMC_THRESHOLD); - maxCreatsToScryLowCMCAway = aic.getIntProperty(AiProps.SCRY_EVALTHR_CREATCOUNT_TO_SCRY_AWAY_LOWCMC); - uncastablesToBottom = aic.getBooleanProperty(AiProps.SCRY_IMMEDIATELY_UNCASTABLE_TO_BOTTOM); - uncastableCMCThreshold = aic.getIntProperty(AiProps.SCRY_IMMEDIATELY_UNCASTABLE_CMC_DIFF); - } + int maxLandsToScryLandsToTop = AiProfileUtil.getIntProperty(player, AiProps.SCRY_NUM_LANDS_TO_STILL_NEED_MORE); + int minLandsToScryLandsAway = AiProfileUtil.getIntProperty(player, AiProps.SCRY_NUM_LANDS_TO_NOT_NEED_MORE); + int minCreatsToScryCreatsAway = AiProfileUtil.getIntProperty(player, AiProps.SCRY_NUM_CREATURES_TO_NOT_NEED_SUBPAR_ONES); + int minCreatEvalThreshold = AiProfileUtil.getIntProperty(player, AiProps.SCRY_EVALTHR_TO_SCRY_AWAY_LOWCMC_CREATURE); + int lowCMCThreshold = AiProfileUtil.getIntProperty(player, AiProps.SCRY_EVALTHR_CMC_THRESHOLD); + int maxCreatsToScryLowCMCAway = AiProfileUtil.getIntProperty(player, AiProps.SCRY_EVALTHR_CREATCOUNT_TO_SCRY_AWAY_LOWCMC); + boolean uncastablesToBottom = AiProfileUtil.getBoolProperty(player, AiProps.SCRY_IMMEDIATELY_UNCASTABLE_TO_BOTTOM); + int uncastableCMCThreshold = AiProfileUtil.getIntProperty(player, AiProps.SCRY_IMMEDIATELY_UNCASTABLE_CMC_DIFF); CardCollectionView allCards = player.getAllCards(); CardCollectionView cardsInHand = player.getCardsIn(ZoneType.Hand); diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java b/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java index 37d3f51d887..b7e81dc00ba 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java @@ -1060,7 +1060,6 @@ public class ComputerUtilCard { public static boolean useRemovalNow(final SpellAbility sa, final Card c, final int dmg, ZoneType destination) { final Player ai = sa.getActivatingPlayer(); - final AiController aic = ((PlayerControllerAi) ai.getController()).getAi(); final Game game = ai.getGame(); final PhaseHandler ph = game.getPhaseHandler(); final PhaseType phaseType = ph.getPhase(); @@ -1207,7 +1206,7 @@ public class ComputerUtilCard { } } else if (c.isPlaneswalker()) { threat = 1; - } else if (aic.getBooleanProperty(AiProps.ACTIVELY_DESTROY_ARTS_AND_NONAURA_ENCHS) && ((c.isArtifact() && !c.isCreature()) || (c.isEnchantment() && !c.isAura()))) { + } else if (AiProfileUtil.getBoolProperty(ai, AiProps.ACTIVELY_DESTROY_ARTS_AND_NONAURA_ENCHS) && ((c.isArtifact() && !c.isCreature()) || (c.isEnchantment() && !c.isAura()))) { // non-creature artifacts and global enchantments with suspicious intrinsic abilities boolean priority = false; if (c.getOwner().isOpponentOf(ai) && c.getController().isOpponentOf(ai)) { @@ -1311,7 +1310,7 @@ public class ComputerUtilCard { AiController aic = ((PlayerControllerAi) ai.getController()).getAi(); simAI = aic.usesSimulation(); if (!simAI) { - holdCombatTricks = aic.getBooleanProperty(AiProps.TRY_TO_HOLD_COMBAT_TRICKS_UNTIL_BLOCK); + holdCombatTricks = aic.getBoolProperty(AiProps.TRY_TO_HOLD_COMBAT_TRICKS_UNTIL_BLOCK); chanceToHoldCombatTricks = aic.getIntProperty(AiProps.CHANCE_TO_HOLD_COMBAT_TRICKS_UNTIL_BLOCK); } } @@ -1640,7 +1639,7 @@ public class ComputerUtilCard { // if we got here, Berserk will result in the pumped creature dying at EOT and the opponent will not lose // (other similar cards with AILogic$ Berserk that do not die only when attacking are excluded from consideration) if (ai.getController() instanceof PlayerControllerAi) { - boolean aggr = ((PlayerControllerAi) ai.getController()).getAi().getBooleanProperty(AiProps.USE_BERSERK_AGGRESSIVELY) + boolean aggr = ((PlayerControllerAi) ai.getController()).getAi().getBoolProperty(AiProps.USE_BERSERK_AGGRESSIVELY) || sa.hasParam("AtEOT"); if (!aggr) { return false; @@ -1907,17 +1906,10 @@ public class ComputerUtilCard { return oppCards; } - boolean enablePriorityRemoval = false; - boolean priorityRemovalOnlyInDanger = false; - int priorityRemovalThreshold = 0; - int lifeInDanger = 5; - if (ai.getController().isAI()) { - AiController aic = ((PlayerControllerAi) ai.getController()).getAi(); - enablePriorityRemoval = aic.getBooleanProperty(AiProps.ACTIVELY_DESTROY_IMMEDIATELY_UNBLOCKABLE); - priorityRemovalThreshold = aic.getIntProperty(AiProps.DESTROY_IMMEDIATELY_UNBLOCKABLE_THRESHOLD); - priorityRemovalOnlyInDanger = aic.getBooleanProperty(AiProps.DESTROY_IMMEDIATELY_UNBLOCKABLE_ONLY_IN_DNGR); - lifeInDanger = aic.getIntProperty(AiProps.DESTROY_IMMEDIATELY_UNBLOCKABLE_LIFE_IN_DNGR); - } + boolean enablePriorityRemoval = AiProfileUtil.getBoolProperty(ai, AiProps.ACTIVELY_DESTROY_IMMEDIATELY_UNBLOCKABLE); + int priorityRemovalThreshold = AiProfileUtil.getIntProperty(ai, AiProps.DESTROY_IMMEDIATELY_UNBLOCKABLE_THRESHOLD); + boolean priorityRemovalOnlyInDanger = AiProfileUtil.getBoolProperty(ai, AiProps.DESTROY_IMMEDIATELY_UNBLOCKABLE_ONLY_IN_DNGR); + int lifeInDanger = AiProfileUtil.getIntProperty(ai, AiProps.DESTROY_IMMEDIATELY_UNBLOCKABLE_LIFE_IN_DNGR); if (!enablePriorityRemoval) { // Nothing to do here, the profile does not allow prioritizing diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtilCombat.java b/forge-ai/src/main/java/forge/ai/ComputerUtilCombat.java index 978f55e9625..4f59b5eaef6 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtilCombat.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtilCombat.java @@ -443,13 +443,12 @@ public class ComputerUtilCombat { } } - int threshold = 0; - int maxTreshold = 0; - if (ai.getController().isAI()) { - threshold = ((PlayerControllerAi) ai.getController()).getAi().getIntProperty(AiProps.AI_IN_DANGER_THRESHOLD); - maxTreshold = ((PlayerControllerAi) ai.getController()).getAi().getIntProperty(AiProps.AI_IN_DANGER_MAX_THRESHOLD) - threshold; + if (resultingPoison(ai, combat) > Math.max(7, ai.getPoisonCounters())) { + return true; } + int threshold = AiProfileUtil.getIntProperty(ai, AiProps.AI_IN_DANGER_THRESHOLD); + int maxTreshold = AiProfileUtil.getIntProperty(ai, AiProps.AI_IN_DANGER_MAX_THRESHOLD) - threshold; int chance = MyRandom.getRandom().nextInt(80) + 5; while (maxTreshold > 0) { if (MyRandom.getRandom().nextInt(100) < chance) { @@ -458,10 +457,6 @@ public class ComputerUtilCombat { maxTreshold--; } - if (resultingPoison(ai, combat) > Math.max(7, ai.getPoisonCounters())) { - return true; - } - return !ai.cantLoseForZeroOrLessLife() && lifeThatWouldRemain(ai, combat) - payment < Math.min(threshold, ai.getLife()); } diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtilCost.java b/forge-ai/src/main/java/forge/ai/ComputerUtilCost.java index 556fdac384a..e657d9a5ce7 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtilCost.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtilCost.java @@ -356,7 +356,7 @@ public class ComputerUtilCost { final boolean beforeNextTurn = ai.getGame().getPhaseHandler().is(PhaseType.END_OF_TURN) && ai.getGame().getPhaseHandler().getNextTurn().equals(ai) && ComputerUtilCard.evaluateCreature(source) <= 150; final boolean creatureInDanger = ComputerUtil.predictCreatureWillDieThisTurn(ai, source, sourceAbility, false) && !ComputerUtilCombat.willOpposingCreatureDieInCombat(ai, source, combat); - final int lifeThreshold = ai.getController().isAI() ? (((PlayerControllerAi) ai.getController()).getAi().getIntProperty(AiProps.AI_IN_DANGER_THRESHOLD)) : 4; + final int lifeThreshold = AiProfileUtil.getIntProperty(ai, AiProps.AI_IN_DANGER_THRESHOLD); final boolean aiInDanger = ai.getLife() <= lifeThreshold && ai.canLoseLife() && !ai.cantLoseForZeroOrLessLife(); if (creatureInDanger && !ComputerUtilCombat.isDangerousToSacInCombat(ai, source, combat)) { return true; diff --git a/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java b/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java index 5276b419e5f..53336b7b63a 100644 --- a/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java +++ b/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java @@ -139,9 +139,9 @@ public class PlayerControllerAi extends PlayerController { } } - boolean sbLimitedFormats = getAi().getBooleanProperty(AiProps.SIDEBOARDING_IN_LIMITED_FORMATS); - boolean sbSharedTypesOnly = getAi().getBooleanProperty(AiProps.SIDEBOARDING_SHARED_TYPE_ONLY); - boolean sbPlaneswalkerException = getAi().getBooleanProperty(AiProps.SIDEBOARDING_PLANESWALKER_EQ_CREATURE); + boolean sbLimitedFormats = getAi().getBoolProperty(AiProps.SIDEBOARDING_IN_LIMITED_FORMATS); + boolean sbSharedTypesOnly = getAi().getBoolProperty(AiProps.SIDEBOARDING_SHARED_TYPE_ONLY); + boolean sbPlaneswalkerException = getAi().getBoolProperty(AiProps.SIDEBOARDING_PLANESWALKER_EQ_CREATURE); int sbChanceOnWin = getAi().getIntProperty(AiProps.SIDEBOARDING_CHANCE_ON_WIN); int sbChancePerCard = getAi().getIntProperty(AiProps.SIDEBOARDING_CHANCE_PER_CARD); @@ -1372,7 +1372,7 @@ public class PlayerControllerAi extends PlayerController { @Override public CardCollectionView cheatShuffle(CardCollectionView list) { - return brains.getBooleanProperty(AiProps.CHEAT_WITH_MANA_ON_SHUFFLE) ? brains.cheatShuffle(list) : list; + return brains.getBoolProperty(AiProps.CHEAT_WITH_MANA_ON_SHUFFLE) ? brains.cheatShuffle(list) : list; } @Override @@ -1544,12 +1544,10 @@ public class PlayerControllerAi extends PlayerController { } // Don't choose Tomb of Annihilation when life in danger unless we can win right away or can't lose for 0 life - if (ai.getController().isAI()) { // FIXME: is this needed? Can simulation ever run this for a non-AI player? - int lifeInDanger = (((PlayerControllerAi) ai.getController()).getAi().getIntProperty(AiProps.AI_IN_DANGER_THRESHOLD)); - if ((ai.getLife() <= lifeInDanger && !ai.cantLoseForZeroOrLessLife()) - && !(ai.getLife() > 1 && ai.getWeakestOpponent().getLife() == 1)) { - dungeonNames.remove("Tomb of Annihilation"); - } + int lifeInDanger = AiProfileUtil.getIntProperty(player, AiProps.AI_IN_DANGER_THRESHOLD); + if ((ai.getLife() <= lifeInDanger && !ai.cantLoseForZeroOrLessLife()) + && !(ai.getLife() > 1 && ai.getWeakestOpponent().getLife() == 1)) { + dungeonNames.remove("Tomb of Annihilation"); } try { diff --git a/forge-ai/src/main/java/forge/ai/SpecialCardAi.java b/forge-ai/src/main/java/forge/ai/SpecialCardAi.java index 6caeb28aa86..eb056144291 100644 --- a/forge-ai/src/main/java/forge/ai/SpecialCardAi.java +++ b/forge-ai/src/main/java/forge/ai/SpecialCardAi.java @@ -798,7 +798,7 @@ public class SpecialCardAi { public static class Intuition { public static CardCollection considerMultiple(final Player ai, final SpellAbility sa) { if (ai.getController().isAI()) { - if (!((PlayerControllerAi) ai.getController()).getAi().getBooleanProperty(AiProps.INTUITION_ALTERNATIVE_LOGIC)) { + if (!((PlayerControllerAi) ai.getController()).getAi().getBoolProperty(AiProps.INTUITION_ALTERNATIVE_LOGIC)) { return new CardCollection(); // fall back to standard ChangeZoneAi considerations } } 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 49fad8a85c1..b5d65b29b77 100644 --- a/forge-ai/src/main/java/forge/ai/ability/AttachAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/AttachAi.java @@ -73,11 +73,8 @@ public class AttachAi extends SpellAbilityAi { } } - // Flash logic - boolean advancedFlash = false; - if (ai.getController().isAI()) { - advancedFlash = ((PlayerControllerAi)ai.getController()).getAi().getBooleanProperty(AiProps.FLASH_ENABLE_ADVANCED_LOGIC); - } + boolean advancedFlash = AiProfileUtil.getBoolProperty(ai, AiProps.FLASH_ENABLE_ADVANCED_LOGIC); + if ((source.hasKeyword(Keyword.FLASH) || (!ai.canCastSorcery() && sa.canCastTiming(ai))) && source.isAura() && advancedFlash && !doAdvancedFlashAuraLogic(ai, sa, sa.getTargetCard())) { return new AiAbilityDecision(0, AiPlayDecision.CantPlayAi); @@ -108,9 +105,8 @@ public class AttachAi extends SpellAbilityAi { Card source = sa.getHostCard(); Game game = ai.getGame(); Combat combat = game.getCombat(); - AiController aic = ((PlayerControllerAi)ai.getController()).getAi(); - if (!aic.getBooleanProperty(AiProps.FLASH_USE_BUFF_AURAS_AS_COMBAT_TRICKS)) { + if (!AiProfileUtil.getBoolProperty(ai, AiProps.FLASH_USE_BUFF_AURAS_AS_COMBAT_TRICKS)) { // Currently this only works with buff auras, so if the relevant toggle is disabled, just return true // for instant speed use. To be improved later. return true; @@ -190,9 +186,9 @@ public class AttachAi extends SpellAbilityAi { return false; } - int chanceToCastAtEOT = aic.getIntProperty(AiProps.FLASH_BUFF_AURA_CHANCE_CAST_AT_EOT); - int chanceToCastEarly = aic.getIntProperty(AiProps.FLASH_BUFF_AURA_CHANCE_TO_CAST_EARLY); - int chanceToRespondToStack = aic.getIntProperty(AiProps.FLASH_BUFF_AURA_CHANCE_TO_RESPOND_TO_STACK); + int chanceToCastAtEOT = AiProfileUtil.getIntProperty(ai, AiProps.FLASH_BUFF_AURA_CHANCE_CAST_AT_EOT); + int chanceToCastEarly = AiProfileUtil.getIntProperty(ai, AiProps.FLASH_BUFF_AURA_CHANCE_TO_CAST_EARLY); + int chanceToRespondToStack = AiProfileUtil.getIntProperty(ai, AiProps.FLASH_BUFF_AURA_CHANCE_TO_RESPOND_TO_STACK); boolean hasFloatMana = ai.getManaPool().totalMana() > 0; boolean willDiscardNow = game.getPhaseHandler().is(PhaseType.END_OF_TURN, ai) @@ -912,7 +908,7 @@ public class AttachAi extends SpellAbilityAi { if (sa.getHostCard().getAttachedTo() != null && sa.getHostCard().getAttachedTo().isCreature() && sa.getPayCosts() != null && sa.getPayCosts().hasSpecificCostType(CostSacrifice.class)) { final int oldEvalRating = ComputerUtilCard.evaluateCreature(sa.getHostCard().getAttachedTo()); - final int threshold = ai.isAI() ? ((PlayerControllerAi)ai.getController()).getAi().getIntProperty(AiProps.SAC_TO_REATTACH_TARGET_EVAL_THRESHOLD) : Integer.MAX_VALUE; + final int threshold = AiProfileUtil.getIntProperty(ai, AiProps.SAC_TO_REATTACH_TARGET_EVAL_THRESHOLD); prefList = CardLists.filter(prefList, c -> { if (!c.isCreature()) { return false; @@ -1388,7 +1384,7 @@ public class AttachAi extends SpellAbilityAi { } // make sure to prioritize casting spells in main 2 (creatures, other equipment, etc.) rather than moving equipment around - boolean decideMoveFromUseless = uselessCreature && aic.getBooleanProperty(AiProps.PRIORITIZE_MOVE_EQUIPMENT_IF_USELESS); + boolean decideMoveFromUseless = uselessCreature && aic.getBoolProperty(AiProps.PRIORITIZE_MOVE_EQUIPMENT_IF_USELESS); if (!decideMoveFromUseless && AiCardMemory.isMemorySetEmpty(aiPlayer, AiCardMemory.MemorySet.HELD_MANA_SOURCES_FOR_MAIN2)) { SpellAbility futureSpell = aic.predictSpellToCastInMain2(ApiType.Attach); 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 0753244b0ce..25d0213930d 100644 --- a/forge-ai/src/main/java/forge/ai/ability/ChangeZoneAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/ChangeZoneAi.java @@ -1300,15 +1300,9 @@ public class ChangeZoneAi extends SpellAbilityAi { } // Reload planeswalkers else if (!aiPlaneswalkers.isEmpty() && (sa.getHostCard().isSorcery() || !game.getPhaseHandler().isPlayerTurn(ai))) { - int maxLoyaltyToConsider = 2; - int loyaltyDiff = 2; - int chance = 30; - if (ai.getController().isAI()) { - AiController aic = ((PlayerControllerAi) ai.getController()).getAi(); - maxLoyaltyToConsider = aic.getIntProperty(AiProps.BLINK_RELOAD_PLANESWALKER_MAX_LOYALTY); - loyaltyDiff = aic.getIntProperty(AiProps.BLINK_RELOAD_PLANESWALKER_LOYALTY_DIFF); - chance = aic.getIntProperty(AiProps.BLINK_RELOAD_PLANESWALKER_CHANCE); - } + int maxLoyaltyToConsider = AiProfileUtil.getIntProperty(ai, AiProps.BLINK_RELOAD_PLANESWALKER_MAX_LOYALTY); + int loyaltyDiff = AiProfileUtil.getIntProperty(ai, AiProps.BLINK_RELOAD_PLANESWALKER_LOYALTY_DIFF); + int chance = AiProfileUtil.getIntProperty(ai, AiProps.BLINK_RELOAD_PLANESWALKER_CHANCE); if (MyRandom.percentTrue(chance)) { aiPlaneswalkers.sort(CardPredicates.compareByCounterType(CounterEnumType.LOYALTY)); for (Card pw : aiPlaneswalkers) { @@ -1670,15 +1664,9 @@ public class ChangeZoneAi extends SpellAbilityAi { } if (card.hasCounters()) { if (card.isPlaneswalker()) { - int maxLoyaltyToConsider = 2; - int loyaltyDiff = 2; - int chance = 30; - if (decider.getController().isAI()) { - AiController aic = ((PlayerControllerAi) decider.getController()).getAi(); - maxLoyaltyToConsider = aic.getIntProperty(AiProps.BLINK_RELOAD_PLANESWALKER_MAX_LOYALTY); - loyaltyDiff = aic.getIntProperty(AiProps.BLINK_RELOAD_PLANESWALKER_LOYALTY_DIFF); - chance = aic.getIntProperty(AiProps.BLINK_RELOAD_PLANESWALKER_CHANCE); - } + int maxLoyaltyToConsider = AiProfileUtil.getIntProperty(decider, AiProps.BLINK_RELOAD_PLANESWALKER_MAX_LOYALTY); + int loyaltyDiff = AiProfileUtil.getIntProperty(decider, AiProps.BLINK_RELOAD_PLANESWALKER_LOYALTY_DIFF); + int chance = AiProfileUtil.getIntProperty(decider, AiProps.BLINK_RELOAD_PLANESWALKER_CHANCE); if (MyRandom.percentTrue(chance)) { int curLoyalty = card.getCounters(CounterEnumType.LOYALTY); int freshLoyalty = Integer.parseInt(card.getCurrentState().getBaseLoyalty()); diff --git a/forge-ai/src/main/java/forge/ai/ability/ChangeZoneAllAi.java b/forge-ai/src/main/java/forge/ai/ability/ChangeZoneAllAi.java index 4157894e361..4e50c772f70 100644 --- a/forge-ai/src/main/java/forge/ai/ability/ChangeZoneAllAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/ChangeZoneAllAi.java @@ -140,17 +140,15 @@ public class ChangeZoneAllAi extends SpellAbilityAi { } computerType = new CardCollection(); } - int creatureEvalThreshold = 200; // value difference (in evaluateCreatureList units) - int nonCreatureEvalThreshold = 3; // CMC difference - if (ai.getController().isAI()) { - AiController aic = ((PlayerControllerAi)ai.getController()).getAi(); - if (destination == ZoneType.Hand) { - creatureEvalThreshold = aic.getIntProperty(AiProps.BOUNCE_ALL_TO_HAND_CREAT_EVAL_DIFF); - nonCreatureEvalThreshold = aic.getIntProperty(AiProps.BOUNCE_ALL_TO_HAND_NONCREAT_EVAL_DIFF); - } else { - creatureEvalThreshold = aic.getIntProperty(AiProps.BOUNCE_ALL_ELSEWHERE_CREAT_EVAL_DIFF); - nonCreatureEvalThreshold = aic.getIntProperty(AiProps.BOUNCE_ALL_ELSEWHERE_NONCREAT_EVAL_DIFF); - } + + int creatureEvalThreshold; // value difference (in evaluateCreatureList units) + int nonCreatureEvalThreshold; // CMC difference + if (destination == ZoneType.Hand) { + creatureEvalThreshold = AiProfileUtil.getIntProperty(ai, AiProps.BOUNCE_ALL_TO_HAND_CREAT_EVAL_DIFF); + nonCreatureEvalThreshold = AiProfileUtil.getIntProperty(ai, AiProps.BOUNCE_ALL_TO_HAND_NONCREAT_EVAL_DIFF); + } else { + creatureEvalThreshold = AiProfileUtil.getIntProperty(ai, AiProps.BOUNCE_ALL_ELSEWHERE_CREAT_EVAL_DIFF); + nonCreatureEvalThreshold = AiProfileUtil.getIntProperty(ai, AiProps.BOUNCE_ALL_ELSEWHERE_NONCREAT_EVAL_DIFF); } // mass zone change for creatures: if in dire danger, do it; otherwise, only do it if the opponent's diff --git a/forge-ai/src/main/java/forge/ai/ability/CopySpellAbilityAi.java b/forge-ai/src/main/java/forge/ai/ability/CopySpellAbilityAi.java index d238bc9fe08..d3965115e49 100644 --- a/forge-ai/src/main/java/forge/ai/ability/CopySpellAbilityAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/CopySpellAbilityAi.java @@ -19,8 +19,8 @@ public class CopySpellAbilityAi extends SpellAbilityAi { @Override protected AiAbilityDecision checkApiLogic(Player aiPlayer, SpellAbility sa) { Game game = aiPlayer.getGame(); - int chance = ((PlayerControllerAi)aiPlayer.getController()).getAi().getIntProperty(AiProps.CHANCE_TO_COPY_OWN_SPELL_WHILE_ON_STACK); - int diff = ((PlayerControllerAi)aiPlayer.getController()).getAi().getIntProperty(AiProps.ALWAYS_COPY_SPELL_IF_CMC_DIFF); + int chance = AiProfileUtil.getIntProperty(aiPlayer, AiProps.CHANCE_TO_COPY_OWN_SPELL_WHILE_ON_STACK); + int diff = AiProfileUtil.getIntProperty(aiPlayer, AiProps.ALWAYS_COPY_SPELL_IF_CMC_DIFF); String logic = sa.getParamOrDefault("AILogic", ""); if (game.getStack().isEmpty()) { diff --git a/forge-ai/src/main/java/forge/ai/ability/CounterAi.java b/forge-ai/src/main/java/forge/ai/ability/CounterAi.java index f8c6060117a..31d41c6e110 100644 --- a/forge-ai/src/main/java/forge/ai/ability/CounterAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/CounterAi.java @@ -148,18 +148,16 @@ public class CounterAi extends SpellAbilityAi { } // Specific constraints for the AI to use/not use counterspells against specific groups of spells - // (specified in the AI profile) - AiController aic = ((PlayerControllerAi)ai.getController()).getAi(); - boolean ctrCmc0ManaPerms = aic.getBooleanProperty(AiProps.ALWAYS_COUNTER_CMC_0_MANA_MAKING_PERMS); - boolean ctrDamageSpells = aic.getBooleanProperty(AiProps.ALWAYS_COUNTER_DAMAGE_SPELLS); - boolean ctrRemovalSpells = aic.getBooleanProperty(AiProps.ALWAYS_COUNTER_REMOVAL_SPELLS); - boolean ctrPumpSpells = aic.getBooleanProperty(AiProps.ALWAYS_COUNTER_PUMP_SPELLS); - boolean ctrAuraSpells = aic.getBooleanProperty(AiProps.ALWAYS_COUNTER_AURAS); - boolean ctrOtherCounters = aic.getBooleanProperty(AiProps.ALWAYS_COUNTER_OTHER_COUNTERSPELLS); - int ctrChanceCMC1 = aic.getIntProperty(AiProps.CHANCE_TO_COUNTER_CMC_1); - int ctrChanceCMC2 = aic.getIntProperty(AiProps.CHANCE_TO_COUNTER_CMC_2); - int ctrChanceCMC3 = aic.getIntProperty(AiProps.CHANCE_TO_COUNTER_CMC_3); - String ctrNamed = aic.getProperty(AiProps.ALWAYS_COUNTER_SPELLS_FROM_NAMED_CARDS); + boolean ctrCmc0ManaPerms = AiProfileUtil.getBoolProperty(ai, AiProps.ALWAYS_COUNTER_CMC_0_MANA_MAKING_PERMS); + boolean ctrDamageSpells = AiProfileUtil.getBoolProperty(ai, AiProps.ALWAYS_COUNTER_DAMAGE_SPELLS); + boolean ctrRemovalSpells = AiProfileUtil.getBoolProperty(ai, AiProps.ALWAYS_COUNTER_REMOVAL_SPELLS); + boolean ctrPumpSpells = AiProfileUtil.getBoolProperty(ai, AiProps.ALWAYS_COUNTER_PUMP_SPELLS); + boolean ctrAuraSpells = AiProfileUtil.getBoolProperty(ai, AiProps.ALWAYS_COUNTER_AURAS); + boolean ctrOtherCounters = AiProfileUtil.getBoolProperty(ai, AiProps.ALWAYS_COUNTER_OTHER_COUNTERSPELLS); + int ctrChanceCMC1 = AiProfileUtil.getIntProperty(ai, AiProps.CHANCE_TO_COUNTER_CMC_1); + int ctrChanceCMC2 = AiProfileUtil.getIntProperty(ai, AiProps.CHANCE_TO_COUNTER_CMC_2); + int ctrChanceCMC3 = AiProfileUtil.getIntProperty(ai, AiProps.CHANCE_TO_COUNTER_CMC_3); + String ctrNamed = AiProfileUtil.getProperty(ai, AiProps.ALWAYS_COUNTER_SPELLS_FROM_NAMED_CARDS); boolean dontCounter = false; if (tgtCMC == 1 && !MyRandom.percentTrue(ctrChanceCMC1)) { @@ -170,7 +168,7 @@ public class CounterAi extends SpellAbilityAi { dontCounter = true; } - if (tgtSA != null && tgtCMC < aic.getIntProperty(AiProps.MIN_SPELL_CMC_TO_COUNTER)) { + if (tgtSA != null && tgtCMC < AiProfileUtil.getIntProperty(ai, AiProps.MIN_SPELL_CMC_TO_COUNTER)) { dontCounter = true; Card tgtSource = tgtSA.getHostCard(); if ((tgtSource != null && tgtCMC == 0 && tgtSource.isPermanent() && !tgtSource.getManaAbilities().isEmpty() && ctrCmc0ManaPerms) diff --git a/forge-ai/src/main/java/forge/ai/ability/CountersProliferateAi.java b/forge-ai/src/main/java/forge/ai/ability/CountersProliferateAi.java index ef11dc61c80..7b18cbe1da8 100644 --- a/forge-ai/src/main/java/forge/ai/ability/CountersProliferateAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/CountersProliferateAi.java @@ -112,7 +112,7 @@ public class CountersProliferateAi extends SpellAbilityAi { final CounterType poison = CounterEnumType.POISON; - boolean aggroAI = (((PlayerControllerAi) ai.getController()).getAi()).getBooleanProperty(AiProps.PLAY_AGGRO); + boolean aggroAI = AiProfileUtil.getBoolProperty(ai, AiProps.PLAY_AGGRO); // because countertype can't be chosen anymore, only look for poison counters for (final Player p : IterableUtil.filter(options, Player.class)) { if (p.isOpponentOf(ai)) { 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 b697bbc3445..4e22ce5b56b 100644 --- a/forge-ai/src/main/java/forge/ai/ability/CountersPutAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/CountersPutAi.java @@ -141,7 +141,7 @@ public class CountersPutAi extends CountersAi { final boolean isClockwork = "True".equals(sa.getParam("UpTo")) && "Self".equals(sa.getParam("Defined")) && "P1P0".equals(sa.getParam("CounterType")) && "Count$xPaid".equals(source.getSVar("X")) && sa.hasParam("MaxFromEffect"); - boolean playAggro = ((PlayerControllerAi) ai.getController()).getAi().getBooleanProperty(AiProps.PLAY_AGGRO); + boolean playAggro = AiProfileUtil.getBoolProperty(ai, AiProps.PLAY_AGGRO); if ("ExistingCounter".equals(type)) { final boolean eachExisting = sa.hasParam("EachExistingCounter"); @@ -219,10 +219,8 @@ public class CountersPutAi extends CountersAi { } else if ("PayEnergy".equals(logic)) { return new AiAbilityDecision(100, AiPlayDecision.WillPlay); } else if ("PayEnergyConservatively".equals(logic)) { - boolean onlyInCombat = ai.getController().isAI() - && ((PlayerControllerAi) ai.getController()).getAi().getBooleanProperty(AiProps.CONSERVATIVE_ENERGY_PAYMENT_ONLY_IN_COMBAT); - boolean onlyDefensive = ai.getController().isAI() - && ((PlayerControllerAi) ai.getController()).getAi().getBooleanProperty(AiProps.CONSERVATIVE_ENERGY_PAYMENT_ONLY_DEFENSIVELY); + boolean onlyInCombat = AiProfileUtil.getBoolProperty(ai, AiProps.CONSERVATIVE_ENERGY_PAYMENT_ONLY_IN_COMBAT); + boolean onlyDefensive = AiProfileUtil.getBoolProperty(ai, AiProps.CONSERVATIVE_ENERGY_PAYMENT_ONLY_DEFENSIVELY); if (playAggro) { // aggro profiles ignore conservative play for this AI logic 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 d83cddd4bb8..a6a60df9069 100644 --- a/forge-ai/src/main/java/forge/ai/ability/DamageDealAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/DamageDealAi.java @@ -108,16 +108,13 @@ public class DamageDealAi extends DamageAiBase { dmg = ComputerUtilCost.getMaxXValue(sa, ai, sa.isTrigger()); // Try not to waste spells like Blaze or Fireball on early targets, try to do more damage with them if possible - if (ai.getController().isAI()) { - AiController aic = ((PlayerControllerAi)ai.getController()).getAi(); - int holdChance = aic.getIntProperty(AiProps.HOLD_X_DAMAGE_SPELLS_FOR_MORE_DAMAGE_CHANCE); - if (MyRandom.percentTrue(holdChance)) { - int threshold = aic.getIntProperty(AiProps.HOLD_X_DAMAGE_SPELLS_THRESHOLD); - boolean inDanger = ComputerUtil.aiLifeInDanger(ai, false, 0); - boolean isLethal = sa.usesTargeting() && sa.getTargetRestrictions().canTgtPlayer() && dmg >= ai.getWeakestOpponent().getLife() && !ai.getWeakestOpponent().cantLoseForZeroOrLessLife(); - if (dmg < threshold && ai.getGame().getPhaseHandler().getTurn() / 2 < threshold && !inDanger && !isLethal) { - return new AiAbilityDecision(0, AiPlayDecision.CantPlayAi); - } + int holdChance = AiProfileUtil.getIntProperty(ai, AiProps.HOLD_X_DAMAGE_SPELLS_FOR_MORE_DAMAGE_CHANCE); + if (MyRandom.percentTrue(holdChance)) { + int threshold = AiProfileUtil.getIntProperty(ai, AiProps.HOLD_X_DAMAGE_SPELLS_THRESHOLD); + boolean inDanger = ComputerUtil.aiLifeInDanger(ai, false, 0); + boolean isLethal = sa.usesTargeting() && sa.getTargetRestrictions().canTgtPlayer() && dmg >= ai.getWeakestOpponent().getLife() && !ai.getWeakestOpponent().cantLoseForZeroOrLessLife(); + if (dmg < threshold && ai.getGame().getPhaseHandler().getTurn() / 2 < threshold && !inDanger && !isLethal) { + return new AiAbilityDecision(0, AiPlayDecision.CantPlayAi); } } @@ -1086,7 +1083,7 @@ public class DamageDealAi extends DamageAiBase { } Game game = ai.getGame(); - int chance = ((PlayerControllerAi)ai.getController()).getAi().getIntProperty(AiProps.CHANCE_TO_CHAIN_TWO_DAMAGE_SPELLS); + int chance = AiProfileUtil.getIntProperty(ai, AiProps.CHANCE_TO_CHAIN_TWO_DAMAGE_SPELLS); if (chance > 0 && (ComputerUtilCombat.lifeInDanger(ai, game.getCombat()) || ComputerUtil.aiLifeInDanger(ai, true, 0))) { chance = 100; // in danger, do it even if normally the chance is low (unless chaining is completely disabled) diff --git a/forge-ai/src/main/java/forge/ai/ability/DestroyAi.java b/forge-ai/src/main/java/forge/ai/ability/DestroyAi.java index bae0fee23b5..104f1e55b7d 100644 --- a/forge-ai/src/main/java/forge/ai/ability/DestroyAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/DestroyAi.java @@ -409,12 +409,11 @@ public class DestroyAi extends SpellAbilityAi { int oppLandsOTB = tgtPlayer.getLandsInPlay().size(); // AI profile-dependent properties - AiController aic = ((PlayerControllerAi)ai.getController()).getAi(); - int amountNoTempoCheck = aic.getIntProperty(AiProps.STRIPMINE_MIN_LANDS_OTB_FOR_NO_TEMPO_CHECK); - int amountNoTimingCheck = aic.getIntProperty(AiProps.STRIPMINE_MIN_LANDS_FOR_NO_TIMING_CHECK); - int amountLandsInHand = aic.getIntProperty(AiProps.STRIPMINE_MIN_LANDS_IN_HAND_TO_ACTIVATE); - int amountLandsToManalock = aic.getIntProperty(AiProps.STRIPMINE_MAX_LANDS_TO_ATTEMPT_MANALOCKING); - boolean highPriorityIfNoLandDrop = aic.getBooleanProperty(AiProps.STRIPMINE_HIGH_PRIORITY_ON_SKIPPED_LANDDROP); + int amountNoTempoCheck = AiProfileUtil.getIntProperty(ai, AiProps.STRIPMINE_MIN_LANDS_OTB_FOR_NO_TEMPO_CHECK); + int amountNoTimingCheck = AiProfileUtil.getIntProperty(ai, AiProps.STRIPMINE_MIN_LANDS_FOR_NO_TIMING_CHECK); + int amountLandsInHand = AiProfileUtil.getIntProperty(ai, AiProps.STRIPMINE_MIN_LANDS_IN_HAND_TO_ACTIVATE); + int amountLandsToManalock = AiProfileUtil.getIntProperty(ai, AiProps.STRIPMINE_MAX_LANDS_TO_ATTEMPT_MANALOCKING); + boolean highPriorityIfNoLandDrop = AiProfileUtil.getBoolProperty(ai, AiProps.STRIPMINE_HIGH_PRIORITY_ON_SKIPPED_LANDDROP); // if the opponent didn't play a land and has few lands OTB, might be worth mana-locking him PhaseHandler ph = ai.getGame().getPhaseHandler(); 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 0a9cc6285b7..6623ad3f812 100644 --- a/forge-ai/src/main/java/forge/ai/ability/DrawAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/DrawAi.java @@ -264,7 +264,7 @@ public class DrawAi extends SpellAbilityAi { if (sa.getPayCosts().hasSpecificCostType(CostPayLife.class)) { // [Necrologia, Pay X Life : Draw X Cards] // Don't draw more than what's "safe" and don't risk a near death experience - boolean aggroAI = (((PlayerControllerAi) ai.getController()).getAi()).getBooleanProperty(AiProps.PLAY_AGGRO); + boolean aggroAI = AiProfileUtil.getBoolProperty(ai, AiProps.PLAY_AGGRO); while (ComputerUtil.aiLifeInDanger(ai, aggroAI, numCards) && numCards > 0) { numCards--; } diff --git a/forge-ai/src/main/java/forge/ai/ability/EndureAi.java b/forge-ai/src/main/java/forge/ai/ability/EndureAi.java index 0bda7e14c19..f9e59ab053b 100644 --- a/forge-ai/src/main/java/forge/ai/ability/EndureAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/EndureAi.java @@ -41,7 +41,7 @@ public class EndureAi extends SpellAbilityAi { return new AiAbilityDecision(0, AiPlayDecision.AnotherTime); } int curLife = aiPlayer.getLife(); - int dangerLife = (((PlayerControllerAi) aiPlayer.getController()).getAi().getIntProperty(AiProps.AI_IN_DANGER_THRESHOLD)); + int dangerLife = AiProfileUtil.getIntProperty(aiPlayer, AiProps.AI_IN_DANGER_THRESHOLD); if (curLife <= dangerLife) { return new AiAbilityDecision(0, AiPlayDecision.CantAffordX); } diff --git a/forge-ai/src/main/java/forge/ai/ability/ExploreAi.java b/forge-ai/src/main/java/forge/ai/ability/ExploreAi.java index 75fe2533f10..e80bc69eacb 100644 --- a/forge-ai/src/main/java/forge/ai/ability/ExploreAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/ExploreAi.java @@ -37,14 +37,8 @@ public class ExploreAi extends SpellAbilityAi { CardCollection landsOTB = CardLists.filter(cardsOTB, CardPredicates.LANDS_PRODUCING_MANA); CardCollection landsInHand = CardLists.filter(cardsInHand, CardPredicates.LANDS_PRODUCING_MANA); - int maxCMCDiff = 1; - int numLandsToStillNeedMore = 2; - - if (ai.getController().isAI()) { - AiController aic = ((PlayerControllerAi)ai.getController()).getAi(); - maxCMCDiff = aic.getIntProperty(AiProps.EXPLORE_MAX_CMC_DIFF_TO_PUT_IN_GRAVEYARD); - numLandsToStillNeedMore = aic.getIntProperty(AiProps.EXPLORE_NUM_LANDS_TO_STILL_NEED_MORE); - } + int maxCMCDiff = AiProfileUtil.getIntProperty(ai, AiProps.EXPLORE_MAX_CMC_DIFF_TO_PUT_IN_GRAVEYARD); + int numLandsToStillNeedMore = AiProfileUtil.getIntProperty(ai, AiProps.EXPLORE_NUM_LANDS_TO_STILL_NEED_MORE); if (landsInHand.isEmpty() && landsOTB.size() <= numLandsToStillNeedMore) { // We need more lands to improve our mana base, explore away the non-lands diff --git a/forge-ai/src/main/java/forge/ai/ability/LifeExchangeVariantAi.java b/forge-ai/src/main/java/forge/ai/ability/LifeExchangeVariantAi.java index 44e7020bb91..551227631a3 100644 --- a/forge-ai/src/main/java/forge/ai/ability/LifeExchangeVariantAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/LifeExchangeVariantAi.java @@ -93,9 +93,9 @@ public class LifeExchangeVariantAi extends SpellAbilityAi { if (game.getCombat().isUnblocked(source) && def.canLoseLife() && aiLife >= def.getLife() && source.getNetPower() < def.getLife()) { // Unblocked Evra which can deal lethal damage return new AiAbilityDecision(100, AiPlayDecision.WillPlay); - } else if (ai.getController().isAI() && aiLife > source.getNetPower() && source.hasKeyword(Keyword.LIFELINK)) { - int dangerMin = (((PlayerControllerAi) ai.getController()).getAi().getIntProperty(AiProps.AI_IN_DANGER_THRESHOLD)); - int dangerMax = (((PlayerControllerAi) ai.getController()).getAi().getIntProperty(AiProps.AI_IN_DANGER_MAX_THRESHOLD)); + } else if (aiLife > source.getNetPower() && source.hasKeyword(Keyword.LIFELINK)) { + int dangerMin = AiProfileUtil.getIntProperty(ai, AiProps.AI_IN_DANGER_THRESHOLD); + int dangerMax = AiProfileUtil.getIntProperty(ai, AiProps.AI_IN_DANGER_MAX_THRESHOLD); int dangerDiff = dangerMax - dangerMin; int lifeInDanger = dangerDiff <= 0 ? dangerMin : MyRandom.getRandom().nextInt(dangerDiff) + dangerMin; if (source.getNetPower() >= lifeInDanger && ai.canGainLife() && ComputerUtil.lifegainPositive(ai, source)) { diff --git a/forge-ai/src/main/java/forge/ai/ability/PermanentCreatureAi.java b/forge-ai/src/main/java/forge/ai/ability/PermanentCreatureAi.java index 200d5648d91..21ead406822 100644 --- a/forge-ai/src/main/java/forge/ai/ability/PermanentCreatureAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/PermanentCreatureAi.java @@ -65,10 +65,7 @@ public class PermanentCreatureAi extends PermanentAi { } // Flash logic - boolean advancedFlash = false; - if (ai.getController().isAI()) { - advancedFlash = ((PlayerControllerAi)ai.getController()).getAi().getBooleanProperty(AiProps.FLASH_ENABLE_ADVANCED_LOGIC); - } + boolean advancedFlash = AiProfileUtil.getBoolProperty(ai, AiProps.FLASH_ENABLE_ADVANCED_LOGIC); if (card.hasKeyword(Keyword.FLASH) || (!ai.canCastSorcery() && sa.canCastTiming(ai) && !sa.isCastFromPlayEffect())) { if (advancedFlash) { return doAdvancedFlashLogic(card, ai, sa); @@ -135,11 +132,11 @@ public class PermanentCreatureAi extends PermanentAi { } } - int chanceToObeyAmbushAI = aic.getIntProperty(AiProps.FLASH_CHANCE_TO_OBEY_AMBUSHAI); - int chanceToAddBlocker = aic.getIntProperty(AiProps.FLASH_CHANCE_TO_CAST_AS_VALUABLE_BLOCKER); - int chanceToCastForETB = aic.getIntProperty(AiProps.FLASH_CHANCE_TO_CAST_DUE_TO_ETB_EFFECTS); - int chanceToRespondToStack = aic.getIntProperty(AiProps.FLASH_CHANCE_TO_RESPOND_TO_STACK_WITH_ETB); - int chanceToProcETBBeforeMain1 = aic.getIntProperty(AiProps.FLASH_CHANCE_TO_CAST_FOR_ETB_BEFORE_MAIN1); + int chanceToObeyAmbushAI = AiProfileUtil.getIntProperty(ai, AiProps.FLASH_CHANCE_TO_OBEY_AMBUSHAI); + int chanceToAddBlocker = AiProfileUtil.getIntProperty(ai, AiProps.FLASH_CHANCE_TO_CAST_AS_VALUABLE_BLOCKER); + int chanceToCastForETB = AiProfileUtil.getIntProperty(ai, AiProps.FLASH_CHANCE_TO_CAST_DUE_TO_ETB_EFFECTS); + int chanceToRespondToStack = AiProfileUtil.getIntProperty(ai, AiProps.FLASH_CHANCE_TO_RESPOND_TO_STACK_WITH_ETB); + int chanceToProcETBBeforeMain1 = AiProfileUtil.getIntProperty(ai, AiProps.FLASH_CHANCE_TO_CAST_FOR_ETB_BEFORE_MAIN1); boolean canCastAtOppTurn = true; for (Card c : ai.getGame().getCardsIn(ZoneType.Battlefield)) { for (StaticAbility s : c.getStaticAbilities()) { diff --git a/forge-ai/src/main/java/forge/ai/ability/RearrangeTopOfLibraryAi.java b/forge-ai/src/main/java/forge/ai/ability/RearrangeTopOfLibraryAi.java index 57454123c44..aae77e5179b 100644 --- a/forge-ai/src/main/java/forge/ai/ability/RearrangeTopOfLibraryAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/RearrangeTopOfLibraryAi.java @@ -100,13 +100,8 @@ public class RearrangeTopOfLibraryAi extends SpellAbilityAi { return false; } - int uncastableCMCThreshold = 2; - int minLandsToScryLandsAway = 4; - if (player.getController().isAI()) { - AiController aic = ((PlayerControllerAi)player.getController()).getAi(); - minLandsToScryLandsAway = aic.getIntProperty(AiProps.SCRY_NUM_LANDS_TO_NOT_NEED_MORE); - uncastableCMCThreshold = aic.getIntProperty(AiProps.SCRY_IMMEDIATELY_UNCASTABLE_CMC_DIFF); - } + int minLandsToScryLandsAway = AiProfileUtil.getIntProperty(player, AiProps.SCRY_NUM_LANDS_TO_NOT_NEED_MORE); + int uncastableCMCThreshold = AiProfileUtil.getIntProperty(player, AiProps.SCRY_IMMEDIATELY_UNCASTABLE_CMC_DIFF); int landsOTB = CardLists.count(p.getCardsIn(ZoneType.Battlefield), CardPredicates.LANDS_PRODUCING_MANA); int cmc = top.isSplitCard() ? Math.min(top.getCMC(Card.SplitCMCMode.LeftSplitCMC), top.getCMC(Card.SplitCMCMode.RightSplitCMC)) diff --git a/forge-ai/src/main/java/forge/ai/ability/RollPlanarDiceAi.java b/forge-ai/src/main/java/forge/ai/ability/RollPlanarDiceAi.java index 9e9aefbabb6..761fe5124d0 100644 --- a/forge-ai/src/main/java/forge/ai/ability/RollPlanarDiceAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/RollPlanarDiceAi.java @@ -29,14 +29,13 @@ public class RollPlanarDiceAi extends SpellAbilityAi { } private boolean willRollOnPlane(Player ai, Card plane) { - AiController aic = ((PlayerControllerAi)ai.getController()).getAi(); boolean decideToRoll = false; boolean rollInMain1 = false; String modeName = "never"; - int maxActivations = aic.getIntProperty(AiProps.DEFAULT_MAX_PLANAR_DIE_ROLLS_PER_TURN); - int chance = aic.getIntProperty(AiProps.DEFAULT_PLANAR_DIE_ROLL_CHANCE); - int hesitationChance = aic.getIntProperty(AiProps.PLANAR_DIE_ROLL_HESITATION_CHANCE); - int minTurnToRoll = aic.getIntProperty(AiProps.DEFAULT_MIN_TURN_TO_ROLL_PLANAR_DIE); + int maxActivations = AiProfileUtil.getIntProperty(ai, AiProps.DEFAULT_MAX_PLANAR_DIE_ROLLS_PER_TURN); + int chance = AiProfileUtil.getIntProperty(ai, AiProps.DEFAULT_PLANAR_DIE_ROLL_CHANCE); + int hesitationChance = AiProfileUtil.getIntProperty(ai, AiProps.PLANAR_DIE_ROLL_HESITATION_CHANCE); + int minTurnToRoll = AiProfileUtil.getIntProperty(ai, AiProps.DEFAULT_MIN_TURN_TO_ROLL_PLANAR_DIE); if (plane.hasSVar("AIRollPlanarDieParams")) { String[] params = plane.getSVar("AIRollPlanarDieParams").toLowerCase().trim().split("\\|"); 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 be10645fcd6..840e9081d2e 100644 --- a/forge-ai/src/main/java/forge/ai/ability/SurveilAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/SurveilAi.java @@ -70,7 +70,7 @@ public class SurveilAi extends SpellAbilityAi { // Only Surveil for life when at decent amount of life remaining final Cost cost = sa.getPayCosts(); if (cost != null && cost.hasSpecificCostType(CostPayLife.class)) { - final int maxLife = ((PlayerControllerAi)ai.getController()).getAi().getIntProperty(AiProps.SURVEIL_LIFEPERC_AFTER_PAYING_LIFE); + final int maxLife = AiProfileUtil.getIntProperty(ai, AiProps.SURVEIL_LIFEPERC_AFTER_PAYING_LIFE); if (!ComputerUtilCost.checkLifeCost(ai, cost, sa.getHostCard(), ai.getStartingLife() * maxLife / 100, sa)) { return new AiAbilityDecision(0, AiPlayDecision.CostNotAcceptable); } 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 f58046f3de7..d83457565b8 100644 --- a/forge-ai/src/main/java/forge/ai/ability/TapAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/TapAi.java @@ -30,8 +30,7 @@ public class TapAi extends TapAiBase { // 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 - AiController aic = ((PlayerControllerAi) ai.getController()).getAi(); - if (!aic.getBooleanProperty(AiProps.PLAY_AGGRO)) { + if (!AiProfileUtil.getBoolProperty(ai, AiProps.PLAY_AGGRO)) { return new AiAbilityDecision(0, AiPlayDecision.CantPlayAi); } } else { 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 e1cd252d84a..c755bee4cfd 100644 --- a/forge-ai/src/main/java/forge/ai/ability/TokenAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/TokenAi.java @@ -186,16 +186,9 @@ public class TokenAi extends SpellAbilityAi { } } - double chance = 1.0F; // 100% - boolean alwaysFromPW = true; - boolean alwaysOnOppAttack = true; - - if (ai.getController().isAI()) { - AiController aic = ((PlayerControllerAi) ai.getController()).getAi(); - chance = (double)aic.getIntProperty(AiProps.TOKEN_GENERATION_ABILITY_CHANCE) / 100; - alwaysFromPW = aic.getBooleanProperty(AiProps.TOKEN_GENERATION_ALWAYS_IF_FROM_PLANESWALKER); - alwaysOnOppAttack = aic.getBooleanProperty(AiProps.TOKEN_GENERATION_ALWAYS_IF_OPP_ATTACKS); - } + double chance = (double)AiProfileUtil.getIntProperty(ai, AiProps.TOKEN_GENERATION_ABILITY_CHANCE) / 100; + boolean alwaysFromPW = AiProfileUtil.getBoolProperty(ai, AiProps.TOKEN_GENERATION_ALWAYS_IF_FROM_PLANESWALKER); + boolean alwaysOnOppAttack = AiProfileUtil.getBoolProperty(ai, AiProps.TOKEN_GENERATION_ALWAYS_IF_OPP_ATTACKS); if (sa.isPwAbility() && alwaysFromPW) { return new AiAbilityDecision(100, AiPlayDecision.WillPlay); diff --git a/forge-game/src/main/java/forge/game/ability/effects/VentureEffect.java b/forge-game/src/main/java/forge/game/ability/effects/VentureEffect.java index 8b1c6ac6566..647faba9c8e 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/VentureEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/VentureEffect.java @@ -27,7 +27,7 @@ import forge.item.PaperCardPredicates; import forge.util.Localizer; import forge.util.PredicateString.StringOp; -public class VentureEffect extends SpellAbilityEffect { +public class VentureEffect extends SpellAbilityEffect { private Card getDungeonCard(SpellAbility sa, Player player, Map moveParams) { final Game game = player.getGame(); @@ -44,7 +44,7 @@ public class VentureEffect extends SpellAbilityEffect { } } - List dungeonCards = null; + List dungeonCards; if (sa.hasParam("Dungeon")) { String dungeonType = sa.getParam("Dungeon"); Predicate rulesPredicate = CardRulesPredicates.IS_DUNGEON.and(CardRulesPredicates.subType(StringOp.EQUALS, dungeonType));