Refactor/Code Cleanup

This commit is contained in:
Anthony Calosa
2019-09-02 18:08:56 +08:00
parent 83b6b9faeb
commit c38bae910a
411 changed files with 1354 additions and 3010 deletions

View File

@@ -1,5 +1,5 @@
package forge.ai; package forge.ai;
public enum AIOption { public enum AIOption {
USE_SIMULATION; USE_SIMULATION
} }

View File

@@ -602,11 +602,7 @@ public class AiAttackController {
return true; return true;
} }
if (totalPoisonDamage >= 10 - opp.getPoisonCounters()) { return totalPoisonDamage >= 10 - opp.getPoisonCounters();
return true;
}
return false;
} }
private final GameEntity chooseDefender(final Combat c, final boolean bAssault) { private final GameEntity chooseDefender(final Combat c, final boolean bAssault) {
@@ -1021,7 +1017,7 @@ public class AiAttackController {
} // stay at home to block } // stay at home to block
if ( LOG_AI_ATTACKS ) if ( LOG_AI_ATTACKS )
System.out.println(String.valueOf(this.aiAggression) + " = ai aggression"); System.out.println(this.aiAggression + " = ai aggression");
// **************** // ****************
// Evaluation the end // Evaluation the end
@@ -1454,10 +1450,7 @@ public class AiAttackController {
if (color != null) { if (color != null) {
return color; return color;
} }
if (artifact != null) { return artifact;//should never get here
return artifact;
}
return null; //should never get here
} }
private void doLightmineFieldAttackLogic(List<Card> attackersLeft, int numForcedAttackers, boolean playAggro) { private void doLightmineFieldAttackLogic(List<Card> attackersLeft, int numForcedAttackers, boolean playAggro) {

View File

@@ -859,7 +859,7 @@ public class AiBlockController {
damageToPW += ComputerUtilCombat.predictDamageTo((Card) def, pwatkr.getNetCombatDamage(), pwatkr, true); damageToPW += ComputerUtilCombat.predictDamageTo((Card) def, pwatkr.getNetCombatDamage(), pwatkr, true);
} }
} }
if ((!onlyIfLethal && damageToPW > 0) || damageToPW >= ((Card) def).getCounters(CounterType.LOYALTY)) { if ((!onlyIfLethal && damageToPW > 0) || damageToPW >= def.getCounters(CounterType.LOYALTY)) {
threatenedPWs.add((Card) def); threatenedPWs.add((Card) def);
} }
} }
@@ -879,7 +879,7 @@ public class AiBlockController {
if (!chumpPWDefenders.isEmpty()) { if (!chumpPWDefenders.isEmpty()) {
for (final Card attacker : attackers) { for (final Card attacker : attackers) {
GameEntity def = combat.getDefenderByAttacker(attacker); GameEntity def = combat.getDefenderByAttacker(attacker);
if (def instanceof Card && threatenedPWs.contains((Card) def)) { if (def instanceof Card && threatenedPWs.contains(def)) {
if (attacker.hasKeyword(Keyword.TRAMPLE)) { if (attacker.hasKeyword(Keyword.TRAMPLE)) {
// don't bother trying to chump a trampling creature // don't bother trying to chump a trampling creature
continue; continue;
@@ -914,7 +914,7 @@ public class AiBlockController {
pwDefenders.addAll(combat.getBlockers(pwAtk)); pwDefenders.addAll(combat.getBlockers(pwAtk));
} else { } else {
isFullyBlocked = false; isFullyBlocked = false;
damageToPW += ComputerUtilCombat.predictDamageTo((Card) pw, pwAtk.getNetCombatDamage(), pwAtk, true); damageToPW += ComputerUtilCombat.predictDamageTo(pw, pwAtk.getNetCombatDamage(), pwAtk, true);
} }
} }
if (!isFullyBlocked && damageToPW >= pw.getCounters(CounterType.LOYALTY)) { if (!isFullyBlocked && damageToPW >= pw.getCounters(CounterType.LOYALTY)) {
@@ -1329,13 +1329,9 @@ public class AiBlockController {
&& ((Card) combat.getDefenderByAttacker(attacker)).isPlaneswalker(); && ((Card) combat.getDefenderByAttacker(attacker)).isPlaneswalker();
boolean wantToTradeDownToSavePW = chanceToTradeDownToSaveWalker > 0; boolean wantToTradeDownToSavePW = chanceToTradeDownToSaveWalker > 0;
if (((evalBlk <= evalAtk + 1) || (wantToSavePlaneswalker && wantToTradeDownToSavePW)) // "1" accounts for tapped. return ((evalBlk <= evalAtk + 1) || (wantToSavePlaneswalker && wantToTradeDownToSavePW)) // "1" accounts for tapped.
&& powerParityOrHigher && powerParityOrHigher
&& (creatureParityOrAllowedDiff || wantToTradeWithCreatInHand) && (creatureParityOrAllowedDiff || wantToTradeWithCreatInHand)
&& (MyRandom.percentTrue(chance) || wantToSavePlaneswalker)) { && (MyRandom.percentTrue(chance) || wantToSavePlaneswalker);
return true;
}
return false;
} }
} }

View File

@@ -137,7 +137,7 @@ public class AiCardMemory {
Set<Card> memorySet = getMemorySet(set); Set<Card> memorySet = getMemorySet(set);
return memorySet == null ? false : memorySet.contains(c); return memorySet != null && memorySet.contains(c);
} }
/** /**
@@ -291,7 +291,7 @@ public class AiCardMemory {
* @return true, if the given memory set contains no remembered cards. * @return true, if the given memory set contains no remembered cards.
*/ */
public boolean isMemorySetEmpty(MemorySet set) { public boolean isMemorySetEmpty(MemorySet set) {
return set == null ? true : getMemorySet(set).isEmpty(); return set == null || getMemorySet(set).isEmpty();
} }
/** /**

View File

@@ -1516,13 +1516,9 @@ public class AiController {
// Hopefully there's not much to do with the extra mana immediately, can wait for Main 2 // Hopefully there's not much to do with the extra mana immediately, can wait for Main 2
return true; return true;
} }
if ((predictedMana <= totalCMCInHand && canCastWithLandDrop) || (hasRelevantAbsOTB && !isTapLand) || hasLandBasedEffect) { // Might need an extra land to cast something, or for some kind of an ETB ability with a cost or an
// Might need an extra land to cast something, or for some kind of an ETB ability with a cost or an // alternative cost (if we cast it in Main 1), or to use an activated ability on the battlefield
// alternative cost (if we cast it in Main 1), or to use an activated ability on the battlefield return (predictedMana > totalCMCInHand || !canCastWithLandDrop) && (!hasRelevantAbsOTB || isTapLand) && !hasLandBasedEffect;
return false;
}
return true;
} }
private final SpellAbility getSpellAbilityToPlay() { private final SpellAbility getSpellAbilityToPlay() {
@@ -1641,12 +1637,8 @@ public class AiController {
return SpellApiToAi.Converter.get(spell.getApi()).doTriggerAI(player, spell, mandatory); return SpellApiToAi.Converter.get(spell.getApi()).doTriggerAI(player, spell, mandatory);
if (spell instanceof WrappedAbility) if (spell instanceof WrappedAbility)
return doTrigger(((WrappedAbility)spell).getWrappedAbility(), mandatory); return doTrigger(((WrappedAbility)spell).getWrappedAbility(), mandatory);
if (spell.getPayCosts() == Cost.Zero && spell.getTargetRestrictions() == null) { // For non-converted triggers (such as Cumulative Upkeep) that don't have costs or targets to worry about
// For non-converted triggers (such as Cumulative Upkeep) that don't have costs or targets to worry about return spell.getPayCosts() == Cost.Zero && spell.getTargetRestrictions() == null;
return true;
}
return false;
} }
/** /**
@@ -1690,16 +1682,11 @@ public class AiController {
left = AbilityUtils.calculateAmount(hostCard, svarToCheck, sa); left = AbilityUtils.calculateAmount(hostCard, svarToCheck, sa);
} }
System.out.println("aiShouldRun?" + left + comparator + compareTo); System.out.println("aiShouldRun?" + left + comparator + compareTo);
if (Expressions.compare(left, comparator, compareTo)) { return Expressions.compare(left, comparator, compareTo);
return true;
}
} else if (effect.getMapParams().containsKey("AICheckDredge")) { } else if (effect.getMapParams().containsKey("AICheckDredge")) {
return player.getCardsIn(ZoneType.Library).size() > 8 || player.isCardInPlay("Laboratory Maniac"); return player.getCardsIn(ZoneType.Library).size() > 8 || player.isCardInPlay("Laboratory Maniac");
} else if (sa != null && doTrigger(sa, false)) { } else return sa != null && doTrigger(sa, false);
return true;
}
return false;
} }
public List<SpellAbility> chooseSaToActivateFromOpeningHand(List<SpellAbility> usableFromOpeningHand) { public List<SpellAbility> chooseSaToActivateFromOpeningHand(List<SpellAbility> usableFromOpeningHand) {
@@ -2078,9 +2065,7 @@ public class AiController {
// AI-specific restrictions specified as activation parameters in spell abilities // AI-specific restrictions specified as activation parameters in spell abilities
if (sa.hasParam("AILifeThreshold")) { if (sa.hasParam("AILifeThreshold")) {
if (player.getLife() <= Integer.parseInt(sa.getParam("AILifeThreshold"))) { return player.getLife() > Integer.parseInt(sa.getParam("AILifeThreshold"));
return false;
}
} }
return true; return true;

View File

@@ -56,7 +56,7 @@ public class AiCostDecision extends CostDecisionMakerBase {
@Override @Override
public PaymentDecision visit(CostChooseCreatureType cost) { public PaymentDecision visit(CostChooseCreatureType cost) {
String choice = player.getController().chooseSomeType("Creature", ability, CardType.getAllCreatureTypes(), String choice = player.getController().chooseSomeType("Creature", ability, CardType.getAllCreatureTypes(),
Lists.<String>newArrayList()); Lists.newArrayList());
return PaymentDecision.type(choice); return PaymentDecision.type(choice);
} }
@@ -475,7 +475,7 @@ public class AiCostDecision extends CostDecisionMakerBase {
if (ability.getPayCosts().hasTapCost() && typeList.contains(ability.getHostCard())) { if (ability.getPayCosts().hasTapCost() && typeList.contains(ability.getHostCard())) {
c--; c--;
} }
source.setSVar("ChosenX", "Number$" + Integer.toString(c)); source.setSVar("ChosenX", "Number$" + c);
} else { } else {
if (!isVehicle) { if (!isVehicle) {
c = AbilityUtils.calculateAmount(source, amount, ability); c = AbilityUtils.calculateAmount(source, amount, ability);
@@ -809,7 +809,7 @@ public class AiCostDecision extends CostDecisionMakerBase {
final String sVar = ability.getSVar(amount); final String sVar = ability.getSVar(amount);
if (sVar.equals("XChoice")) { if (sVar.equals("XChoice")) {
c = AbilityUtils.calculateAmount(source, "ChosenX", ability); c = AbilityUtils.calculateAmount(source, "ChosenX", ability);
source.setSVar("ChosenX", "Number$" + String.valueOf(c)); source.setSVar("ChosenX", "Number$" + c);
} else if (amount.equals("All")) { } else if (amount.equals("All")) {
c = source.getCounters(cost.counter); c = source.getCounters(cost.counter);
} else if (sVar.equals("Targeted$CardManaCost")) { } else if (sVar.equals("Targeted$CardManaCost")) {
@@ -865,7 +865,7 @@ public class AiCostDecision extends CostDecisionMakerBase {
} }
typeList = CardLists.filter(typeList, Presets.TAPPED); typeList = CardLists.filter(typeList, Presets.TAPPED);
c = typeList.size(); c = typeList.size();
source.setSVar("ChosenX", "Number$" + Integer.toString(c)); source.setSVar("ChosenX", "Number$" + c);
} else { } else {
c = AbilityUtils.calculateAmount(source, amount, ability); c = AbilityUtils.calculateAmount(source, amount, ability);
} }

View File

@@ -17,5 +17,5 @@ public enum AiPlayDecision {
WouldBecomeZeroToughnessCreature, WouldBecomeZeroToughnessCreature,
WouldDestroyWorldEnchantment, WouldDestroyWorldEnchantment,
BadEtbEffects, BadEtbEffects,
CurseEffects; CurseEffects
} }

View File

@@ -423,7 +423,7 @@ public class ComputerUtil {
int mana = ComputerUtilMana.getAvailableManaEstimate(ai, false); int mana = ComputerUtilMana.getAvailableManaEstimate(ai, false);
boolean cantAffordSoon = activate.getCMC() > mana + 1; boolean cantAffordSoon = activate.getCMC() > mana + 1;
boolean wrongColor = !activate.determineColor().hasNoColorsExcept(ColorSet.fromNames(ComputerUtilCost.getAvailableManaColors(ai, ImmutableList.<Card>of())).getColor()); boolean wrongColor = !activate.determineColor().hasNoColorsExcept(ColorSet.fromNames(ComputerUtilCost.getAvailableManaColors(ai, ImmutableList.of())).getColor());
// Only do this for spells, not activated abilities // Only do this for spells, not activated abilities
// We can't pay for this spell even if we play another land, or have wrong colors // We can't pay for this spell even if we play another land, or have wrong colors
@@ -524,7 +524,7 @@ public class ComputerUtil {
typeList = CardLists.filter(typeList, CardPredicates.canBeSacrificedBy(ability)); typeList = CardLists.filter(typeList, CardPredicates.canBeSacrificedBy(ability));
if ((target != null) && target.getController() == ai && typeList.contains(target)) { if ((target != null) && target.getController() == ai) {
typeList.remove(target); // don't sacrifice the card we're pumping typeList.remove(target); // don't sacrifice the card we're pumping
} }
@@ -554,7 +554,7 @@ public class ComputerUtil {
final Card target, final int amount) { final Card target, final int amount) {
CardCollection typeList = CardLists.getValidCards(ai.getCardsIn(zone), type.split(";"), activate.getController(), activate, null); CardCollection typeList = CardLists.getValidCards(ai.getCardsIn(zone), type.split(";"), activate.getController(), activate, null);
if ((target != null) && target.getController() == ai && typeList.contains(target)) { if ((target != null) && target.getController() == ai) {
typeList.remove(target); // don't exile the card we're pumping typeList.remove(target); // don't exile the card we're pumping
} }
@@ -575,7 +575,7 @@ public class ComputerUtil {
final Card target, final int amount) { final Card target, final int amount) {
CardCollection typeList = CardLists.getValidCards(ai.getCardsIn(zone), type.split(";"), activate.getController(), activate, null); CardCollection typeList = CardLists.getValidCards(ai.getCardsIn(zone), type.split(";"), activate.getController(), activate, null);
if ((target != null) && target.getController() == ai && typeList.contains(target)) { if ((target != null) && target.getController() == ai) {
typeList.remove(target); // don't move the card we're pumping typeList.remove(target); // don't move the card we're pumping
} }
@@ -704,7 +704,7 @@ public class ComputerUtil {
public static CardCollection chooseReturnType(final Player ai, final String type, final Card activate, final Card target, final int amount) { public static CardCollection chooseReturnType(final Player ai, final String type, final Card activate, final Card target, final int amount) {
final CardCollection typeList = final CardCollection typeList =
CardLists.getValidCards(ai.getCardsIn(ZoneType.Battlefield), type.split(";"), activate.getController(), activate, null); CardLists.getValidCards(ai.getCardsIn(ZoneType.Battlefield), type.split(";"), activate.getController(), activate, null);
if ((target != null) && target.getController() == ai && typeList.contains(target)) { if ((target != null) && target.getController() == ai) {
// don't bounce the card we're pumping // don't bounce the card we're pumping
typeList.remove(target); typeList.remove(target);
} }
@@ -794,12 +794,8 @@ public class ComputerUtil {
if (c.hasSVar("SacMe") || ComputerUtilCard.evaluateCreature(c) < sacThreshold) { if (c.hasSVar("SacMe") || ComputerUtilCard.evaluateCreature(c) < sacThreshold) {
return true; return true;
} }
if (ComputerUtilCard.hasActiveUndyingOrPersist(c)) { return ComputerUtilCard.hasActiveUndyingOrPersist(c);
return true;
}
return false;
} }
}); });
} }
@@ -1107,10 +1103,8 @@ public class ComputerUtil {
creatures2.add(creatures.get(i)); creatures2.add(creatures.get(i));
} }
} }
if (((creatures2.size() + CardUtil.getThisTurnCast("Creature.YouCtrl", vengevines.get(0)).size()) > 1) return ((creatures2.size() + CardUtil.getThisTurnCast("Creature.YouCtrl", vengevines.get(0)).size()) > 1)
&& card.isCreature() && card.getManaCost().getCMC() <= 3) { && card.isCreature() && card.getManaCost().getCMC() <= 3;
return true;
}
} }
return false; return false;
} }
@@ -1161,30 +1155,25 @@ public class ComputerUtil {
final int highestCMC = Math.max(6, Aggregates.max(nonLandsInHand, CardPredicates.Accessors.fnGetCmc)); final int highestCMC = Math.max(6, Aggregates.max(nonLandsInHand, CardPredicates.Accessors.fnGetCmc));
final int discardCMC = discard.getCMC(); final int discardCMC = discard.getCMC();
if (discard.isLand()) { if (discard.isLand()) {
if (landsInPlay.size() >= highestCMC // Don't need more land.
return landsInPlay.size() >= highestCMC
|| (landsInPlay.size() + landsInHand.size() > 6 && landsInHand.size() > 1) || (landsInPlay.size() + landsInHand.size() > 6 && landsInHand.size() > 1)
|| (landsInPlay.size() > 3 && nonLandsInHand.size() == 0)) { || (landsInPlay.size() > 3 && nonLandsInHand.size() == 0);
// Don't need more land.
return true;
}
} else { //non-land } else { //non-land
if (discardCMC > landsInPlay.size() + landsInHand.size() + 2) { if (discardCMC > landsInPlay.size() + landsInHand.size() + 2) {
// not castable for some time. // not castable for some time.
return true; return true;
} else if (!game.getPhaseHandler().isPlayerTurn(ai) } else // Probably don't need small stuff now.
if (!game.getPhaseHandler().isPlayerTurn(ai)
&& game.getPhaseHandler().getPhase().isAfter(PhaseType.MAIN2) && game.getPhaseHandler().getPhase().isAfter(PhaseType.MAIN2)
&& discardCMC > landsInPlay.size() + landsInHand.size() && discardCMC > landsInPlay.size() + landsInHand.size()
&& discardCMC > landsInPlay.size() + 1 && discardCMC > landsInPlay.size() + 1
&& nonLandsInHand.size() > 1) { && nonLandsInHand.size() > 1) {
// not castable for at least one other turn. // not castable for at least one other turn.
return true; return true;
} else if (landsInPlay.size() > 5 && discard.getCMC() <= 1 } else return landsInPlay.size() > 5 && discard.getCMC() <= 1
&& !discard.hasProperty("hasXCost", ai, null, null)) { && !discard.hasProperty("hasXCost", ai, null, null);
// Probably don't need small stuff now.
return true;
}
} }
return false;
} }
// returns true if it's better to wait until blockers are declared // returns true if it's better to wait until blockers are declared
@@ -1925,16 +1914,12 @@ public class ComputerUtil {
if (predictThreatenedObjects(ai, null).contains(source)) { if (predictThreatenedObjects(ai, null).contains(source)) {
return true; return true;
} }
if (game.getPhaseHandler().inCombat() && return game.getPhaseHandler().inCombat() &&
ComputerUtilCombat.combatantWouldBeDestroyed(ai, source, game.getCombat())) { ComputerUtilCombat.combatantWouldBeDestroyed(ai, source, game.getCombat());
return true;
}
} else if (zone.getZoneType() == ZoneType.Exile && sa.getMayPlay() != null) { } else if (zone.getZoneType() == ZoneType.Exile && sa.getMayPlay() != null) {
// play cards in exile that can only be played that turn // play cards in exile that can only be played that turn
if (game.getPhaseHandler().getPhase() == PhaseType.MAIN2) { if (game.getPhaseHandler().getPhase() == PhaseType.MAIN2) {
if (source.mayPlay(sa.getMayPlay()) != null) { return source.mayPlay(sa.getMayPlay()) != null;
return true;
}
} }
} }
return false; return false;
@@ -1967,11 +1952,8 @@ public class ComputerUtil {
final CardCollectionView lands = CardLists.filter(handList, new Predicate<Card>() { final CardCollectionView lands = CardLists.filter(handList, new Predicate<Card>() {
@Override @Override
public boolean apply(final Card c) { public boolean apply(final Card c) {
if (c.getManaCost().getCMC() > 0 || c.hasSVar("NeedsToPlay") return c.getManaCost().getCMC() <= 0 && !c.hasSVar("NeedsToPlay")
|| (!c.getType().isLand() && !c.getType().isArtifact())) { && (c.getType().isLand() || c.getType().isArtifact());
return false;
}
return true;
} }
}); });
@@ -1986,10 +1968,7 @@ public class ComputerUtil {
final CardCollectionView castables = CardLists.filter(handList, new Predicate<Card>() { final CardCollectionView castables = CardLists.filter(handList, new Predicate<Card>() {
@Override @Override
public boolean apply(final Card c) { public boolean apply(final Card c) {
if (c.getManaCost().getCMC() > 0 && c.getManaCost().getCMC() <= landSize) { return c.getManaCost().getCMC() <= 0 || c.getManaCost().getCMC() > landSize;
return false;
}
return true;
} }
}); });
@@ -2186,10 +2165,7 @@ public class ComputerUtil {
CardCollection goodChoices = CardLists.filter(validCards, new Predicate<Card>() { CardCollection goodChoices = CardLists.filter(validCards, new Predicate<Card>() {
@Override @Override
public boolean apply(final Card c) { public boolean apply(final Card c) {
if (c.hasSVar("DiscardMeByOpp") || c.hasSVar("DiscardMe")) { return !c.hasSVar("DiscardMeByOpp") && !c.hasSVar("DiscardMe");
return false;
}
return true;
} }
}); });
if (goodChoices.isEmpty()) { if (goodChoices.isEmpty()) {
@@ -2225,7 +2201,7 @@ public class ComputerUtil {
public static String chooseSomeType(Player ai, String kindOfType, String logic, List<String> invalidTypes) { public static String chooseSomeType(Player ai, String kindOfType, String logic, List<String> invalidTypes) {
if (invalidTypes == null) { if (invalidTypes == null) {
invalidTypes = ImmutableList.<String>of(); invalidTypes = ImmutableList.of();
} }
final Game game = ai.getGame(); final Game game = ai.getGame();
@@ -2546,8 +2522,7 @@ public class ComputerUtil {
@Override @Override
public boolean apply(final Card c) { public boolean apply(final Card c) {
if (c.getController() == ai) { if (c.getController() == ai) {
if (c.getSVar("Targeting").equals("Dies") || c.getSVar("Targeting").equals("Counter")) return !c.getSVar("Targeting").equals("Dies") && !c.getSVar("Targeting").equals("Counter");
return false;
} }
return true; return true;
} }
@@ -2872,11 +2847,8 @@ public class ComputerUtil {
return false; return false;
} else if (Iterables.any(list, CardTraitPredicates.hasParam("AiLogic", "LoseLife"))) { } else if (Iterables.any(list, CardTraitPredicates.hasParam("AiLogic", "LoseLife"))) {
return false; return false;
} else if (Iterables.any(list, CardTraitPredicates.hasParam("AiLogic", "LichDraw"))) { } else return !Iterables.any(list, CardTraitPredicates.hasParam("AiLogic", "LichDraw"));
return false;
}
return true;
} }
public static boolean lifegainNegative(final Player player, final Card source) { public static boolean lifegainNegative(final Player player, final Card source) {
@@ -3074,10 +3046,7 @@ public class ComputerUtil {
if ((serious) && (ComputerUtilCombat.lifeInSeriousDanger(ai, combat, payment))) { if ((serious) && (ComputerUtilCombat.lifeInSeriousDanger(ai, combat, payment))) {
return true; return true;
} }
if ((!serious) && (ComputerUtilCombat.lifeInDanger(ai, combat, payment))) { return (!serious) && (ComputerUtilCombat.lifeInDanger(ai, combat, payment));
return true;
}
return false;
} }
} }

View File

@@ -368,7 +368,7 @@ public class ComputerUtilCard {
} }
if (hasEnchantmants || hasArtifacts) { if (hasEnchantmants || hasArtifacts) {
final List<Card> ae = CardLists.filter(list, Predicates.and(Predicates.<Card>or(CardPredicates.Presets.ARTIFACTS, CardPredicates.Presets.ENCHANTMENTS), new Predicate<Card>() { final List<Card> ae = CardLists.filter(list, Predicates.and(Predicates.or(CardPredicates.Presets.ARTIFACTS, CardPredicates.Presets.ENCHANTMENTS), new Predicate<Card>() {
@Override @Override
public boolean apply(Card card) { public boolean apply(Card card) {
return !card.hasSVar("DoNotDiscardIfAble"); return !card.hasSVar("DoNotDiscardIfAble");
@@ -1301,7 +1301,7 @@ public class ComputerUtilCard {
combatTrick = true; combatTrick = true;
final List<String> kws = sa.hasParam("KW") ? Arrays.asList(sa.getParam("KW").split(" & ")) final List<String> kws = sa.hasParam("KW") ? Arrays.asList(sa.getParam("KW").split(" & "))
: Lists.<String>newArrayList(); : Lists.newArrayList();
for (String kw : kws) { for (String kw : kws) {
if (!kw.equals("Trample") && !kw.equals("First Strike") && !kw.equals("Double Strike")) { if (!kw.equals("Trample") && !kw.equals("First Strike") && !kw.equals("Double Strike")) {
combatTrick = false; combatTrick = false;
@@ -1742,20 +1742,14 @@ public class ComputerUtilCard {
if (!c.isCreature()) { if (!c.isCreature()) {
return false; return false;
} }
if (c.hasKeyword("CARDNAME can't attack or block.") || (c.hasKeyword("CARDNAME doesn't untap during your untap step.") && c.isTapped()) || (c.getOwner() == ai && ai.getOpponents().contains(c.getController()))) { return c.hasKeyword("CARDNAME can't attack or block.") || (c.hasKeyword("CARDNAME doesn't untap during your untap step.") && c.isTapped()) || (c.getOwner() == ai && ai.getOpponents().contains(c.getController()));
return true;
}
return false;
} }
public static boolean hasActiveUndyingOrPersist(final Card c) { public static boolean hasActiveUndyingOrPersist(final Card c) {
if (c.hasKeyword(Keyword.UNDYING) && c.getCounters(CounterType.P1P1) == 0) { if (c.hasKeyword(Keyword.UNDYING) && c.getCounters(CounterType.P1P1) == 0) {
return true; return true;
} }
if (c.hasKeyword(Keyword.PERSIST) && c.getCounters(CounterType.M1M1) == 0) { return c.hasKeyword(Keyword.PERSIST) && c.getCounters(CounterType.M1M1) == 0;
return true;
}
return false;
} }
public static boolean isPresentOnBattlefield(final Game game, final String cardName) { public static boolean isPresentOnBattlefield(final Game game, final String cardName) {

View File

@@ -84,7 +84,7 @@ public class ComputerUtilCombat {
return Iterables.any(defenders, new Predicate<GameEntity>() { return Iterables.any(defenders, new Predicate<GameEntity>() {
@Override public boolean apply(final GameEntity input) { @Override public boolean apply(final GameEntity input) {
return ComputerUtilCombat.canAttackNextTurn(attacker, input); return ComputerUtilCombat.canAttackNextTurn(attacker, input);
}; }
}); });
} // canAttackNextTurn(Card) } // canAttackNextTurn(Card)
@@ -119,11 +119,7 @@ public class ComputerUtilCombat {
} }
// The creature won't untap next turn // The creature won't untap next turn
if (atacker.isTapped() && !Untap.canUntap(atacker)) { return !atacker.isTapped() || Untap.canUntap(atacker);
return false;
}
return true;
} // canAttackNextTurn(Card, GameEntity) } // canAttackNextTurn(Card, GameEntity)
/** /**
@@ -889,12 +885,10 @@ public class ComputerUtilCombat {
|| CardTraitBase.matchesValid(attacker, trigParams.get("ValidTarget").split(","), source))) { || CardTraitBase.matchesValid(attacker, trigParams.get("ValidTarget").split(","), source))) {
return true; return true;
} }
if (CardTraitBase.matchesValid(attacker, trigParams.get("ValidSource").split(","), source) return CardTraitBase.matchesValid(attacker, trigParams.get("ValidSource").split(","), source)
&& attacker.getNetCombatDamage() > 0 && attacker.getNetCombatDamage() > 0
&& (!trigParams.containsKey("ValidTarget") && (!trigParams.containsKey("ValidTarget")
|| CardTraitBase.matchesValid(defender, trigParams.get("ValidTarget").split(","), source))) { || CardTraitBase.matchesValid(defender, trigParams.get("ValidTarget").split(","), source));
return true;
}
} }
return false; return false;
} }
@@ -1414,7 +1408,7 @@ public class ComputerUtilCombat {
if (att.matches("[0-9][0-9]?") || att.matches("-" + "[0-9][0-9]?")) { if (att.matches("[0-9][0-9]?") || att.matches("-" + "[0-9][0-9]?")) {
power += Integer.parseInt(att); power += Integer.parseInt(att);
} else { } else {
String bonus = new String(source.getSVar(att)); String bonus = source.getSVar(att);
if (bonus.contains("TriggerCount$NumBlockers")) { if (bonus.contains("TriggerCount$NumBlockers")) {
bonus = TextUtil.fastReplace(bonus, "TriggerCount$NumBlockers", "Number$1"); bonus = TextUtil.fastReplace(bonus, "TriggerCount$NumBlockers", "Number$1");
} else if (bonus.contains("TriggeredPlayersDefenders$Amount")) { // for Melee } else if (bonus.contains("TriggeredPlayersDefenders$Amount")) { // for Melee
@@ -1655,7 +1649,7 @@ public class ComputerUtilCombat {
if (def.matches("[0-9][0-9]?") || def.matches("-" + "[0-9][0-9]?")) { if (def.matches("[0-9][0-9]?") || def.matches("-" + "[0-9][0-9]?")) {
toughness += Integer.parseInt(def); toughness += Integer.parseInt(def);
} else { } else {
String bonus = new String(source.getSVar(def)); String bonus = source.getSVar(def);
if (bonus.contains("TriggerCount$NumBlockers")) { if (bonus.contains("TriggerCount$NumBlockers")) {
bonus = TextUtil.fastReplace(bonus, "TriggerCount$NumBlockers", "Number$1"); bonus = TextUtil.fastReplace(bonus, "TriggerCount$NumBlockers", "Number$1");
} else if (bonus.contains("TriggeredPlayersDefenders$Amount")) { // for Melee } else if (bonus.contains("TriggeredPlayersDefenders$Amount")) { // for Melee
@@ -1795,11 +1789,7 @@ public class ComputerUtilCombat {
} }
// all damage will be prevented // all damage will be prevented
if (attacker.hasKeyword("PreventAllDamageBy Creature.blockingSource")) { return attacker.hasKeyword("PreventAllDamageBy Creature.blockingSource");
return true;
}
return false;
} }
// can the blocker destroy the attacker? // can the blocker destroy the attacker?
@@ -1922,9 +1912,7 @@ public class ComputerUtilCombat {
return false; return false;
} }
} }
if (attackerLife <= 2 * defenderDamage) { return attackerLife <= 2 * defenderDamage;
return true;
}
} // defender double strike } // defender double strike
else { // no double strike for defender else { // no double strike for defender
@@ -1948,7 +1936,7 @@ public class ComputerUtilCombat {
return defenderDamage >= attackerLife; return defenderDamage >= attackerLife;
} // defender no double strike } // defender no double strike
return false; // should never arrive here // should never arrive here
} // canDestroyAttacker } // canDestroyAttacker
// For AI safety measures like Regeneration // For AI safety measures like Regeneration
@@ -2169,9 +2157,7 @@ public class ComputerUtilCombat {
return false; return false;
} }
} }
if (defenderLife <= 2 * attackerDamage) { return defenderLife <= 2 * attackerDamage;
return true;
}
} // attacker double strike } // attacker double strike
else { // no double strike for attacker else { // no double strike for attacker
@@ -2195,7 +2181,7 @@ public class ComputerUtilCombat {
return attackerDamage >= defenderLife; return attackerDamage >= defenderLife;
} // attacker no double strike } // attacker no double strike
return false; // should never arrive here // should never arrive here
} // canDestroyBlocker } // canDestroyBlocker

View File

@@ -424,7 +424,7 @@ public class ComputerUtilCost {
continue; continue;
} }
final int remainingLife = ai.getLife(); final int remainingLife = ai.getLife();
final int lifeCost = ((CostPayLife) part).convertAmount(); final int lifeCost = part.convertAmount();
if ((remainingLife - lifeCost) < 10) { if ((remainingLife - lifeCost) < 10) {
return false; //Don't pay life if it would put AI under 10 life return false; //Don't pay life if it would put AI under 10 life
} else if ((remainingLife / lifeCost) < 4) { } else if ((remainingLife / lifeCost) < 4) {
@@ -552,7 +552,7 @@ public class ComputerUtilCost {
final Card source = sa.getHostCard(); final Card source = sa.getHostCard();
final String aiLogic = sa.getParam("UnlessAI"); final String aiLogic = sa.getParam("UnlessAI");
boolean payForOwnOnly = "OnlyOwn".equals(aiLogic); boolean payForOwnOnly = "OnlyOwn".equals(aiLogic);
boolean payOwner = sa.hasParam("UnlessAI") ? aiLogic.startsWith("Defined") : false; boolean payOwner = sa.hasParam("UnlessAI") && aiLogic.startsWith("Defined");
boolean payNever = "Never".equals(aiLogic); boolean payNever = "Never".equals(aiLogic);
boolean shockland = "Shockland".equals(aiLogic); boolean shockland = "Shockland".equals(aiLogic);
boolean isMine = sa.getActivatingPlayer().equals(payer); boolean isMine = sa.getActivatingPlayer().equals(payer);

View File

@@ -905,10 +905,8 @@ public class ComputerUtilMana {
AiCardMemory.clearMemorySet(ai, AiCardMemory.MemorySet.HELD_MANA_SOURCES_FOR_MAIN2); AiCardMemory.clearMemorySet(ai, AiCardMemory.MemorySet.HELD_MANA_SOURCES_FOR_MAIN2);
} }
else { else {
if (AiCardMemory.isRememberedCard(ai, sourceCard, AiCardMemory.MemorySet.HELD_MANA_SOURCES_FOR_MAIN2)) { // This mana source is held elsewhere for a Main Phase 2 spell.
// This mana source is held elsewhere for a Main Phase 2 spell. return AiCardMemory.isRememberedCard(ai, sourceCard, AiCardMemory.MemorySet.HELD_MANA_SOURCES_FOR_MAIN2);
return true;
}
} }
return false; return false;

View File

@@ -1061,7 +1061,7 @@ public abstract class GameState {
} }
private void applyCountersToGameEntity(GameEntity entity, String counterString) { private void applyCountersToGameEntity(GameEntity entity, String counterString) {
entity.setCounters(Maps.<CounterType, Integer>newEnumMap(CounterType.class)); entity.setCounters(Maps.newEnumMap(CounterType.class));
String[] allCounterStrings = counterString.split(","); String[] allCounterStrings = counterString.split(",");
for (final String counterPair : allCounterStrings) { for (final String counterPair : allCounterStrings) {
String[] pair = counterPair.split("=", 2); String[] pair = counterPair.split("=", 2);
@@ -1107,7 +1107,7 @@ public abstract class GameState {
Map<CounterType, Integer> counters = c.getCounters(); Map<CounterType, Integer> counters = c.getCounters();
// Note: Not clearCounters() since we want to keep the counters // Note: Not clearCounters() since we want to keep the counters
// var as-is. // var as-is.
c.setCounters(Maps.<CounterType, Integer>newEnumMap(CounterType.class)); c.setCounters(Maps.newEnumMap(CounterType.class));
if (c.isAura()) { if (c.isAura()) {
// dummy "enchanting" to indicate that the card will be force-attached elsewhere // dummy "enchanting" to indicate that the card will be force-attached elsewhere
// (will be overridden later, so the actual value shouldn't matter) // (will be overridden later, so the actual value shouldn't matter)

View File

@@ -492,7 +492,7 @@ public class PlayerControllerAi extends PlayerController {
Card toDiscard = Aggregates.itemWithMin(cardsOfType, CardPredicates.Accessors.fnGetCmc); Card toDiscard = Aggregates.itemWithMin(cardsOfType, CardPredicates.Accessors.fnGetCmc);
return new CardCollection(toDiscard); return new CardCollection(toDiscard);
} }
return getAi().getCardsToDiscard(num, (String[])null, sa); return getAi().getCardsToDiscard(num, null, sa);
} }
@@ -612,7 +612,7 @@ public class PlayerControllerAi extends PlayerController {
@Override @Override
public CardCollection chooseCardsToDiscardToMaximumHandSize(int numDiscard) { public CardCollection chooseCardsToDiscardToMaximumHandSize(int numDiscard) {
return brains.getCardsToDiscard(numDiscard, (String[])null, null); return brains.getCardsToDiscard(numDiscard, null, null);
} }
@Override @Override
@@ -669,7 +669,7 @@ public class PlayerControllerAi extends PlayerController {
throw new InvalidParameterException("SA is not api-based, this is not supported yet"); throw new InvalidParameterException("SA is not api-based, this is not supported yet");
} }
return SpellApiToAi.Converter.get(api).chooseNumber(player, sa, min, max, params); return SpellApiToAi.Converter.get(api).chooseNumber(player, sa, min, max, params);
}; }
@Override @Override
public int chooseNumber(SpellAbility sa, String title, List<Integer> options, Player relatedPlayer) { public int chooseNumber(SpellAbility sa, String title, List<Integer> options, Player relatedPlayer) {

View File

@@ -94,12 +94,8 @@ public class SpecialCardAi {
int minCMC = isLowCMCDeck ? 3 : 4; // probably not worth wasting a lotus on a low-CMC spell (<4 CMC), except in low-CMC decks, where 3 CMC may be fine int minCMC = isLowCMCDeck ? 3 : 4; // probably not worth wasting a lotus on a low-CMC spell (<4 CMC), except in low-CMC decks, where 3 CMC may be fine
int paidCMC = cost.getConvertedManaCost(); int paidCMC = cost.getConvertedManaCost();
if (paidCMC < minCMC) { if (paidCMC < minCMC) {
if (paidCMC == 3 && numManaSrcs < 3) { // if it's a CMC 3 spell and we're more than one mana source short for it, might be worth it anyway
// if it's a CMC 3 spell and we're more than one mana source short for it, might be worth it anyway return paidCMC == 3 && numManaSrcs < 3;
return true;
}
return false;
} }
return true; return true;
@@ -218,11 +214,7 @@ public class SpecialCardAi {
} }
} }
if (ai.getLife() <= sa.getHostCard().getNetPower() && !hasUsefulBlocker) { return ai.getLife() <= sa.getHostCard().getNetPower() && !hasUsefulBlocker;
return true;
} else {
return false;
}
} }
public static int getSacThreshold() { public static int getSacThreshold() {
@@ -335,7 +327,7 @@ public class SpecialCardAi {
boolean canTrample = source.hasKeyword(Keyword.TRAMPLE); boolean canTrample = source.hasKeyword(Keyword.TRAMPLE);
if (!isBlocking && combat.getDefenderByAttacker(source) instanceof Card) { if (!isBlocking && combat.getDefenderByAttacker(source) instanceof Card) {
int loyalty = ((Card)combat.getDefenderByAttacker(source)).getCounters(CounterType.LOYALTY); int loyalty = combat.getDefenderByAttacker(source).getCounters(CounterType.LOYALTY);
int totalDamageToPW = 0; int totalDamageToPW = 0;
for (Card atk : (combat.getAttackersOf(combat.getDefenderByAttacker(source)))) { for (Card atk : (combat.getAttackersOf(combat.getDefenderByAttacker(source)))) {
if (combat.isUnblocked(atk)) { if (combat.isUnblocked(atk)) {
@@ -385,15 +377,12 @@ public class SpecialCardAi {
// Already enough to kill the blockers and survive, don't overpump // Already enough to kill the blockers and survive, don't overpump
return false; return false;
} }
if (oppCantDie && !source.hasKeyword(Keyword.TRAMPLE) && !source.hasKeyword(Keyword.WITHER) // Can't kill or cripple anyone, as well as can't Trample over, so don't pump
&& !source.hasKeyword(Keyword.INFECT) && predictedPT.getLeft() <= oppT) { return !oppCantDie || source.hasKeyword(Keyword.TRAMPLE) || source.hasKeyword(Keyword.WITHER)
// Can't kill or cripple anyone, as well as can't Trample over, so don't pump || source.hasKeyword(Keyword.INFECT) || predictedPT.getLeft() > oppT;
return false;
}
// If we got here, it should be a favorable combat pump, resulting in at least one // If we got here, it should be a favorable combat pump, resulting in at least one
// opposing creature dying, and hopefully with the Pummeler surviving combat. // opposing creature dying, and hopefully with the Pummeler surviving combat.
return true;
} }
public static boolean predictOverwhelmingDamage(final Player ai, final SpellAbility sa) { public static boolean predictOverwhelmingDamage(final Player ai, final SpellAbility sa) {
@@ -411,11 +400,7 @@ public class SpecialCardAi {
Pair<Integer, Integer> predictedPT = getPumpedPT(ai, source.getNetCombatDamage(), source.getNetToughness()); Pair<Integer, Integer> predictedPT = getPumpedPT(ai, source.getNetCombatDamage(), source.getNetToughness());
int oppT = Aggregates.sum(potentialBlockers, CardPredicates.Accessors.fnGetNetToughness); int oppT = Aggregates.sum(potentialBlockers, CardPredicates.Accessors.fnGetNetToughness);
if (potentialBlockers.isEmpty() || (source.hasKeyword(Keyword.TRAMPLE) && predictedPT.getLeft() - oppT >= oppLife)) { return potentialBlockers.isEmpty() || (source.hasKeyword(Keyword.TRAMPLE) && predictedPT.getLeft() - oppT >= oppLife);
return true;
}
return false;
} }
public static Pair<Integer, Integer> getPumpedPT(Player ai, int power, int toughness) { public static Pair<Integer, Integer> getPumpedPT(Player ai, int power, int toughness) {
@@ -486,15 +471,13 @@ public class SpecialCardAi {
} }
if (isExileMode) { if (isExileMode) {
// We probably need a low-CMC card to exile to it, exiling a higher CMC spell may be suboptimal
// since the AI does not prioritize/value cards vs. permission at the moment.
if (blueCards.size() < 2) { if (blueCards.size() < 2) {
// Need to have something else in hand that is blue in addition to Force of Will itself, // Need to have something else in hand that is blue in addition to Force of Will itself,
// otherwise the AI will fail to play the card and the card will disappear from the pool // otherwise the AI will fail to play the card and the card will disappear from the pool
return false; return false;
} else if (CardLists.filter(blueCards, CardPredicates.lessCMC(3)).isEmpty()) { } else return !CardLists.filter(blueCards, CardPredicates.lessCMC(3)).isEmpty();
// We probably need a low-CMC card to exile to it, exiling a higher CMC spell may be suboptimal
// since the AI does not prioritize/value cards vs. permission at the moment.
return false;
}
} }
return true; return true;
@@ -522,7 +505,7 @@ public class SpecialCardAi {
best.add(sp); // these SAs are prioritized since the AI sees a reason to play them now best.add(sp); // these SAs are prioritized since the AI sees a reason to play them now
} }
final List<String> keywords = sp.hasParam("KW") ? Arrays.asList(sp.getParam("KW").split(" & ")) final List<String> keywords = sp.hasParam("KW") ? Arrays.asList(sp.getParam("KW").split(" & "))
: Lists.<String>newArrayList(); : Lists.newArrayList();
for (String kw : keywords) { for (String kw : keywords) {
if (!tgtCard.hasKeyword(kw)) { if (!tgtCard.hasKeyword(kw)) {
if ("Indestructible".equals(kw) && ai.getOpponents().getCreaturesInPlay().isEmpty()) { if ("Indestructible".equals(kw) && ai.getOpponents().getCreaturesInPlay().isEmpty()) {
@@ -568,10 +551,7 @@ public class SpecialCardAi {
@Override @Override
public boolean apply(final Card c) { public boolean apply(final Card c) {
// Don't enchant creatures that can survive // Don't enchant creatures that can survive
if (!c.canBeDestroyed() || c.getNetCombatDamage() < c.getNetToughness() || c.isEnchantedBy("Guilty Conscience")) { return c.canBeDestroyed() && c.getNetCombatDamage() >= c.getNetToughness() && !c.isEnchantedBy("Guilty Conscience");
return false;
}
return true;
} }
}); });
chosen = ComputerUtilCard.getBestCreatureAI(creatures); chosen = ComputerUtilCard.getBestCreatureAI(creatures);
@@ -912,15 +892,12 @@ public class SpecialCardAi {
} else if (blackViseOTB && computerHandSize + exiledWithNecro - 1 >= 4) { } else if (blackViseOTB && computerHandSize + exiledWithNecro - 1 >= 4) {
// try not to overdraw in presence of Black Vise // try not to overdraw in presence of Black Vise
return false; return false;
} else if (computerHandSize + exiledWithNecro - 1 >= maxHandSize) { } else // Only activate in AI's own turn (sans the exception above)
if (computerHandSize + exiledWithNecro - 1 >= maxHandSize) {
// Only draw until we reach max hand size // Only draw until we reach max hand size
return false; return false;
} else if (!ph.isPlayerTurn(ai) || !ph.is(PhaseType.MAIN2)) { } else return ph.isPlayerTurn(ai) && ph.is(PhaseType.MAIN2);
// Only activate in AI's own turn (sans the exception above)
return false;
}
return true;
} }
} }
@@ -941,11 +918,7 @@ public class SpecialCardAi {
} }
// Maybe use it for some important high-impact spells even if there are more cards in hand? // Maybe use it for some important high-impact spells even if there are more cards in hand?
if (ai.getCardsIn(ZoneType.Hand).size() > 1 && !hasEnsnaringBridgeEffect) { return ai.getCardsIn(ZoneType.Hand).size() <= 1 || hasEnsnaringBridgeEffect;
return false;
}
return true;
} }
} }
@@ -1310,12 +1283,8 @@ public class SpecialCardAi {
} }
} }
if (aiHandSize < HAND_SIZE_THRESHOLD || maxOppHandSize - aiHandSize > HAND_SIZE_THRESHOLD) { // use in case we're getting low on cards or if we're significantly behind our opponent in cards in hand
// use in case we're getting low on cards or if we're significantly behind our opponent in cards in hand return aiHandSize < HAND_SIZE_THRESHOLD || maxOppHandSize - aiHandSize > HAND_SIZE_THRESHOLD;
return true;
}
return false;
} }
} }
@@ -1342,9 +1311,7 @@ public class SpecialCardAi {
if (topGY == null if (topGY == null
|| !topGY.isCreature() || !topGY.isCreature()
|| ComputerUtilCard.evaluateCreature(creatHand) > ComputerUtilCard.evaluateCreature(topGY) + 80) { || ComputerUtilCard.evaluateCreature(creatHand) > ComputerUtilCard.evaluateCreature(topGY) + 80) {
if (numCreatsInHand > 1 || !ComputerUtilMana.canPayManaCost(creatHand.getSpellPermanent(), ai, 0)) { return numCreatsInHand > 1 || !ComputerUtilMana.canPayManaCost(creatHand.getSpellPermanent(), ai, 0);
return true;
}
} }
} }
@@ -1459,15 +1426,12 @@ public class SpecialCardAi {
} else if (blackViseOTB && computerHandSize + 1 > 4) { } else if (blackViseOTB && computerHandSize + 1 > 4) {
// try not to overdraw in presence of Black Vise // try not to overdraw in presence of Black Vise
return false; return false;
} else if (computerHandSize + 1 > maxHandSize) { } else // Only activate in AI's own turn (sans the exception above)
if (computerHandSize + 1 > maxHandSize) {
// Only draw until we reach max hand size // Only draw until we reach max hand size
return false; return false;
} else if (!ph.isPlayerTurn(ai)) { } else return ph.isPlayerTurn(ai);
// Only activate in AI's own turn (sans the exception above)
return false;
}
return true;
} }
} }

View File

@@ -133,10 +133,7 @@ public abstract class SpellAbilityAi {
if (!ComputerUtilCost.checkSacrificeCost(ai, cost, source, sa)) { if (!ComputerUtilCost.checkSacrificeCost(ai, cost, source, sa)) {
return false; return false;
} }
if (!ComputerUtilCost.checkRemoveCounterCost(cost, source, sa)) { return ComputerUtilCost.checkRemoveCounterCost(cost, source, sa);
return false;
}
return true;
} }
/** /**

View File

@@ -56,12 +56,9 @@ public class ActivateAbilityAi extends SpellAbilityAi {
} else { } else {
final List<Player> defined = AbilityUtils.getDefinedPlayers(source, sa.getParam("Defined"), sa); final List<Player> defined = AbilityUtils.getDefinedPlayers(source, sa.getParam("Defined"), sa);
if (!defined.contains(opp)) { return defined.contains(opp);
return false;
}
} }
return true;
} else { } else {
sa.resetTargets(); sa.resetTargets();
sa.getTargets().add(opp); sa.getTargets().add(opp);

View File

@@ -67,10 +67,8 @@ public class AddTurnAi extends SpellAbilityAi {
return false; return false;
} }
} }
if (!StringUtils.isNumeric(sa.getParam("NumTurns"))) { // TODO: improve ai for Sage of Hours
// TODO: improve ai for Sage of Hours return StringUtils.isNumeric(sa.getParam("NumTurns"));
return false;
}
// not sure if the AI should be playing with cards that give the // not sure if the AI should be playing with cards that give the
// Human more turns. // Human more turns.
} }

View File

@@ -23,9 +23,7 @@ public class AmassAi extends SpellAbilityAi {
final Game game = ai.getGame(); final Game game = ai.getGame();
if (!aiArmies.isEmpty()) { if (!aiArmies.isEmpty()) {
if (CardLists.count(aiArmies, CardPredicates.canReceiveCounters(CounterType.P1P1)) <= 0) { return CardLists.count(aiArmies, CardPredicates.canReceiveCounters(CounterType.P1P1)) > 0;
return false;
}
} else { } else {
final String tokenScript = "b_0_0_zombie_army"; final String tokenScript = "b_0_0_zombie_army";
final int amount = AbilityUtils.calculateAmount(host, sa.getParamOrDefault("Num", "1"), sa); final int amount = AbilityUtils.calculateAmount(host, sa.getParamOrDefault("Num", "1"), sa);
@@ -57,12 +55,9 @@ public class AmassAi extends SpellAbilityAi {
//reset static abilities //reset static abilities
game.getAction().checkStaticAbilities(false); game.getAction().checkStaticAbilities(false);
if (!result) { return result;
return false;
}
} }
return true;
} }
@Override @Override

View File

@@ -117,12 +117,9 @@ public class AnimateAi extends SpellAbilityAi {
boolean activateAsPotentialBlocker = sa.hasParam("UntilYourNextTurn") boolean activateAsPotentialBlocker = sa.hasParam("UntilYourNextTurn")
&& ai.getGame().getPhaseHandler().getNextTurn() != ai && ai.getGame().getPhaseHandler().getNextTurn() != ai
&& source.isPermanent(); && source.isPermanent();
if (ph.isPlayerTurn(ai) && ai.getLife() < 6 && opponent.getLife() > 6 return !ph.isPlayerTurn(ai) || ai.getLife() >= 6 || opponent.getLife() <= 6
&& Iterables.any(opponent.getCardsIn(ZoneType.Battlefield), CardPredicates.Presets.CREATURES) || !Iterables.any(opponent.getCardsIn(ZoneType.Battlefield), CardPredicates.Presets.CREATURES)
&& !sa.hasParam("AILogic") && !sa.hasParam("Permanent") && !activateAsPotentialBlocker) { || sa.hasParam("AILogic") || sa.hasParam("Permanent") || activateAsPotentialBlocker;
return false;
}
return true;
} }
@Override @Override
@@ -207,21 +204,16 @@ public class AnimateAi extends SpellAbilityAi {
return bFlag; // All of the defined stuff is animated, not very useful return bFlag; // All of the defined stuff is animated, not very useful
} else { } else {
sa.resetTargets(); sa.resetTargets();
if (!animateTgtAI(sa)) { return animateTgtAI(sa);
return false;
}
} }
return true;
} }
@Override @Override
public boolean chkAIDrawback(SpellAbility sa, Player aiPlayer) { public boolean chkAIDrawback(SpellAbility sa, Player aiPlayer) {
if (sa.usesTargeting()) { if (sa.usesTargeting()) {
sa.resetTargets(); sa.resetTargets();
if (!animateTgtAI(sa)) { return animateTgtAI(sa);
return false;
}
} }
return true; return true;

View File

@@ -8,11 +8,7 @@ public class AnimateAllAi extends SpellAbilityAi {
@Override @Override
protected boolean canPlayAI(Player aiPlayer, SpellAbility sa) { protected boolean canPlayAI(Player aiPlayer, SpellAbility sa) {
if ("Always".equals(sa.getParam("AILogic"))) { return "Always".equals(sa.getParam("AILogic"));
return true;
}
return false;
} // end animateAllCanPlayAI() } // end animateAllCanPlayAI()
@Override @Override

View File

@@ -123,9 +123,7 @@ public class AttachAi extends SpellAbilityAi {
return !(c.hasProtectionFrom(source) || c.hasKeyword(Keyword.SHROUD) || c.hasKeyword(Keyword.HEXPROOF)); return !(c.hasProtectionFrom(source) || c.hasKeyword(Keyword.SHROUD) || c.hasKeyword(Keyword.HEXPROOF));
} }
}); });
if (targets.isEmpty()) { return !targets.isEmpty();
return false;
}
} }
return true; return true;
@@ -239,9 +237,7 @@ public class AttachAi extends SpellAbilityAi {
return false; return false;
} }
if (!(combat.isAttacking(attachTarget) || combat.isBlocking(attachTarget))) { return combat.isAttacking(attachTarget) || combat.isBlocking(attachTarget);
return false;
}
} }
return true; return true;
@@ -987,9 +983,7 @@ public class AttachAi extends SpellAbilityAi {
return false; return false;
} }
// don't equip creatures that don't gain anything // don't equip creatures that don't gain anything
if (card.hasSVar("NonStackingAttachEffect") && newTarget.isEquippedBy(card.getName())) { return !card.hasSVar("NonStackingAttachEffect") || !newTarget.isEquippedBy(card.getName());
return false;
}
} }
} }
@@ -1353,7 +1347,7 @@ public class AttachAi extends SpellAbilityAi {
CardCollection prefList = list; CardCollection prefList = list;
// Filter AI-specific targets if provided // Filter AI-specific targets if provided
prefList = ComputerUtil.filterAITgts(sa, aiPlayer, (CardCollection)list, true); prefList = ComputerUtil.filterAITgts(sa, aiPlayer, list, true);
Card c = attachGeneralAI(aiPlayer, sa, prefList, mandatory, attachSource, sa.getParam("AILogic")); Card c = attachGeneralAI(aiPlayer, sa, prefList, mandatory, attachSource, sa.getParam("AILogic"));
@@ -1557,86 +1551,55 @@ public class AttachAi extends SpellAbilityAi {
} }
if (evasive) { if (evasive) {
if (card.getNetCombatDamage() + powerBonus <= 0 return card.getNetCombatDamage() + powerBonus > 0
|| !ComputerUtilCombat.canAttackNextTurn(card) && ComputerUtilCombat.canAttackNextTurn(card)
|| !canBeBlocked) { && canBeBlocked;
return false;
}
} else if (keyword.equals("Haste")) { } else if (keyword.equals("Haste")) {
if (!card.hasSickness() || !ph.isPlayerTurn(sa.getActivatingPlayer()) || card.isTapped() return card.hasSickness() && ph.isPlayerTurn(sa.getActivatingPlayer()) && !card.isTapped()
|| card.getNetCombatDamage() + powerBonus <= 0 && card.getNetCombatDamage() + powerBonus > 0
|| card.hasKeyword("CARDNAME can attack as though it had haste.") && !card.hasKeyword("CARDNAME can attack as though it had haste.")
|| ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS) && !ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS)
|| !ComputerUtilCombat.canAttackNextTurn(card)) { && ComputerUtilCombat.canAttackNextTurn(card);
return false;
}
} else if (keyword.endsWith("Indestructible")) { } else if (keyword.endsWith("Indestructible")) {
return true; return true;
} else if (keyword.endsWith("Deathtouch") || keyword.endsWith("Wither")) { } else if (keyword.endsWith("Deathtouch") || keyword.endsWith("Wither")) {
if (card.getNetCombatDamage() + powerBonus <= 0 return card.getNetCombatDamage() + powerBonus > 0
|| ((!canBeBlocked || !ComputerUtilCombat.canAttackNextTurn(card)) && ((canBeBlocked && ComputerUtilCombat.canAttackNextTurn(card))
&& !CombatUtil.canBlock(card, true))) { || CombatUtil.canBlock(card, true));
return false;
}
} else if (keyword.equals("Double Strike") || keyword.equals("Lifelink")) { } else if (keyword.equals("Double Strike") || keyword.equals("Lifelink")) {
if (card.getNetCombatDamage() + powerBonus <= 0 return card.getNetCombatDamage() + powerBonus > 0
|| (!ComputerUtilCombat.canAttackNextTurn(card) && !CombatUtil.canBlock(card, true))) { && (ComputerUtilCombat.canAttackNextTurn(card) || CombatUtil.canBlock(card, true));
return false;
}
} else if (keyword.equals("First Strike")) { } else if (keyword.equals("First Strike")) {
if (card.getNetCombatDamage() + powerBonus <= 0 || card.hasKeyword(Keyword.DOUBLE_STRIKE) return card.getNetCombatDamage() + powerBonus > 0 && !card.hasKeyword(Keyword.DOUBLE_STRIKE)
|| (!ComputerUtilCombat.canAttackNextTurn(card) && !CombatUtil.canBlock(card, true))) { && (ComputerUtilCombat.canAttackNextTurn(card) || CombatUtil.canBlock(card, true));
return false;
}
} else if (keyword.startsWith("Flanking")) { } else if (keyword.startsWith("Flanking")) {
if (card.getNetCombatDamage() + powerBonus <= 0 return card.getNetCombatDamage() + powerBonus > 0
|| !ComputerUtilCombat.canAttackNextTurn(card) && ComputerUtilCombat.canAttackNextTurn(card)
|| !canBeBlocked) { && canBeBlocked;
return false;
}
} else if (keyword.startsWith("Bushido")) { } else if (keyword.startsWith("Bushido")) {
if ((!canBeBlocked || !ComputerUtilCombat.canAttackNextTurn(card)) return (canBeBlocked && ComputerUtilCombat.canAttackNextTurn(card))
&& !CombatUtil.canBlock(card, true)) { || CombatUtil.canBlock(card, true);
return false;
}
} else if (keyword.equals("Trample")) { } else if (keyword.equals("Trample")) {
if (card.getNetCombatDamage() + powerBonus <= 1 return card.getNetCombatDamage() + powerBonus > 1
|| !canBeBlocked && canBeBlocked
|| !ComputerUtilCombat.canAttackNextTurn(card)) { && ComputerUtilCombat.canAttackNextTurn(card);
return false;
}
} else if (keyword.equals("Infect")) { } else if (keyword.equals("Infect")) {
if (card.getNetCombatDamage() + powerBonus <= 0 return card.getNetCombatDamage() + powerBonus > 0
|| !ComputerUtilCombat.canAttackNextTurn(card)) { && ComputerUtilCombat.canAttackNextTurn(card);
return false;
}
} else if (keyword.equals("Vigilance")) { } else if (keyword.equals("Vigilance")) {
if (card.getNetCombatDamage() + powerBonus <= 0 return card.getNetCombatDamage() + powerBonus > 0
|| !ComputerUtilCombat.canAttackNextTurn(card) && ComputerUtilCombat.canAttackNextTurn(card)
|| !CombatUtil.canBlock(card, true)) { && CombatUtil.canBlock(card, true);
return false;
}
} else if (keyword.equals("Reach")) { } else if (keyword.equals("Reach")) {
if (card.hasKeyword(Keyword.FLYING) || !CombatUtil.canBlock(card, true)) { return !card.hasKeyword(Keyword.FLYING) && CombatUtil.canBlock(card, true);
return false;
}
} else if (keyword.endsWith("CARDNAME can block an additional creature each combat.")) { } else if (keyword.endsWith("CARDNAME can block an additional creature each combat.")) {
if (!CombatUtil.canBlock(card, true) || card.hasKeyword("CARDNAME can block any number of creatures.") return CombatUtil.canBlock(card, true) && !card.hasKeyword("CARDNAME can block any number of creatures.")
|| card.hasKeyword("CARDNAME can block an additional ninety-nine creatures each combat.")) { && !card.hasKeyword("CARDNAME can block an additional ninety-nine creatures each combat.");
return false;
}
} else if (keyword.equals("CARDNAME can attack as though it didn't have defender.")) { } else if (keyword.equals("CARDNAME can attack as though it didn't have defender.")) {
if (!card.hasKeyword(Keyword.DEFENDER) || card.getNetCombatDamage() + powerBonus <= 0) { return card.hasKeyword(Keyword.DEFENDER) && card.getNetCombatDamage() + powerBonus > 0;
return false;
}
} else if (keyword.equals("Shroud") || keyword.equals("Hexproof")) { } else if (keyword.equals("Shroud") || keyword.equals("Hexproof")) {
if (card.hasKeyword(Keyword.SHROUD) || card.hasKeyword(Keyword.HEXPROOF)) { return !card.hasKeyword(Keyword.SHROUD) && !card.hasKeyword(Keyword.HEXPROOF);
return false; } else return !keyword.equals("Defender");
}
} else if (keyword.equals("Defender")) {
return false;
}
return true;
} }
/** /**
@@ -1657,17 +1620,11 @@ public class AttachAi extends SpellAbilityAi {
if (keyword.endsWith("CARDNAME can't attack.") || keyword.equals("Defender") if (keyword.endsWith("CARDNAME can't attack.") || keyword.equals("Defender")
|| keyword.endsWith("CARDNAME can't attack or block.")) { || keyword.endsWith("CARDNAME can't attack or block.")) {
if (!ComputerUtilCombat.canAttackNextTurn(card) || card.getNetCombatDamage() < 1) { return ComputerUtilCombat.canAttackNextTurn(card) && card.getNetCombatDamage() >= 1;
return false;
}
} else if (keyword.endsWith("CARDNAME attacks each turn if able.") || keyword.endsWith("CARDNAME attacks each combat if able.")) { } else if (keyword.endsWith("CARDNAME attacks each turn if able.") || keyword.endsWith("CARDNAME attacks each combat if able.")) {
if (!ComputerUtilCombat.canAttackNextTurn(card) || !CombatUtil.canBlock(card, true) || ai.getCreaturesInPlay().isEmpty()) { return ComputerUtilCombat.canAttackNextTurn(card) && CombatUtil.canBlock(card, true) && !ai.getCreaturesInPlay().isEmpty();
return false;
}
} else if (keyword.endsWith("CARDNAME can't block.") || keyword.contains("CantBlock")) { } else if (keyword.endsWith("CARDNAME can't block.") || keyword.contains("CantBlock")) {
if (!CombatUtil.canBlock(card, true)) { return CombatUtil.canBlock(card, true);
return false;
}
} else if (keyword.endsWith("CARDNAME's activated abilities can't be activated.")) { } else if (keyword.endsWith("CARDNAME's activated abilities can't be activated.")) {
for (SpellAbility ability : card.getSpellAbilities()) { for (SpellAbility ability : card.getSpellAbilities()) {
if (ability.isAbility()) { if (ability.isAbility()) {
@@ -1676,18 +1633,12 @@ public class AttachAi extends SpellAbilityAi {
} }
return false; return false;
} else if (keyword.endsWith("Prevent all combat damage that would be dealt by CARDNAME.")) { } else if (keyword.endsWith("Prevent all combat damage that would be dealt by CARDNAME.")) {
if (!ComputerUtilCombat.canAttackNextTurn(card) || card.getNetCombatDamage() < 1) { return ComputerUtilCombat.canAttackNextTurn(card) && card.getNetCombatDamage() >= 1;
return false;
}
} else if (keyword.endsWith("Prevent all combat damage that would be dealt to and dealt by CARDNAME.") } else if (keyword.endsWith("Prevent all combat damage that would be dealt to and dealt by CARDNAME.")
|| keyword.endsWith("Prevent all damage that would be dealt to and dealt by CARDNAME.")) { || keyword.endsWith("Prevent all damage that would be dealt to and dealt by CARDNAME.")) {
if (!ComputerUtilCombat.canAttackNextTurn(card) || card.getNetCombatDamage() < 2) { return ComputerUtilCombat.canAttackNextTurn(card) && card.getNetCombatDamage() >= 2;
return false;
}
} else if (keyword.endsWith("CARDNAME doesn't untap during your untap step.")) { } else if (keyword.endsWith("CARDNAME doesn't untap during your untap step.")) {
if (card.isUntapped()) { return !card.isUntapped();
return false;
}
} }
return true; return true;
} }
@@ -1711,12 +1662,8 @@ public class AttachAi extends SpellAbilityAi {
return true; return true;
} }
if (sa.getHostCard().isEquipment() && ComputerUtilCard.isUselessCreature(ai, c)) { // useless to equip a creature that can't attack or block.
// useless to equip a creature that can't attack or block. return !sa.getHostCard().isEquipment() || !ComputerUtilCard.isUselessCreature(ai, c);
return false;
}
return true;
} }
public static Card doPumpOrCurseAILogic(final Player ai, final SpellAbility sa, final List<Card> list, final String type) { public static Card doPumpOrCurseAILogic(final Player ai, final SpellAbility sa, final List<Card> list, final String type) {

View File

@@ -355,9 +355,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
@Override @Override
public boolean apply(final Card c) { public boolean apply(final Card c) {
if (c.getType().isLegendary()) { if (c.getType().isLegendary()) {
if (ai.isCardInPlay(c.getName())) { return !ai.isCardInPlay(c.getName());
return false;
}
} }
return true; return true;
} }
@@ -738,11 +736,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
} }
final AbilitySub subAb = sa.getSubAbility(); final AbilitySub subAb = sa.getSubAbility();
if (subAb != null && !SpellApiToAi.Converter.get(subAb.getApi()).chkDrawbackWithSubs(ai, subAb)) { return subAb == null || SpellApiToAi.Converter.get(subAb.getApi()).chkDrawbackWithSubs(ai, subAb);
return false;
}
return true;
} }
/* /*
@@ -864,7 +858,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
list = CardLists.getTargetableCards(list, sa); list = CardLists.getTargetableCards(list, sa);
// Filter AI-specific targets if provided // Filter AI-specific targets if provided
list = ComputerUtil.filterAITgts(sa, ai, (CardCollection)list, true); list = ComputerUtil.filterAITgts(sa, ai, list, true);
if (sa.hasParam("AITgtsOnlyBetterThanSelf")) { if (sa.hasParam("AITgtsOnlyBetterThanSelf")) {
list = CardLists.filter(list, new Predicate<Card>() { list = CardLists.filter(list, new Predicate<Card>() {
@Override @Override
@@ -995,11 +989,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
@Override @Override
public boolean apply(final Card c) { public boolean apply(final Card c) {
for (Card aura : c.getEnchantedBy()) { for (Card aura : c.getEnchantedBy()) {
if (aura.getController().isOpponentOf(ai)) { return aura.getController().isOpponentOf(ai);
return true;
} else {
return false;
}
} }
if (blink) { if (blink) {
return c.isToken(); return c.isToken();
@@ -1471,16 +1461,12 @@ public class ChangeZoneAi extends SpellAbilityAi {
if (!list.isEmpty()) { if (!list.isEmpty()) {
final Card attachedTo = list.get(0); final Card attachedTo = list.get(0);
// This code is for the Dragon auras // This code is for the Dragon auras
if (attachedTo.getController().isOpponentOf(ai)) { return !attachedTo.getController().isOpponentOf(ai);
return false;
}
} }
} }
} else if (isPreferredTarget(ai, sa, mandatory, true)) { } else if (isPreferredTarget(ai, sa, mandatory, true)) {
// do nothing // do nothing
} else if (!isUnpreferredTarget(ai, sa, mandatory)) { } else return isUnpreferredTarget(ai, sa, mandatory);
return false;
}
return true; return true;
} }
@@ -1532,9 +1518,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
@Override @Override
public boolean apply(final Card c) { public boolean apply(final Card c) {
if (c.getType().isLegendary()) { if (c.getType().isLegendary()) {
if (decider.isCardInPlay(c.getName())) { return !decider.isCardInPlay(c.getName());
return false;
}
} }
return true; return true;
} }
@@ -1543,10 +1527,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
fetchList = CardLists.filter(fetchList, new Predicate<Card>() { fetchList = CardLists.filter(fetchList, new Predicate<Card>() {
@Override @Override
public boolean apply(final Card c) { public boolean apply(final Card c) {
if (ComputerUtilCard.isCardRemAIDeck(c) || ComputerUtilCard.isCardRemRandomDeck(c)) { return !ComputerUtilCard.isCardRemAIDeck(c) && !ComputerUtilCard.isCardRemRandomDeck(c);
return false;
}
return true;
} }
}); });
} }
@@ -1718,9 +1699,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
@Override @Override
public boolean apply(final Card c) { public boolean apply(final Card c) {
if (c.getType().isLegendary()) { if (c.getType().isLegendary()) {
if (ai.isCardInPlay(c.getName())) { return !ai.isCardInPlay(c.getName());
return false;
}
} }
return true; return true;
} }
@@ -1826,20 +1805,16 @@ public class ChangeZoneAi extends SpellAbilityAi {
&& "Battlefield".equals(causeSub.getParam("Destination"))) { && "Battlefield".equals(causeSub.getParam("Destination"))) {
// A blink effect implemented using ChangeZone API // A blink effect implemented using ChangeZone API
return false; return false;
} else if (subApi == ApiType.DelayedTrigger) { } else // This is an intrinsic effect that blinks the card (e.g. Obzedat, Ghost Council), no need to
// return the commander to the Command zone.
if (subApi == ApiType.DelayedTrigger) {
SpellAbility exec = causeSub.getAdditionalAbility("Execute"); SpellAbility exec = causeSub.getAdditionalAbility("Execute");
if (exec != null && exec.getApi() == ApiType.ChangeZone) { if (exec != null && exec.getApi() == ApiType.ChangeZone) {
if ("Exile".equals(exec.getParam("Origin")) && "Battlefield".equals(exec.getParam("Destination"))) { // A blink effect implemented using a delayed trigger
// A blink effect implemented using a delayed trigger return !"Exile".equals(exec.getParam("Origin")) || !"Battlefield".equals(exec.getParam("Destination"));
return false;
}
} }
} else if (causeSa.getHostCard() != null && causeSa.getHostCard().equals((Card)sa.getReplacingObject("Card")) } else return causeSa.getHostCard() == null || !causeSa.getHostCard().equals(sa.getReplacingObject("Card"))
&& causeSa.getActivatingPlayer().equals(aiPlayer)) { || !causeSa.getActivatingPlayer().equals(aiPlayer);
// This is an intrinsic effect that blinks the card (e.g. Obzedat, Ghost Council), no need to
// return the commander to the Command zone.
return false;
}
} }
// Normally we want the commander back in Command zone to recast him later // Normally we want the commander back in Command zone to recast him later

View File

@@ -335,11 +335,8 @@ public class ChangeZoneAllAi extends SpellAbilityAi {
return true; return true;
// if AI creature is better than Human Creature // if AI creature is better than Human Creature
if (ComputerUtilCard.evaluateCreatureList(aiCards) >= ComputerUtilCard return ComputerUtilCard.evaluateCreatureList(aiCards) >= ComputerUtilCard
.evaluateCreatureList(humanCards)) { .evaluateCreatureList(humanCards);
return true;
}
return false;
} }
return true; return true;
} }
@@ -441,29 +438,21 @@ public class ChangeZoneAllAi extends SpellAbilityAi {
if (sa.getParam("GainControl") != null) { if (sa.getParam("GainControl") != null) {
// Check if the cards are valuable enough // Check if the cards are valuable enough
if ((CardLists.getNotType(humanType, "Creature").size() == 0) && (CardLists.getNotType(computerType, "Creature").size() == 0)) { if ((CardLists.getNotType(humanType, "Creature").size() == 0) && (CardLists.getNotType(computerType, "Creature").size() == 0)) {
if ((ComputerUtilCard.evaluateCreatureList(computerType) + ComputerUtilCard return (ComputerUtilCard.evaluateCreatureList(computerType) + ComputerUtilCard
.evaluateCreatureList(humanType)) < 1) { .evaluateCreatureList(humanType)) >= 1;
return false;
}
} // otherwise evaluate both lists by CMC and pass only if human } // otherwise evaluate both lists by CMC and pass only if human
// permanents are less valuable // permanents are less valuable
else if ((ComputerUtilCard.evaluatePermanentList(computerType) + ComputerUtilCard else return (ComputerUtilCard.evaluatePermanentList(computerType) + ComputerUtilCard
.evaluatePermanentList(humanType)) < 1) { .evaluatePermanentList(humanType)) >= 1;
return false;
}
} else { } else {
// don't activate if human gets more back than AI does // don't activate if human gets more back than AI does
if ((CardLists.getNotType(humanType, "Creature").isEmpty()) && (CardLists.getNotType(computerType, "Creature").isEmpty())) { if ((CardLists.getNotType(humanType, "Creature").isEmpty()) && (CardLists.getNotType(computerType, "Creature").isEmpty())) {
if (ComputerUtilCard.evaluateCreatureList(computerType) <= ComputerUtilCard return ComputerUtilCard.evaluateCreatureList(computerType) > ComputerUtilCard
.evaluateCreatureList(humanType)) { .evaluateCreatureList(humanType);
return false;
}
} // otherwise evaluate both lists by CMC and pass only if human } // otherwise evaluate both lists by CMC and pass only if human
// permanents are less valuable // permanents are less valuable
else if (ComputerUtilCard.evaluatePermanentList(computerType) <= ComputerUtilCard else return ComputerUtilCard.evaluatePermanentList(computerType) > ComputerUtilCard
.evaluatePermanentList(humanType)) { .evaluatePermanentList(humanType);
return false;
}
} }
} }

View File

@@ -71,21 +71,15 @@ public class ChooseCardAi extends SpellAbilityAi {
choices = CardLists.filterControlledBy(choices, ai.getOpponents()); choices = CardLists.filterControlledBy(choices, ai.getOpponents());
} }
if (aiLogic.equals("AtLeast1") || aiLogic.equals("OppPreferred")) { if (aiLogic.equals("AtLeast1") || aiLogic.equals("OppPreferred")) {
if (choices.isEmpty()) { return !choices.isEmpty();
return false;
}
} else if (aiLogic.equals("AtLeast2") || aiLogic.equals("BestBlocker")) { } else if (aiLogic.equals("AtLeast2") || aiLogic.equals("BestBlocker")) {
if (choices.size() < 2) { return choices.size() >= 2;
return false;
}
} else if (aiLogic.equals("Clone") || aiLogic.equals("Vesuva")) { } else if (aiLogic.equals("Clone") || aiLogic.equals("Vesuva")) {
final String filter = aiLogic.equals("Clone") ? "Permanent.YouDontCtrl,Permanent.nonLegendary" final String filter = aiLogic.equals("Clone") ? "Permanent.YouDontCtrl,Permanent.nonLegendary"
: "Permanent.YouDontCtrl+notnamedVesuva,Permanent.nonLegendary+notnamedVesuva"; : "Permanent.YouDontCtrl+notnamedVesuva,Permanent.nonLegendary+notnamedVesuva";
choices = CardLists.getValidCards(choices, filter, host.getController(), host); choices = CardLists.getValidCards(choices, filter, host.getController(), host);
if (choices.isEmpty()) { return !choices.isEmpty();
return false;
}
} else if (aiLogic.equals("Never")) { } else if (aiLogic.equals("Never")) {
return false; return false;
} else if (aiLogic.equals("NeedsPrevention")) { } else if (aiLogic.equals("NeedsPrevention")) {
@@ -103,9 +97,7 @@ public class ChooseCardAi extends SpellAbilityAi {
return ComputerUtilCombat.damageIfUnblocked(c, ai, combat, true) > ref; return ComputerUtilCombat.damageIfUnblocked(c, ai, combat, true) > ref;
} }
}); });
if (choices.isEmpty()) { return !choices.isEmpty();
return false;
}
} else if (aiLogic.equals("Ashiok")) { } else if (aiLogic.equals("Ashiok")) {
final int loyalty = host.getCounters(CounterType.LOYALTY) - 1; final int loyalty = host.getCounters(CounterType.LOYALTY) - 1;
for (int i = loyalty; i >= 0; i--) { for (int i = loyalty; i >= 0; i--) {
@@ -117,13 +109,9 @@ public class ChooseCardAi extends SpellAbilityAi {
} }
} }
if (choices.isEmpty()) { return !choices.isEmpty();
return false;
}
} else if (aiLogic.equals("RandomNonLand")) { } else if (aiLogic.equals("RandomNonLand")) {
if (CardLists.getValidCards(choices, "Card.nonLand", host.getController(), host).isEmpty()) { return !CardLists.getValidCards(choices, "Card.nonLand", host.getController(), host).isEmpty();
return false;
}
} else if (aiLogic.equals("Duneblast")) { } else if (aiLogic.equals("Duneblast")) {
CardCollection aiCreatures = ai.getCreaturesInPlay(); CardCollection aiCreatures = ai.getCreaturesInPlay();
CardCollection oppCreatures = ai.getWeakestOpponent().getCreaturesInPlay(); CardCollection oppCreatures = ai.getWeakestOpponent().getCreaturesInPlay();
@@ -139,10 +127,8 @@ public class ChooseCardAi extends SpellAbilityAi {
aiCreatures.remove(chosen); aiCreatures.remove(chosen);
int minGain = 200; int minGain = 200;
if ((ComputerUtilCard.evaluateCreatureList(aiCreatures) + minGain) >= ComputerUtilCard return (ComputerUtilCard.evaluateCreatureList(aiCreatures) + minGain) < ComputerUtilCard
.evaluateCreatureList(oppCreatures)) { .evaluateCreatureList(oppCreatures);
return false;
}
} }
return true; return true;
} }

View File

@@ -52,10 +52,7 @@ public class ChooseColorAi extends SpellAbilityAi {
} }
if ("Addle".equals(sourceName)) { if ("Addle".equals(sourceName)) {
if (ph.getPhase().isBefore(PhaseType.COMBAT_DECLARE_ATTACKERS) || ai.getWeakestOpponent().getCardsIn(ZoneType.Hand).isEmpty()) { return !ph.getPhase().isBefore(PhaseType.COMBAT_DECLARE_ATTACKERS) && !ai.getWeakestOpponent().getCardsIn(ZoneType.Hand).isEmpty();
return false;
}
return true;
} }
if (logic.equals("MostExcessOpponentControls")) { if (logic.equals("MostExcessOpponentControls")) {

View File

@@ -33,9 +33,7 @@ public class ChooseDirectionAi extends SpellAbilityAi {
CardCollection right = CardLists.filterControlledBy(all, game.getNextPlayerAfter(ai, Direction.Right)); CardCollection right = CardLists.filterControlledBy(all, game.getNextPlayerAfter(ai, Direction.Right));
int leftValue = Aggregates.sum(left, CardPredicates.Accessors.fnGetCmc); int leftValue = Aggregates.sum(left, CardPredicates.Accessors.fnGetCmc);
int rightValue = Aggregates.sum(right, CardPredicates.Accessors.fnGetCmc); int rightValue = Aggregates.sum(right, CardPredicates.Accessors.fnGetCmc);
if (aiValue > leftValue || aiValue > rightValue) { return aiValue <= leftValue && aiValue <= rightValue;
return false;
}
} }
} }
return true; return true;

View File

@@ -385,9 +385,7 @@ public class ChooseGenericEffectAi extends SpellAbilityAi {
final Player opp = player.getWeakestOpponent(); final Player opp = player.getWeakestOpponent();
if (opp != null) { if (opp != null) {
// TODO add predict Combat Damage? // TODO add predict Combat Damage?
if (opp.getLife() < copy.getNetPower()) { return opp.getLife() < copy.getNetPower();
return true;
}
} }
// haste might not be good enough? // haste might not be good enough?

View File

@@ -97,10 +97,7 @@ public class ChooseSourceAi extends SpellAbilityAi {
return false; return false;
} }
int dmg = AbilityUtils.calculateAmount(threatSource, topStack.getParam("NumDmg"), topStack); int dmg = AbilityUtils.calculateAmount(threatSource, topStack.getParam("NumDmg"), topStack);
if (ComputerUtilCombat.predictDamageTo(ai, dmg, threatSource, false) <= 0) { return ComputerUtilCombat.predictDamageTo(ai, dmg, threatSource, false) > 0;
return false;
}
return true;
} }
if (game.getPhaseHandler().getPhase() != PhaseType.COMBAT_DECLARE_BLOCKERS) { if (game.getPhaseHandler().getPhase() != PhaseType.COMBAT_DECLARE_BLOCKERS) {
return false; return false;
@@ -119,9 +116,7 @@ public class ChooseSourceAi extends SpellAbilityAi {
return ComputerUtilCombat.damageIfUnblocked(c, ai, combat, true) > 0; return ComputerUtilCombat.damageIfUnblocked(c, ai, combat, true) > 0;
} }
}); });
if (choices.isEmpty()) { return !choices.isEmpty();
return false;
}
} }
} }

View File

@@ -244,9 +244,6 @@ public class CloneAi extends SpellAbilityAi {
} }
// don't activate during main2 unless this effect is permanent // don't activate during main2 unless this effect is permanent
if (ph.is(PhaseType.MAIN2) && !sa.hasParam("Permanent")) { return !ph.is(PhaseType.MAIN2) || sa.hasParam("Permanent");
return false;
}
return true;
} }
} }

View File

@@ -84,9 +84,7 @@ public class ControlGainAi extends SpellAbilityAi {
if (sa.hasParam("AllValid")) { if (sa.hasParam("AllValid")) {
CardCollectionView tgtCards = CardLists.filterControlledBy(game.getCardsIn(ZoneType.Battlefield), opponents); CardCollectionView tgtCards = CardLists.filterControlledBy(game.getCardsIn(ZoneType.Battlefield), opponents);
tgtCards = AbilityUtils.filterListByType(tgtCards, sa.getParam("AllValid"), sa); tgtCards = AbilityUtils.filterListByType(tgtCards, sa.getParam("AllValid"), sa);
if (tgtCards.isEmpty()) { return !tgtCards.isEmpty();
return false;
}
} }
return true; return true;
} else { } else {
@@ -247,7 +245,7 @@ public class ControlGainAi extends SpellAbilityAi {
break; break;
} }
} }
}; }
if (t != null) { if (t != null) {
sa.getTargets().add(t); sa.getTargets().add(t);
@@ -296,15 +294,12 @@ public class ControlGainAi extends SpellAbilityAi {
lose.addAll(Lists.newArrayList(sa.getParam("LoseControl").split(","))); lose.addAll(Lists.newArrayList(sa.getParam("LoseControl").split(",")));
} }
if (lose.contains("EOT") return !lose.contains("EOT")
&& game.getPhaseHandler().getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS)) { || !game.getPhaseHandler().getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS);
return false;
}
} else { } else {
return this.canPlayAI(ai, sa); return this.canPlayAI(ai, sa);
} }
return true;
} // pumpDrawbackAI() } // pumpDrawbackAI()
@Override @Override

View File

@@ -90,9 +90,7 @@ public class CountersMoveAi extends SpellAbilityAi {
} }
// for Simic Fluxmage and other // for Simic Fluxmage and other
if (!ph.getNextTurn().equals(ai) || ph.getPhase().isBefore(PhaseType.END_OF_TURN)) { return ph.getNextTurn().equals(ai) && !ph.getPhase().isBefore(PhaseType.END_OF_TURN);
return false;
}
} else if (CounterType.P1P1.equals(cType) && sa.hasParam("Defined")) { } else if (CounterType.P1P1.equals(cType) && sa.hasParam("Defined")) {
// something like Cyptoplast Root-kin // something like Cyptoplast Root-kin
@@ -107,9 +105,7 @@ public class CountersMoveAi extends SpellAbilityAi {
} }
// Make sure that removing the last counter doesn't kill the creature // Make sure that removing the last counter doesn't kill the creature
if ("Self".equals(sa.getParam("Source"))) { if ("Self".equals(sa.getParam("Source"))) {
if (host != null && host.getNetToughness() - 1 <= 0) { return host == null || host.getNetToughness() - 1 > 0;
return false;
}
} }
} }
return true; return true;
@@ -193,9 +189,7 @@ public class CountersMoveAi extends SpellAbilityAi {
// check for some specific AI preferences // check for some specific AI preferences
if ("DontMoveCounterIfLethal".equals(sa.getParam("AILogic"))) { if ("DontMoveCounterIfLethal".equals(sa.getParam("AILogic"))) {
if (cType == CounterType.P1P1 && src.getNetToughness() - src.getTempToughnessBoost() - 1 <= 0) { return cType != CounterType.P1P1 || src.getNetToughness() - src.getTempToughnessBoost() - 1 > 0;
return false;
}
} }
} }
// no target // no target
@@ -207,9 +201,7 @@ public class CountersMoveAi extends SpellAbilityAi {
public boolean chkAIDrawback(SpellAbility sa, Player ai) { public boolean chkAIDrawback(SpellAbility sa, Player ai) {
if (sa.usesTargeting()) { if (sa.usesTargeting()) {
sa.resetTargets(); sa.resetTargets();
if (!moveTgtAI(ai, sa)) { return moveTgtAI(ai, sa);
return false;
}
} }
return true; return true;
@@ -287,10 +279,7 @@ public class CountersMoveAi extends SpellAbilityAi {
// do not steal a P1P1 from Undying if it would die // do not steal a P1P1 from Undying if it would die
// this way // this way
if (CounterType.P1P1.equals(cType) && srcCardCpy.getNetToughness() <= 0) { if (CounterType.P1P1.equals(cType) && srcCardCpy.getNetToughness() <= 0) {
if (srcCardCpy.getCounters(cType) > 0 || !card.hasKeyword(Keyword.UNDYING) || card.isToken()) { return srcCardCpy.getCounters(cType) > 0 || !card.hasKeyword(Keyword.UNDYING) || card.isToken();
return true;
}
return false;
} }
return true; return true;
} }
@@ -338,11 +327,7 @@ public class CountersMoveAi extends SpellAbilityAi {
return true; return true;
} }
} }
if (CounterType.M1M1.equals(cType) && card.hasKeyword(Keyword.PERSIST)) { return CounterType.M1M1.equals(cType) && card.hasKeyword(Keyword.PERSIST);
return true;
}
return false;
} }
}); });
@@ -436,11 +421,7 @@ public class CountersMoveAi extends SpellAbilityAi {
} }
// source would leave the game // source would leave the game
if (!card.hasSVar("EndOfTurnLeavePlay")) { return !card.hasSVar("EndOfTurnLeavePlay");
return true;
}
return false;
} }
}); });

View File

@@ -45,9 +45,7 @@ public class CountersMultiplyAi extends SpellAbilityAi {
if (c.getCounters(counterType) <= 0) { if (c.getCounters(counterType) <= 0) {
return false; return false;
} }
if (!c.canReceiveCounters(counterType)) { return c.canReceiveCounters(counterType);
return false;
}
} else { } else {
for (Map.Entry<CounterType, Integer> e : c.getCounters().entrySet()) { for (Map.Entry<CounterType, Integer> e : c.getCounters().entrySet()) {
// has negative counter it would double // has negative counter it would double
@@ -87,19 +85,12 @@ public class CountersMultiplyAi extends SpellAbilityAi {
} }
} }
} }
if (ComputerUtil.waitForBlocking(sa)) { return !ComputerUtil.waitForBlocking(sa);
return false;
}
return true;
} }
@Override @Override
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) { protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
if (sa.usesTargeting() && !setTargets(ai, sa) && !mandatory) { return !sa.usesTargeting() || setTargets(ai, sa) || mandatory;
return false;
}
return true;
} }
private CounterType getCounterType(SpellAbility sa) { private CounterType getCounterType(SpellAbility sa) {
@@ -135,9 +126,7 @@ public class CountersMultiplyAi extends SpellAbilityAi {
if (c.getCounters(counterType) <= 0) { if (c.getCounters(counterType) <= 0) {
return false; return false;
} }
if (!c.canReceiveCounters(counterType)) { return c.canReceiveCounters(counterType);
return false;
}
} }
return true; return true;

View File

@@ -83,12 +83,9 @@ public class CountersProliferateAi extends SpellAbilityAi {
} }
})); }));
} }
if (cperms.isEmpty() && hperms.isEmpty() && !opponentPoison && !allyExpOrEnergy) {
return false; return !cperms.isEmpty() || !hperms.isEmpty() || opponentPoison || allyExpOrEnergy;
}
return true;
} }
@Override @Override

View File

@@ -469,7 +469,7 @@ public class CountersPutAi extends SpellAbilityAi {
int left = amount; int left = amount;
for (Card c : list) { for (Card c : list) {
if (ComputerUtilCard.shouldPumpCard(ai, sa, c, i, i, if (ComputerUtilCard.shouldPumpCard(ai, sa, c, i, i,
Lists.<String>newArrayList())) { Lists.newArrayList())) {
sa.getTargets().add(c); sa.getTargets().add(c);
abTgt.addDividedAllocation(c, i); abTgt.addDividedAllocation(c, i);
left -= i; left -= i;
@@ -506,7 +506,7 @@ public class CountersPutAi extends SpellAbilityAi {
if (type.equals("P1P1") && !SpellAbilityAi.isSorcerySpeed(sa)) { if (type.equals("P1P1") && !SpellAbilityAi.isSorcerySpeed(sa)) {
for (Card c : list) { for (Card c : list) {
if (ComputerUtilCard.shouldPumpCard(ai, sa, c, amount, amount, if (ComputerUtilCard.shouldPumpCard(ai, sa, c, amount, amount,
Lists.<String>newArrayList())) { Lists.newArrayList())) {
choice = c; choice = c;
break; break;
} }
@@ -592,11 +592,7 @@ public class CountersPutAi extends SpellAbilityAi {
} }
} }
if (ComputerUtil.waitForBlocking(sa)) { return !ComputerUtil.waitForBlocking(sa);
return false;
}
return true;
} }
@Override @Override
@@ -1071,10 +1067,8 @@ public class CountersPutAi extends SpellAbilityAi {
} }
int totBlkPower = Aggregates.sum(combat.getBlockers(source), CardPredicates.Accessors.fnGetNetPower); int totBlkPower = Aggregates.sum(combat.getBlockers(source), CardPredicates.Accessors.fnGetNetPower);
if (source.getNetToughness() <= totBlkPower return source.getNetToughness() <= totBlkPower
&& source.getNetToughness() + amount > totBlkPower) { && source.getNetToughness() + amount > totBlkPower;
return true;
}
} }
} else if (combat.isBlocking(source)) { } else if (combat.isBlocking(source)) {
for (Card blocked : combat.getAttackersBlockedBy(source)) { for (Card blocked : combat.getAttackersBlockedBy(source)) {
@@ -1085,10 +1079,8 @@ public class CountersPutAi extends SpellAbilityAi {
} }
int totAtkPower = Aggregates.sum(combat.getAttackersBlockedBy(source), CardPredicates.Accessors.fnGetNetPower); int totAtkPower = Aggregates.sum(combat.getAttackersBlockedBy(source), CardPredicates.Accessors.fnGetNetPower);
if (source.getNetToughness() <= totAtkPower return source.getNetToughness() <= totAtkPower
&& source.getNetToughness() + amount > totAtkPower) { && source.getNetToughness() + amount > totAtkPower;
return true;
}
} }
return false; return false;
} }

View File

@@ -101,7 +101,7 @@ public class CountersRemoveAi extends SpellAbilityAi {
} }
// Filter AI-specific targets if provided // Filter AI-specific targets if provided
list = ComputerUtil.filterAITgts(sa, ai, (CardCollection)list, false); list = ComputerUtil.filterAITgts(sa, ai, list, false);
boolean noLegendary = game.getStaticEffects().getGlobalRuleChange(GlobalRuleChange.noLegendRule); boolean noLegendary = game.getStaticEffects().getGlobalRuleChange(GlobalRuleChange.noLegendRule);

View File

@@ -37,9 +37,7 @@ public abstract class DamageAiBase extends SpellAbilityAi {
} }
if ("SelfDamage".equals(sa.getParam("AILogic"))) { if ("SelfDamage".equals(sa.getParam("AILogic"))) {
if (comp.getLife() * 0.75 < enemy.getLife()) { if (comp.getLife() * 0.75 < enemy.getLife()) {
if (!lifelink) { return !lifelink;
return true;
}
} }
} }
return false; return false;

View File

@@ -233,12 +233,8 @@ public class DamageAllAi extends SpellAbilityAi {
return true; return true;
} }
if (!computerList.isEmpty() && ComputerUtilCard.evaluateCreatureList(computerList) > ComputerUtilCard return computerList.isEmpty() || ComputerUtilCard.evaluateCreatureList(computerList) <= ComputerUtilCard
.evaluateCreatureList(humanList)) { .evaluateCreatureList(humanList);
return false;
}
return true;
} }
/** /**
@@ -320,11 +316,7 @@ public class DamageAllAi extends SpellAbilityAi {
return true; return true;
} }
if (!computerList.isEmpty() && ComputerUtilCard.evaluateCreatureList(computerList) + 50 >= ComputerUtilCard return computerList.isEmpty() || ComputerUtilCard.evaluateCreatureList(computerList) + 50 < ComputerUtilCard
.evaluateCreatureList(humanList)) { .evaluateCreatureList(humanList);
return false;
}
return true;
} }
} }

View File

@@ -80,10 +80,7 @@ public class DamageDealAi extends DamageAiBase {
dmg--; // the card will be spent casting the spell, so actual damage is 1 less dmg--; // the card will be spent casting the spell, so actual damage is 1 less
} }
} }
if (!this.damageTargetAI(ai, sa, dmg, true)) { return this.damageTargetAI(ai, sa, dmg, true);
return false;
}
return true;
} }
@Override @Override
@@ -490,9 +487,7 @@ public class DamageDealAi extends DamageAiBase {
for (final Object o : objects) { for (final Object o : objects) {
if (o instanceof Card) { if (o instanceof Card) {
final Card c = (Card) o; final Card c = (Card) o;
if (hPlay.contains(c)) { hPlay.remove(c);
hPlay.remove(c);
}
} }
} }
hPlay = CardLists.getTargetableCards(hPlay, sa); hPlay = CardLists.getTargetableCards(hPlay, sa);
@@ -850,10 +845,7 @@ public class DamageDealAi extends DamageAiBase {
if (!positive && !(saMe instanceof AbilitySub)) { if (!positive && !(saMe instanceof AbilitySub)) {
return false; return false;
} }
if (!urgent && !SpellAbilityAi.playReusable(ai, saMe)) { return urgent || SpellAbilityAi.playReusable(ai, saMe);
return false;
}
return true;
} }
/** /**
@@ -952,9 +944,7 @@ public class DamageDealAi extends DamageAiBase {
final TargetRestrictions tgt = sa.getTargetRestrictions(); final TargetRestrictions tgt = sa.getTargetRestrictions();
if (tgt == null) { if (tgt == null) {
// If it's not mandatory check a few things // If it's not mandatory check a few things
if (!mandatory && !this.damageChooseNontargeted(ai, sa, dmg)) { return mandatory || this.damageChooseNontargeted(ai, sa, dmg);
return false;
}
} else { } else {
if (!this.damageChoosingTargets(ai, sa, tgt, dmg, mandatory, true) && !mandatory) { if (!this.damageChoosingTargets(ai, sa, tgt, dmg, mandatory, true) && !mandatory) {
return false; return false;

View File

@@ -124,7 +124,7 @@ public class DebuffAi extends SpellAbilityAi {
final TargetRestrictions tgt = sa.getTargetRestrictions(); final TargetRestrictions tgt = sa.getTargetRestrictions();
sa.resetTargets(); sa.resetTargets();
CardCollection list = getCurseCreatures(ai, sa, kws == null ? Lists.<String>newArrayList() : kws); CardCollection list = getCurseCreatures(ai, sa, kws == null ? Lists.newArrayList() : kws);
list = CardLists.getValidCards(list, tgt.getValidTgts(), sa.getActivatingPlayer(), sa.getHostCard(), sa); list = CardLists.getValidCards(list, tgt.getValidTgts(), sa.getActivatingPlayer(), sa.getHostCard(), sa);
// several uses here: // several uses here:

View File

@@ -27,7 +27,7 @@ public class DelayedTriggerAi extends SpellAbilityAi {
trigsa.setActivatingPlayer(ai); trigsa.setActivatingPlayer(ai);
if (trigsa instanceof AbilitySub) { if (trigsa instanceof AbilitySub) {
return SpellApiToAi.Converter.get(((AbilitySub) trigsa).getApi()).chkDrawbackWithSubs(ai, (AbilitySub)trigsa); return SpellApiToAi.Converter.get(trigsa.getApi()).chkDrawbackWithSubs(ai, (AbilitySub)trigsa);
} else { } else {
return AiPlayDecision.WillPlay == ((PlayerControllerAi)ai.getController()).getAi().canPlaySa(trigsa); return AiPlayDecision.WillPlay == ((PlayerControllerAi)ai.getController()).getAi().canPlaySa(trigsa);
} }

View File

@@ -48,7 +48,7 @@ public class DestroyAi extends SpellAbilityAi {
return false; return false;
} }
hasXCost = abCost.getCostMana() != null ? abCost.getCostMana().getAmountOfX() > 0 : false; hasXCost = abCost.getCostMana() != null && abCost.getCostMana().getAmountOfX() > 0;
} }
if ("AtOpponentsCombatOrAfter".equals(sa.getParam("AILogic"))) { if ("AtOpponentsCombatOrAfter".equals(sa.getParam("AILogic"))) {
@@ -132,7 +132,7 @@ public class DestroyAi extends SpellAbilityAi {
} }
// Filter AI-specific targets if provided // Filter AI-specific targets if provided
list = ComputerUtil.filterAITgts(sa, ai, (CardCollection)list, true); list = ComputerUtil.filterAITgts(sa, ai, list, true);
list = CardLists.getNotKeyword(list, Keyword.INDESTRUCTIBLE); list = CardLists.getNotKeyword(list, Keyword.INDESTRUCTIBLE);
if (CardLists.getNotType(list, "Creature").isEmpty()) { if (CardLists.getNotType(list, "Creature").isEmpty()) {
@@ -295,11 +295,9 @@ public class DestroyAi extends SpellAbilityAi {
return false; return false;
} }
if (list.isEmpty() return !list.isEmpty()
|| !CardLists.filterControlledBy(list, ai).isEmpty() && CardLists.filterControlledBy(list, ai).isEmpty()
|| CardLists.getNotKeyword(list, Keyword.INDESTRUCTIBLE).isEmpty()) { && !CardLists.getNotKeyword(list, Keyword.INDESTRUCTIBLE).isEmpty();
return false;
}
} }
return true; return true;
} }
@@ -342,7 +340,7 @@ public class DestroyAi extends SpellAbilityAi {
} }
// Filter AI-specific targets if provided // Filter AI-specific targets if provided
preferred = ComputerUtil.filterAITgts(sa, ai, (CardCollection)preferred, true); preferred = ComputerUtil.filterAITgts(sa, ai, preferred, true);
for (final Card c : preferred) { for (final Card c : preferred) {
list.remove(c); list.remove(c);
@@ -400,16 +398,11 @@ public class DestroyAi extends SpellAbilityAi {
} }
} }
if (sa.getTargets().getNumTargeted() < tgt.getMinTargets(sa.getHostCard(), sa)) { return sa.getTargets().getNumTargeted() >= tgt.getMinTargets(sa.getHostCard(), sa);
return false;
}
} else { } else {
if (!mandatory) { return mandatory;
return false;
}
} }
return true;
} }
public boolean doLandForLandRemovalLogic(SpellAbility sa, Player ai, Card tgtLand, String logic) { public boolean doLandForLandRemovalLogic(SpellAbility sa, Player ai, Card tgtLand, String logic) {

View File

@@ -146,10 +146,7 @@ public class DestroyAllAi extends SpellAbilityAi {
AiBlockController block = new AiBlockController(ai); AiBlockController block = new AiBlockController(ai);
block.assignBlockersForCombat(combat); block.assignBlockersForCombat(combat);
if (ComputerUtilCombat.lifeInSeriousDanger(ai, combat)) { return ComputerUtilCombat.lifeInSeriousDanger(ai, combat);
return true;
}
return false;
} // only lands involved } // only lands involved
else if (CardLists.getNotType(opplist, "Land").isEmpty() && CardLists.getNotType(ailist, "Land").isEmpty()) { else if (CardLists.getNotType(opplist, "Land").isEmpty() && CardLists.getNotType(ailist, "Land").isEmpty()) {
if (ai.isCardInPlay("Crucible of Worlds") && !opponent.isCardInPlay("Crucible of Worlds") && !opplist.isEmpty()) { if (ai.isCardInPlay("Crucible of Worlds") && !opponent.isCardInPlay("Crucible of Worlds") && !opplist.isEmpty()) {
@@ -164,14 +161,9 @@ public class DestroyAllAi extends SpellAbilityAi {
} }
} }
// check if the AI would lose more lands than the opponent would // check if the AI would lose more lands than the opponent would
if (ComputerUtilCard.evaluatePermanentList(ailist) > ComputerUtilCard.evaluatePermanentList(opplist) + 1) { return ComputerUtilCard.evaluatePermanentList(ailist) <= ComputerUtilCard.evaluatePermanentList(opplist) + 1;
return false;
}
} // otherwise evaluate both lists by CMC and pass only if human permanents are more valuable } // otherwise evaluate both lists by CMC and pass only if human permanents are more valuable
else if ((ComputerUtilCard.evaluatePermanentList(ailist) + 3) >= ComputerUtilCard.evaluatePermanentList(opplist)) { else return (ComputerUtilCard.evaluatePermanentList(ailist) + 3) < ComputerUtilCard.evaluatePermanentList(opplist);
return false;
}
return true;
} }
} }

View File

@@ -51,12 +51,9 @@ public class DrainManaAi extends SpellAbilityAi {
} else { } else {
final List<Player> defined = AbilityUtils.getDefinedPlayers(source, sa.getParam("Defined"), sa); final List<Player> defined = AbilityUtils.getDefinedPlayers(source, sa.getParam("Defined"), sa);
if (!defined.contains(opp)) { return defined.contains(opp);
return false;
}
} }
return true;
} else { } else {
sa.resetTargets(); sa.resetTargets();
sa.getTargets().add(opp); sa.getTargets().add(opp);

View File

@@ -68,10 +68,7 @@ public class DrawAi extends SpellAbilityAi {
return false; return false;
} }
if (!canLoot(ai, sa)) { return canLoot(ai, sa);
return false;
}
return true;
} }
/* /*
@@ -107,11 +104,7 @@ public class DrawAi extends SpellAbilityAi {
} }
} }
if (!ComputerUtilCost.checkRemoveCounterCost(cost, source, sa)) { return ComputerUtilCost.checkRemoveCounterCost(cost, source, sa);
return false;
}
return true;
} }
/* /*
@@ -202,9 +195,7 @@ public class DrawAi extends SpellAbilityAi {
if (numHand == 0 && numDraw == numDiscard) { if (numHand == 0 && numDraw == numDiscard) {
return false; // no looting since everything is dumped return false; // no looting since everything is dumped
} }
if (numHand + numDraw < numDiscard) { return numHand + numDraw >= numDiscard; // net loss of cards
return false; // net loss of cards
}
} }
return true; return true;
} }
@@ -486,11 +477,8 @@ public class DrawAi extends SpellAbilityAi {
// ability is not targeted // ability is not targeted
if (numCards >= computerLibrarySize) { if (numCards >= computerLibrarySize) {
if (ai.isCardInPlay("Laboratory Maniac")) { return ai.isCardInPlay("Laboratory Maniac");
return true;
}
// Don't deck yourself // Don't deck yourself
return false;
} }
if (numCards == 0 && !drawback) { if (numCards == 0 && !drawback) {
@@ -503,9 +491,7 @@ public class DrawAi extends SpellAbilityAi {
&& !assumeSafeX) { && !assumeSafeX) {
// Don't draw too many cards and then risk discarding cards at // Don't draw too many cards and then risk discarding cards at
// EOT // EOT
if (!drawback) { return drawback;
return false;
}
} }
} }
return true; return true;

View File

@@ -283,11 +283,7 @@ public class EffectAi extends SpellAbilityAi {
return false; return false;
} }
final SpellAbility topStack = game.getStack().peekAbility(); final SpellAbility topStack = game.getStack().peekAbility();
if (topStack.getActivatingPlayer().isOpponentOf(ai) && topStack.getApi() == ApiType.GainLife) { return topStack.getActivatingPlayer().isOpponentOf(ai) && topStack.getApi() == ApiType.GainLife;
return true;
} else {
return false;
}
} else if (logic.equals("Fight")) { } else if (logic.equals("Fight")) {
return FightAi.canFightAi(ai, sa, 0, 0); return FightAi.canFightAi(ai, sa, 0, 0);
} else if (logic.equals("Burn")) { } else if (logic.equals("Burn")) {
@@ -301,11 +297,9 @@ public class EffectAi extends SpellAbilityAi {
return false; return false;
} }
if (logic.contains(":")) { if (logic.contains(":")) {
String k[] = logic.split(":"); String[] k = logic.split(":");
Integer i = Integer.valueOf(k[1]); Integer i = Integer.valueOf(k[1]);
if (ai.getCreaturesInPlay().size() < i) { return ai.getCreaturesInPlay().size() >= i;
return false;
}
} }
return true; return true;
} else if (logic.equals("CastFromGraveThisTurn")) { } else if (logic.equals("CastFromGraveThisTurn")) {

View File

@@ -273,9 +273,8 @@ public class FightAi extends SpellAbilityAi {
if (!canKill(opponent, fighter, -pumpDefense)) { // can survive if (!canKill(opponent, fighter, -pumpDefense)) { // can survive
return true; return true;
} else { } else {
if (MyRandom.getRandom().nextInt(20)<(opponent.getCMC() - fighter.getCMC())) { // trade // trade
return true; return MyRandom.getRandom().nextInt(20) < (opponent.getCMC() - fighter.getCMC());
}
} }
} }
return false; return false;
@@ -289,10 +288,7 @@ public class FightAi extends SpellAbilityAi {
|| ComputerUtil.canRegenerate(opponent.getController(), opponent)) { || ComputerUtil.canRegenerate(opponent.getController(), opponent)) {
return false; return false;
} }
if (fighter.hasKeyword(Keyword.DEATHTOUCH) return fighter.hasKeyword(Keyword.DEATHTOUCH)
|| ComputerUtilCombat.getDamageToKill(opponent) <= fighter.getNetPower() + pumpAttack) { || ComputerUtilCombat.getDamageToKill(opponent) <= fighter.getNetPower() + pumpAttack;
return true;
}
return false;
} }
} }

View File

@@ -20,9 +20,7 @@ public class FlipACoinAi extends SpellAbilityAi {
if (AILogic.equals("Never")) { if (AILogic.equals("Never")) {
return false; return false;
} else if (AILogic.equals("PhaseOut")) { } else if (AILogic.equals("PhaseOut")) {
if (!ComputerUtil.predictThreatenedObjects(sa.getActivatingPlayer(), sa).contains(sa.getHostCard())) { return ComputerUtil.predictThreatenedObjects(sa.getActivatingPlayer(), sa).contains(sa.getHostCard());
return false;
}
} else if (AILogic.equals("KillOrcs")) { } else if (AILogic.equals("KillOrcs")) {
if (ai.getGame().getPhaseHandler().getPhase().isBefore(PhaseType.END_OF_TURN) ) { if (ai.getGame().getPhaseHandler().getPhase().isBefore(PhaseType.END_OF_TURN) ) {
return false; return false;

View File

@@ -11,9 +11,7 @@ public class GameWinAi extends SpellAbilityAi {
*/ */
@Override @Override
protected boolean canPlayAI(Player ai, SpellAbility sa) { protected boolean canPlayAI(Player ai, SpellAbility sa) {
if (ai.cantWin()) { return !ai.cantWin();
return false;
}
// TODO Check conditions are met on card (e.g. Coalition Victory) // TODO Check conditions are met on card (e.g. Coalition Victory)
@@ -21,7 +19,6 @@ public class GameWinAi extends SpellAbilityAi {
// In general, don't return true. // In general, don't return true.
// But this card wins the game, I can make an exception for that // But this card wins the game, I can make an exception for that
return true;
} }
@Override @Override

View File

@@ -120,9 +120,7 @@ public class LifeExchangeVariantAi extends SpellAbilityAi {
MagicStack stack = game.getStack(); MagicStack stack = game.getStack();
if (!stack.isEmpty()) { if (!stack.isEmpty()) {
SpellAbility saTop = stack.peekAbility(); SpellAbility saTop = stack.peekAbility();
if (ComputerUtil.predictDamageFromSpell(saTop, ai) >= aiLife) { return ComputerUtil.predictDamageFromSpell(saTop, ai) >= aiLife;
return true;
}
} }
} }

View File

@@ -49,9 +49,7 @@ public class LifeGainAi extends SpellAbilityAi {
return false; return false;
} }
if (!ComputerUtilCost.checkRemoveCounterCost(cost, source)) { return ComputerUtilCost.checkRemoveCounterCost(cost, source);
return false;
}
} else { } else {
// don't sac possible blockers // don't sac possible blockers
if (!ph.getPhase().equals(PhaseType.COMBAT_DECLARE_BLOCKERS) if (!ph.getPhase().equals(PhaseType.COMBAT_DECLARE_BLOCKERS)
@@ -63,9 +61,7 @@ public class LifeGainAi extends SpellAbilityAi {
skipCheck |= ComputerUtilCost.isSacrificeSelfCost(cost) && !source.isCreature(); skipCheck |= ComputerUtilCost.isSacrificeSelfCost(cost) && !source.isCreature();
if (!skipCheck) { if (!skipCheck) {
if (!ComputerUtilCost.checkSacrificeCost(ai, cost, source, sa,false)) { return ComputerUtilCost.checkSacrificeCost(ai, cost, source, sa, false);
return false;
}
} }
} }
} }
@@ -106,13 +102,9 @@ public class LifeGainAi extends SpellAbilityAi {
return false; return false;
} }
if (!lifeCritical && !activateForCost return lifeCritical || activateForCost
&& (!ph.getNextTurn().equals(ai) || ph.getPhase().isBefore(PhaseType.END_OF_TURN)) || (ph.getNextTurn().equals(ai) && !ph.getPhase().isBefore(PhaseType.END_OF_TURN))
&& !sa.hasParam("PlayerTurn") && !SpellAbilityAi.isSorcerySpeed(sa)) { || sa.hasParam("PlayerTurn") || SpellAbilityAi.isSorcerySpeed(sa);
return false;
}
return true;
} }
/* /*
@@ -304,9 +296,7 @@ public class LifeGainAi extends SpellAbilityAi {
hasTgt = true; hasTgt = true;
} }
} }
if (!hasTgt) { return hasTgt;
return false;
}
} }
return true; return true;
} }

View File

@@ -53,9 +53,7 @@ public class LifeLoseAi extends SpellAbilityAi {
} }
if (sa.usesTargeting()) { if (sa.usesTargeting()) {
if (!doTgt(ai, sa, false)) { return doTgt(ai, sa, false);
return false;
}
} }
return true; return true;
@@ -148,12 +146,8 @@ public class LifeLoseAi extends SpellAbilityAi {
return false; return false;
} }
if (SpellAbilityAi.isSorcerySpeed(sa) || sa.hasParam("ActivationPhases") || SpellAbilityAi.playReusable(ai, sa) return SpellAbilityAi.isSorcerySpeed(sa) || sa.hasParam("ActivationPhases") || SpellAbilityAi.playReusable(ai, sa)
|| ComputerUtil.activateForCost(sa, ai)) { || ComputerUtil.activateForCost(sa, ai);
return true;
}
return false;
} }
/* /*
@@ -187,12 +181,8 @@ public class LifeLoseAi extends SpellAbilityAi {
? new FCollection<Player>(sa.getTargets().getTargetPlayers()) ? new FCollection<Player>(sa.getTargets().getTargetPlayers())
: AbilityUtils.getDefinedPlayers(sa.getHostCard(), sa.getParam("Defined"), sa); : AbilityUtils.getDefinedPlayers(sa.getHostCard(), sa.getParam("Defined"), sa);
if (!mandatory && tgtPlayers.contains(ai) && amount > 0 && amount + 3 > ai.getLife()) { // For cards like Foul Imp, ETB you lose life
// For cards like Foul Imp, ETB you lose life return mandatory || !tgtPlayers.contains(ai) || amount <= 0 || amount + 3 <= ai.getLife();
return false;
}
return true;
} }
protected boolean doTgt(Player ai, SpellAbility sa, boolean mandatory) { protected boolean doTgt(Player ai, SpellAbility sa, boolean mandatory) {

View File

@@ -80,12 +80,9 @@ public class ManaEffectAi extends SpellAbilityAi {
if (sa.hasParam("AILogic")) { if (sa.hasParam("AILogic")) {
return true; // handled elsewhere, does not meet the standard requirements return true; // handled elsewhere, does not meet the standard requirements
} }
if (!(sa.getPayCosts() != null && sa.getPayCosts().hasNoManaCost() && sa.getPayCosts().isReusuableResource() return sa.getPayCosts() != null && sa.getPayCosts().hasNoManaCost() && sa.getPayCosts().isReusuableResource()
&& sa.getSubAbility() == null && ComputerUtil.playImmediately(ai, sa))) { && sa.getSubAbility() == null && ComputerUtil.playImmediately(ai, sa);
return false;
}
return true;
// return super.checkApiLogic(ai, sa); // return super.checkApiLogic(ai, sa);
} }

View File

@@ -80,9 +80,7 @@ public class ManifestAi extends SpellAbilityAi {
// Set PayX here to maximum value. // Set PayX here to maximum value.
int x = ComputerUtilMana.determineLeftoverMana(sa, ai); int x = ComputerUtilMana.determineLeftoverMana(sa, ai);
source.setSVar("PayX", Integer.toString(x)); source.setSVar("PayX", Integer.toString(x));
if (x <= 0) { return x > 0;
return false;
}
} }
return true; return true;
@@ -121,9 +119,7 @@ public class ManifestAi extends SpellAbilityAi {
return false; return false;
// card has ETBTrigger or ETBReplacement // card has ETBTrigger or ETBReplacement
if (card.hasETBTrigger(false) || card.hasETBReplacement()) { return !card.hasETBTrigger(false) && !card.hasETBReplacement();
return false;
}
} }
return true; return true;
} }
@@ -185,10 +181,7 @@ public class ManifestAi extends SpellAbilityAi {
CardCollection filtered = CardLists.filter(options, new Predicate<Card>() { CardCollection filtered = CardLists.filter(options, new Predicate<Card>() {
@Override @Override
public boolean apply(Card input) { public boolean apply(Card input) {
if (shouldManyfest(input, ai, sa)) { return !shouldManyfest(input, ai, sa);
return false;
}
return true;
} }
}); });
if (!filtered.isEmpty()) { if (!filtered.isEmpty()) {

View File

@@ -31,19 +31,13 @@ public class MillAi extends SpellAbilityAi {
PhaseHandler ph = ai.getGame().getPhaseHandler(); PhaseHandler ph = ai.getGame().getPhaseHandler();
if (aiLogic.equals("Main1")) { if (aiLogic.equals("Main1")) {
if (ph.getPhase().isBefore(PhaseType.MAIN2) && !sa.hasParam("ActivationPhases") return !ph.getPhase().isBefore(PhaseType.MAIN2) || sa.hasParam("ActivationPhases")
&& !ComputerUtil.castSpellInMain1(ai, sa)) { || ComputerUtil.castSpellInMain1(ai, sa);
return false;
}
} else if (aiLogic.equals("EndOfOppTurn")) { } else if (aiLogic.equals("EndOfOppTurn")) {
if (!(ph.is(PhaseType.END_OF_TURN) && ph.getNextTurn().equals(ai))) { return ph.is(PhaseType.END_OF_TURN) && ph.getNextTurn().equals(ai);
return false;
}
} else if (aiLogic.equals("LilianaMill")) { } else if (aiLogic.equals("LilianaMill")) {
// Only mill if a "Raise Dead" target is available, in case of control decks with few creatures // Only mill if a "Raise Dead" target is available, in case of control decks with few creatures
if (CardLists.filter(ai.getCardsIn(ZoneType.Graveyard), CardPredicates.Presets.CREATURES).size() < 1) { return CardLists.filter(ai.getCardsIn(ZoneType.Graveyard), CardPredicates.Presets.CREATURES).size() >= 1;
return false;
}
} }
return true; return true;
} }
@@ -62,11 +56,9 @@ public class MillAi extends SpellAbilityAi {
} }
} }
if (sa.getHostCard().isCreature() && sa.getPayCosts().hasTapCost()) { if (sa.getHostCard().isCreature() && sa.getPayCosts().hasTapCost()) {
if (!(ph.is(PhaseType.END_OF_TURN) && ph.getNextTurn().equals(ai))) { // creatures with a tap cost to mill (e.g. Doorkeeper) should be activated at the opponent's end step
// creatures with a tap cost to mill (e.g. Doorkeeper) should be activated at the opponent's end step // because they are also potentially useful for combat
// because they are also potentially useful for combat return ph.is(PhaseType.END_OF_TURN) && ph.getNextTurn().equals(ai);
return false;
}
} }
return true; return true;
} }
@@ -100,9 +92,7 @@ public class MillAi extends SpellAbilityAi {
// Set PayX here to maximum value. // Set PayX here to maximum value.
final int cardsToDiscard = getNumToDiscard(ai, sa); final int cardsToDiscard = getNumToDiscard(ai, sa);
source.setSVar("PayX", Integer.toString(cardsToDiscard)); source.setSVar("PayX", Integer.toString(cardsToDiscard));
if (cardsToDiscard <= 0) { return cardsToDiscard > 0;
return false;
}
} }
return true; return true;
} }

View File

@@ -24,9 +24,7 @@ public class PeekAndRevealAi extends SpellAbilityAi {
return false; return false;
} }
if ("Main2".equals(sa.getParam("AILogic"))) { if ("Main2".equals(sa.getParam("AILogic"))) {
if (aiPlayer.getGame().getPhaseHandler().getPhase().isBefore(PhaseType.MAIN2)) { return !aiPlayer.getGame().getPhaseHandler().getPhase().isBefore(PhaseType.MAIN2);
return false;
}
} }
// So far this only appears on Triggers, but will expand // So far this only appears on Triggers, but will expand
// once things get converted from Dig + NoMove // once things get converted from Dig + NoMove

View File

@@ -38,10 +38,7 @@ public class PermanentAi extends SpellAbilityAi {
} }
// Wait for Main2 if possible // Wait for Main2 if possible
if (ph.is(PhaseType.MAIN1) && ph.isPlayerTurn(ai) && !ComputerUtil.castPermanentInMain1(ai, sa) && !sa.hasParam("WithoutManaCost")) { return !ph.is(PhaseType.MAIN1) || !ph.isPlayerTurn(ai) || ComputerUtil.castPermanentInMain1(ai, sa) || sa.hasParam("WithoutManaCost");
return false;
}
return true;
} }
/** /**
@@ -259,9 +256,7 @@ public class PermanentAi extends SpellAbilityAi {
} }
} }
if (dontCast) { return !dontCast;
return false;
}
} }
return true; return true;

View File

@@ -40,9 +40,7 @@ public class PermanentCreatureAi extends PermanentAi {
ComputerUtilCard.applyStaticContPT(game, copy, null); ComputerUtilCard.applyStaticContPT(game, copy, null);
if (copy.getNetToughness() <= 0) { return copy.getNetToughness() > 0;
return false;
}
} }
return true; return true;
} }
@@ -225,13 +223,9 @@ public class PermanentCreatureAi extends PermanentAi {
*/ */
final Card copy = CardUtil.getLKICopy(sa.getHostCard()); final Card copy = CardUtil.getLKICopy(sa.getHostCard());
ComputerUtilCard.applyStaticContPT(game, copy, null); ComputerUtilCard.applyStaticContPT(game, copy, null);
if (copy.getNetToughness() <= 0 && !copy.hasStartOfKeyword("etbCounter") && mana.countX() == 0 // AiPlayDecision.WouldBecomeZeroToughnessCreature
&& !copy.hasETBTrigger(false) && !copy.hasETBReplacement() && !copy.hasSVar("NoZeroToughnessAI")) { return copy.getNetToughness() > 0 || copy.hasStartOfKeyword("etbCounter") || mana.countX() != 0
// AiPlayDecision.WouldBecomeZeroToughnessCreature || copy.hasETBTrigger(false) || copy.hasETBReplacement() || copy.hasSVar("NoZeroToughnessAI");
return false;
}
return true;
} }
} }

