mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-11 16:26:22 +00:00
Refactor AiProfile handling (#9012)
Co-authored-by: tool4EvEr <tool4EvEr@>
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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<String> 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<String> 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<Card> 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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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--;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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("\\|");
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user