Refactor AiProfile handling (#9012)

Co-authored-by: tool4EvEr <tool4EvEr@>
This commit is contained in:
tool4ever
2025-10-28 05:26:18 +01:00
committed by GitHub
parent 364845ab4e
commit 954031cdb2
30 changed files with 175 additions and 261 deletions

View File

@@ -393,7 +393,7 @@ public class AiAttackController {
if (ai.getController().isAI()) { if (ai.getController().isAI()) {
PlayerControllerAi aic = ((PlayerControllerAi) ai.getController()); PlayerControllerAi aic = ((PlayerControllerAi) ai.getController());
pilotsNonAggroDeck = aic.pilotsNonAggroDeck(); 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 // TODO make switchable via AI property
int thresholdMod = 0; int thresholdMod = 0;
@@ -614,13 +614,7 @@ public class AiAttackController {
// if true, the AI will attempt to identify which blockers will already be taken, // 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 // thus attempting to predict how many creatures with evasion can actively block
boolean predictEvasion = false; boolean predictEvasion = AiProfileUtil.getBoolProperty(ai, AiProps.COMBAT_ASSAULT_ATTACK_EVASION_PREDICTION);
if (ai.getController().isAI()) {
AiController aic = ((PlayerControllerAi) ai.getController()).getAi();
if (aic.getBooleanProperty(AiProps.COMBAT_ASSAULT_ATTACK_EVASION_PREDICTION)) {
predictEvasion = true;
}
}
CardCollection accountedBlockers = new CardCollection(this.blockers); CardCollection accountedBlockers = new CardCollection(this.blockers);
CardCollection categorizedAttackers = new CardCollection(); CardCollection categorizedAttackers = new CardCollection();
@@ -860,12 +854,12 @@ public class AiAttackController {
AiController aic = ((PlayerControllerAi) ai.getController()).getAi(); AiController aic = ((PlayerControllerAi) ai.getController()).getAi();
simAI = aic.usesSimulation(); simAI = aic.usesSimulation();
if (!simAI) { if (!simAI) {
playAggro = aic.getBooleanProperty(AiProps.PLAY_AGGRO); playAggro = aic.getBoolProperty(AiProps.PLAY_AGGRO);
chanceToAttackToTrade = aic.getIntProperty(AiProps.CHANCE_TO_ATTACK_INTO_TRADE); 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); extraChanceIfOppHasMana = aic.getIntProperty(AiProps.CHANCE_TO_ATKTRADE_WHEN_OPP_HAS_MANA);
tradeIfLowerLifePressure = aic.getBooleanProperty(AiProps.RANDOMLY_ATKTRADE_ONLY_ON_LOWER_LIFE_PRESSURE); tradeIfLowerLifePressure = aic.getBoolProperty(AiProps.RANDOMLY_ATKTRADE_ONLY_ON_LOWER_LIFE_PRESSURE);
predictEvasion = aic.getBooleanProperty(AiProps.COMBAT_ATTRITION_ATTACK_EVASION_PREDICTION); 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 // Check if maybe we are too reckless in adding this attacker
if (canKillAllDangerous) { if (canKillAllDangerous) {
boolean avoidAttackingIntoBlock = ai.getController().isAI() 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 attackerWillDie = defPower >= attacker.getNetToughness();
boolean uselessAttack = !hasCombatEffect && !hasAttackEffect; boolean uselessAttack = !hasCombatEffect && !hasAttackEffect;
boolean noContributionToAttack = attackers.size() <= defenders.size() || attacker.getNetPower() <= 0; boolean noContributionToAttack = attackers.size() <= defenders.size() || attacker.getNetPower() <= 0;

View File

@@ -861,10 +861,9 @@ public class AiBlockController {
return; return;
} }
AiController aic = ((PlayerControllerAi) ai.getController()).getAi(); final int evalThresholdToken = AiProfileUtil.getIntProperty(ai, AiProps.THRESHOLD_TOKEN_CHUMP_TO_SAVE_PLANESWALKER);
final int evalThresholdToken = aic.getIntProperty(AiProps.THRESHOLD_TOKEN_CHUMP_TO_SAVE_PLANESWALKER); final int evalThresholdNonToken = AiProfileUtil.getIntProperty(ai, AiProps.THRESHOLD_NONTOKEN_CHUMP_TO_SAVE_PLANESWALKER);
final int evalThresholdNonToken = aic.getIntProperty(AiProps.THRESHOLD_NONTOKEN_CHUMP_TO_SAVE_PLANESWALKER); final boolean onlyIfLethal = AiProfileUtil.getBoolProperty(ai, AiProps.CHUMP_TO_SAVE_PLANESWALKER_ONLY_ON_LETHAL);
final boolean onlyIfLethal = aic.getBooleanProperty(AiProps.CHUMP_TO_SAVE_PLANESWALKER_ONLY_ON_LETHAL);
if (evalThresholdToken > 0 || evalThresholdNonToken > 0) { if (evalThresholdToken > 0 || evalThresholdNonToken > 0) {
// detect how much damage is threatened to each of the planeswalkers, see which ones would be // 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); clearBlockers(combat, possibleBlockers);
diff = (ai.getLife() * 2) - 5; // This is the minimal gain for an unnecessary trade 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; diff = 0;
} }
@@ -1284,9 +1283,9 @@ public class AiBlockController {
AiController aic = ((PlayerControllerAi) ai.getController()).getAi(); AiController aic = ((PlayerControllerAi) ai.getController()).getAi();
// simulation must get same results or it may crash // simulation must get same results or it may crash
if (!aic.usesSimulation()) { if (!aic.usesSimulation()) {
enableRandomTrades = aic.getBooleanProperty(AiProps.ENABLE_RANDOM_FAVORABLE_TRADES_ON_BLOCK); enableRandomTrades = aic.getBoolProperty(AiProps.ENABLE_RANDOM_FAVORABLE_TRADES_ON_BLOCK);
randomTradeIfBehindOnBoard = aic.getBooleanProperty(AiProps.RANDOMLY_TRADE_EVEN_WHEN_HAVE_LESS_CREATS); randomTradeIfBehindOnBoard = aic.getBoolProperty(AiProps.RANDOMLY_TRADE_EVEN_WHEN_HAVE_LESS_CREATS);
randomTradeIfCreatInHand = aic.getBooleanProperty(AiProps.ALSO_TRADE_WHEN_HAVE_A_REPLACEMENT_CREAT); randomTradeIfCreatInHand = aic.getBoolProperty(AiProps.ALSO_TRADE_WHEN_HAVE_A_REPLACEMENT_CREAT);
minRandomTradeChance = aic.getIntProperty(AiProps.MIN_CHANCE_TO_RANDOMLY_TRADE_ON_BLOCK); minRandomTradeChance = aic.getIntProperty(AiProps.MIN_CHANCE_TO_RANDOMLY_TRADE_ON_BLOCK);
maxRandomTradeChance = aic.getIntProperty(AiProps.MAX_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); chanceModForEmbalm = aic.getIntProperty(AiProps.CHANCE_DECREASE_TO_TRADE_VS_EMBALM);

View File

@@ -764,7 +764,7 @@ public class AiController {
return predictSpellToCastInMain2(exceptSA, true); return predictSpellToCastInMain2(exceptSA, true);
} }
private SpellAbility predictSpellToCastInMain2(ApiType exceptSA, boolean handOnly) { private SpellAbility predictSpellToCastInMain2(ApiType exceptSA, boolean handOnly) {
if (!getBooleanProperty(AiProps.PREDICT_SPELLS_FOR_MAIN2)) { if (!getBoolProperty(AiProps.PREDICT_SPELLS_FOR_MAIN2)) {
return null; return null;
} }
@@ -930,7 +930,7 @@ public class AiController {
final Card card = sa.getHostCard(); final Card card = sa.getHostCard();
// Trying to play a card that has Buyback without a Buyback cost, look for possible additional considerations // 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)) { if (card.hasKeyword(Keyword.BUYBACK) && !sa.isBuyback() && !canPlaySpellWithoutBuyback(card, sa)) {
return AiPlayDecision.NeedsToPlayCriteriaNotMet; return AiPlayDecision.NeedsToPlayCriteriaNotMet;
} }
@@ -1299,27 +1299,13 @@ public class AiController {
} }
public String getProperty(AiProps propName) { public String getProperty(AiProps propName) {
return AiProfileUtil.getAIProp(getPlayer().getLobbyPlayer(), propName); return AiProfileUtil.getProperty(getPlayer(), propName);
} }
public int getIntProperty(AiProps propName) { public int getIntProperty(AiProps propName) {
String prop = AiProfileUtil.getAIProp(getPlayer().getLobbyPlayer(), propName); return AiProfileUtil.getIntProperty(getPlayer(), propName);
if (prop == null || prop.isEmpty()) {
return Integer.parseInt(propName.getDefault());
} }
public boolean getBoolProperty(AiProps propName) {
return Integer.parseInt(prop); return AiProfileUtil.getBoolProperty(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 AiPlayDecision canPlayFromEffectAI(Spell spell, boolean mandatory, boolean withoutPayingManaCost) { public AiPlayDecision canPlayFromEffectAI(Spell spell, boolean mandatory, boolean withoutPayingManaCost) {
@@ -1330,7 +1316,7 @@ public class AiController {
final Card card = spell.getHostCard(); final Card card = spell.getHostCard();
if (spell instanceof SpellApiBased) { if (spell instanceof SpellApiBased) {
boolean chance = false; boolean chance;
if (withoutPayingManaCost) { if (withoutPayingManaCost) {
chance = SpellApiToAi.Converter.get(spell).doTriggerNoCostWithSubs(player, spell, mandatory).willingToPlay(); chance = SpellApiToAi.Converter.get(spell).doTriggerNoCostWithSubs(player, spell, mandatory).willingToPlay();
} else { } else {
@@ -1468,7 +1454,7 @@ public class AiController {
CardCollection inHand = CardLists.filter(player.getCardsIn(ZoneType.Hand), CardPredicates.NON_LANDS); CardCollection inHand = CardLists.filter(player.getCardsIn(ZoneType.Hand), CardPredicates.NON_LANDS);
CardCollectionView otb = player.getCardsIn(ZoneType.Battlefield); 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)) { if (!otb.anyMatch(CardPredicates.NON_LANDS)) {
return false; return false;
} }

View File

@@ -18,6 +18,7 @@
package forge.ai; package forge.ai;
import forge.LobbyPlayer; import forge.LobbyPlayer;
import forge.game.player.Player;
import forge.util.Aggregates; import forge.util.Aggregates;
import forge.util.FileUtil; import forge.util.FileUtil;
import forge.util.TextUtil; import forge.util.TextUtil;
@@ -113,6 +114,23 @@ public class AiProfileUtil {
return profileMap; 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. * Returns an AI property value for the current profile.
* *

View File

@@ -345,14 +345,12 @@ public class ComputerUtil {
return sacMeList.getFirst(); return sacMeList.getFirst();
} else { } else {
// empty sacMeList, so get some viable average preference if the option is enabled // empty sacMeList, so get some viable average preference if the option is enabled
if (ai.getController().isAI()) { boolean enableDefaultPref = AiProfileUtil.getBoolProperty(ai, AiProps.SACRIFICE_DEFAULT_PREF_ENABLE);
AiController aic = ((PlayerControllerAi) ai.getController()).getAi();
boolean enableDefaultPref = aic.getBooleanProperty(AiProps.SACRIFICE_DEFAULT_PREF_ENABLE);
if (enableDefaultPref) { if (enableDefaultPref) {
int minCMC = aic.getIntProperty(AiProps.SACRIFICE_DEFAULT_PREF_MIN_CMC); int minCMC = AiProfileUtil.getIntProperty(ai, AiProps.SACRIFICE_DEFAULT_PREF_MIN_CMC);
int maxCMC = aic.getIntProperty(AiProps.SACRIFICE_DEFAULT_PREF_MAX_CMC); int maxCMC = AiProfileUtil.getIntProperty(ai, AiProps.SACRIFICE_DEFAULT_PREF_MAX_CMC);
int maxCreatureEval = aic.getIntProperty(AiProps.SACRIFICE_DEFAULT_PREF_MAX_CREATURE_EVAL); int maxCreatureEval = AiProfileUtil.getIntProperty(ai, AiProps.SACRIFICE_DEFAULT_PREF_MAX_CREATURE_EVAL);
boolean allowTokens = aic.getBooleanProperty(AiProps.SACRIFICE_DEFAULT_PREF_ALLOW_TOKENS); boolean allowTokens = AiProfileUtil.getBoolProperty(ai, AiProps.SACRIFICE_DEFAULT_PREF_ALLOW_TOKENS);
List<String> dontSac = Arrays.asList("Black Lotus", "Mox Pearl", "Mox Jet", "Mox Emerald", "Mox Ruby", "Mox Sapphire", "Lotus Petal"); List<String> dontSac = Arrays.asList("Black Lotus", "Mox Pearl", "Mox Jet", "Mox Emerald", "Mox Ruby", "Mox Sapphire", "Lotus Petal");
CardCollection allowList = CardLists.filter(typeList, card -> { CardCollection allowList = CardLists.filter(typeList, card -> {
if (card.isCreature() && ComputerUtilCard.evaluateCreature(card) > maxCreatureEval) { if (card.isCreature() && ComputerUtilCard.evaluateCreature(card) > maxCreatureEval) {
@@ -369,7 +367,6 @@ public class ComputerUtil {
} }
} }
} }
}
// Sac lands // Sac lands
final CardCollection landsInPlay = CardLists.getType(typeList, "Land"); final CardCollection landsInPlay = CardLists.getType(typeList, "Land");
@@ -1999,8 +1996,7 @@ public class ComputerUtil {
else if ((threatApi == ApiType.Attach && (topStack.isCurse() || "Curse".equals(topStack.getParam("AILogic")))) else if ((threatApi == ApiType.Attach && (topStack.isCurse() || "Curse".equals(topStack.getParam("AILogic"))))
&& (saviourApi == ApiType.Pump || saviourApi == ApiType.PumpAll && (saviourApi == ApiType.Pump || saviourApi == ApiType.PumpAll
|| saviourApi == ApiType.Protection || saviourApi == null)) { || saviourApi == ApiType.Protection || saviourApi == null)) {
AiController aic = aiPlayer.isAI() ? ((PlayerControllerAi)aiPlayer.getController()).getAi() : null; boolean enableCurseAuraRemoval = AiProfileUtil.getBoolProperty(aiPlayer, AiProps.ACTIVELY_DESTROY_IMMEDIATELY_UNBLOCKABLE);
boolean enableCurseAuraRemoval = aic != null ? aic.getBooleanProperty(AiProps.ACTIVELY_DESTROY_IMMEDIATELY_UNBLOCKABLE) : false;
if (enableCurseAuraRemoval) { if (enableCurseAuraRemoval) {
for (final Object o : objects) { for (final Object o : objects) {
if (o instanceof Card c) { if (o instanceof Card c) {
@@ -2041,9 +2037,7 @@ public class ComputerUtil {
// a creature will [hopefully] die from a spell on stack // a creature will [hopefully] die from a spell on stack
boolean willDieFromSpell = false; boolean willDieFromSpell = false;
boolean noStackCheck = false; boolean noStackCheck = false;
if (ai.getController().isAI()) { if (AiProfileUtil.getBoolProperty(ai, AiProps.DONT_EVAL_KILLSPELLS_ON_STACK_WITH_PERMISSION)) {
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 // 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. // 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()) { for (SpellAbilityStackInstance si : game.getStack()) {
@@ -2054,7 +2048,6 @@ public class ComputerUtil {
} }
} }
} }
}
willDieFromSpell = !noStackCheck && predictThreatenedObjects(creature.getController(), excludeSa).contains(creature); willDieFromSpell = !noStackCheck && predictThreatenedObjects(creature.getController(), excludeSa).contains(creature);
if (nonCombatOnly) { if (nonCombatOnly) {
@@ -2080,8 +2073,7 @@ public class ComputerUtil {
* @return a filtered list with no dying creatures in it * @return a filtered list with no dying creatures in it
*/ */
public static CardCollection filterCreaturesThatWillDieThisTurn(final Player ai, final CardCollection list, final SpellAbility excludeSa) { public static CardCollection filterCreaturesThatWillDieThisTurn(final Player ai, final CardCollection list, final SpellAbility excludeSa) {
AiController aic = ((PlayerControllerAi)ai.getController()).getAi(); if (AiProfileUtil.getBoolProperty(ai, AiProps.AVOID_TARGETING_CREATS_THAT_WILL_DIE)) {
if (aic.getBooleanProperty(AiProps.AVOID_TARGETING_CREATS_THAT_WILL_DIE)) {
// Try to avoid targeting creatures that are dead on board // Try to avoid targeting creatures that are dead on board
List<Card> willBeKilled = CardLists.filter(list, card -> card.isCreature() && predictCreatureWillDieThisTurn(ai, card, excludeSa)); List<Card> willBeKilled = CardLists.filter(list, card -> card.isCreature() && predictCreatureWillDieThisTurn(ai, card, excludeSa));
list.removeAll(willBeKilled); list.removeAll(willBeKilled);
@@ -2254,25 +2246,14 @@ public class ComputerUtil {
boolean bottom = false; boolean bottom = false;
// AI profile-based toggles // AI profile-based toggles
int maxLandsToScryLandsToTop = 3; int maxLandsToScryLandsToTop = AiProfileUtil.getIntProperty(player, AiProps.SCRY_NUM_LANDS_TO_STILL_NEED_MORE);
int minLandsToScryLandsAway = 8; int minLandsToScryLandsAway = AiProfileUtil.getIntProperty(player, AiProps.SCRY_NUM_LANDS_TO_NOT_NEED_MORE);
int minCreatsToScryCreatsAway = 5; int minCreatsToScryCreatsAway = AiProfileUtil.getIntProperty(player, AiProps.SCRY_NUM_CREATURES_TO_NOT_NEED_SUBPAR_ONES);
int minCreatEvalThreshold = 160; // just a bit higher than a baseline 2/2 creature or a 1/1 mana dork int minCreatEvalThreshold = AiProfileUtil.getIntProperty(player, AiProps.SCRY_EVALTHR_TO_SCRY_AWAY_LOWCMC_CREATURE);
int lowCMCThreshold = 3; int lowCMCThreshold = AiProfileUtil.getIntProperty(player, AiProps.SCRY_EVALTHR_CMC_THRESHOLD);
int maxCreatsToScryLowCMCAway = 3; int maxCreatsToScryLowCMCAway = AiProfileUtil.getIntProperty(player, AiProps.SCRY_EVALTHR_CREATCOUNT_TO_SCRY_AWAY_LOWCMC);
boolean uncastablesToBottom = false; boolean uncastablesToBottom = AiProfileUtil.getBoolProperty(player, AiProps.SCRY_IMMEDIATELY_UNCASTABLE_TO_BOTTOM);
int uncastableCMCThreshold = 1; int uncastableCMCThreshold = AiProfileUtil.getIntProperty(player, AiProps.SCRY_IMMEDIATELY_UNCASTABLE_CMC_DIFF);
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);
}
CardCollectionView allCards = player.getAllCards(); CardCollectionView allCards = player.getAllCards();
CardCollectionView cardsInHand = player.getCardsIn(ZoneType.Hand); CardCollectionView cardsInHand = player.getCardsIn(ZoneType.Hand);

View File

@@ -1060,7 +1060,6 @@ public class ComputerUtilCard {
public static boolean useRemovalNow(final SpellAbility sa, final Card c, final int dmg, ZoneType destination) { public static boolean useRemovalNow(final SpellAbility sa, final Card c, final int dmg, ZoneType destination) {
final Player ai = sa.getActivatingPlayer(); final Player ai = sa.getActivatingPlayer();
final AiController aic = ((PlayerControllerAi) ai.getController()).getAi();
final Game game = ai.getGame(); final Game game = ai.getGame();
final PhaseHandler ph = game.getPhaseHandler(); final PhaseHandler ph = game.getPhaseHandler();
final PhaseType phaseType = ph.getPhase(); final PhaseType phaseType = ph.getPhase();
@@ -1207,7 +1206,7 @@ public class ComputerUtilCard {
} }
} else if (c.isPlaneswalker()) { } else if (c.isPlaneswalker()) {
threat = 1; 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 // non-creature artifacts and global enchantments with suspicious intrinsic abilities
boolean priority = false; boolean priority = false;
if (c.getOwner().isOpponentOf(ai) && c.getController().isOpponentOf(ai)) { if (c.getOwner().isOpponentOf(ai) && c.getController().isOpponentOf(ai)) {
@@ -1311,7 +1310,7 @@ public class ComputerUtilCard {
AiController aic = ((PlayerControllerAi) ai.getController()).getAi(); AiController aic = ((PlayerControllerAi) ai.getController()).getAi();
simAI = aic.usesSimulation(); simAI = aic.usesSimulation();
if (!simAI) { 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); 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 // 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) // (other similar cards with AILogic$ Berserk that do not die only when attacking are excluded from consideration)
if (ai.getController() instanceof PlayerControllerAi) { 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"); || sa.hasParam("AtEOT");
if (!aggr) { if (!aggr) {
return false; return false;
@@ -1907,17 +1906,10 @@ public class ComputerUtilCard {
return oppCards; return oppCards;
} }
boolean enablePriorityRemoval = false; boolean enablePriorityRemoval = AiProfileUtil.getBoolProperty(ai, AiProps.ACTIVELY_DESTROY_IMMEDIATELY_UNBLOCKABLE);
boolean priorityRemovalOnlyInDanger = false; int priorityRemovalThreshold = AiProfileUtil.getIntProperty(ai, AiProps.DESTROY_IMMEDIATELY_UNBLOCKABLE_THRESHOLD);
int priorityRemovalThreshold = 0; boolean priorityRemovalOnlyInDanger = AiProfileUtil.getBoolProperty(ai, AiProps.DESTROY_IMMEDIATELY_UNBLOCKABLE_ONLY_IN_DNGR);
int lifeInDanger = 5; int lifeInDanger = AiProfileUtil.getIntProperty(ai, AiProps.DESTROY_IMMEDIATELY_UNBLOCKABLE_LIFE_IN_DNGR);
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);
}
if (!enablePriorityRemoval) { if (!enablePriorityRemoval) {
// Nothing to do here, the profile does not allow prioritizing // Nothing to do here, the profile does not allow prioritizing

View File

@@ -443,13 +443,12 @@ public class ComputerUtilCombat {
} }
} }
int threshold = 0; if (resultingPoison(ai, combat) > Math.max(7, ai.getPoisonCounters())) {
int maxTreshold = 0; return true;
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;
} }
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; int chance = MyRandom.getRandom().nextInt(80) + 5;
while (maxTreshold > 0) { while (maxTreshold > 0) {
if (MyRandom.getRandom().nextInt(100) < chance) { if (MyRandom.getRandom().nextInt(100) < chance) {
@@ -458,10 +457,6 @@ public class ComputerUtilCombat {
maxTreshold--; maxTreshold--;
} }
if (resultingPoison(ai, combat) > Math.max(7, ai.getPoisonCounters())) {
return true;
}
return !ai.cantLoseForZeroOrLessLife() && lifeThatWouldRemain(ai, combat) - payment < Math.min(threshold, ai.getLife()); return !ai.cantLoseForZeroOrLessLife() && lifeThatWouldRemain(ai, combat) - payment < Math.min(threshold, ai.getLife());
} }

View File

@@ -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 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) final boolean creatureInDanger = ComputerUtil.predictCreatureWillDieThisTurn(ai, source, sourceAbility, false)
&& !ComputerUtilCombat.willOpposingCreatureDieInCombat(ai, source, combat); && !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(); final boolean aiInDanger = ai.getLife() <= lifeThreshold && ai.canLoseLife() && !ai.cantLoseForZeroOrLessLife();
if (creatureInDanger && !ComputerUtilCombat.isDangerousToSacInCombat(ai, source, combat)) { if (creatureInDanger && !ComputerUtilCombat.isDangerousToSacInCombat(ai, source, combat)) {
return true; return true;

View File

@@ -139,9 +139,9 @@ public class PlayerControllerAi extends PlayerController {
} }
} }
boolean sbLimitedFormats = getAi().getBooleanProperty(AiProps.SIDEBOARDING_IN_LIMITED_FORMATS); boolean sbLimitedFormats = getAi().getBoolProperty(AiProps.SIDEBOARDING_IN_LIMITED_FORMATS);
boolean sbSharedTypesOnly = getAi().getBooleanProperty(AiProps.SIDEBOARDING_SHARED_TYPE_ONLY); boolean sbSharedTypesOnly = getAi().getBoolProperty(AiProps.SIDEBOARDING_SHARED_TYPE_ONLY);
boolean sbPlaneswalkerException = getAi().getBooleanProperty(AiProps.SIDEBOARDING_PLANESWALKER_EQ_CREATURE); boolean sbPlaneswalkerException = getAi().getBoolProperty(AiProps.SIDEBOARDING_PLANESWALKER_EQ_CREATURE);
int sbChanceOnWin = getAi().getIntProperty(AiProps.SIDEBOARDING_CHANCE_ON_WIN); int sbChanceOnWin = getAi().getIntProperty(AiProps.SIDEBOARDING_CHANCE_ON_WIN);
int sbChancePerCard = getAi().getIntProperty(AiProps.SIDEBOARDING_CHANCE_PER_CARD); int sbChancePerCard = getAi().getIntProperty(AiProps.SIDEBOARDING_CHANCE_PER_CARD);
@@ -1372,7 +1372,7 @@ public class PlayerControllerAi extends PlayerController {
@Override @Override
public CardCollectionView cheatShuffle(CardCollectionView list) { 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 @Override
@@ -1544,13 +1544,11 @@ 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 // 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 = AiProfileUtil.getIntProperty(player, AiProps.AI_IN_DANGER_THRESHOLD);
int lifeInDanger = (((PlayerControllerAi) ai.getController()).getAi().getIntProperty(AiProps.AI_IN_DANGER_THRESHOLD));
if ((ai.getLife() <= lifeInDanger && !ai.cantLoseForZeroOrLessLife()) if ((ai.getLife() <= lifeInDanger && !ai.cantLoseForZeroOrLessLife())
&& !(ai.getLife() > 1 && ai.getWeakestOpponent().getLife() == 1)) { && !(ai.getLife() > 1 && ai.getWeakestOpponent().getLife() == 1)) {
dungeonNames.remove("Tomb of Annihilation"); dungeonNames.remove("Tomb of Annihilation");
} }
}
try { try {
// if this fail somehow add fallback to get any from dungeonCards // if this fail somehow add fallback to get any from dungeonCards

View File

@@ -798,7 +798,7 @@ public class SpecialCardAi {
public static class Intuition { public static class Intuition {
public static CardCollection considerMultiple(final Player ai, final SpellAbility sa) { public static CardCollection considerMultiple(final Player ai, final SpellAbility sa) {
if (ai.getController().isAI()) { 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 return new CardCollection(); // fall back to standard ChangeZoneAi considerations
} }
} }

View File

@@ -73,11 +73,8 @@ public class AttachAi extends SpellAbilityAi {
} }
} }
// Flash logic boolean advancedFlash = AiProfileUtil.getBoolProperty(ai, AiProps.FLASH_ENABLE_ADVANCED_LOGIC);
boolean advancedFlash = false;
if (ai.getController().isAI()) {
advancedFlash = ((PlayerControllerAi)ai.getController()).getAi().getBooleanProperty(AiProps.FLASH_ENABLE_ADVANCED_LOGIC);
}
if ((source.hasKeyword(Keyword.FLASH) || (!ai.canCastSorcery() && sa.canCastTiming(ai))) if ((source.hasKeyword(Keyword.FLASH) || (!ai.canCastSorcery() && sa.canCastTiming(ai)))
&& source.isAura() && advancedFlash && !doAdvancedFlashAuraLogic(ai, sa, sa.getTargetCard())) { && source.isAura() && advancedFlash && !doAdvancedFlashAuraLogic(ai, sa, sa.getTargetCard())) {
return new AiAbilityDecision(0, AiPlayDecision.CantPlayAi); return new AiAbilityDecision(0, AiPlayDecision.CantPlayAi);
@@ -108,9 +105,8 @@ public class AttachAi extends SpellAbilityAi {
Card source = sa.getHostCard(); Card source = sa.getHostCard();
Game game = ai.getGame(); Game game = ai.getGame();
Combat combat = game.getCombat(); 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 // 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. // for instant speed use. To be improved later.
return true; return true;
@@ -190,9 +186,9 @@ public class AttachAi extends SpellAbilityAi {
return false; return false;
} }
int chanceToCastAtEOT = aic.getIntProperty(AiProps.FLASH_BUFF_AURA_CHANCE_CAST_AT_EOT); int chanceToCastAtEOT = AiProfileUtil.getIntProperty(ai, AiProps.FLASH_BUFF_AURA_CHANCE_CAST_AT_EOT);
int chanceToCastEarly = aic.getIntProperty(AiProps.FLASH_BUFF_AURA_CHANCE_TO_CAST_EARLY); int chanceToCastEarly = AiProfileUtil.getIntProperty(ai, AiProps.FLASH_BUFF_AURA_CHANCE_TO_CAST_EARLY);
int chanceToRespondToStack = aic.getIntProperty(AiProps.FLASH_BUFF_AURA_CHANCE_TO_RESPOND_TO_STACK); int chanceToRespondToStack = AiProfileUtil.getIntProperty(ai, AiProps.FLASH_BUFF_AURA_CHANCE_TO_RESPOND_TO_STACK);
boolean hasFloatMana = ai.getManaPool().totalMana() > 0; boolean hasFloatMana = ai.getManaPool().totalMana() > 0;
boolean willDiscardNow = game.getPhaseHandler().is(PhaseType.END_OF_TURN, ai) 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() if (sa.getHostCard().getAttachedTo() != null && sa.getHostCard().getAttachedTo().isCreature()
&& sa.getPayCosts() != null && sa.getPayCosts().hasSpecificCostType(CostSacrifice.class)) { && sa.getPayCosts() != null && sa.getPayCosts().hasSpecificCostType(CostSacrifice.class)) {
final int oldEvalRating = ComputerUtilCard.evaluateCreature(sa.getHostCard().getAttachedTo()); 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 -> { prefList = CardLists.filter(prefList, c -> {
if (!c.isCreature()) { if (!c.isCreature()) {
return false; 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 // 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)) { if (!decideMoveFromUseless && AiCardMemory.isMemorySetEmpty(aiPlayer, AiCardMemory.MemorySet.HELD_MANA_SOURCES_FOR_MAIN2)) {
SpellAbility futureSpell = aic.predictSpellToCastInMain2(ApiType.Attach); SpellAbility futureSpell = aic.predictSpellToCastInMain2(ApiType.Attach);

View File

@@ -1300,15 +1300,9 @@ public class ChangeZoneAi extends SpellAbilityAi {
} }
// Reload planeswalkers // Reload planeswalkers
else if (!aiPlaneswalkers.isEmpty() && (sa.getHostCard().isSorcery() || !game.getPhaseHandler().isPlayerTurn(ai))) { else if (!aiPlaneswalkers.isEmpty() && (sa.getHostCard().isSorcery() || !game.getPhaseHandler().isPlayerTurn(ai))) {
int maxLoyaltyToConsider = 2; int maxLoyaltyToConsider = AiProfileUtil.getIntProperty(ai, AiProps.BLINK_RELOAD_PLANESWALKER_MAX_LOYALTY);
int loyaltyDiff = 2; int loyaltyDiff = AiProfileUtil.getIntProperty(ai, AiProps.BLINK_RELOAD_PLANESWALKER_LOYALTY_DIFF);
int chance = 30; int chance = AiProfileUtil.getIntProperty(ai, AiProps.BLINK_RELOAD_PLANESWALKER_CHANCE);
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);
}
if (MyRandom.percentTrue(chance)) { if (MyRandom.percentTrue(chance)) {
aiPlaneswalkers.sort(CardPredicates.compareByCounterType(CounterEnumType.LOYALTY)); aiPlaneswalkers.sort(CardPredicates.compareByCounterType(CounterEnumType.LOYALTY));
for (Card pw : aiPlaneswalkers) { for (Card pw : aiPlaneswalkers) {
@@ -1670,15 +1664,9 @@ public class ChangeZoneAi extends SpellAbilityAi {
} }
if (card.hasCounters()) { if (card.hasCounters()) {
if (card.isPlaneswalker()) { if (card.isPlaneswalker()) {
int maxLoyaltyToConsider = 2; int maxLoyaltyToConsider = AiProfileUtil.getIntProperty(decider, AiProps.BLINK_RELOAD_PLANESWALKER_MAX_LOYALTY);
int loyaltyDiff = 2; int loyaltyDiff = AiProfileUtil.getIntProperty(decider, AiProps.BLINK_RELOAD_PLANESWALKER_LOYALTY_DIFF);
int chance = 30; int chance = AiProfileUtil.getIntProperty(decider, AiProps.BLINK_RELOAD_PLANESWALKER_CHANCE);
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);
}
if (MyRandom.percentTrue(chance)) { if (MyRandom.percentTrue(chance)) {
int curLoyalty = card.getCounters(CounterEnumType.LOYALTY); int curLoyalty = card.getCounters(CounterEnumType.LOYALTY);
int freshLoyalty = Integer.parseInt(card.getCurrentState().getBaseLoyalty()); int freshLoyalty = Integer.parseInt(card.getCurrentState().getBaseLoyalty());

View File

@@ -140,17 +140,15 @@ public class ChangeZoneAllAi extends SpellAbilityAi {
} }
computerType = new CardCollection(); computerType = new CardCollection();
} }
int creatureEvalThreshold = 200; // value difference (in evaluateCreatureList units)
int nonCreatureEvalThreshold = 3; // CMC difference int creatureEvalThreshold; // value difference (in evaluateCreatureList units)
if (ai.getController().isAI()) { int nonCreatureEvalThreshold; // CMC difference
AiController aic = ((PlayerControllerAi)ai.getController()).getAi();
if (destination == ZoneType.Hand) { if (destination == ZoneType.Hand) {
creatureEvalThreshold = aic.getIntProperty(AiProps.BOUNCE_ALL_TO_HAND_CREAT_EVAL_DIFF); creatureEvalThreshold = AiProfileUtil.getIntProperty(ai, AiProps.BOUNCE_ALL_TO_HAND_CREAT_EVAL_DIFF);
nonCreatureEvalThreshold = aic.getIntProperty(AiProps.BOUNCE_ALL_TO_HAND_NONCREAT_EVAL_DIFF); nonCreatureEvalThreshold = AiProfileUtil.getIntProperty(ai, AiProps.BOUNCE_ALL_TO_HAND_NONCREAT_EVAL_DIFF);
} else { } else {
creatureEvalThreshold = aic.getIntProperty(AiProps.BOUNCE_ALL_ELSEWHERE_CREAT_EVAL_DIFF); creatureEvalThreshold = AiProfileUtil.getIntProperty(ai, AiProps.BOUNCE_ALL_ELSEWHERE_CREAT_EVAL_DIFF);
nonCreatureEvalThreshold = aic.getIntProperty(AiProps.BOUNCE_ALL_ELSEWHERE_NONCREAT_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 // mass zone change for creatures: if in dire danger, do it; otherwise, only do it if the opponent's

View File

@@ -19,8 +19,8 @@ public class CopySpellAbilityAi extends SpellAbilityAi {
@Override @Override
protected AiAbilityDecision checkApiLogic(Player aiPlayer, SpellAbility sa) { protected AiAbilityDecision checkApiLogic(Player aiPlayer, SpellAbility sa) {
Game game = aiPlayer.getGame(); Game game = aiPlayer.getGame();
int chance = ((PlayerControllerAi)aiPlayer.getController()).getAi().getIntProperty(AiProps.CHANCE_TO_COPY_OWN_SPELL_WHILE_ON_STACK); int chance = AiProfileUtil.getIntProperty(aiPlayer, AiProps.CHANCE_TO_COPY_OWN_SPELL_WHILE_ON_STACK);
int diff = ((PlayerControllerAi)aiPlayer.getController()).getAi().getIntProperty(AiProps.ALWAYS_COPY_SPELL_IF_CMC_DIFF); int diff = AiProfileUtil.getIntProperty(aiPlayer, AiProps.ALWAYS_COPY_SPELL_IF_CMC_DIFF);
String logic = sa.getParamOrDefault("AILogic", ""); String logic = sa.getParamOrDefault("AILogic", "");
if (game.getStack().isEmpty()) { if (game.getStack().isEmpty()) {

View File

@@ -148,18 +148,16 @@ public class CounterAi extends SpellAbilityAi {
} }
// Specific constraints for the AI to use/not use counterspells against specific groups of spells // Specific constraints for the AI to use/not use counterspells against specific groups of spells
// (specified in the AI profile) boolean ctrCmc0ManaPerms = AiProfileUtil.getBoolProperty(ai, AiProps.ALWAYS_COUNTER_CMC_0_MANA_MAKING_PERMS);
AiController aic = ((PlayerControllerAi)ai.getController()).getAi(); boolean ctrDamageSpells = AiProfileUtil.getBoolProperty(ai, AiProps.ALWAYS_COUNTER_DAMAGE_SPELLS);
boolean ctrCmc0ManaPerms = aic.getBooleanProperty(AiProps.ALWAYS_COUNTER_CMC_0_MANA_MAKING_PERMS); boolean ctrRemovalSpells = AiProfileUtil.getBoolProperty(ai, AiProps.ALWAYS_COUNTER_REMOVAL_SPELLS);
boolean ctrDamageSpells = aic.getBooleanProperty(AiProps.ALWAYS_COUNTER_DAMAGE_SPELLS); boolean ctrPumpSpells = AiProfileUtil.getBoolProperty(ai, AiProps.ALWAYS_COUNTER_PUMP_SPELLS);
boolean ctrRemovalSpells = aic.getBooleanProperty(AiProps.ALWAYS_COUNTER_REMOVAL_SPELLS); boolean ctrAuraSpells = AiProfileUtil.getBoolProperty(ai, AiProps.ALWAYS_COUNTER_AURAS);
boolean ctrPumpSpells = aic.getBooleanProperty(AiProps.ALWAYS_COUNTER_PUMP_SPELLS); boolean ctrOtherCounters = AiProfileUtil.getBoolProperty(ai, AiProps.ALWAYS_COUNTER_OTHER_COUNTERSPELLS);
boolean ctrAuraSpells = aic.getBooleanProperty(AiProps.ALWAYS_COUNTER_AURAS); int ctrChanceCMC1 = AiProfileUtil.getIntProperty(ai, AiProps.CHANCE_TO_COUNTER_CMC_1);
boolean ctrOtherCounters = aic.getBooleanProperty(AiProps.ALWAYS_COUNTER_OTHER_COUNTERSPELLS); int ctrChanceCMC2 = AiProfileUtil.getIntProperty(ai, AiProps.CHANCE_TO_COUNTER_CMC_2);
int ctrChanceCMC1 = aic.getIntProperty(AiProps.CHANCE_TO_COUNTER_CMC_1); int ctrChanceCMC3 = AiProfileUtil.getIntProperty(ai, AiProps.CHANCE_TO_COUNTER_CMC_3);
int ctrChanceCMC2 = aic.getIntProperty(AiProps.CHANCE_TO_COUNTER_CMC_2); String ctrNamed = AiProfileUtil.getProperty(ai, AiProps.ALWAYS_COUNTER_SPELLS_FROM_NAMED_CARDS);
int ctrChanceCMC3 = aic.getIntProperty(AiProps.CHANCE_TO_COUNTER_CMC_3);
String ctrNamed = aic.getProperty(AiProps.ALWAYS_COUNTER_SPELLS_FROM_NAMED_CARDS);
boolean dontCounter = false; boolean dontCounter = false;
if (tgtCMC == 1 && !MyRandom.percentTrue(ctrChanceCMC1)) { if (tgtCMC == 1 && !MyRandom.percentTrue(ctrChanceCMC1)) {
@@ -170,7 +168,7 @@ public class CounterAi extends SpellAbilityAi {
dontCounter = true; 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; dontCounter = true;
Card tgtSource = tgtSA.getHostCard(); Card tgtSource = tgtSA.getHostCard();
if ((tgtSource != null && tgtCMC == 0 && tgtSource.isPermanent() && !tgtSource.getManaAbilities().isEmpty() && ctrCmc0ManaPerms) if ((tgtSource != null && tgtCMC == 0 && tgtSource.isPermanent() && !tgtSource.getManaAbilities().isEmpty() && ctrCmc0ManaPerms)

View File

@@ -112,7 +112,7 @@ public class CountersProliferateAi extends SpellAbilityAi {
final CounterType poison = CounterEnumType.POISON; 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 // because countertype can't be chosen anymore, only look for poison counters
for (final Player p : IterableUtil.filter(options, Player.class)) { for (final Player p : IterableUtil.filter(options, Player.class)) {
if (p.isOpponentOf(ai)) { if (p.isOpponentOf(ai)) {

View File

@@ -141,7 +141,7 @@ public class CountersPutAi extends CountersAi {
final boolean isClockwork = "True".equals(sa.getParam("UpTo")) && "Self".equals(sa.getParam("Defined")) final boolean isClockwork = "True".equals(sa.getParam("UpTo")) && "Self".equals(sa.getParam("Defined"))
&& "P1P0".equals(sa.getParam("CounterType")) && "Count$xPaid".equals(source.getSVar("X")) && "P1P0".equals(sa.getParam("CounterType")) && "Count$xPaid".equals(source.getSVar("X"))
&& sa.hasParam("MaxFromEffect"); && 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)) { if ("ExistingCounter".equals(type)) {
final boolean eachExisting = sa.hasParam("EachExistingCounter"); final boolean eachExisting = sa.hasParam("EachExistingCounter");
@@ -219,10 +219,8 @@ public class CountersPutAi extends CountersAi {
} else if ("PayEnergy".equals(logic)) { } else if ("PayEnergy".equals(logic)) {
return new AiAbilityDecision(100, AiPlayDecision.WillPlay); return new AiAbilityDecision(100, AiPlayDecision.WillPlay);
} else if ("PayEnergyConservatively".equals(logic)) { } else if ("PayEnergyConservatively".equals(logic)) {
boolean onlyInCombat = ai.getController().isAI() boolean onlyInCombat = AiProfileUtil.getBoolProperty(ai, AiProps.CONSERVATIVE_ENERGY_PAYMENT_ONLY_IN_COMBAT);
&& ((PlayerControllerAi) ai.getController()).getAi().getBooleanProperty(AiProps.CONSERVATIVE_ENERGY_PAYMENT_ONLY_IN_COMBAT); boolean onlyDefensive = AiProfileUtil.getBoolProperty(ai, AiProps.CONSERVATIVE_ENERGY_PAYMENT_ONLY_DEFENSIVELY);
boolean onlyDefensive = ai.getController().isAI()
&& ((PlayerControllerAi) ai.getController()).getAi().getBooleanProperty(AiProps.CONSERVATIVE_ENERGY_PAYMENT_ONLY_DEFENSIVELY);
if (playAggro) { if (playAggro) {
// aggro profiles ignore conservative play for this AI logic // aggro profiles ignore conservative play for this AI logic

View File

@@ -108,18 +108,15 @@ public class DamageDealAi extends DamageAiBase {
dmg = ComputerUtilCost.getMaxXValue(sa, ai, sa.isTrigger()); 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 // 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()) { int holdChance = AiProfileUtil.getIntProperty(ai, AiProps.HOLD_X_DAMAGE_SPELLS_FOR_MORE_DAMAGE_CHANCE);
AiController aic = ((PlayerControllerAi)ai.getController()).getAi();
int holdChance = aic.getIntProperty(AiProps.HOLD_X_DAMAGE_SPELLS_FOR_MORE_DAMAGE_CHANCE);
if (MyRandom.percentTrue(holdChance)) { if (MyRandom.percentTrue(holdChance)) {
int threshold = aic.getIntProperty(AiProps.HOLD_X_DAMAGE_SPELLS_THRESHOLD); int threshold = AiProfileUtil.getIntProperty(ai, AiProps.HOLD_X_DAMAGE_SPELLS_THRESHOLD);
boolean inDanger = ComputerUtil.aiLifeInDanger(ai, false, 0); boolean inDanger = ComputerUtil.aiLifeInDanger(ai, false, 0);
boolean isLethal = sa.usesTargeting() && sa.getTargetRestrictions().canTgtPlayer() && dmg >= ai.getWeakestOpponent().getLife() && !ai.getWeakestOpponent().cantLoseForZeroOrLessLife(); 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) { if (dmg < threshold && ai.getGame().getPhaseHandler().getTurn() / 2 < threshold && !inDanger && !isLethal) {
return new AiAbilityDecision(0, AiPlayDecision.CantPlayAi); return new AiAbilityDecision(0, AiPlayDecision.CantPlayAi);
} }
} }
}
// Set PayX here to maximum value. It will be adjusted later depending on the target. // Set PayX here to maximum value. It will be adjusted later depending on the target.
sa.setXManaCostPaid(dmg); sa.setXManaCostPaid(dmg);
@@ -1086,7 +1083,7 @@ public class DamageDealAi extends DamageAiBase {
} }
Game game = ai.getGame(); 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))) { 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) chance = 100; // in danger, do it even if normally the chance is low (unless chaining is completely disabled)

View File

@@ -409,12 +409,11 @@ public class DestroyAi extends SpellAbilityAi {
int oppLandsOTB = tgtPlayer.getLandsInPlay().size(); int oppLandsOTB = tgtPlayer.getLandsInPlay().size();
// AI profile-dependent properties // AI profile-dependent properties
AiController aic = ((PlayerControllerAi)ai.getController()).getAi(); int amountNoTempoCheck = AiProfileUtil.getIntProperty(ai, AiProps.STRIPMINE_MIN_LANDS_OTB_FOR_NO_TEMPO_CHECK);
int amountNoTempoCheck = aic.getIntProperty(AiProps.STRIPMINE_MIN_LANDS_OTB_FOR_NO_TEMPO_CHECK); int amountNoTimingCheck = AiProfileUtil.getIntProperty(ai, AiProps.STRIPMINE_MIN_LANDS_FOR_NO_TIMING_CHECK);
int amountNoTimingCheck = aic.getIntProperty(AiProps.STRIPMINE_MIN_LANDS_FOR_NO_TIMING_CHECK); int amountLandsInHand = AiProfileUtil.getIntProperty(ai, AiProps.STRIPMINE_MIN_LANDS_IN_HAND_TO_ACTIVATE);
int amountLandsInHand = aic.getIntProperty(AiProps.STRIPMINE_MIN_LANDS_IN_HAND_TO_ACTIVATE); int amountLandsToManalock = AiProfileUtil.getIntProperty(ai, AiProps.STRIPMINE_MAX_LANDS_TO_ATTEMPT_MANALOCKING);
int amountLandsToManalock = aic.getIntProperty(AiProps.STRIPMINE_MAX_LANDS_TO_ATTEMPT_MANALOCKING); boolean highPriorityIfNoLandDrop = AiProfileUtil.getBoolProperty(ai, AiProps.STRIPMINE_HIGH_PRIORITY_ON_SKIPPED_LANDDROP);
boolean highPriorityIfNoLandDrop = aic.getBooleanProperty(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 // if the opponent didn't play a land and has few lands OTB, might be worth mana-locking him
PhaseHandler ph = ai.getGame().getPhaseHandler(); PhaseHandler ph = ai.getGame().getPhaseHandler();

View File

@@ -264,7 +264,7 @@ public class DrawAi extends SpellAbilityAi {
if (sa.getPayCosts().hasSpecificCostType(CostPayLife.class)) { if (sa.getPayCosts().hasSpecificCostType(CostPayLife.class)) {
// [Necrologia, Pay X Life : Draw X Cards] // [Necrologia, Pay X Life : Draw X Cards]
// Don't draw more than what's "safe" and don't risk a near death experience // 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) { while (ComputerUtil.aiLifeInDanger(ai, aggroAI, numCards) && numCards > 0) {
numCards--; numCards--;
} }

View File

@@ -41,7 +41,7 @@ public class EndureAi extends SpellAbilityAi {
return new AiAbilityDecision(0, AiPlayDecision.AnotherTime); return new AiAbilityDecision(0, AiPlayDecision.AnotherTime);
} }
int curLife = aiPlayer.getLife(); 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) { if (curLife <= dangerLife) {
return new AiAbilityDecision(0, AiPlayDecision.CantAffordX); return new AiAbilityDecision(0, AiPlayDecision.CantAffordX);
} }

View File

@@ -37,14 +37,8 @@ public class ExploreAi extends SpellAbilityAi {
CardCollection landsOTB = CardLists.filter(cardsOTB, CardPredicates.LANDS_PRODUCING_MANA); CardCollection landsOTB = CardLists.filter(cardsOTB, CardPredicates.LANDS_PRODUCING_MANA);
CardCollection landsInHand = CardLists.filter(cardsInHand, CardPredicates.LANDS_PRODUCING_MANA); CardCollection landsInHand = CardLists.filter(cardsInHand, CardPredicates.LANDS_PRODUCING_MANA);
int maxCMCDiff = 1; int maxCMCDiff = AiProfileUtil.getIntProperty(ai, AiProps.EXPLORE_MAX_CMC_DIFF_TO_PUT_IN_GRAVEYARD);
int numLandsToStillNeedMore = 2; int numLandsToStillNeedMore = AiProfileUtil.getIntProperty(ai, AiProps.EXPLORE_NUM_LANDS_TO_STILL_NEED_MORE);
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);
}
if (landsInHand.isEmpty() && landsOTB.size() <= numLandsToStillNeedMore) { if (landsInHand.isEmpty() && landsOTB.size() <= numLandsToStillNeedMore) {
// We need more lands to improve our mana base, explore away the non-lands // We need more lands to improve our mana base, explore away the non-lands

View File

@@ -93,9 +93,9 @@ public class LifeExchangeVariantAi extends SpellAbilityAi {
if (game.getCombat().isUnblocked(source) && def.canLoseLife() && aiLife >= def.getLife() && source.getNetPower() < def.getLife()) { if (game.getCombat().isUnblocked(source) && def.canLoseLife() && aiLife >= def.getLife() && source.getNetPower() < def.getLife()) {
// Unblocked Evra which can deal lethal damage // Unblocked Evra which can deal lethal damage
return new AiAbilityDecision(100, AiPlayDecision.WillPlay); return new AiAbilityDecision(100, AiPlayDecision.WillPlay);
} else if (ai.getController().isAI() && aiLife > source.getNetPower() && source.hasKeyword(Keyword.LIFELINK)) { } else if (aiLife > source.getNetPower() && source.hasKeyword(Keyword.LIFELINK)) {
int dangerMin = (((PlayerControllerAi) ai.getController()).getAi().getIntProperty(AiProps.AI_IN_DANGER_THRESHOLD)); int dangerMin = AiProfileUtil.getIntProperty(ai, AiProps.AI_IN_DANGER_THRESHOLD);
int dangerMax = (((PlayerControllerAi) ai.getController()).getAi().getIntProperty(AiProps.AI_IN_DANGER_MAX_THRESHOLD)); int dangerMax = AiProfileUtil.getIntProperty(ai, AiProps.AI_IN_DANGER_MAX_THRESHOLD);
int dangerDiff = dangerMax - dangerMin; int dangerDiff = dangerMax - dangerMin;
int lifeInDanger = dangerDiff <= 0 ? dangerMin : MyRandom.getRandom().nextInt(dangerDiff) + dangerMin; int lifeInDanger = dangerDiff <= 0 ? dangerMin : MyRandom.getRandom().nextInt(dangerDiff) + dangerMin;
if (source.getNetPower() >= lifeInDanger && ai.canGainLife() && ComputerUtil.lifegainPositive(ai, source)) { if (source.getNetPower() >= lifeInDanger && ai.canGainLife() && ComputerUtil.lifegainPositive(ai, source)) {

View File

@@ -65,10 +65,7 @@ public class PermanentCreatureAi extends PermanentAi {
} }
// Flash logic // Flash logic
boolean advancedFlash = false; boolean advancedFlash = AiProfileUtil.getBoolProperty(ai, AiProps.FLASH_ENABLE_ADVANCED_LOGIC);
if (ai.getController().isAI()) {
advancedFlash = ((PlayerControllerAi)ai.getController()).getAi().getBooleanProperty(AiProps.FLASH_ENABLE_ADVANCED_LOGIC);
}
if (card.hasKeyword(Keyword.FLASH) || (!ai.canCastSorcery() && sa.canCastTiming(ai) && !sa.isCastFromPlayEffect())) { if (card.hasKeyword(Keyword.FLASH) || (!ai.canCastSorcery() && sa.canCastTiming(ai) && !sa.isCastFromPlayEffect())) {
if (advancedFlash) { if (advancedFlash) {
return doAdvancedFlashLogic(card, ai, sa); 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 chanceToObeyAmbushAI = AiProfileUtil.getIntProperty(ai, AiProps.FLASH_CHANCE_TO_OBEY_AMBUSHAI);
int chanceToAddBlocker = aic.getIntProperty(AiProps.FLASH_CHANCE_TO_CAST_AS_VALUABLE_BLOCKER); int chanceToAddBlocker = AiProfileUtil.getIntProperty(ai, AiProps.FLASH_CHANCE_TO_CAST_AS_VALUABLE_BLOCKER);
int chanceToCastForETB = aic.getIntProperty(AiProps.FLASH_CHANCE_TO_CAST_DUE_TO_ETB_EFFECTS); int chanceToCastForETB = AiProfileUtil.getIntProperty(ai, AiProps.FLASH_CHANCE_TO_CAST_DUE_TO_ETB_EFFECTS);
int chanceToRespondToStack = aic.getIntProperty(AiProps.FLASH_CHANCE_TO_RESPOND_TO_STACK_WITH_ETB); int chanceToRespondToStack = AiProfileUtil.getIntProperty(ai, AiProps.FLASH_CHANCE_TO_RESPOND_TO_STACK_WITH_ETB);
int chanceToProcETBBeforeMain1 = aic.getIntProperty(AiProps.FLASH_CHANCE_TO_CAST_FOR_ETB_BEFORE_MAIN1); int chanceToProcETBBeforeMain1 = AiProfileUtil.getIntProperty(ai, AiProps.FLASH_CHANCE_TO_CAST_FOR_ETB_BEFORE_MAIN1);
boolean canCastAtOppTurn = true; boolean canCastAtOppTurn = true;
for (Card c : ai.getGame().getCardsIn(ZoneType.Battlefield)) { for (Card c : ai.getGame().getCardsIn(ZoneType.Battlefield)) {
for (StaticAbility s : c.getStaticAbilities()) { for (StaticAbility s : c.getStaticAbilities()) {

View File

@@ -100,13 +100,8 @@ public class RearrangeTopOfLibraryAi extends SpellAbilityAi {
return false; return false;
} }
int uncastableCMCThreshold = 2; int minLandsToScryLandsAway = AiProfileUtil.getIntProperty(player, AiProps.SCRY_NUM_LANDS_TO_NOT_NEED_MORE);
int minLandsToScryLandsAway = 4; int uncastableCMCThreshold = AiProfileUtil.getIntProperty(player, AiProps.SCRY_IMMEDIATELY_UNCASTABLE_CMC_DIFF);
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 landsOTB = CardLists.count(p.getCardsIn(ZoneType.Battlefield), CardPredicates.LANDS_PRODUCING_MANA); 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)) int cmc = top.isSplitCard() ? Math.min(top.getCMC(Card.SplitCMCMode.LeftSplitCMC), top.getCMC(Card.SplitCMCMode.RightSplitCMC))

View File

@@ -29,14 +29,13 @@ public class RollPlanarDiceAi extends SpellAbilityAi {
} }
private boolean willRollOnPlane(Player ai, Card plane) { private boolean willRollOnPlane(Player ai, Card plane) {
AiController aic = ((PlayerControllerAi)ai.getController()).getAi();
boolean decideToRoll = false; boolean decideToRoll = false;
boolean rollInMain1 = false; boolean rollInMain1 = false;
String modeName = "never"; String modeName = "never";
int maxActivations = aic.getIntProperty(AiProps.DEFAULT_MAX_PLANAR_DIE_ROLLS_PER_TURN); int maxActivations = AiProfileUtil.getIntProperty(ai, AiProps.DEFAULT_MAX_PLANAR_DIE_ROLLS_PER_TURN);
int chance = aic.getIntProperty(AiProps.DEFAULT_PLANAR_DIE_ROLL_CHANCE); int chance = AiProfileUtil.getIntProperty(ai, AiProps.DEFAULT_PLANAR_DIE_ROLL_CHANCE);
int hesitationChance = aic.getIntProperty(AiProps.PLANAR_DIE_ROLL_HESITATION_CHANCE); int hesitationChance = AiProfileUtil.getIntProperty(ai, AiProps.PLANAR_DIE_ROLL_HESITATION_CHANCE);
int minTurnToRoll = aic.getIntProperty(AiProps.DEFAULT_MIN_TURN_TO_ROLL_PLANAR_DIE); int minTurnToRoll = AiProfileUtil.getIntProperty(ai, AiProps.DEFAULT_MIN_TURN_TO_ROLL_PLANAR_DIE);
if (plane.hasSVar("AIRollPlanarDieParams")) { if (plane.hasSVar("AIRollPlanarDieParams")) {
String[] params = plane.getSVar("AIRollPlanarDieParams").toLowerCase().trim().split("\\|"); String[] params = plane.getSVar("AIRollPlanarDieParams").toLowerCase().trim().split("\\|");

View File

@@ -70,7 +70,7 @@ public class SurveilAi extends SpellAbilityAi {
// Only Surveil for life when at decent amount of life remaining // Only Surveil for life when at decent amount of life remaining
final Cost cost = sa.getPayCosts(); final Cost cost = sa.getPayCosts();
if (cost != null && cost.hasSpecificCostType(CostPayLife.class)) { 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)) { if (!ComputerUtilCost.checkLifeCost(ai, cost, sa.getHostCard(), ai.getStartingLife() * maxLife / 100, sa)) {
return new AiAbilityDecision(0, AiPlayDecision.CostNotAcceptable); return new AiAbilityDecision(0, AiPlayDecision.CostNotAcceptable);
} }

View File

@@ -30,8 +30,7 @@ public class TapAi extends TapAiBase {
// Cast it if it's a sorcery. // Cast it if it's a sorcery.
} else if (phase.getPhase().isBefore(PhaseType.COMBAT_DECLARE_BLOCKERS)) { } else if (phase.getPhase().isBefore(PhaseType.COMBAT_DECLARE_BLOCKERS)) {
// Aggro Brains are willing to use TapEffects aggressively instead of defensively // Aggro Brains are willing to use TapEffects aggressively instead of defensively
AiController aic = ((PlayerControllerAi) ai.getController()).getAi(); if (!AiProfileUtil.getBoolProperty(ai, AiProps.PLAY_AGGRO)) {
if (!aic.getBooleanProperty(AiProps.PLAY_AGGRO)) {
return new AiAbilityDecision(0, AiPlayDecision.CantPlayAi); return new AiAbilityDecision(0, AiPlayDecision.CantPlayAi);
} }
} else { } else {

View File

@@ -186,16 +186,9 @@ public class TokenAi extends SpellAbilityAi {
} }
} }
double chance = 1.0F; // 100% double chance = (double)AiProfileUtil.getIntProperty(ai, AiProps.TOKEN_GENERATION_ABILITY_CHANCE) / 100;
boolean alwaysFromPW = true; boolean alwaysFromPW = AiProfileUtil.getBoolProperty(ai, AiProps.TOKEN_GENERATION_ALWAYS_IF_FROM_PLANESWALKER);
boolean alwaysOnOppAttack = true; boolean alwaysOnOppAttack = AiProfileUtil.getBoolProperty(ai, AiProps.TOKEN_GENERATION_ALWAYS_IF_OPP_ATTACKS);
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);
}
if (sa.isPwAbility() && alwaysFromPW) { if (sa.isPwAbility() && alwaysFromPW) {
return new AiAbilityDecision(100, AiPlayDecision.WillPlay); return new AiAbilityDecision(100, AiPlayDecision.WillPlay);

View File

@@ -44,7 +44,7 @@ public class VentureEffect extends SpellAbilityEffect {
} }
} }
List<PaperCard> dungeonCards = null; List<PaperCard> dungeonCards;
if (sa.hasParam("Dungeon")) { if (sa.hasParam("Dungeon")) {
String dungeonType = sa.getParam("Dungeon"); String dungeonType = sa.getParam("Dungeon");
Predicate<CardRules> rulesPredicate = CardRulesPredicates.IS_DUNGEON.and(CardRulesPredicates.subType(StringOp.EQUALS, dungeonType)); Predicate<CardRules> rulesPredicate = CardRulesPredicates.IS_DUNGEON.and(CardRulesPredicates.subType(StringOp.EQUALS, dungeonType));