View File

@@ -19,10 +19,7 @@ public class PermanentNoncreatureAi extends PermanentAi {
@Override @Override
protected boolean checkAiLogic(final Player ai, final SpellAbility sa, final String aiLogic) { protected boolean checkAiLogic(final Player ai, final SpellAbility sa, final String aiLogic) {
if ("Never".equals(aiLogic) || "DontCast".equals(aiLogic)) { return !"Never".equals(aiLogic) && !"DontCast".equals(aiLogic);
return false;
}
return true;
} }
/** /**
@@ -54,10 +51,8 @@ public class PermanentNoncreatureAi extends PermanentAi {
// TODO: consider replacing the condition with host.hasSVar("OblivionRing") // TODO: consider replacing the condition with host.hasSVar("OblivionRing")
targets = CardLists.filterControlledBy(targets, ai.getOpponents()); targets = CardLists.filterControlledBy(targets, ai.getOpponents());
} }
if (targets.isEmpty()) { // AiPlayDecision.AnotherTime
// AiPlayDecision.AnotherTime return !targets.isEmpty();
return false;
}
} }
return true; return true;
} }

View File

@@ -33,9 +33,7 @@ public class PhasesAi extends SpellAbilityAi {
if (tgtCards.contains(source)) { if (tgtCards.contains(source)) {
// Protect it from something // Protect it from something
final boolean isThreatened = ComputerUtil.predictThreatenedObjects(aiPlayer, null, true).contains(source); final boolean isThreatened = ComputerUtil.predictThreatenedObjects(aiPlayer, null, true).contains(source);
if (isThreatened) { return isThreatened;
return true;
}
} else { } else {
// Card def = tgtCards.get(0); // Card def = tgtCards.get(0);
// Phase this out if it might attack me, or before it can be // Phase this out if it might attack me, or before it can be

View File

@@ -178,11 +178,7 @@ public class PlayAi extends SpellAbilityAi {
// Before accepting, see if the spell has a valid number of targets (it should at this point). // Before accepting, see if the spell has a valid number of targets (it should at this point).
// Proceeding past this point if the spell is not correctly targeted will result // Proceeding past this point if the spell is not correctly targeted will result
// in "Failed to add to stack" error and the card disappearing from the game completely. // in "Failed to add to stack" error and the card disappearing from the game completely.
if (!spell.isTargetNumberValid()) { return spell.isTargetNumberValid();
return false;
}
return true;
} }
} }
return false; return false;

View File

@@ -24,11 +24,8 @@ public class PoisonAi extends SpellAbilityAi {
*/ */
@Override @Override
protected boolean checkPhaseRestrictions(final Player ai, final SpellAbility sa, final PhaseHandler ph) { protected boolean checkPhaseRestrictions(final Player ai, final SpellAbility sa, final PhaseHandler ph) {
if (ph.getPhase().isBefore(PhaseType.MAIN2) return !ph.getPhase().isBefore(PhaseType.MAIN2)
&& !sa.hasParam("ActivationPhases")) { || sa.hasParam("ActivationPhases");
return false;
}
return true;
} }
/* /*
@@ -78,13 +75,10 @@ public class PoisonAi extends SpellAbilityAi {
} }
Player max = players.max(PlayerPredicates.compareByPoison()); Player max = players.max(PlayerPredicates.compareByPoison());
if (ai.getPoisonCounters() == max.getPoisonCounters()) { // ai is one of the max
// ai is one of the max return ai.getPoisonCounters() != max.getPoisonCounters();
return false;
}
} }
return true;
} }
private boolean tgtPlayer(Player ai, SpellAbility sa, boolean mandatory) { private boolean tgtPlayer(Player ai, SpellAbility sa, boolean mandatory) {
@@ -96,11 +90,8 @@ public class PoisonAi extends SpellAbilityAi {
public boolean apply(Player input) { public boolean apply(Player input) {
if (input.cantLose()) { if (input.cantLose()) {
return false; return false;
} else if (!input.canReceiveCounters(CounterType.POISON)) { } else return input.canReceiveCounters(CounterType.POISON);
return false;
}
return true;
} }
}); });
@@ -132,10 +123,7 @@ public class PoisonAi extends SpellAbilityAi {
if (input.cantLose()) { if (input.cantLose()) {
return true; return true;
} }
if (!input.canReceiveCounters(CounterType.POISON)) { return !input.canReceiveCounters(CounterType.POISON);
return true;
}
return false;
} }
}); });

View File

@@ -162,11 +162,8 @@ public class ProtectAi extends SpellAbilityAi {
@Override @Override
protected boolean checkPhaseRestrictions(final Player ai, final SpellAbility sa, final PhaseHandler ph) { protected boolean checkPhaseRestrictions(final Player ai, final SpellAbility sa, final PhaseHandler ph) {
final boolean notAiMain1 = !(ph.getPlayerTurn() == ai && ph.getPhase() == PhaseType.MAIN1); final boolean notAiMain1 = !(ph.getPlayerTurn() == ai && ph.getPhase() == PhaseType.MAIN1);
if (SpellAbilityAi.isSorcerySpeed(sa) && notAiMain1) { // sorceries can only give protection in order to create an unblockable attacker
// sorceries can only give protection in order to create an unblockable attacker return !SpellAbilityAi.isSorcerySpeed(sa) || !notAiMain1;
return false;
}
return true;
} }
@Override @Override
@@ -177,9 +174,7 @@ public class ProtectAi extends SpellAbilityAi {
return false; return false;
} else if (cards.size() == 1) { } else if (cards.size() == 1) {
// Affecting single card // Affecting single card
if ((getProtectCreatures(ai, sa)).contains(cards.get(0))) { return (getProtectCreatures(ai, sa)).contains(cards.get(0));
return true;
}
} }
/* /*
* when this happens we need to expand AI to consider if its ok * when this happens we need to expand AI to consider if its ok

View File

@@ -92,10 +92,7 @@ public class PumpAi extends PumpAiBase {
return true; return true;
} }
if (!ph.getNextTurn().equals(ai) || ph.getPhase().isBefore(PhaseType.END_OF_TURN)) { return ph.getNextTurn().equals(ai) && !ph.getPhase().isBefore(PhaseType.END_OF_TURN);
return false;
}
return true;
} else if (logic.equals("Aristocrat")) { } else if (logic.equals("Aristocrat")) {
final boolean isThreatened = ComputerUtil.predictThreatenedObjects(ai, null, true).contains(sa.getHostCard()); final boolean isThreatened = ComputerUtil.predictThreatenedObjects(ai, null, true).contains(sa.getHostCard());
if (!ph.is(PhaseType.COMBAT_DECLARE_BLOCKERS) && !isThreatened) { if (!ph.is(PhaseType.COMBAT_DECLARE_BLOCKERS) && !isThreatened) {
@@ -121,9 +118,7 @@ public class PumpAi extends PumpAiBase {
|| ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_BLOCKERS))) { || ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_BLOCKERS))) {
// Instant-speed pumps should not be cast outside of combat when the // Instant-speed pumps should not be cast outside of combat when the
// stack is empty // stack is empty
if (!sa.isCurse() && !SpellAbilityAi.isSorcerySpeed(sa) && !main1Preferred) { return sa.isCurse() || SpellAbilityAi.isSorcerySpeed(sa) || main1Preferred;
return false;
}
} }
return true; return true;
} }
@@ -134,7 +129,7 @@ public class PumpAi extends PumpAiBase {
final Card source = sa.getHostCard(); final Card source = sa.getHostCard();
final String sourceName = ComputerUtilAbility.getAbilitySourceName(sa); final String sourceName = ComputerUtilAbility.getAbilitySourceName(sa);
final List<String> keywords = sa.hasParam("KW") ? Arrays.asList(sa.getParam("KW").split(" & ")) final List<String> keywords = sa.hasParam("KW") ? Arrays.asList(sa.getParam("KW").split(" & "))
: Lists.<String>newArrayList(); : Lists.newArrayList();
final String numDefense = sa.hasParam("NumDef") ? sa.getParam("NumDef") : ""; final String numDefense = sa.hasParam("NumDef") ? sa.getParam("NumDef") : "";
final String numAttack = sa.hasParam("NumAtt") ? sa.getParam("NumAtt") : ""; final String numAttack = sa.hasParam("NumAtt") ? sa.getParam("NumAtt") : "";
@@ -191,11 +186,8 @@ public class PumpAi extends PumpAiBase {
srcCardCpy.setCounters(cType, srcCardCpy.getCounters(cType) - amount); srcCardCpy.setCounters(cType, srcCardCpy.getCounters(cType) - amount);
if (CounterType.P1P1.equals(cType) && srcCardCpy.getNetToughness() <= 0) { if (CounterType.P1P1.equals(cType) && srcCardCpy.getNetToughness() <= 0) {
if (srcCardCpy.getCounters(cType) > 0 || !card.hasKeyword(Keyword.UNDYING) return srcCardCpy.getCounters(cType) > 0 || !card.hasKeyword(Keyword.UNDYING)
|| card.isToken()) { || card.isToken();
return true;
}
return false;
} }
return false; return false;
} }
@@ -244,11 +236,8 @@ public class PumpAi extends PumpAiBase {
srcCardCpy.setCounters(cType, srcCardCpy.getCounters(cType) - amount); srcCardCpy.setCounters(cType, srcCardCpy.getCounters(cType) - amount);
if (CounterType.P1P1.equals(cType) && srcCardCpy.getNetToughness() <= 0) { if (CounterType.P1P1.equals(cType) && srcCardCpy.getNetToughness() <= 0) {
if (srcCardCpy.getCounters(cType) > 0 || !card.hasKeyword(Keyword.UNDYING) return srcCardCpy.getCounters(cType) > 0 || !card.hasKeyword(Keyword.UNDYING)
|| card.isToken()) { || card.isToken();
return true;
}
return false;
} }
return true; return true;
} }
@@ -395,9 +384,7 @@ public class PumpAi extends PumpAiBase {
Card pumped = ComputerUtilCard.getPumpedCreature(ai, sa, card, 0, 0, keywords); Card pumped = ComputerUtilCard.getPumpedCreature(ai, sa, card, 0, 0, keywords);
if (game.getPhaseHandler().is(PhaseType.COMBAT_DECLARE_ATTACKERS, ai) if (game.getPhaseHandler().is(PhaseType.COMBAT_DECLARE_ATTACKERS, ai)
|| game.getPhaseHandler().is(PhaseType.COMBAT_BEGIN, ai)) { || game.getPhaseHandler().is(PhaseType.COMBAT_BEGIN, ai)) {
if (!ComputerUtilCard.doesSpecifiedCreatureAttackAI(ai, pumped)) { return ComputerUtilCard.doesSpecifiedCreatureAttackAI(ai, pumped);
return false;
}
} }
return true; return true;
@@ -432,7 +419,7 @@ public class PumpAi extends PumpAiBase {
private boolean pumpTgtAI(final Player ai, final SpellAbility sa, final int defense, final int attack, final boolean mandatory, private boolean pumpTgtAI(final Player ai, final SpellAbility sa, final int defense, final int attack, final boolean mandatory,
boolean immediately) { boolean immediately) {
final List<String> keywords = sa.hasParam("KW") ? Arrays.asList(sa.getParam("KW").split(" & ")) final List<String> keywords = sa.hasParam("KW") ? Arrays.asList(sa.getParam("KW").split(" & "))
: Lists.<String>newArrayList(); : Lists.newArrayList();
final Game game = ai.getGame(); final Game game = ai.getGame();
final Card source = sa.getHostCard(); final Card source = sa.getHostCard();
final boolean isFight = "Fight".equals(sa.getParam("AILogic")) || "PowerDmg".equals(sa.getParam("AILogic")); final boolean isFight = "Fight".equals(sa.getParam("AILogic")) || "PowerDmg".equals(sa.getParam("AILogic"));
@@ -555,7 +542,7 @@ public class PumpAi extends PumpAiBase {
} }
// Filter AI-specific targets if provided // Filter AI-specific targets if provided
list = ComputerUtil.filterAITgts(sa, ai, (CardCollection)list, true); list = ComputerUtil.filterAITgts(sa, ai, list, true);
if (list.isEmpty()) { if (list.isEmpty()) {
if (ComputerUtil.activateForCost(sa, ai)) { if (ComputerUtil.activateForCost(sa, ai)) {
@@ -788,15 +775,11 @@ public class PumpAi extends PumpAiBase {
if (!source.hasKeyword(Keyword.INDESTRUCTIBLE) && source.getNetToughness() + defense <= source.getDamage()) { if (!source.hasKeyword(Keyword.INDESTRUCTIBLE) && source.getNetToughness() + defense <= source.getDamage()) {
return false; return false;
} }
if (source.getNetToughness() + defense <= 0) { return source.getNetToughness() + defense > 0;
return false;
}
} }
} else { } else {
//Targeted //Targeted
if (!pumpTgtAI(ai, sa, defense, attack, false, true)) { return pumpTgtAI(ai, sa, defense, attack, false, true);
return false;
}
} }
return true; return true;
@@ -849,9 +832,7 @@ public class PumpAi extends PumpAiBase {
} }
} }
); );
if (sacFodder.size() >= numCreatsToSac) { return sacFodder.size() >= numCreatsToSac;
return true;
}
} }
} }

View File

@@ -56,24 +56,17 @@ public abstract class PumpAiBase extends SpellAbilityAi {
if (!CardUtil.isStackingKeyword(keyword) && card.hasKeyword(keyword)) { if (!CardUtil.isStackingKeyword(keyword) && card.hasKeyword(keyword)) {
return false; return false;
} else if (keyword.equals("Defender") || keyword.endsWith("CARDNAME can't attack.")) { } else if (keyword.equals("Defender") || keyword.endsWith("CARDNAME can't attack.")) {
if (!ph.isPlayerTurn(card.getController()) || !CombatUtil.canAttack(card, ai) return ph.isPlayerTurn(card.getController()) && CombatUtil.canAttack(card, ai)
|| (card.getNetCombatDamage() <= 0) && (card.getNetCombatDamage() > 0)
|| ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS)) { && !ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS);
return false;
}
} else if (keyword.endsWith("CARDNAME can't attack or block.")) { } else if (keyword.endsWith("CARDNAME can't attack or block.")) {
if (sa.hasParam("UntilYourNextTurn")) { if (sa.hasParam("UntilYourNextTurn")) {
if (CombatUtil.canAttack(card, ai) || CombatUtil.canBlock(card, true)) { return CombatUtil.canAttack(card, ai) || CombatUtil.canBlock(card, true);
return true;
}
return false;
} }
if (!ph.isPlayerTurn(ai)) { if (!ph.isPlayerTurn(ai)) {
if (!CombatUtil.canAttack(card, ai) return CombatUtil.canAttack(card, ai)
|| (card.getNetCombatDamage() <= 0) && (card.getNetCombatDamage() > 0)
|| ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS)) { && !ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS);
return false;
}
} else { } else {
if (ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_BLOCKERS) if (ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_BLOCKERS)
|| ph.getPhase().isBefore(PhaseType.MAIN1)) { || ph.getPhase().isBefore(PhaseType.MAIN1)) {
@@ -90,9 +83,7 @@ public abstract class PumpAiBase extends SpellAbilityAi {
return CombatUtil.canAttack(c, card.getController()) || (combat != null && combat.isAttacking(c)); return CombatUtil.canAttack(c, card.getController()) || (combat != null && combat.isAttacking(c));
} }
}); });
if (!CombatUtil.canBlockAtLeastOne(card, attackers)) { return CombatUtil.canBlockAtLeastOne(card, attackers);
return false;
}
} }
} else if (keyword.endsWith("CARDNAME can't block.")) { } else if (keyword.endsWith("CARDNAME can't block.")) {
if (!ph.isPlayerTurn(ai) || ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_BLOCKERS) if (!ph.isPlayerTurn(ai) || ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_BLOCKERS)
@@ -112,24 +103,18 @@ public abstract class PumpAiBase extends SpellAbilityAi {
&& card.getController().equals(combat.getDefenderPlayerByAttacker(c))); && card.getController().equals(combat.getDefenderPlayerByAttacker(c)));
} }
}); });
if (!CombatUtil.canBlockAtLeastOne(card, attackers)) { return CombatUtil.canBlockAtLeastOne(card, attackers);
return false;
}
} else if (keyword.endsWith("CantBlockCardUIDSource")) { // can't block CARDNAME this turn } else if (keyword.endsWith("CantBlockCardUIDSource")) { // can't block CARDNAME this turn
if (!ph.isPlayerTurn(ai) || ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_BLOCKERS) if (!ph.isPlayerTurn(ai) || ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_BLOCKERS)
|| ph.getPhase().isBefore(PhaseType.MAIN1) || !CombatUtil.canBlock(sa.getHostCard(), card)) { || ph.getPhase().isBefore(PhaseType.MAIN1) || !CombatUtil.canBlock(sa.getHostCard(), card)) {
return false; return false;
} }
// target needs to be a creature, controlled by the player which is attacked // target needs to be a creature, controlled by the player which is attacked
if (sa.getHostCard().isTapped() && (combat == null || !combat.isAttacking(sa.getHostCard()) return !sa.getHostCard().isTapped() || (combat != null && combat.isAttacking(sa.getHostCard())
|| !card.getController().equals(combat.getDefenderPlayerByAttacker(sa.getHostCard())))) { && card.getController().equals(combat.getDefenderPlayerByAttacker(sa.getHostCard())));
return false;
}
} else if (keyword.endsWith("This card doesn't untap during your next untap step.")) { } else if (keyword.endsWith("This card doesn't untap during your next untap step.")) {
if (ph.getPhase().isBefore(PhaseType.MAIN2) || card.isUntapped() || !ph.isPlayerTurn(ai) return !ph.getPhase().isBefore(PhaseType.MAIN2) && !card.isUntapped() && ph.isPlayerTurn(ai)
|| !Untap.canUntap(card)) { && Untap.canUntap(card);
return false;
}
} else if (keyword.endsWith("Prevent all combat damage that would be dealt by CARDNAME.") } else if (keyword.endsWith("Prevent all combat damage that would be dealt by CARDNAME.")
|| keyword.endsWith("Prevent all damage that would be dealt by CARDNAME.")) { || keyword.endsWith("Prevent all damage that would be dealt by CARDNAME.")) {
if (ph.isPlayerTurn(ai) && (!(CombatUtil.canBlock(card) || combat != null && combat.isBlocking(card)) if (ph.isPlayerTurn(ai) && (!(CombatUtil.canBlock(card) || combat != null && combat.isBlocking(card))
@@ -139,28 +124,18 @@ public abstract class PumpAiBase extends SpellAbilityAi {
|| CardLists.getNotKeyword(ai.getCreaturesInPlay(), Keyword.DEFENDER).isEmpty())) { || CardLists.getNotKeyword(ai.getCreaturesInPlay(), Keyword.DEFENDER).isEmpty())) {
return false; return false;
} }
if (!ph.isPlayerTurn(ai) && (combat == null || !combat.isAttacking(card) || card.getNetCombatDamage() <= 0)) { return ph.isPlayerTurn(ai) || (combat != null && combat.isAttacking(card) && card.getNetCombatDamage() > 0);
return false;
}
} else if (keyword.endsWith("CARDNAME attacks each turn if able.") } else if (keyword.endsWith("CARDNAME attacks each turn if able.")
|| keyword.endsWith("CARDNAME attacks each combat if able.")) { || keyword.endsWith("CARDNAME attacks each combat if able.")) {
if (ph.isPlayerTurn(ai) || !CombatUtil.canAttack(card, ai) || !CombatUtil.canBeBlocked(card, ai) return !ph.isPlayerTurn(ai) && CombatUtil.canAttack(card, ai) && CombatUtil.canBeBlocked(card, ai)
|| ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS)) { && !ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS);
return false;
}
} else if (keyword.endsWith("CARDNAME can't be regenerated.")) { } else if (keyword.endsWith("CARDNAME can't be regenerated.")) {
if (card.getShieldCount() > 0) { if (card.getShieldCount() > 0) {
return true; return true;
} }
if (card.hasKeyword("If CARDNAME would be destroyed, regenerate it.") && combat != null return card.hasKeyword("If CARDNAME would be destroyed, regenerate it.") && combat != null
&& (combat.isBlocked(card) || combat.isBlocking(card))) { && (combat.isBlocked(card) || combat.isBlocking(card));
return true; } else return !keyword.endsWith("CARDNAME's activated abilities can't be activated."); //too complex
}
return false;
} else if (keyword.endsWith("CARDNAME's activated abilities can't be activated.")) {
return false; //too complex
}
return true;
} }
/** /**
@@ -187,12 +162,10 @@ public abstract class PumpAiBase extends SpellAbilityAi {
final boolean evasive = (keyword.endsWith("Unblockable") || keyword.endsWith("Shadow") || keyword.startsWith("CantBeBlockedBy")); final boolean evasive = (keyword.endsWith("Unblockable") || keyword.endsWith("Shadow") || keyword.startsWith("CantBeBlockedBy"));
// give evasive keywords to creatures that can or do attack // give evasive keywords to creatures that can or do attack
if (evasive) { if (evasive) {
if (ph.isPlayerTurn(opp) || !(CombatUtil.canAttack(card, opp) || (combat != null && combat.isAttacking(card))) return !ph.isPlayerTurn(opp) && (CombatUtil.canAttack(card, opp) || (combat != null && combat.isAttacking(card)))
|| ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS) && !ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS)
|| newPower <= 0 && newPower > 0
|| CardLists.filter(opp.getCreaturesInPlay(), CardPredicates.possibleBlockers(card)).isEmpty()) { && !CardLists.filter(opp.getCreaturesInPlay(), CardPredicates.possibleBlockers(card)).isEmpty();
return false;
}
} else if (keyword.endsWith("Flying")) { } else if (keyword.endsWith("Flying")) {
CardCollectionView attackingFlyer = CardCollection.EMPTY; CardCollectionView attackingFlyer = CardCollection.EMPTY;
if (combat != null) { if (combat != null) {
@@ -221,13 +194,11 @@ public abstract class PumpAiBase extends SpellAbilityAi {
} }
} }
} }
if (ph.isPlayerTurn(opp) || !(CombatUtil.canAttack(card, opp) || (combat != null && combat.isAttacking(card))) return !ph.isPlayerTurn(opp) && (CombatUtil.canAttack(card, opp) || (combat != null && combat.isAttacking(card)))
|| ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS) && !ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS)
|| newPower <= 0 && newPower > 0
|| !Iterables.any(CardLists.filter(opp.getCreaturesInPlay(), CardPredicates.possibleBlockers(card)), && Iterables.any(CardLists.filter(opp.getCreaturesInPlay(), CardPredicates.possibleBlockers(card)),
Predicates.not(flyingOrReach))) { Predicates.not(flyingOrReach));
return false;
}
} else if (keyword.endsWith("Horsemanship")) { } else if (keyword.endsWith("Horsemanship")) {
if (ph.isPlayerTurn(opp) if (ph.isPlayerTurn(opp)
&& ph.getPhase().equals(PhaseType.COMBAT_DECLARE_ATTACKERS) && ph.getPhase().equals(PhaseType.COMBAT_DECLARE_ATTACKERS)
@@ -236,46 +207,35 @@ public abstract class PumpAiBase extends SpellAbilityAi {
&& ComputerUtilCombat.lifeInDanger(ai, game.getCombat())) { && ComputerUtilCombat.lifeInDanger(ai, game.getCombat())) {
return true; return true;
} }
if (ph.isPlayerTurn(opp) || !(CombatUtil.canAttack(card, opp) || (combat != null && combat.isAttacking(card))) return !ph.isPlayerTurn(opp) && (CombatUtil.canAttack(card, opp) || (combat != null && combat.isAttacking(card)))
|| ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS) && !ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS)
|| newPower <= 0 && newPower > 0
|| CardLists.getNotKeyword(CardLists.filter(opp.getCreaturesInPlay(), CardPredicates.possibleBlockers(card)), && !CardLists.getNotKeyword(CardLists.filter(opp.getCreaturesInPlay(), CardPredicates.possibleBlockers(card)),
Keyword.HORSEMANSHIP).isEmpty()) { Keyword.HORSEMANSHIP).isEmpty();
return false;
}
} else if (keyword.endsWith("Intimidate")) { } else if (keyword.endsWith("Intimidate")) {
if (ph.isPlayerTurn(opp) || !(CombatUtil.canAttack(card, opp) || (combat != null && combat.isAttacking(card))) return !ph.isPlayerTurn(opp) && (CombatUtil.canAttack(card, opp) || (combat != null && combat.isAttacking(card)))
|| ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS) && !ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS)
|| newPower <= 0 && newPower > 0
|| CardLists.getNotType(CardLists.filter( && !CardLists.getNotType(CardLists.filter(
opp.getCreaturesInPlay(), CardPredicates.possibleBlockers(card)), "Artifact").isEmpty()) { opp.getCreaturesInPlay(), CardPredicates.possibleBlockers(card)), "Artifact").isEmpty();
return false;
}
} else if (keyword.endsWith("Fear")) { } else if (keyword.endsWith("Fear")) {
if (ph.isPlayerTurn(opp) || !(CombatUtil.canAttack(card, opp) || (combat != null && combat.isAttacking(card))) return !ph.isPlayerTurn(opp) && (CombatUtil.canAttack(card, opp) || (combat != null && combat.isAttacking(card)))
|| ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS) && !ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS)
|| newPower <= 0 && newPower > 0
|| CardLists.getNotColor(CardLists.getNotType(CardLists.filter( && !CardLists.getNotColor(CardLists.getNotType(CardLists.filter(
opp.getCreaturesInPlay(), CardPredicates.possibleBlockers(card)), "Artifact"), MagicColor.BLACK).isEmpty()) { opp.getCreaturesInPlay(), CardPredicates.possibleBlockers(card)), "Artifact"), MagicColor.BLACK).isEmpty();
return false;
}
} else if (keyword.endsWith("Haste")) { } else if (keyword.endsWith("Haste")) {
if (!card.hasSickness() || ph.isPlayerTurn(opp) || card.isTapped() return card.hasSickness() && !ph.isPlayerTurn(opp) && !card.isTapped()
|| newPower <= 0 && newPower > 0
|| card.hasKeyword("CARDNAME can attack as though it had haste.") && !card.hasKeyword("CARDNAME can attack as though it had haste.")
|| ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS) && !ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS)
|| !ComputerUtilCombat.canAttackNextTurn(card)) { && ComputerUtilCombat.canAttackNextTurn(card);
return false;
}
} else if (keyword.endsWith("Indestructible")) { } else if (keyword.endsWith("Indestructible")) {
// Predicting threatened objects in relevant non-combat situations happens elsewhere, // Predicting threatened objects in relevant non-combat situations happens elsewhere,
// so we are only worrying about combat relevance of Indestructible at this point. // so we are only worrying about combat relevance of Indestructible at this point.
if (combat == null return combat != null
|| !((combat.isBlocked(card) || combat.isBlocking(card)) && ((combat.isBlocked(card) || combat.isBlocking(card))
&& ComputerUtilCombat.combatantWouldBeDestroyed(ai, card, combat))) { && ComputerUtilCombat.combatantWouldBeDestroyed(ai, card, combat));
return false;
}
return true;
} else if (keyword.endsWith("Deathtouch")) { } else if (keyword.endsWith("Deathtouch")) {
if (ph.isPlayerTurn(opp) && ph.getPhase().equals(PhaseType.COMBAT_DECLARE_ATTACKERS)) { if (ph.isPlayerTurn(opp) && ph.getPhase().equals(PhaseType.COMBAT_DECLARE_ATTACKERS)) {
List<Card> attackers = combat.getAttackers(); List<Card> attackers = combat.getAttackers();
@@ -297,12 +257,10 @@ public abstract class PumpAiBase extends SpellAbilityAi {
} }
return false; return false;
} else if (keyword.equals("Bushido")) { } else if (keyword.equals("Bushido")) {
if (ph.isPlayerTurn(opp) || !(CombatUtil.canAttack(card, opp) || (combat != null && combat.isAttacking(card))) return !ph.isPlayerTurn(opp) && (CombatUtil.canAttack(card, opp) || (combat != null && combat.isAttacking(card)))
|| ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_BLOCKERS) && !ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_BLOCKERS)
|| opp.getCreaturesInPlay().isEmpty() && !opp.getCreaturesInPlay().isEmpty()
|| CardLists.filter(opp.getCreaturesInPlay(), CardPredicates.possibleBlockers(card)).isEmpty()) { && !CardLists.filter(opp.getCreaturesInPlay(), CardPredicates.possibleBlockers(card)).isEmpty();
return false;
}
} else if (keyword.equals("First Strike")) { } else if (keyword.equals("First Strike")) {
if (card.hasKeyword(Keyword.DOUBLE_STRIKE)) { if (card.hasKeyword(Keyword.DOUBLE_STRIKE)) {
return false; return false;
@@ -321,40 +279,31 @@ public abstract class PumpAiBase extends SpellAbilityAi {
if (!ComputerUtilCombat.canDestroyAttacker(ai, attacker, card, combat, true) if (!ComputerUtilCombat.canDestroyAttacker(ai, attacker, card, combat, true)
&& ComputerUtilCombat.canDestroyAttacker(ai, attacker, card, combat, false)) && ComputerUtilCombat.canDestroyAttacker(ai, attacker, card, combat, false))
return true; return true;
if (ComputerUtilCombat.canDestroyBlocker(ai, card, attacker, combat, true) return ComputerUtilCombat.canDestroyBlocker(ai, card, attacker, combat, true)
&& !ComputerUtilCombat.canDestroyBlocker(ai, card, attacker, combat, false)) && !ComputerUtilCombat.canDestroyBlocker(ai, card, attacker, combat, false);
return true;
} }
return false; return false;
} else if (keyword.equals("Double Strike")) { } else if (keyword.equals("Double Strike")) {
if (ph.isPlayerTurn(opp) || !(CombatUtil.canAttack(card, opp) || (combat != null && combat.isAttacking(card))) return !ph.isPlayerTurn(opp) && (CombatUtil.canAttack(card, opp) || (combat != null && combat.isAttacking(card)))
|| newPower <= 0 && newPower > 0
|| ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_BLOCKERS)) { && !ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_BLOCKERS);
return false;
}
} else if (keyword.startsWith("Rampage")) { } else if (keyword.startsWith("Rampage")) {
if (ph.isPlayerTurn(opp) || !(CombatUtil.canAttack(card, opp) || (combat != null && combat.isAttacking(card))) return !ph.isPlayerTurn(opp) && (CombatUtil.canAttack(card, opp) || (combat != null && combat.isAttacking(card)))
|| newPower <= 0 && newPower > 0
|| ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS) && !ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS)
|| CardLists.filter(opp.getCreaturesInPlay(), CardPredicates.possibleBlockers(card)).size() < 2) { && CardLists.filter(opp.getCreaturesInPlay(), CardPredicates.possibleBlockers(card)).size() >= 2;
return false;
}
} else if (keyword.startsWith("Flanking")) { } else if (keyword.startsWith("Flanking")) {
if (ph.isPlayerTurn(opp) || !(CombatUtil.canAttack(card, opp) || (combat != null && combat.isAttacking(card))) return !ph.isPlayerTurn(opp) && (CombatUtil.canAttack(card, opp) || (combat != null && combat.isAttacking(card)))
|| newPower <= 0 && newPower > 0
|| ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS) && !ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS)
|| CardLists.getNotKeyword(CardLists.filter(opp.getCreaturesInPlay(), CardPredicates.possibleBlockers(card)), && !CardLists.getNotKeyword(CardLists.filter(opp.getCreaturesInPlay(), CardPredicates.possibleBlockers(card)),
Keyword.FLANKING).isEmpty()) { Keyword.FLANKING).isEmpty();
return false;
}
} else if (keyword.startsWith("Trample")) { } else if (keyword.startsWith("Trample")) {
if (ph.isPlayerTurn(opp) || !(CombatUtil.canAttack(card, opp) || (combat != null && combat.isAttacking(card))) return !ph.isPlayerTurn(opp) && (CombatUtil.canAttack(card, opp) || (combat != null && combat.isAttacking(card)))
|| !CombatUtil.canBeBlocked(card, opp) && CombatUtil.canBeBlocked(card, opp)
|| ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS) && !ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS)
|| newPower <= 1 && newPower > 1
|| CardLists.filter(opp.getCreaturesInPlay(), CardPredicates.possibleBlockers(card)).isEmpty()) { && !CardLists.filter(opp.getCreaturesInPlay(), CardPredicates.possibleBlockers(card)).isEmpty();
return false;
}
} else if (keyword.equals("Infect")) { } else if (keyword.equals("Infect")) {
if (newPower <= 0) { if (newPower <= 0) {
return false; return false;
@@ -362,11 +311,9 @@ public abstract class PumpAiBase extends SpellAbilityAi {
if (combat != null && combat.isBlocking(card) && !card.hasKeyword(Keyword.WITHER)) { if (combat != null && combat.isBlocking(card) && !card.hasKeyword(Keyword.WITHER)) {
return true; return true;
} }
if ((ph.isPlayerTurn(opp)) return (!ph.isPlayerTurn(opp))
|| !(CombatUtil.canAttack(card, opp) || (combat != null && combat.isAttacking(card))) && (CombatUtil.canAttack(card, opp) || (combat != null && combat.isAttacking(card)))
|| ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_BLOCKERS)) { && !ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_BLOCKERS);
return false;
}
} else if (keyword.endsWith("Wither")) { } else if (keyword.endsWith("Wither")) {
if (newPower <= 0 || card.hasKeyword(Keyword.INFECT)) { if (newPower <= 0 || card.hasKeyword(Keyword.INFECT)) {
return false; return false;
@@ -378,20 +325,16 @@ public abstract class PumpAiBase extends SpellAbilityAi {
} }
return combat != null && ( combat.isAttacking(card) || combat.isBlocking(card) ); return combat != null && ( combat.isAttacking(card) || combat.isBlocking(card) );
} else if (keyword.equals("Vigilance")) { } else if (keyword.equals("Vigilance")) {
if (ph.isPlayerTurn(opp) || !CombatUtil.canAttack(card, opp) return !ph.isPlayerTurn(opp) && CombatUtil.canAttack(card, opp)
|| newPower <= 0 && newPower > 0
|| ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS) && !ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS)
|| CardLists.getNotKeyword(opp.getCreaturesInPlay(), Keyword.DEFENDER).isEmpty()) { && !CardLists.getNotKeyword(opp.getCreaturesInPlay(), Keyword.DEFENDER).isEmpty();
return false;
}
} else if (keyword.equals("Reach")) { } else if (keyword.equals("Reach")) {
if (ph.isPlayerTurn(ai) return !ph.isPlayerTurn(ai)
|| !ph.getPhase().equals(PhaseType.COMBAT_DECLARE_ATTACKERS) && ph.getPhase().equals(PhaseType.COMBAT_DECLARE_ATTACKERS)
|| CardLists.getKeyword(game.getCombat().getAttackers(), Keyword.FLYING).isEmpty() && !CardLists.getKeyword(game.getCombat().getAttackers(), Keyword.FLYING).isEmpty()
|| card.hasKeyword(Keyword.FLYING) && !card.hasKeyword(Keyword.FLYING)
|| !CombatUtil.canBlock(card)) { && CombatUtil.canBlock(card);
return false;
}
} else if (keyword.endsWith("CARDNAME can block an additional creature each combat.")) { } else if (keyword.endsWith("CARDNAME can block an additional creature each combat.")) {
if (ph.isPlayerTurn(ai) if (ph.isPlayerTurn(ai)
|| !ph.getPhase().equals(PhaseType.COMBAT_DECLARE_ATTACKERS)) { || !ph.getPhase().equals(PhaseType.COMBAT_DECLARE_ATTACKERS)) {
@@ -407,63 +350,43 @@ public abstract class PumpAiBase extends SpellAbilityAi {
} }
} }
} }
if (possibleBlockNum <= canBlockNum) { return possibleBlockNum > canBlockNum;
return false;
}
} else if (keyword.equals("Shroud") || keyword.equals("Hexproof")) { } else if (keyword.equals("Shroud") || keyword.equals("Hexproof")) {
if (!ComputerUtil.predictThreatenedObjects(sa.getActivatingPlayer(), sa).contains(card)) { return ComputerUtil.predictThreatenedObjects(sa.getActivatingPlayer(), sa).contains(card);
return false;
}
} else if (keyword.equals("Persist")) { } else if (keyword.equals("Persist")) {
if (card.getBaseToughness() <= 1 || card.hasKeyword(Keyword.UNDYING)) { return card.getBaseToughness() > 1 && !card.hasKeyword(Keyword.UNDYING);
return false;
}
} else if (keyword.equals("Islandwalk")) { } else if (keyword.equals("Islandwalk")) {
if (ph.isPlayerTurn(opp) || !(CombatUtil.canAttack(card, opp) || (combat != null && combat.isAttacking(card))) return !ph.isPlayerTurn(opp) && (CombatUtil.canAttack(card, opp) || (combat != null && combat.isAttacking(card)))
|| ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS) && !ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS)
|| newPower <= 0 && newPower > 0
|| CardLists.getType(opp.getLandsInPlay(), "Island").isEmpty() && !CardLists.getType(opp.getLandsInPlay(), "Island").isEmpty()
|| CardLists.filter(opp.getCreaturesInPlay(), CardPredicates.possibleBlockers(card)).isEmpty()) { && !CardLists.filter(opp.getCreaturesInPlay(), CardPredicates.possibleBlockers(card)).isEmpty();
return false;
}
} else if (keyword.equals("Swampwalk")) { } else if (keyword.equals("Swampwalk")) {
if (ph.isPlayerTurn(opp) || !(CombatUtil.canAttack(card, opp) || (combat != null && combat.isAttacking(card))) return !ph.isPlayerTurn(opp) && (CombatUtil.canAttack(card, opp) || (combat != null && combat.isAttacking(card)))
|| ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS) && !ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS)
|| newPower <= 0 && newPower > 0
|| CardLists.getType(opp.getLandsInPlay(), "Swamp").isEmpty() && !CardLists.getType(opp.getLandsInPlay(), "Swamp").isEmpty()
|| CardLists.filter(opp.getCreaturesInPlay(), CardPredicates.possibleBlockers(card)).isEmpty()) { && !CardLists.filter(opp.getCreaturesInPlay(), CardPredicates.possibleBlockers(card)).isEmpty();
return false;
}
} else if (keyword.equals("Mountainwalk")) { } else if (keyword.equals("Mountainwalk")) {
if (ph.isPlayerTurn(opp) || !(CombatUtil.canAttack(card, opp) || (combat != null && combat.isAttacking(card))) return !ph.isPlayerTurn(opp) && (CombatUtil.canAttack(card, opp) || (combat != null && combat.isAttacking(card)))
|| ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS) && !ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS)
|| newPower <= 0 && newPower > 0
|| CardLists.getType(opp.getLandsInPlay(), "Mountain").isEmpty() && !CardLists.getType(opp.getLandsInPlay(), "Mountain").isEmpty()
|| CardLists.filter(opp.getCreaturesInPlay(), CardPredicates.possibleBlockers(card)).isEmpty()) { && !CardLists.filter(opp.getCreaturesInPlay(), CardPredicates.possibleBlockers(card)).isEmpty();
return false;
}
} else if (keyword.equals("Forestwalk")) { } else if (keyword.equals("Forestwalk")) {
if (ph.isPlayerTurn(opp) || !(CombatUtil.canAttack(card, opp) || (combat != null && combat.isAttacking(card))) return !ph.isPlayerTurn(opp) && (CombatUtil.canAttack(card, opp) || (combat != null && combat.isAttacking(card)))
|| ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS) && !ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS)
|| newPower <= 0 && newPower > 0
|| CardLists.getType(opp.getLandsInPlay(), "Forest").isEmpty() && !CardLists.getType(opp.getLandsInPlay(), "Forest").isEmpty()
|| CardLists.filter(opp.getCreaturesInPlay(), CardPredicates.possibleBlockers(card)).isEmpty()) { && !CardLists.filter(opp.getCreaturesInPlay(), CardPredicates.possibleBlockers(card)).isEmpty();
return false;
}
} else if (keyword.endsWith("CARDNAME can attack as though it didn't have defender.")) { } else if (keyword.endsWith("CARDNAME can attack as though it didn't have defender.")) {
if (!ph.isPlayerTurn(ai) || !card.hasKeyword(Keyword.DEFENDER) return ph.isPlayerTurn(ai) && card.hasKeyword(Keyword.DEFENDER)
|| ph.getPhase().isAfter(PhaseType.COMBAT_BEGIN) && !ph.getPhase().isAfter(PhaseType.COMBAT_BEGIN)
|| card.isTapped() || newPower <= 0) { && !card.isTapped() && newPower > 0;
return false;
}
} else if (keyword.equals("Prevent all combat damage that would be dealt to CARDNAME.")) { } else if (keyword.equals("Prevent all combat damage that would be dealt to CARDNAME.")) {
if (combat == null || !(combat.isBlocking(card) || combat.isBlocked(card))) { return combat != null && (combat.isBlocking(card) || combat.isBlocked(card));
return false;
}
} else if (keyword.equals("Menace")) { } else if (keyword.equals("Menace")) {
if (combat == null || !combat.isAttacking(card)) { return combat != null && combat.isAttacking(card);
return false;
}
} }
return true; return true;
} }
@@ -547,10 +470,7 @@ public abstract class PumpAiBase extends SpellAbilityAi {
return true; return true;
} }
//Don't waste a -7/-0 spell on a 1/1 creature //Don't waste a -7/-0 spell on a 1/1 creature
if (c.getNetPower() + attack > -2 || c.getNetPower() > 3) { return c.getNetPower() + attack > -2 || c.getNetPower() > 3;
return true;
}
return false;
} }
}); });
} else { } else {

View File

@@ -141,10 +141,7 @@ public class PumpAllAi extends PumpAiBase {
// evaluate both lists and pass only if human creatures are more // evaluate both lists and pass only if human creatures are more
// valuable // valuable
if ((ComputerUtilCard.evaluateCreatureList(comp) + 200) >= ComputerUtilCard.evaluateCreatureList(human)) { return (ComputerUtilCard.evaluateCreatureList(comp) + 200) < ComputerUtilCard.evaluateCreatureList(human);
return false;
}
return true;
} // end Curse } // end Curse
return !CardLists.getValidCards(getPumpCreatures(ai, sa, defense, power, keywords, false), valid, source.getController(), source).isEmpty(); return !CardLists.getValidCards(getPumpCreatures(ai, sa, defense, power, keywords, false), valid, source.getController(), source).isEmpty();

View File

@@ -18,9 +18,7 @@ public class RemoveFromCombatAi extends SpellAbilityAi {
// AI should only activate this during Human's turn // AI should only activate this during Human's turn
if ("RemoveBestAttacker".equals(sa.getParam("AILogic"))) { if ("RemoveBestAttacker".equals(sa.getParam("AILogic"))) {
if (aiPlayer.getGame().getCombat() != null && aiPlayer.getGame().getCombat().getDefenders().contains(aiPlayer)) { return aiPlayer.getGame().getCombat() != null && aiPlayer.getGame().getCombat().getDefenders().contains(aiPlayer);
return true;
}
} }
// TODO - implement AI // TODO - implement AI

View File

@@ -32,9 +32,7 @@ public class RepeatAi extends SpellAbilityAi {
// Set PayX here to maximum value. // Set PayX here to maximum value.
final int max = ComputerUtilMana.determineLeftoverMana(sa, ai); final int max = ComputerUtilMana.determineLeftoverMana(sa, ai);
source.setSVar("PayX", Integer.toString(max)); source.setSVar("PayX", Integer.toString(max));
if (max <= 0) { return max > 0;
return false;
}
} }
return true; return true;
} }

View File

@@ -35,9 +35,7 @@ public class RepeatEachAi extends SpellAbilityAi {
List<Card> humTokenCreats = CardLists.filter(aiPlayer.getOpponents().getCreaturesInPlay(), Presets.TOKEN); List<Card> humTokenCreats = CardLists.filter(aiPlayer.getOpponents().getCreaturesInPlay(), Presets.TOKEN);
List<Card> compTokenCreats = CardLists.filter(aiPlayer.getCreaturesInPlay(), Presets.TOKEN); List<Card> compTokenCreats = CardLists.filter(aiPlayer.getCreaturesInPlay(), Presets.TOKEN);
if (compTokenCreats.size() <= humTokenCreats.size()) { return compTokenCreats.size() > humTokenCreats.size();
return false;
}
} else if ("BalanceLands".equals(logic)) { } else if ("BalanceLands".equals(logic)) {
if (CardLists.filter(aiPlayer.getCardsIn(ZoneType.Battlefield), Presets.LANDS).size() >= 5) { if (CardLists.filter(aiPlayer.getCardsIn(ZoneType.Battlefield), Presets.LANDS).size() >= 5) {
return false; return false;
@@ -111,9 +109,7 @@ public class RepeatEachAi extends SpellAbilityAi {
} }
} }
// would not hit oppoent, don't do that // would not hit oppoent, don't do that
if (!hitOpp) { return hitOpp;
return false;
}
} }
// TODO Add some normal AI variability here // TODO Add some normal AI variability here

View File

@@ -80,11 +80,7 @@ public class RevealAi extends RevealAiBase {
} }
if (!revealHandTargetAI(ai, sa/*, false, mandatory*/)) { return revealHandTargetAI(ai, sa/*, false, mandatory*/);
return false;
}
return true;
} }
} }

View File

@@ -30,11 +30,7 @@ public class RevealHandAi extends RevealAiBase {
@Override @Override
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) { protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
if (!revealHandTargetAI(ai, sa/*, false, mandatory*/)) { return revealHandTargetAI(ai, sa/*, false, mandatory*/);
return false;
}
return true;
} }
} }

View File

@@ -144,7 +144,7 @@ public class RollPlanarDiceAi extends SpellAbilityAi {
} }
} }
return decideToRoll ? true : false; return decideToRoll;
} }
/* (non-Javadoc) /* (non-Javadoc)

View File

@@ -136,9 +136,7 @@ public class SacrificeAi extends SpellAbilityAi {
// Since all of the cards have AI:RemoveDeck:All, I enabled 1 for 1 // Since all of the cards have AI:RemoveDeck:All, I enabled 1 for 1
// (or X for X) trades for special decks // (or X for X) trades for special decks
if (humanList.size() < amount) { return humanList.size() >= amount;
return false;
}
} else if (defined.equals("You")) { } else if (defined.equals("You")) {
List<Card> computerList = List<Card> computerList =
CardLists.getValidCards(ai.getCardsIn(ZoneType.Battlefield), valid.split(","), sa.getActivatingPlayer(), sa.getHostCard(), sa); CardLists.getValidCards(ai.getCardsIn(ZoneType.Battlefield), valid.split(","), sa.getActivatingPlayer(), sa.getHostCard(), sa);

View File

@@ -145,10 +145,10 @@ public class ScryAi extends SpellAbilityAi {
if (maxToRemove <= 0) { if (maxToRemove <= 0) {
return false; return false;
} }
sa.setSVar("ChosenX", "Number$" + Integer.toString(maxToRemove)); sa.setSVar("ChosenX", "Number$" + maxToRemove);
} else { } else {
// no Instant or Sorceries anymore, just scry // no Instant or Sorceries anymore, just scry
sa.setSVar("ChosenX", "Number$" + Integer.toString(Math.min(counterNum, libsize))); sa.setSVar("ChosenX", "Number$" + Math.min(counterNum, libsize));
} }
} }
return true; return true;

View File

@@ -33,10 +33,7 @@ public class SetStateAi extends SpellAbilityAi {
return false; return false;
} }
if("Transform".equals(mode) || "Flip".equals(mode)) { return "Transform".equals(mode) || "Flip".equals(mode);
return true;
}
return false;
} }
@Override @Override
@@ -92,9 +89,7 @@ public class SetStateAi extends SpellAbilityAi {
} }
} }
if (sa.getTargets().getNumTargeted() < tgt.getMinTargets(source, sa)) { return sa.getTargets().getNumTargeted() >= tgt.getMinTargets(source, sa);
return false;
}
} }
} else if ("TurnFace".equals(mode)) { } else if ("TurnFace".equals(mode)) {
if (!sa.usesTargeting()) { if (!sa.usesTargeting()) {
@@ -123,9 +118,7 @@ public class SetStateAi extends SpellAbilityAi {
} }
} }
if (sa.getTargets().getNumTargeted() < tgt.getMinTargets(source, sa)) { return sa.getTargets().getNumTargeted() >= tgt.getMinTargets(source, sa);
return false;
}
} }
} }
return true; return true;
@@ -254,9 +247,7 @@ public class SetStateAi extends SpellAbilityAi {
// for legendary KI counter creatures // for legendary KI counter creatures
if (othercard.getCounters(CounterType.KI) >= source.getCounters(CounterType.KI)) { if (othercard.getCounters(CounterType.KI) >= source.getCounters(CounterType.KI)) {
// if the other legendary is useless try to replace it // if the other legendary is useless try to replace it
if (!ComputerUtilCard.isUselessCreature(aiPlayer, othercard)) { return ComputerUtilCard.isUselessCreature(aiPlayer, othercard);
return false;
}
} }
} }
} }
@@ -266,10 +257,6 @@ public class SetStateAi extends SpellAbilityAi {
public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) { public boolean confirmAction(Player player, SpellAbility sa, PlayerActionConfirmMode mode, String message) {
// TODO: improve the AI for when it may want to transform something that's optional to transform // TODO: improve the AI for when it may want to transform something that's optional to transform
if (!isSafeToTransformIntoLegendary(player, sa.getHostCard())) { return isSafeToTransformIntoLegendary(player, sa.getHostCard());
return false;
}
return true;
} }
} }

View File

@@ -11,10 +11,7 @@ public class SkipTurnAi extends SpellAbilityAi {
*/ */
@Override @Override
protected boolean canPlayAI(Player aiPlayer, SpellAbility sa) { protected boolean canPlayAI(Player aiPlayer, SpellAbility sa) {
if ("Always".equals(sa.getParam("AILogic"))) { return "Always".equals(sa.getParam("AILogic"));
return true;
}
return false;
} }
/* (non-Javadoc) /* (non-Javadoc)

View File

@@ -76,9 +76,7 @@ public class SurveilAi extends SpellAbilityAi {
if ("Never".equals(aiLogic)) { if ("Never".equals(aiLogic)) {
return false; return false;
} else if ("Once".equals(aiLogic)) { } else if ("Once".equals(aiLogic)) {
if (AiCardMemory.isRememberedCard(ai, source, AiCardMemory.MemorySet.ACTIVATED_THIS_TURN)) { return !AiCardMemory.isRememberedCard(ai, source, AiCardMemory.MemorySet.ACTIVATED_THIS_TURN);
return false;
}
} }
// TODO: add card-specific Surveil AI logic here when/if necessary // TODO: add card-specific Surveil AI logic here when/if necessary

View File

@@ -64,9 +64,7 @@ public class TapAi extends TapAiBase {
bFlag |= c.isUntapped(); bFlag |= c.isUntapped();
} }
if (!bFlag) { return bFlag;
return false;
}
} else { } else {
if ("TapForXCounters".equals(sa.getParam("AILogic"))) { if ("TapForXCounters".equals(sa.getParam("AILogic"))) {
// e.g. Waxmane Baku // e.g. Waxmane Baku
@@ -83,12 +81,9 @@ public class TapAi extends TapAiBase {
} }
sa.resetTargets(); sa.resetTargets();
if (!tapPrefTargeting(ai, source, tgt, sa, false)) { return tapPrefTargeting(ai, source, tgt, sa, false);
return false;
}
} }
return true;
} }
} }

View File

@@ -248,12 +248,8 @@ public abstract class TapAiBase extends SpellAbilityAi {
sa.getTargets().add(choice); sa.getTargets().add(choice);
} }
if (sa.getTargets().getNumTargeted() == 0) { // Nothing was ever targeted, so we need to bail.
// Nothing was ever targeted, so we need to bail. return sa.getTargets().getNumTargeted() != 0;
return false;
}
return true;
} }
/** /**
@@ -307,11 +303,7 @@ public abstract class TapAiBase extends SpellAbilityAi {
// just tap whatever we can // just tap whatever we can
tapList = list; tapList = list;
if (tapTargetList(ai, sa, tapList, mandatory)) { return tapTargetList(ai, sa, tapList, mandatory);
return true;
}
return false;
} }
@Override @Override

View File

@@ -93,9 +93,7 @@ public class TapAllAi extends SpellAbilityAi {
return CombatUtil.canAttack(c) && ComputerUtilCombat.canAttackNextTurn(c); return CombatUtil.canAttack(c) && ComputerUtilCombat.canAttackNextTurn(c);
} }
}); });
if(!any) { return any;
return false;
}
} }
return true; return true;
} }

View File

@@ -103,12 +103,9 @@ public class TokenAi extends SpellAbilityAi {
if (actualToken == null) { if (actualToken == null) {
final AbilitySub sub = sa.getSubAbility(); final AbilitySub sub = sa.getSubAbility();
if (pwPlus || (sub != null && SpellApiToAi.Converter.get(sub.getApi()).chkAIDrawback(sub, ai))) { // useful
return true; // planeswalker plus ability or sub-ability is // no token created
// useful return pwPlus || (sub != null && SpellApiToAi.Converter.get(sub.getApi()).chkAIDrawback(sub, ai)); // planeswalker plus ability or sub-ability is
} else {
return false; // no token created
}
} }
// X-cost spells // X-cost spells
@@ -154,10 +151,7 @@ public class TokenAi extends SpellAbilityAi {
&& !haste && !pwMinus) { && !haste && !pwMinus) {
return false; return false;
} }
if ((ph.getPhase().isAfter(PhaseType.COMBAT_BEGIN) || !ph.isPlayerTurn(ai)) && oneShot) { return (!ph.getPhase().isAfter(PhaseType.COMBAT_BEGIN) && ph.isPlayerTurn(ai)) || !oneShot;
return false;
}
return true;
} }
@Override @Override
@@ -269,10 +263,8 @@ public class TokenAi extends SpellAbilityAi {
list.add(token); list.add(token);
list = CardLists.getValidCards(list, valid.split(","), ai.getWeakestOpponent(), topStack.getHostCard(), sa); list = CardLists.getValidCards(list, valid.split(","), ai.getWeakestOpponent(), topStack.getHostCard(), sa);
list = CardLists.filter(list, CardPredicates.canBeSacrificedBy(topStack)); list = CardLists.filter(list, CardPredicates.canBeSacrificedBy(topStack));
if (ComputerUtilCard.evaluateCreature(token) < ComputerUtilCard.evaluateCreature(list.get(0)) return ComputerUtilCard.evaluateCreature(token) < ComputerUtilCard.evaluateCreature(list.get(0))
&& list.contains(token)) { && list.contains(token);
return true;
}
} }
return false; return false;
} }

View File

@@ -82,9 +82,7 @@ public class UnattachAllAi extends SpellAbilityAi {
//don't equip a worse creature //don't equip a worse creature
if (card.isEquipping()) { if (card.isEquipping()) {
Card oldTarget = card.getEquipping(); Card oldTarget = card.getEquipping();
if (ComputerUtilCard.evaluateCreature(oldTarget) > ComputerUtilCard.evaluateCreature(newTarget)) { return ComputerUtilCard.evaluateCreature(oldTarget) <= ComputerUtilCard.evaluateCreature(newTarget);
return false;
}
} }
} }

View File

@@ -45,11 +45,7 @@ public class UntapAi extends SpellAbilityAi {
return false; return false;
} }
if (!ComputerUtilCost.checkDiscardCost(ai, cost, sa.getHostCard())) { return ComputerUtilCost.checkDiscardCost(ai, cost, sa.getHostCard());
return false;
}
return true;
} }
@Override @Override
@@ -63,16 +59,11 @@ public class UntapAi extends SpellAbilityAi {
if (tgt == null) { if (tgt == null) {
final List<Card> pDefined = AbilityUtils.getDefinedCards(source, sa.getParam("Defined"), sa); final List<Card> pDefined = AbilityUtils.getDefinedCards(source, sa.getParam("Defined"), sa);
if (pDefined != null && pDefined.get(0).isUntapped() && pDefined.get(0).getController() == ai) { return pDefined == null || !pDefined.get(0).isUntapped() || pDefined.get(0).getController() != ai;
return false;
}
} else { } else {
if (!untapPrefTargeting(ai, tgt, sa, false)) { return untapPrefTargeting(ai, tgt, sa, false);
return false;
}
} }
return true;
} }
@Override @Override
@@ -86,11 +77,7 @@ public class UntapAi extends SpellAbilityAi {
// TODO: use Defined to determine, if this is an unfavorable result // TODO: use Defined to determine, if this is an unfavorable result
final List<Card> pDefined = AbilityUtils.getDefinedCards(sa.getHostCard(), sa.getParam("Defined"), sa); final List<Card> pDefined = AbilityUtils.getDefinedCards(sa.getHostCard(), sa.getParam("Defined"), sa);
if (pDefined != null && pDefined.get(0).isUntapped() && pDefined.get(0).getController() == ai) { return pDefined == null || !pDefined.get(0).isUntapped() || pDefined.get(0).getController() != ai;
return false;
}
return true;
} else { } else {
if (untapPrefTargeting(ai, tgt, sa, mandatory)) { if (untapPrefTargeting(ai, tgt, sa, mandatory)) {
return true; return true;
@@ -271,11 +258,7 @@ public class UntapAi extends SpellAbilityAi {
// just tap whatever we can // just tap whatever we can
tapList = list; tapList = list;
if (untapTargetList(source, tgt, sa, mandatory, tapList)) { return untapTargetList(source, tgt, sa, mandatory, tapList);
return true;
}
return false;
} }
private boolean untapTargetList(final Card source, final TargetRestrictions tgt, final SpellAbility sa, final boolean mandatory, private boolean untapTargetList(final Card source, final TargetRestrictions tgt, final SpellAbility sa, final boolean mandatory,
@@ -438,13 +421,10 @@ public class UntapAi extends SpellAbilityAi {
// no harm in doing this past declare blockers during the opponent's turn and right before our turn, // no harm in doing this past declare blockers during the opponent's turn and right before our turn,
// maybe we'll serendipitously untap into something like a removal spell or burn spell that'll help // maybe we'll serendipitously untap into something like a removal spell or burn spell that'll help
if (ph.getNextTurn() == ai return ph.getNextTurn() == ai
&& (ph.is(PhaseType.COMBAT_DECLARE_BLOCKERS) || ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_BLOCKERS))) { && (ph.is(PhaseType.COMBAT_DECLARE_BLOCKERS) || ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_BLOCKERS));
return true;
}
// haven't found any immediate playable options // haven't found any immediate playable options
return false;
} }
} }

View File

@@ -80,7 +80,7 @@ public class PossibleTargetSelector {
} }
private static class SimilarTargetSkipper { private static class SimilarTargetSkipper {
private ArrayListMultimap<String, Card> validTargetsMap = ArrayListMultimap.<String, Card>create(); private ArrayListMultimap<String, Card> validTargetsMap = ArrayListMultimap.create();
private HashMap<Card, String> cardTypeStrings = new HashMap<Card, String>(); private HashMap<Card, String> cardTypeStrings = new HashMap<Card, String>();
private HashMap<Card, Integer> creatureScores; private HashMap<Card, Integer> creatureScores;

View File

@@ -49,7 +49,7 @@ public class SpellAbilityChoicesIterator {
// TODO: Do we need to do something special to support cards that have extra costs // TODO: Do we need to do something special to support cards that have extra costs
// when choosing more modes, like Blessed Alliance? // when choosing more modes, like Blessed Alliance?
if (!allowRepeat) { if (!allowRepeat) {
modeIterator = CombinatoricsUtils.combinationsIterator(choices.size(), num);; modeIterator = CombinatoricsUtils.combinationsIterator(choices.size(), num);
} else { } else {
// Note: When allowRepeat is true, it does result in many possibilities being tried. // Note: When allowRepeat is true, it does result in many possibilities being tried.
// We should ideally prune some of those at a higher level. // We should ideally prune some of those at a higher level.

View File

@@ -308,9 +308,7 @@ public class SpellAbilityPicker {
return true; return true;
} }
List<PhaseType> phases = conditions.getPhases(); List<PhaseType> phases = conditions.getPhases();
if (phases.isEmpty() || phases.contains(PhaseType.MAIN1)) { return phases.isEmpty() || phases.contains(PhaseType.MAIN1);
return true;
}
} }
return false; return false;

View File

@@ -64,7 +64,7 @@ public class CardStorageReader {
void report(int current, int total); void report(int current, int total);
// does nothing, used when they pass null instead of an instance // does nothing, used when they pass null instead of an instance
public final static ProgressObserver emptyObserver = new ProgressObserver() { ProgressObserver emptyObserver = new ProgressObserver() {
@Override public void setOperationName(final String name, final boolean usePercents) {} @Override public void setOperationName(final String name, final boolean usePercents) {}
@Override public void report(final int current, final int total) {} @Override public void report(final int current, final int total) {}
}; };

View File

@@ -123,7 +123,7 @@ public final class ImageKeys {
int index = filename.lastIndexOf('_'); int index = filename.lastIndexOf('_');
if (index != -1) { if (index != -1) {
String setlessFilename = filename.substring(0, index); String setlessFilename = filename.substring(0, index);
String setCode = filename.substring(index + 1, filename.length()); String setCode = filename.substring(index + 1);
// try with upper case set // try with upper case set
file = findFile(dir, setlessFilename + "_" + setCode.toUpperCase()); file = findFile(dir, setlessFilename + "_" + setCode.toUpperCase());
if (file != null) { return file; } if (file != null) { return file; }
@@ -133,7 +133,7 @@ public final class ImageKeys {
// if there's an art variant try without it // if there's an art variant try without it
if (setlessFilename.matches(".*[0-9]*$")) { if (setlessFilename.matches(".*[0-9]*$")) {
file = findFile(dir, setlessFilename.replaceAll("[0-9]*$", "")); file = findFile(dir, setlessFilename.replaceAll("[0-9]*$", ""));
if (file != null) { return file; } return file;
} }
} }
} else if (filename.contains("/")) { } else if (filename.contains("/")) {
@@ -144,7 +144,7 @@ public final class ImageKeys {
// try lowering the art index to the minimum for regular cards // try lowering the art index to the minimum for regular cards
if (setlessFilename.contains(".full")) { if (setlessFilename.contains(".full")) {
file = findFile(dir, setlessFilename.replaceAll("[0-9]*[.]full", "1.full")); file = findFile(dir, setlessFilename.replaceAll("[0-9]*[.]full", "1.full"));
if (file != null) { return file; } return file;
} }
} }

View File

@@ -47,13 +47,8 @@ public abstract class LobbyPlayer {
} }
LobbyPlayer other = (LobbyPlayer) obj; LobbyPlayer other = (LobbyPlayer) obj;
if (name == null) { if (name == null) {
if (other.name != null) { return other.name == null;
return false; } else return name.equals(other.name);
}
} else if (!name.equals(other.name)) {
return false;
}
return true;
} }
public int getAvatarIndex() { public int getAvatarIndex() {

View File

@@ -41,7 +41,7 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
public final static char NameSetSeparator = '|'; public final static char NameSetSeparator = '|';
// need this to obtain cardReference by name+set+artindex // need this to obtain cardReference by name+set+artindex
private final ListMultimap<String, PaperCard> allCardsByName = Multimaps.newListMultimap(new TreeMap<String,Collection<PaperCard>>(String.CASE_INSENSITIVE_ORDER), CollectionSuppliers.<PaperCard>arrayLists()); private final ListMultimap<String, PaperCard> allCardsByName = Multimaps.newListMultimap(new TreeMap<String,Collection<PaperCard>>(String.CASE_INSENSITIVE_ORDER), CollectionSuppliers.arrayLists());
private final Map<String, PaperCard> uniqueCardsByName = Maps.newTreeMap(String.CASE_INSENSITIVE_ORDER); private final Map<String, PaperCard> uniqueCardsByName = Maps.newTreeMap(String.CASE_INSENSITIVE_ORDER);
private final Map<String, CardRules> rulesByName; private final Map<String, CardRules> rulesByName;
private final Map<String, ICardFace> facesByName = Maps.newTreeMap(String.CASE_INSENSITIVE_ORDER); private final Map<String, ICardFace> facesByName = Maps.newTreeMap(String.CASE_INSENSITIVE_ORDER);
@@ -62,7 +62,7 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
Random(false); Random(false);
final boolean filterSets; final boolean filterSets;
private SetPreference(boolean filterIrregularSets) { SetPreference(boolean filterIrregularSets) {
filterSets = filterIrregularSets; filterSets = filterIrregularSets;
} }

View File

@@ -189,7 +189,7 @@ public final class CardEdition implements Comparable<CardEdition> { // immutable
public String getBoosterMustContain() { return boosterMustContain; } public String getBoosterMustContain() { return boosterMustContain; }
public CardInSet[] getCards() { return cards; } public CardInSet[] getCards() { return cards; }
public Map<String, Integer> getTokens() { return tokenNormalized; }; public Map<String, Integer> getTokens() { return tokenNormalized; }
public static final Function<CardEdition, String> FN_GET_CODE = new Function<CardEdition, String>() { public static final Function<CardEdition, String> FN_GET_CODE = new Function<CardEdition, String>() {
@Override @Override

View File

@@ -20,7 +20,7 @@ final class CardFace implements ICardFace {
public enum FaceSelectionMethod { // public enum FaceSelectionMethod { //
USE_ACTIVE_FACE, USE_ACTIVE_FACE,
USE_PRIMARY_FACE, USE_PRIMARY_FACE,
COMBINE; COMBINE
} }
@@ -87,7 +87,7 @@ final class CardFace implements ICardFace {
void setInitialLoyalty(String value) { this.initialLoyalty = value; } void setInitialLoyalty(String value) { this.initialLoyalty = value; }
void setPtText(String value) { void setPtText(String value) {
final String k[] = value.split("/"); final String[] k = value.split("/");
if (k.length != 2) { if (k.length != 2) {
throw new RuntimeException("Creature '" + this.getName() + "' has bad p/t stats"); throw new RuntimeException("Creature '" + this.getName() + "' has bad p/t stats");

View File

@@ -85,7 +85,7 @@ public final class CardFacePredicates {
@Override @Override
public boolean apply(ICardFace input) { public boolean apply(ICardFace input) {
String k[] = valid.split("\\.", 2); String[] k = valid.split("\\.", 2);
if ("Card".equals(k[0])) { if ("Card".equals(k[0])) {
// okay // okay
@@ -110,10 +110,7 @@ public final class CardFacePredicates {
static protected boolean hasProperty(ICardFace input, final String v) { static protected boolean hasProperty(ICardFace input, final String v) {
if (v.startsWith("non")) { if (v.startsWith("non")) {
return !hasProperty(input, v.substring(3)); return !hasProperty(input, v.substring(3));
} else if (!input.getType().hasStringType(v)) { } else return input.getType().hasStringType(v);
return false;
}
return true;
} }
} }

View File

@@ -35,7 +35,7 @@ public enum CardRarity {
private final String shortName, longName; private final String shortName, longName;
private CardRarity(final String shortName0, final String longName0) { CardRarity(final String shortName0, final String longName0) {
shortName = shortName0; shortName = shortName0;
longName = longName0; longName = longName0;
} }

View File

@@ -435,10 +435,10 @@ public final class CardRulesPredicates {
return this.op(card.getManaCost().getGenericCost(), this.operand); return this.op(card.getManaCost().getGenericCost(), this.operand);
case POWER: case POWER:
value = card.getIntPower(); value = card.getIntPower();
return value != Integer.MAX_VALUE ? this.op(value, this.operand) : false; return value != Integer.MAX_VALUE && this.op(value, this.operand);
case TOUGHNESS: case TOUGHNESS:
value = card.getIntToughness(); value = card.getIntToughness();
return value != Integer.MAX_VALUE ? this.op(value, this.operand) : false; return value != Integer.MAX_VALUE && this.op(value, this.operand);
default: default:
return false; return false;
} }

View File

@@ -10,7 +10,7 @@ public enum CardSplitType
Split(FaceSelectionMethod.COMBINE, CardStateName.RightSplit), Split(FaceSelectionMethod.COMBINE, CardStateName.RightSplit),
Flip(FaceSelectionMethod.USE_PRIMARY_FACE, CardStateName.Flipped); Flip(FaceSelectionMethod.USE_PRIMARY_FACE, CardStateName.Flipped);
private CardSplitType(FaceSelectionMethod calcMode, CardStateName stateName) { CardSplitType(FaceSelectionMethod calcMode, CardStateName stateName) {
method = calcMode; method = calcMode;
this.changedStateName = stateName; this.changedStateName = stateName;
} }

View File

@@ -73,7 +73,7 @@ public final class CardType implements Comparable<CardType>, CardTypeView {
public final boolean isPermanent; public final boolean isPermanent;
private static final ImmutableList<String> allCoreTypeNames = EnumUtil.getNames(CoreType.class); private static final ImmutableList<String> allCoreTypeNames = EnumUtil.getNames(CoreType.class);
private CoreType(final boolean permanent) { CoreType(final boolean permanent) {
isPermanent = permanent; isPermanent = permanent;
} }
} }
@@ -598,10 +598,7 @@ public final class CardType implements Comparable<CardType>, CardTypeView {
if (multiWordTypes[0].startsWith(type) && !multiWordTypes[0].equals(type)) { if (multiWordTypes[0].startsWith(type) && !multiWordTypes[0].equals(type)) {
return true; return true;
} }
if (multiWordTypes[1].startsWith(type) && !multiWordTypes[1].equals(type)) { return multiWordTypes[1].startsWith(type) && !multiWordTypes[1].equals(type);
return true;
}
return false;
} }
public static class Constant { public static class Constant {

View File

@@ -5,5 +5,5 @@ package forge.card;
* *
*/ */
public interface ICardFace extends ICardCharacteristics, ICardRawAbilites, Comparable<ICardFace> { public interface ICardFace extends ICardCharacteristics, ICardRawAbilites, Comparable<ICardFace> {
public String getAltName(); String getAltName();
} }

View File

@@ -168,7 +168,7 @@ public final class MagicColor {
private final String name, symbol; private final String name, symbol;
private final byte colormask; private final byte colormask;
private Color(String name0, byte colormask0, String symbol0) { Color(String name0, byte colormask0, String symbol0) {
name = name0; name = name0;
colormask = colormask0; colormask = colormask0;
symbol = symbol0; symbol = symbol0;

View File

@@ -44,7 +44,7 @@ public abstract class ManaAtom {
if (s.length() == 2) { //if name is two characters, check for combination of two colors if (s.length() == 2) { //if name is two characters, check for combination of two colors
return (byte)(fromName(s.charAt(0)) | fromName(s.charAt(1))); return (byte)(fromName(s.charAt(0)) | fromName(s.charAt(1)));
} else if (s.length() == 1) { } else if (s.length() == 1) {
return (byte) fromName(s.charAt(0)); return fromName(s.charAt(0));
} }
s = s.toLowerCase(); s = s.toLowerCase();

Some files were not shown because too many files have changed in this diff Show More