Merge branch 'master' into 'start_trigger_run_params_refactor'

# Conflicts:
#   forge-gui-desktop/src/test/java/forge/gamesimulationtests/util/GameWrapper.java
This commit is contained in:
Ryan Wiedemann
2019-09-07 23:36:05 +00:00
881 changed files with 4590 additions and 4753 deletions

View File

@@ -6,7 +6,7 @@
<parent> <parent>
<artifactId>forge</artifactId> <artifactId>forge</artifactId>
<groupId>forge</groupId> <groupId>forge</groupId>
<version>1.6.28-SNAPSHOT</version> <version>1.6.29-SNAPSHOT</version>
</parent> </parent>
<artifactId>forge-ai</artifactId> <artifactId>forge-ai</artifactId>

View File

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

View File

@@ -81,7 +81,7 @@ public class AiAttackController {
this.defendingOpponent = choosePreferredDefenderPlayer(); this.defendingOpponent = choosePreferredDefenderPlayer();
this.oppList = getOpponentCreatures(this.defendingOpponent); this.oppList = getOpponentCreatures(this.defendingOpponent);
this.myList = ai.getCreaturesInPlay(); this.myList = ai.getCreaturesInPlay();
this.attackers = new ArrayList<Card>(); this.attackers = new ArrayList<>();
for (Card c : myList) { for (Card c : myList) {
if (CombatUtil.canAttack(c, this.defendingOpponent)) { if (CombatUtil.canAttack(c, this.defendingOpponent)) {
attackers.add(c); attackers.add(c);
@@ -95,7 +95,7 @@ public class AiAttackController {
this.defendingOpponent = choosePreferredDefenderPlayer(); this.defendingOpponent = choosePreferredDefenderPlayer();
this.oppList = getOpponentCreatures(this.defendingOpponent); this.oppList = getOpponentCreatures(this.defendingOpponent);
this.myList = ai.getCreaturesInPlay(); this.myList = ai.getCreaturesInPlay();
this.attackers = new ArrayList<Card>(); this.attackers = new ArrayList<>();
if (CombatUtil.canAttack(attacker, this.defendingOpponent)) { if (CombatUtil.canAttack(attacker, this.defendingOpponent)) {
attackers.add(attacker); attackers.add(attacker);
} }
@@ -103,8 +103,7 @@ public class AiAttackController {
} // overloaded constructor to evaluate single specified attacker } // overloaded constructor to evaluate single specified attacker
public static List<Card> getOpponentCreatures(final Player defender) { public static List<Card> getOpponentCreatures(final Player defender) {
List<Card> defenders = new ArrayList<Card>(); List<Card> defenders = new ArrayList<>(defender.getCreaturesInPlay());
defenders.addAll(defender.getCreaturesInPlay());
Predicate<Card> canAnimate = new Predicate<Card>() { Predicate<Card> canAnimate = new Predicate<Card>() {
@Override @Override
public boolean apply(Card c) { public boolean apply(Card c) {
@@ -151,7 +150,7 @@ public class AiAttackController {
* *
*/ */
public final static List<Card> sortAttackers(final List<Card> in) { public final static List<Card> sortAttackers(final List<Card> in) {
final List<Card> list = new ArrayList<Card>(); final List<Card> list = new ArrayList<>();
// Cards with triggers should come first (for Battle Cry) // Cards with triggers should come first (for Battle Cry)
for (final Card attacker : in) { for (final Card attacker : in) {
@@ -256,7 +255,7 @@ public class AiAttackController {
} }
public final static List<Card> getPossibleBlockers(final List<Card> blockers, final List<Card> attackers) { public final static List<Card> getPossibleBlockers(final List<Card> blockers, final List<Card> attackers) {
List<Card> possibleBlockers = new ArrayList<Card>(blockers); List<Card> possibleBlockers = new ArrayList<>(blockers);
possibleBlockers = CardLists.filter(possibleBlockers, new Predicate<Card>() { possibleBlockers = CardLists.filter(possibleBlockers, new Predicate<Card>() {
@Override @Override
public boolean apply(final Card c) { public boolean apply(final Card c) {
@@ -267,7 +266,7 @@ public class AiAttackController {
} }
public final static boolean canBlockAnAttacker(final Card c, final List<Card> attackers, final boolean nextTurn) { public final static boolean canBlockAnAttacker(final Card c, final List<Card> attackers, final boolean nextTurn) {
final List<Card> attackerList = new ArrayList<Card>(attackers); final List<Card> attackerList = new ArrayList<>(attackers);
if (!c.isCreature()) { if (!c.isCreature()) {
return false; return false;
} }
@@ -280,7 +279,7 @@ public class AiAttackController {
} }
public final static Card getCardCanBlockAnAttacker(final Card c, final List<Card> attackers, final boolean nextTurn) { public final static Card getCardCanBlockAnAttacker(final Card c, final List<Card> attackers, final boolean nextTurn) {
final List<Card> attackerList = new ArrayList<Card>(attackers); final List<Card> attackerList = new ArrayList<>(attackers);
if (!c.isCreature()) { if (!c.isCreature()) {
return null; return null;
} }
@@ -295,9 +294,9 @@ public class AiAttackController {
// this checks to make sure that the computer player doesn't lose when the human player attacks // this checks to make sure that the computer player doesn't lose when the human player attacks
// this method is used by getAttackers() // this method is used by getAttackers()
public final List<Card> notNeededAsBlockers(final Player ai, final List<Card> attackers) { public final List<Card> notNeededAsBlockers(final Player ai, final List<Card> attackers) {
final List<Card> notNeededAsBlockers = new ArrayList<Card>(attackers); final List<Card> notNeededAsBlockers = new ArrayList<>(attackers);
int fixedBlockers = 0; int fixedBlockers = 0;
final List<Card> vigilantes = new ArrayList<Card>(); final List<Card> vigilantes = new ArrayList<>();
//check for time walks //check for time walks
if (ai.getGame().getPhaseHandler().getNextTurn().equals(ai)) { if (ai.getGame().getPhaseHandler().getNextTurn().equals(ai)) {
return attackers; return attackers;
@@ -336,7 +335,7 @@ public class AiAttackController {
} }
} }
List<Card> opponentsAttackers = new ArrayList<Card>(oppList); List<Card> opponentsAttackers = new ArrayList<>(oppList);
opponentsAttackers = CardLists.filter(opponentsAttackers, new Predicate<Card>() { opponentsAttackers = CardLists.filter(opponentsAttackers, new Predicate<Card>() {
@Override @Override
public boolean apply(final Card c) { public boolean apply(final Card c) {
@@ -548,8 +547,7 @@ public class AiAttackController {
remainingAttackers.removeAll(unblockedAttackers); remainingAttackers.removeAll(unblockedAttackers);
for (Card blocker : this.blockers) { for (Card blocker : this.blockers) {
if (blocker.hasKeyword("CARDNAME can block any number of creatures.") if (blocker.canBlockAny()) {
|| blocker.hasKeyword("CARDNAME can block an additional ninety-nine creatures each combat.")) {
for (Card attacker : this.attackers) { for (Card attacker : this.attackers) {
if (CombatUtil.canBlock(attacker, blocker)) { if (CombatUtil.canBlock(attacker, blocker)) {
remainingAttackers.remove(attacker); remainingAttackers.remove(attacker);
@@ -565,14 +563,19 @@ public class AiAttackController {
if (remainingAttackers.isEmpty() || maxBlockersAfterCrew == 0) { if (remainingAttackers.isEmpty() || maxBlockersAfterCrew == 0) {
break; break;
} }
if (blocker.hasKeyword("CARDNAME can block an additional creature each combat.")) {
blockedAttackers.add(remainingAttackers.get(0)); int numExtraBlocks = blocker.canBlockAdditional();
remainingAttackers.remove(0); if (numExtraBlocks > 0) {
maxBlockersAfterCrew--; while (numExtraBlocks-- > 0 && !remainingAttackers.isEmpty()) {
if (remainingAttackers.isEmpty()) { blockedAttackers.add(remainingAttackers.get(0));
break; remainingAttackers.remove(0);
maxBlockersAfterCrew--;
} }
} }
if (remainingAttackers.isEmpty()) {
break;
}
blockedAttackers.add(remainingAttackers.get(0)); blockedAttackers.add(remainingAttackers.get(0));
remainingAttackers.remove(0); remainingAttackers.remove(0);
maxBlockersAfterCrew--; maxBlockersAfterCrew--;
@@ -681,7 +684,7 @@ public class AiAttackController {
// Determine who will be attacked // Determine who will be attacked
GameEntity defender = this.chooseDefender(combat, bAssault); GameEntity defender = this.chooseDefender(combat, bAssault);
List<Card> attackersLeft = new ArrayList<Card>(this.attackers); List<Card> attackersLeft = new ArrayList<>(this.attackers);
// TODO probably use AttackConstraints instead of only GlobalAttackRestrictions? // TODO probably use AttackConstraints instead of only GlobalAttackRestrictions?
GlobalAttackRestrictions restrict = GlobalAttackRestrictions.getGlobalRestrictions(ai, combat.getDefenders()); GlobalAttackRestrictions restrict = GlobalAttackRestrictions.getGlobalRestrictions(ai, combat.getDefenders());
@@ -821,12 +824,12 @@ public class AiAttackController {
int humanForcesForAttritionalAttack = 0; int humanForcesForAttritionalAttack = 0;
// examine the potential forces // examine the potential forces
final List<Card> nextTurnAttackers = new ArrayList<Card>(); final List<Card> nextTurnAttackers = new ArrayList<>();
int candidateCounterAttackDamage = 0; int candidateCounterAttackDamage = 0;
final Player opp = this.defendingOpponent; final Player opp = this.defendingOpponent;
// get the potential damage and strength of the AI forces // get the potential damage and strength of the AI forces
final List<Card> candidateAttackers = new ArrayList<Card>(); final List<Card> candidateAttackers = new ArrayList<>();
int candidateUnblockedDamage = 0; int candidateUnblockedDamage = 0;
for (final Card pCard : this.myList) { for (final Card pCard : this.myList) {
// if the creature can attack then it's a potential attacker this // if the creature can attack then it's a potential attacker this
@@ -885,7 +888,7 @@ public class AiAttackController {
final int outNumber = computerForces - humanForces; final int outNumber = computerForces - humanForces;
for (Card blocker : this.blockers) { for (Card blocker : this.blockers) {
if (blocker.hasKeyword("CARDNAME can block any number of creatures.")) { if (blocker.canBlockAny()) {
aiLifeToPlayerDamageRatio--; aiLifeToPlayerDamageRatio--;
} }
} }
@@ -908,7 +911,7 @@ public class AiAttackController {
// get player life total // get player life total
int humanLife = opp.getLife(); int humanLife = opp.getLife();
// get the list of attackers up to the first blocked one // get the list of attackers up to the first blocked one
final List<Card> attritionalAttackers = new ArrayList<Card>(); final List<Card> attritionalAttackers = new ArrayList<>();
for (int x = 0; x < (this.attackers.size() - humanForces); x++) { for (int x = 0; x < (this.attackers.size() - humanForces); x++) {
attritionalAttackers.add(this.attackers.get(x)); attritionalAttackers.add(this.attackers.get(x));
} }
@@ -1021,7 +1024,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
@@ -1457,7 +1460,7 @@ public class AiAttackController {
if (artifact != null) { if (artifact != null) {
return artifact; return artifact;
} }
return null; //should never get here 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

@@ -148,9 +148,7 @@ public class AiBlockController {
final CardCollection attackers = combat.getAttackersOf(defender); final CardCollection attackers = combat.getAttackersOf(defender);
// Begin with the attackers that pose the biggest threat // Begin with the attackers that pose the biggest threat
CardLists.sortByPowerDesc(attackers); CardLists.sortByPowerDesc(attackers);
for (final Card c : attackers) { sortedAttackers.addAll(attackers);
sortedAttackers.add(c);
}
} else if (defender instanceof Player && defender.equals(ai)) { } else if (defender instanceof Player && defender.equals(ai)) {
firstAttacker = combat.getAttackersOf(defender); firstAttacker = combat.getAttackersOf(defender);
} }
@@ -163,9 +161,7 @@ public class AiBlockController {
} }
} else { } else {
// add creatures attacking the Player to the back of the list // add creatures attacking the Player to the back of the list
for (final Card c : firstAttacker) { sortedAttackers.addAll(firstAttacker);
sortedAttackers.add(c);
}
} }
return sortedAttackers; return sortedAttackers;
} }
@@ -481,8 +477,7 @@ public class AiBlockController {
final int damageNeeded = ComputerUtilCombat.getDamageToKill(attacker) final int damageNeeded = ComputerUtilCombat.getDamageToKill(attacker)
+ ComputerUtilCombat.predictToughnessBonusOfAttacker(attacker, secondBlocker, combat, false); + ComputerUtilCombat.predictToughnessBonusOfAttacker(attacker, secondBlocker, combat, false);
List<Card> usableBlockersAsThird = new ArrayList<>(); List<Card> usableBlockersAsThird = new ArrayList<>(usableBlockers);
usableBlockersAsThird.addAll(usableBlockers);
usableBlockersAsThird.remove(secondBlocker); usableBlockersAsThird.remove(secondBlocker);
// loop over the remaining blockers in search of a good third blocker candidate // loop over the remaining blockers in search of a good third blocker candidate
@@ -859,7 +854,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 +874,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 +909,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 +1324,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

@@ -1645,7 +1645,6 @@ public class AiController {
// 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 true; return true;
} }
return false; return false;
} }
@@ -1690,16 +1689,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 +2072,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

@@ -39,7 +39,7 @@ import java.util.Map;
* @version $Id: AIProfile.java 20169 2013-03-08 08:24:17Z Agetian $ * @version $Id: AIProfile.java 20169 2013-03-08 08:24:17Z Agetian $
*/ */
public class AiProfileUtil { public class AiProfileUtil {
private static Map<String, Map<AiProps, String>> loadedProfiles = new HashMap<String, Map<AiProps, String>>(); private static Map<String, Map<AiProps, String>> loadedProfiles = new HashMap<>();
private static String AI_PROFILE_DIR; private static String AI_PROFILE_DIR;
private static final String AI_PROFILE_EXT = ".ai"; private static final String AI_PROFILE_EXT = ".ai";
@@ -74,7 +74,7 @@ public class AiProfileUtil {
* @param profileName a profile to load. * @param profileName a profile to load.
*/ */
private static final Map<AiProps, String> loadProfile(final String profileName) { private static final Map<AiProps, String> loadProfile(final String profileName) {
Map<AiProps, String> profileMap = new HashMap<AiProps, String>(); Map<AiProps, String> profileMap = new HashMap<>();
List<String> lines = FileUtil.readFile(buildFileName(profileName)); List<String> lines = FileUtil.readFile(buildFileName(profileName));
for (String line : lines) { for (String line : lines) {
@@ -122,7 +122,7 @@ public class AiProfileUtil {
*/ */
public static List<String> getAvailableProfiles() public static List<String> getAvailableProfiles()
{ {
final List<String> availableProfiles = new ArrayList<String>(); final List<String> availableProfiles = new ArrayList<>();
final File dir = new File(AI_PROFILE_DIR); final File dir = new File(AI_PROFILE_DIR);
final String[] children = dir.list(); final String[] children = dir.list();
@@ -146,7 +146,7 @@ public class AiProfileUtil {
* available profiles including special random profile tags. * available profiles including special random profile tags.
*/ */
public static List<String> getProfilesDisplayList() { public static List<String> getProfilesDisplayList() {
final List<String> availableProfiles = new ArrayList<String>(); final List<String> availableProfiles = new ArrayList<>();
availableProfiles.add(AI_PROFILE_RANDOM_MATCH); availableProfiles.add(AI_PROFILE_RANDOM_MATCH);
availableProfiles.add(AI_PROFILE_RANDOM_DUEL); availableProfiles.add(AI_PROFILE_RANDOM_DUEL);
availableProfiles.addAll(getAvailableProfiles()); availableProfiles.addAll(getAvailableProfiles());

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,11 +794,11 @@ 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)) { if (ComputerUtilCard.hasActiveUndyingOrPersist(c)) {
return true; return true;
} }
return false; return false;
} }
}); });
@@ -1514,7 +1514,7 @@ public class ComputerUtil {
*/ */
public static List<GameObject> predictThreatenedObjects(final Player ai, final SpellAbility sa, boolean top) { public static List<GameObject> predictThreatenedObjects(final Player ai, final SpellAbility sa, boolean top) {
final Game game = ai.getGame(); final Game game = ai.getGame();
final List<GameObject> objects = new ArrayList<GameObject>(); final List<GameObject> objects = new ArrayList<>();
if (game.getStack().isEmpty()) { if (game.getStack().isEmpty()) {
return objects; return objects;
} }
@@ -1543,8 +1543,8 @@ public class ComputerUtil {
private static Iterable<? extends GameObject> predictThreatenedObjects(final Player aiPlayer, final SpellAbility saviour, private static Iterable<? extends GameObject> predictThreatenedObjects(final Player aiPlayer, final SpellAbility saviour,
final SpellAbility topStack) { final SpellAbility topStack) {
Iterable<? extends GameObject> objects = new ArrayList<GameObject>(); Iterable<? extends GameObject> objects = new ArrayList<>();
final List<GameObject> threatened = new ArrayList<GameObject>(); final List<GameObject> threatened = new ArrayList<>();
ApiType saviourApi = saviour == null ? null : saviour.getApi(); ApiType saviourApi = saviour == null ? null : saviour.getApi();
int toughness = 0; int toughness = 0;
boolean grantIndestructible = false; boolean grantIndestructible = false;
@@ -1574,7 +1574,7 @@ public class ComputerUtil {
} }
} else { } else {
objects = topStack.getTargets().getTargets(); objects = topStack.getTargets().getTargets();
final List<GameObject> canBeTargeted = new ArrayList<GameObject>(); final List<GameObject> canBeTargeted = new ArrayList<>();
for (Object o : objects) { for (Object o : objects) {
if (o instanceof Card) { if (o instanceof Card) {
final Card c = (Card) o; final Card c = (Card) o;
@@ -1597,7 +1597,7 @@ public class ComputerUtil {
toughness = saviorWithSubs.hasParam("NumDef") ? toughness = saviorWithSubs.hasParam("NumDef") ?
AbilityUtils.calculateAmount(saviorWithSubs.getHostCard(), saviorWithSubs.getParam("NumDef"), saviour) : 0; AbilityUtils.calculateAmount(saviorWithSubs.getHostCard(), saviorWithSubs.getParam("NumDef"), saviour) : 0;
final List<String> keywords = saviorWithSubs.hasParam("KW") ? final List<String> keywords = saviorWithSubs.hasParam("KW") ?
Arrays.asList(saviorWithSubs.getParam("KW").split(" & ")) : new ArrayList<String>(); Arrays.asList(saviorWithSubs.getParam("KW").split(" & ")) : new ArrayList<>();
if (keywords.contains("Indestructible")) { if (keywords.contains("Indestructible")) {
grantIndestructible = true; grantIndestructible = true;
} }
@@ -1630,7 +1630,7 @@ public class ComputerUtil {
final SpellAbility sub = topStack.getSubAbility(); final SpellAbility sub = topStack.getSubAbility();
boolean noRegen = false; boolean noRegen = false;
if (sub != null && sub.getApi() == ApiType.Pump) { if (sub != null && sub.getApi() == ApiType.Pump) {
final List<String> keywords = sub.hasParam("KW") ? Arrays.asList(sub.getParam("KW").split(" & ")) : new ArrayList<String>(); final List<String> keywords = sub.hasParam("KW") ? Arrays.asList(sub.getParam("KW").split(" & ")) : new ArrayList<>();
for (String kw : keywords) { for (String kw : keywords) {
if (kw.contains("can't be regenerated")) { if (kw.contains("can't be regenerated")) {
noRegen = true; noRegen = true;
@@ -1925,9 +1925,9 @@ public class ComputerUtil {
if (predictThreatenedObjects(ai, null).contains(source)) { if (predictThreatenedObjects(ai, null).contains(source)) {
return true; return true;
} }
if (game.getPhaseHandler().inCombat() && if (game.getPhaseHandler().inCombat() &&
ComputerUtilCombat.combatantWouldBeDestroyed(ai, source, game.getCombat())) { ComputerUtilCombat.combatantWouldBeDestroyed(ai, source, game.getCombat())) {
return true; 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
@@ -1967,11 +1967,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 +1983,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;
} }
}); });
@@ -2059,7 +2053,7 @@ public class ComputerUtil {
//Too many lands! //Too many lands!
//Init //Init
int cntColors = MagicColor.WUBRG.length; int cntColors = MagicColor.WUBRG.length;
List<CardCollection> numProducers = new ArrayList<CardCollection>(cntColors); List<CardCollection> numProducers = new ArrayList<>(cntColors);
for (byte col : MagicColor.WUBRG) { for (byte col : MagicColor.WUBRG) {
numProducers.add(col, new CardCollection()); numProducers.add(col, new CardCollection());
} }
@@ -2186,10 +2180,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()) {
@@ -2205,7 +2196,7 @@ public class ComputerUtil {
} }
} }
} }
Collections.sort(goodChoices, CardLists.TextLenComparator); Collections.sort(goodChoices, CardLists.TextLenComparator);
CardLists.sortByCmcDesc(goodChoices); CardLists.sortByCmcDesc(goodChoices);
@@ -2225,7 +2216,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();
@@ -2292,8 +2283,7 @@ public class ComputerUtil {
chosen = ComputerUtilCard.getMostProminentType(list, valid); chosen = ComputerUtilCard.getMostProminentType(list, valid);
} else if (logic.equals("MostNeededType")) { } else if (logic.equals("MostNeededType")) {
// Choose a type that is in the deck, but not in hand or on the battlefield // Choose a type that is in the deck, but not in hand or on the battlefield
final List<String> basics = new ArrayList<String>(); final List<String> basics = new ArrayList<>(CardType.Constant.BASIC_TYPES);
basics.addAll(CardType.Constant.BASIC_TYPES);
CardCollectionView presentCards = CardCollection.combine(ai.getCardsIn(ZoneType.Battlefield), ai.getCardsIn(ZoneType.Hand)); CardCollectionView presentCards = CardCollection.combine(ai.getCardsIn(ZoneType.Battlefield), ai.getCardsIn(ZoneType.Hand));
CardCollectionView possibleCards = ai.getAllCards(); CardCollectionView possibleCards = ai.getAllCards();
@@ -2546,8 +2536,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;
} }
@@ -2598,7 +2587,7 @@ public class ComputerUtil {
int damage = 0; int damage = 0;
final Game game = player.getGame(); final Game game = player.getGame();
final Card card = sa.getHostCard(); final Card card = sa.getHostCard();
final FCollection<Trigger> theTriggers = new FCollection<Trigger>(); final FCollection<Trigger> theTriggers = new FCollection<>();
for (Card c : game.getCardsIn(ZoneType.Battlefield)) { for (Card c : game.getCardsIn(ZoneType.Battlefield)) {
theTriggers.addAll(c.getTriggers()); theTriggers.addAll(c.getTriggers());
@@ -2690,7 +2679,7 @@ public class ComputerUtil {
public static int getDamageFromETB(final Player player, final Card permanent) { public static int getDamageFromETB(final Player player, final Card permanent) {
int damage = 0; int damage = 0;
final Game game = player.getGame(); final Game game = player.getGame();
final FCollection<Trigger> theTriggers = new FCollection<Trigger>(); final FCollection<Trigger> theTriggers = new FCollection<>();
for (Card card : game.getCardsIn(ZoneType.Battlefield)) { for (Card card : game.getCardsIn(ZoneType.Battlefield)) {
theTriggers.addAll(card.getTriggers()); theTriggers.addAll(card.getTriggers());
@@ -2875,7 +2864,6 @@ public class ComputerUtil {
} else if (Iterables.any(list, CardTraitPredicates.hasParam("AiLogic", "LichDraw"))) { } else if (Iterables.any(list, CardTraitPredicates.hasParam("AiLogic", "LichDraw"))) {
return false; return false;
} }
return true; return true;
} }

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");
@@ -564,7 +564,7 @@ public class ComputerUtilCard {
AiBlockController aiBlk = new AiBlockController(ai); AiBlockController aiBlk = new AiBlockController(ai);
Combat combat = new Combat(ai); Combat combat = new Combat(ai);
combat.addAttacker(attacker, ai); combat.addAttacker(attacker, ai);
final List<Card> attackers = new ArrayList<Card>(); final List<Card> attackers = new ArrayList<>();
attackers.add(attacker); attackers.add(attacker);
aiBlk.assignBlockersGivenAttackers(combat, attackers); aiBlk.assignBlockersGivenAttackers(combat, attackers);
return ComputerUtilCombat.attackerWouldBeDestroyed(ai, attacker, combat); return ComputerUtilCombat.attackerWouldBeDestroyed(ai, attacker, combat);
@@ -788,7 +788,7 @@ public class ComputerUtilCard {
public static List<String> getColorByProminence(final List<Card> list) { public static List<String> getColorByProminence(final List<Card> list) {
int cntColors = MagicColor.WUBRG.length; int cntColors = MagicColor.WUBRG.length;
final List<Pair<Byte,Integer>> map = new ArrayList<Pair<Byte,Integer>>(); final List<Pair<Byte,Integer>> map = new ArrayList<>();
for(int i = 0; i < cntColors; i++) { for(int i = 0; i < cntColors; i++) {
map.add(MutablePair.of(MagicColor.WUBRG[i], 0)); map.add(MutablePair.of(MagicColor.WUBRG[i], 0));
} }
@@ -809,7 +809,7 @@ public class ComputerUtilCard {
}); });
// will this part be once dropped? // will this part be once dropped?
List<String> result = new ArrayList<String>(cntColors); List<String> result = new ArrayList<>(cntColors);
for(Pair<Byte, Integer> idx : map) { // fetch color names in the same order for(Pair<Byte, Integer> idx : map) { // fetch color names in the same order
result.add(MagicColor.toLongString(idx.getKey())); result.add(MagicColor.toLongString(idx.getKey()));
} }
@@ -881,7 +881,7 @@ public class ComputerUtilCard {
} }
}; };
public static List<String> chooseColor(SpellAbility sa, int min, int max, List<String> colorChoices) { public static List<String> chooseColor(SpellAbility sa, int min, int max, List<String> colorChoices) {
List<String> chosen = new ArrayList<String>(); List<String> chosen = new ArrayList<>();
Player ai = sa.getActivatingPlayer(); Player ai = sa.getActivatingPlayer();
final Game game = ai.getGame(); final Game game = ai.getGame();
Player opp = ai.getWeakestOpponent(); Player opp = ai.getWeakestOpponent();
@@ -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;
@@ -1571,7 +1571,7 @@ public class ComputerUtilCard {
Card pumped = CardFactory.copyCard(c, true); Card pumped = CardFactory.copyCard(c, true);
pumped.setSickness(c.hasSickness()); pumped.setSickness(c.hasSickness());
final long timestamp = c.getGame().getNextTimestamp(); final long timestamp = c.getGame().getNextTimestamp();
final List<String> kws = new ArrayList<String>(); final List<String> kws = new ArrayList<>();
for (String kw : keywords) { for (String kw : keywords) {
if (kw.startsWith("HIDDEN")) { if (kw.startsWith("HIDDEN")) {
pumped.addHiddenExtrinsicKeyword(kw); pumped.addHiddenExtrinsicKeyword(kw);
@@ -1601,8 +1601,8 @@ public class ComputerUtilCard {
} }
pumped.addNewPT(c.getCurrentPower(), c.getCurrentToughness(), timestamp); pumped.addNewPT(c.getCurrentPower(), c.getCurrentToughness(), timestamp);
pumped.addTempPowerBoost(c.getTempPowerBoost() + power + berserkPower); pumped.setPTBoost(c.getPTBoostMap());
pumped.addTempToughnessBoost(c.getTempToughnessBoost() + toughness); pumped.addPTBoost(power + berserkPower, toughness, timestamp);
pumped.addChangedCardKeywords(kws, null, false, false, timestamp); pumped.addChangedCardKeywords(kws, null, false, false, timestamp);
Set<CounterType> types = c.getCounters().keySet(); Set<CounterType> types = c.getCounters().keySet();
for(CounterType ct : types) { for(CounterType ct : types) {
@@ -1648,6 +1648,8 @@ public class ComputerUtilCard {
} }
list.add(vCard); // account for the static abilities that may be present on the card itself list.add(vCard); // account for the static abilities that may be present on the card itself
for (final Card c : list) { for (final Card c : list) {
// remove old boost that might be copied
vCard.removePTBoost(c.getTimestamp());
for (final StaticAbility stAb : c.getStaticAbilities()) { for (final StaticAbility stAb : c.getStaticAbilities()) {
final Map<String, String> params = stAb.getMapParams(); final Map<String, String> params = stAb.getMapParams();
if (!params.get("Mode").equals("Continuous")) { if (!params.get("Mode").equals("Continuous")) {
@@ -1663,26 +1665,25 @@ public class ComputerUtilCard {
if (!vCard.isValid(valid, c.getController(), c, null)) { if (!vCard.isValid(valid, c.getController(), c, null)) {
continue; continue;
} }
int att = 0;
if (params.containsKey("AddPower")) { if (params.containsKey("AddPower")) {
String addP = params.get("AddPower"); String addP = params.get("AddPower");
int att = 0;
if (addP.equals("AffectedX")) { if (addP.equals("AffectedX")) {
att = CardFactoryUtil.xCount(vCard, AbilityUtils.getSVar(stAb, addP)); att = CardFactoryUtil.xCount(vCard, AbilityUtils.getSVar(stAb, addP));
} else { } else {
att = AbilityUtils.calculateAmount(c, addP, stAb); att = AbilityUtils.calculateAmount(c, addP, stAb);
} }
vCard.addTempPowerBoost(att);
} }
int def = 0;
if (params.containsKey("AddToughness")) { if (params.containsKey("AddToughness")) {
String addT = params.get("AddToughness"); String addT = params.get("AddToughness");
int def = 0;
if (addT.equals("AffectedY")) { if (addT.equals("AffectedY")) {
def = CardFactoryUtil.xCount(vCard, AbilityUtils.getSVar(stAb, addT)); def = CardFactoryUtil.xCount(vCard, AbilityUtils.getSVar(stAb, addT));
} else { } else {
def = AbilityUtils.calculateAmount(c, addT, stAb); def = AbilityUtils.calculateAmount(c, addT, stAb);
} }
vCard.addTempToughnessBoost(def);
} }
vCard.addPTBoost(att, def, c.getTimestamp());
} }
} }
} }

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)
/** /**
@@ -883,20 +879,19 @@ public class ComputerUtilCombat {
} else if (mode == TriggerType.DamageDone) { } else if (mode == TriggerType.DamageDone) {
willTrigger = true; willTrigger = true;
if (trigParams.containsKey("ValidSource")) { if (trigParams.containsKey("ValidSource")) {
if (CardTraitBase.matchesValid(defender, trigParams.get("ValidSource").split(","), source) if (!(CardTraitBase.matchesValid(defender, trigParams.get("ValidSource").split(","), source)
&& defender.getNetCombatDamage() > 0 && defender.getNetCombatDamage() > 0
&& (!trigParams.containsKey("ValidTarget") && (!trigParams.containsKey("ValidTarget")
|| CardTraitBase.matchesValid(attacker, trigParams.get("ValidTarget").split(","), source))) { || CardTraitBase.matchesValid(attacker, trigParams.get("ValidTarget").split(","), source)))) {
return true; return false;
} }
if (CardTraitBase.matchesValid(attacker, trigParams.get("ValidSource").split(","), source) if (!(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;
} }
return willTrigger; return willTrigger;
@@ -970,7 +965,7 @@ public class ComputerUtilCombat {
} }
} }
final FCollection<Trigger> theTriggers = new FCollection<Trigger>(); final FCollection<Trigger> theTriggers = new FCollection<>();
for (Card card : game.getCardsIn(ZoneType.Battlefield)) { for (Card card : game.getCardsIn(ZoneType.Battlefield)) {
theTriggers.addAll(card.getTriggers()); theTriggers.addAll(card.getTriggers());
} }
@@ -1105,7 +1100,7 @@ public class ComputerUtilCombat {
} }
final Game game = attacker.getGame(); final Game game = attacker.getGame();
final FCollection<Trigger> theTriggers = new FCollection<Trigger>(); final FCollection<Trigger> theTriggers = new FCollection<>();
for (Card card : game.getCardsIn(ZoneType.Battlefield)) { for (Card card : game.getCardsIn(ZoneType.Battlefield)) {
theTriggers.addAll(card.getTriggers()); theTriggers.addAll(card.getTriggers());
} }
@@ -1289,7 +1284,7 @@ public class ComputerUtilCombat {
} }
final Game game = attacker.getGame(); final Game game = attacker.getGame();
final FCollection<Trigger> theTriggers = new FCollection<Trigger>(); final FCollection<Trigger> theTriggers = new FCollection<>();
for (Card card : game.getCardsIn(ZoneType.Battlefield)) { for (Card card : game.getCardsIn(ZoneType.Battlefield)) {
theTriggers.addAll(card.getTriggers()); theTriggers.addAll(card.getTriggers());
} }
@@ -1414,7 +1409,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
@@ -1510,7 +1505,7 @@ public class ComputerUtilCombat {
} }
final Game game = attacker.getGame(); final Game game = attacker.getGame();
final FCollection<Trigger> theTriggers = new FCollection<Trigger>(); final FCollection<Trigger> theTriggers = new FCollection<>();
for (Card card : game.getCardsIn(ZoneType.Battlefield)) { for (Card card : game.getCardsIn(ZoneType.Battlefield)) {
theTriggers.addAll(card.getTriggers()); theTriggers.addAll(card.getTriggers());
} }
@@ -1655,7 +1650,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
@@ -1731,7 +1726,7 @@ public class ComputerUtilCombat {
} }
// check Destroy triggers (Cockatrice and friends) // check Destroy triggers (Cockatrice and friends)
final FCollection<Trigger> theTriggers = new FCollection<Trigger>(); final FCollection<Trigger> theTriggers = new FCollection<>();
for (Card card : attacker.getGame().getCardsIn(ZoneType.Battlefield)) { for (Card card : attacker.getGame().getCardsIn(ZoneType.Battlefield)) {
theTriggers.addAll(card.getTriggers()); theTriggers.addAll(card.getTriggers());
} }
@@ -1948,7 +1943,7 @@ public class ComputerUtilCombat {
return defenderDamage >= attackerLife; return defenderDamage >= attackerLife;
} // defender no double strike } // defender no double strike
return false; // should never arrive here return false;// should never arrive here
} // canDestroyAttacker } // canDestroyAttacker
// For AI safety measures like Regeneration // For AI safety measures like Regeneration
@@ -2011,7 +2006,7 @@ public class ComputerUtilCombat {
} }
final Game game = blocker.getGame(); final Game game = blocker.getGame();
final FCollection<Trigger> theTriggers = new FCollection<Trigger>(); final FCollection<Trigger> theTriggers = new FCollection<>();
for (Card card : game.getCardsIn(ZoneType.Battlefield)) { for (Card card : game.getCardsIn(ZoneType.Battlefield)) {
theTriggers.addAll(card.getTriggers()); theTriggers.addAll(card.getTriggers());
} }
@@ -2195,7 +2190,7 @@ public class ComputerUtilCombat {
return attackerDamage >= defenderLife; return attackerDamage >= defenderLife;
} // attacker no double strike } // attacker no double strike
return false; // should never arrive here return false;// 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

@@ -170,8 +170,7 @@ public class ComputerUtilMana {
} }
return ability1.compareTo(ability2); return ability1.compareTo(ability2);
} } else {
else {
return preOrder; return preOrder;
} }
} }
@@ -344,6 +343,10 @@ public class ComputerUtilMana {
payMultipleMana(cost, manaProduced, ai); payMultipleMana(cost, manaProduced, ai);
// remove from available lists // remove from available lists
/*
* Refactoring this code to sourcesForShards.values().removeIf((SpellAbility srcSa) -> srcSa.getHostCard().equals(saPayment.getHostCard()));
* causes Android build not to compile
* */
Iterator<SpellAbility> itSa = sourcesForShards.values().iterator(); Iterator<SpellAbility> itSa = sourcesForShards.values().iterator();
while (itSa.hasNext()) { while (itSa.hasNext()) {
SpellAbility srcSa = itSa.next(); SpellAbility srcSa = itSa.next();
@@ -362,7 +365,7 @@ public class ComputerUtilMana {
private static boolean payManaCost(final ManaCostBeingPaid cost, final SpellAbility sa, final Player ai, final boolean test, boolean checkPlayable) { private static boolean payManaCost(final ManaCostBeingPaid cost, final SpellAbility sa, final Player ai, final boolean test, boolean checkPlayable) {
adjustManaCostToAvoidNegEffects(cost, sa.getHostCard(), ai); adjustManaCostToAvoidNegEffects(cost, sa.getHostCard(), ai);
List<Mana> manaSpentToPay = test ? new ArrayList<Mana>() : sa.getPayingMana(); List<Mana> manaSpentToPay = test ? new ArrayList<>() : sa.getPayingMana();
boolean purePhyrexian = cost.containsOnlyPhyrexianMana(); boolean purePhyrexian = cost.containsOnlyPhyrexianMana();
int testEnergyPool = ai.getCounters(CounterType.ENERGY); int testEnergyPool = ai.getCounters(CounterType.ENERGY);
@@ -487,6 +490,10 @@ public class ComputerUtilMana {
payMultipleMana(cost, manaProduced, ai); payMultipleMana(cost, manaProduced, ai);
// remove from available lists // remove from available lists
/*
* Refactoring this code to sourcesForShards.values().removeIf((SpellAbility srcSa) -> srcSa.getHostCard().equals(saPayment.getHostCard()));
* causes Android build not to compile
* */
Iterator<SpellAbility> itSa = sourcesForShards.values().iterator(); Iterator<SpellAbility> itSa = sourcesForShards.values().iterator();
while (itSa.hasNext()) { while (itSa.hasNext()) {
SpellAbility srcSa = itSa.next(); SpellAbility srcSa = itSa.next();
@@ -517,6 +524,10 @@ public class ComputerUtilMana {
if (hasConverge) { // hack to prevent converge re-using sources if (hasConverge) { // hack to prevent converge re-using sources
// remove from available lists // remove from available lists
/*
* Refactoring this code to sourcesForShards.values().removeIf((SpellAbility srcSa) -> srcSa.getHostCard().equals(saPayment.getHostCard()));
* causes Android build not to compile
* */
Iterator<SpellAbility> itSa = sourcesForShards.values().iterator(); Iterator<SpellAbility> itSa = sourcesForShards.values().iterator();
while (itSa.hasNext()) { while (itSa.hasNext()) {
SpellAbility srcSa = itSa.next(); SpellAbility srcSa = itSa.next();
@@ -880,10 +891,12 @@ public class ComputerUtilMana {
// For combat tricks, always obey mana reservation // For combat tricks, always obey mana reservation
if (curPhase == PhaseType.COMBAT_DECLARE_BLOCKERS || curPhase == PhaseType.CLEANUP) { if (curPhase == PhaseType.COMBAT_DECLARE_BLOCKERS || curPhase == PhaseType.CLEANUP) {
AiCardMemory.clearMemorySet(ai, AiCardMemory.MemorySet.HELD_MANA_SOURCES_FOR_DECLBLK); if (!(ai.getGame().getPhaseHandler().isPlayerTurn(ai))) {
} else if (!(ai.getGame().getPhaseHandler().isPlayerTurn(ai)) && (curPhase == PhaseType.COMBAT_DECLARE_BLOCKERS || curPhase == PhaseType.CLEANUP)) { AiCardMemory.clearMemorySet(ai, AiCardMemory.MemorySet.HELD_MANA_SOURCES_FOR_ENEMY_DECLBLK);
AiCardMemory.clearMemorySet(ai, AiCardMemory.MemorySet.HELD_MANA_SOURCES_FOR_ENEMY_DECLBLK); AiCardMemory.clearMemorySet(ai, AiCardMemory.MemorySet.CHOSEN_FOG_EFFECT);
AiCardMemory.clearMemorySet(ai, AiCardMemory.MemorySet.CHOSEN_FOG_EFFECT); }
else
AiCardMemory.clearMemorySet(ai, AiCardMemory.MemorySet.HELD_MANA_SOURCES_FOR_DECLBLK);
} else { } else {
if ((AiCardMemory.isRememberedCard(ai, sourceCard, AiCardMemory.MemorySet.HELD_MANA_SOURCES_FOR_DECLBLK)) || if ((AiCardMemory.isRememberedCard(ai, sourceCard, AiCardMemory.MemorySet.HELD_MANA_SOURCES_FOR_DECLBLK)) ||
(AiCardMemory.isRememberedCard(ai, sourceCard, AiCardMemory.MemorySet.HELD_MANA_SOURCES_FOR_ENEMY_DECLBLK))) { (AiCardMemory.isRememberedCard(ai, sourceCard, AiCardMemory.MemorySet.HELD_MANA_SOURCES_FOR_ENEMY_DECLBLK))) {
@@ -1346,7 +1359,7 @@ public class ComputerUtilMana {
final ListMultimap<Integer, SpellAbility> manaMap = ArrayListMultimap.create(); final ListMultimap<Integer, SpellAbility> manaMap = ArrayListMultimap.create();
final Game game = ai.getGame(); final Game game = ai.getGame();
List<ReplacementEffect> replacementEffects = new ArrayList<ReplacementEffect>(); List<ReplacementEffect> replacementEffects = new ArrayList<>();
for (final Player p : game.getPlayers()) { for (final Player p : game.getPlayers()) {
for (final Card crd : p.getAllCards()) { for (final Card crd : p.getAllCards()) {
for (final ReplacementEffect replacementEffect : crd.getReplacementEffects()) { for (final ReplacementEffect replacementEffect : crd.getReplacementEffects()) {
@@ -1552,7 +1565,7 @@ public class ComputerUtilMana {
* @return map between creatures and shards to convoke * @return map between creatures and shards to convoke
*/ */
public static Map<Card, ManaCostShard> getConvokeOrImproviseFromList(final ManaCost cost, List<Card> list, boolean improvise) { public static Map<Card, ManaCostShard> getConvokeOrImproviseFromList(final ManaCost cost, List<Card> list, boolean improvise) {
final Map<Card, ManaCostShard> convoke = new HashMap<Card, ManaCostShard>(); final Map<Card, ManaCostShard> convoke = new HashMap<>();
Card convoked = null; Card convoked = null;
if (!improvise) { if (!improvise) {
for (ManaCostShard toPay : cost) { for (ManaCostShard toPay : cost) {

View File

@@ -42,7 +42,7 @@ import java.util.*;
import java.util.Map.Entry; import java.util.Map.Entry;
public abstract class GameState { public abstract class GameState {
private static final Map<ZoneType, String> ZONES = new HashMap<ZoneType, String>(); private static final Map<ZoneType, String> ZONES = new HashMap<>();
static { static {
ZONES.put(ZoneType.Battlefield, "battlefield"); ZONES.put(ZoneType.Battlefield, "battlefield");
ZONES.put(ZoneType.Hand, "hand"); ZONES.put(ZoneType.Hand, "hand");
@@ -67,8 +67,8 @@ public abstract class GameState {
private boolean puzzleCreatorState = false; private boolean puzzleCreatorState = false;
private final Map<ZoneType, String> humanCardTexts = new EnumMap<ZoneType, String>(ZoneType.class); private final Map<ZoneType, String> humanCardTexts = new EnumMap<>(ZoneType.class);
private final Map<ZoneType, String> aiCardTexts = new EnumMap<ZoneType, String>(ZoneType.class); private final Map<ZoneType, String> aiCardTexts = new EnumMap<>(ZoneType.class);
private final Map<Integer, Card> idToCard = new HashMap<>(); private final Map<Integer, Card> idToCard = new HashMap<>();
private final Map<Card, Integer> cardToAttachId = new HashMap<>(); private final Map<Card, Integer> cardToAttachId = new HashMap<>();
@@ -255,7 +255,7 @@ public abstract class GameState {
newText.append(";"); newText.append(";");
} }
if (c.isToken()) { if (c.isToken()) {
newText.append("t:" + new TokenInfo(c).toString()); newText.append("t:").append(new TokenInfo(c).toString());
} else { } else {
if (c.getPaperCard() == null) { if (c.getPaperCard() == null) {
return; return;
@@ -378,7 +378,7 @@ public abstract class GameState {
newText.append("|Attacking"); newText.append("|Attacking");
GameEntity def = c.getGame().getCombat().getDefenderByAttacker(c); GameEntity def = c.getGame().getCombat().getDefenderByAttacker(c);
if (def instanceof Card) { if (def instanceof Card) {
newText.append(":" + def.getId()); newText.append(":").append(def.getId());
} }
} }
} }
@@ -654,15 +654,15 @@ public abstract class GameState {
} }
private String processManaPool(ManaPool manaPool) { private String processManaPool(ManaPool manaPool) {
String mana = ""; StringBuilder mana = new StringBuilder();
for (final byte c : MagicColor.WUBRGC) { for (final byte c : MagicColor.WUBRGC) {
int amount = manaPool.getAmountOfColor(c); int amount = manaPool.getAmountOfColor(c);
for (int i = 0; i < amount; i++) { for (int i = 0; i < amount; i++) {
mana += MagicColor.toShortString(c) + " "; mana.append(MagicColor.toShortString(c)).append(" ");
} }
} }
return mana.trim(); return mana.toString().trim();
} }
private void updateManaPool(Player p, String manaDef, boolean clearPool, boolean persistent) { private void updateManaPool(Player p, String manaDef, boolean clearPool, boolean persistent) {
@@ -1062,7 +1062,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);
@@ -1079,7 +1079,7 @@ public abstract class GameState {
p.getZone(zt).removeAllCards(true); p.getZone(zt).removeAllCards(true);
} }
Map<ZoneType, CardCollectionView> playerCards = new EnumMap<ZoneType, CardCollectionView>(ZoneType.class); Map<ZoneType, CardCollectionView> playerCards = new EnumMap<>(ZoneType.class);
for (Entry<ZoneType, String> kv : cardTexts.entrySet()) { for (Entry<ZoneType, String> kv : cardTexts.entrySet()) {
String value = kv.getValue(); String value = kv.getValue();
playerCards.put(kv.getKey(), processCardsForZone(value.isEmpty() ? new String[0] : value.split(";"), p)); playerCards.put(kv.getKey(), processCardsForZone(value.isEmpty() ? new String[0] : value.split(";"), p));
@@ -1092,7 +1092,7 @@ public abstract class GameState {
for (Entry<ZoneType, CardCollectionView> kv : playerCards.entrySet()) { for (Entry<ZoneType, CardCollectionView> kv : playerCards.entrySet()) {
PlayerZone zone = p.getZone(kv.getKey()); PlayerZone zone = p.getZone(kv.getKey());
if (kv.getKey() == ZoneType.Battlefield) { if (kv.getKey() == ZoneType.Battlefield) {
List<Card> cards = new ArrayList<Card>(); List<Card> cards = new ArrayList<>();
for (final Card c : kv.getValue()) { for (final Card c : kv.getValue()) {
if (c.isToken()) { if (c.isToken()) {
cards.add(c); cards.add(c);
@@ -1108,7 +1108,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

@@ -168,8 +168,8 @@ public class PlayerControllerAi extends PlayerController {
if (delayedReveal != null) { if (delayedReveal != null) {
reveal(delayedReveal.getCards(), delayedReveal.getZone(), delayedReveal.getOwner(), delayedReveal.getMessagePrefix()); reveal(delayedReveal.getCards(), delayedReveal.getZone(), delayedReveal.getOwner(), delayedReveal.getMessagePrefix());
} }
FCollection<T> remaining = new FCollection<T>(optionList); FCollection<T> remaining = new FCollection<>(optionList);
List<T> selecteds = new ArrayList<T>(); List<T> selecteds = new ArrayList<>();
T selected; T selected;
do { do {
selected = chooseSingleEntityForEffect(remaining, null, sa, title, selecteds.size()>=min, targetedPlayer); selected = chooseSingleEntityForEffect(remaining, null, sa, title, selecteds.size()>=min, targetedPlayer);
@@ -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) {
@@ -1072,7 +1072,7 @@ public class PlayerControllerAi extends PlayerController {
} }
}); });
} else { } else {
return new HashMap<Card, ManaCostShard>(); return new HashMap<>();
} }
} }

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)) {
@@ -411,11 +403,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) {
@@ -522,7 +510,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 +556,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);
@@ -911,15 +896,14 @@ 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 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 if (!ph.isPlayerTurn(ai) || !ph.is(PhaseType.MAIN2)) {
// Only activate in AI's own turn (sans the exception above) // Only activate in AI's own turn (sans the exception above)
return false; return false;
} }
return true; return true;
} }
} }
@@ -941,11 +925,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 +1290,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 +1318,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;
}
} }
} }
@@ -1465,8 +1439,7 @@ public class SpecialCardAi {
} else if (!ph.isPlayerTurn(ai)) { } else if (!ph.isPlayerTurn(ai)) {
// Only activate in AI's own turn (sans the exception above) // Only activate in AI's own turn (sans the exception above)
return false; return false;
} }
return true; 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

@@ -207,21 +207,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

@@ -31,10 +31,7 @@ import forge.game.trigger.TriggerType;
import forge.game.zone.ZoneType; import forge.game.zone.ZoneType;
import forge.util.MyRandom; import forge.util.MyRandom;
import java.util.ArrayList; import java.util.*;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
public class AttachAi extends SpellAbilityAi { public class AttachAi extends SpellAbilityAi {
@@ -123,9 +120,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 +234,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;
@@ -455,7 +448,7 @@ public class AttachAi extends SpellAbilityAi {
*/ */
private static Player attachToPlayerAIPreferences(final Player aiPlayer, final SpellAbility sa, private static Player attachToPlayerAIPreferences(final Player aiPlayer, final SpellAbility sa,
final boolean mandatory) { final boolean mandatory) {
List<Player> targetable = new ArrayList<Player>(); List<Player> targetable = new ArrayList<>();
for (final Player player : aiPlayer.getGame().getPlayers()) { for (final Player player : aiPlayer.getGame().getPlayers()) {
if (sa.canTarget(player)) { if (sa.canTarget(player)) {
targetable.add(player); targetable.add(player);
@@ -859,7 +852,7 @@ public class AttachAi extends SpellAbilityAi {
int totToughness = 0; int totToughness = 0;
int totPower = 0; int totPower = 0;
final List<String> keywords = new ArrayList<String>(); final List<String> keywords = new ArrayList<>();
for (final StaticAbility stAbility : attachSource.getStaticAbilities()) { for (final StaticAbility stAbility : attachSource.getStaticAbilities()) {
final Map<String, String> stabMap = stAbility.getMapParams(); final Map<String, String> stabMap = stAbility.getMapParams();
@@ -879,15 +872,11 @@ public class AttachAi extends SpellAbilityAi {
String kws = stabMap.get("AddKeyword"); String kws = stabMap.get("AddKeyword");
if (kws != null) { if (kws != null) {
for (final String kw : kws.split(" & ")) { keywords.addAll(Arrays.asList(kws.split(" & ")));
keywords.add(kw);
}
} }
kws = stabMap.get("AddHiddenKeyword"); kws = stabMap.get("AddHiddenKeyword");
if (kws != null) { if (kws != null) {
for (final String kw : kws.split(" & ")) { keywords.addAll(Arrays.asList(kws.split(" & ")));
keywords.add(kw);
}
} }
} }
} }
@@ -910,7 +899,7 @@ public class AttachAi extends SpellAbilityAi {
Card c = null; Card c = null;
if (prefList == null || prefList.isEmpty()) { if (prefList == null || prefList.isEmpty()) {
prefList = new ArrayList<Card>(list); prefList = new ArrayList<>(list);
} else { } else {
c = ComputerUtilCard.getBestAI(prefList); c = ComputerUtilCard.getBestAI(prefList);
if (c != null) { if (c != null) {
@@ -964,7 +953,7 @@ public class AttachAi extends SpellAbilityAi {
protected boolean doTriggerAINoCost(final Player ai, final SpellAbility sa, final boolean mandatory) { protected boolean doTriggerAINoCost(final Player ai, final SpellAbility sa, final boolean mandatory) {
final Card card = sa.getHostCard(); final Card card = sa.getHostCard();
// Check if there are any valid targets // Check if there are any valid targets
List<GameObject> targets = new ArrayList<GameObject>(); List<GameObject> targets = new ArrayList<>();
final TargetRestrictions tgt = sa.getTargetRestrictions(); final TargetRestrictions tgt = sa.getTargetRestrictions();
if (tgt == null) { if (tgt == null) {
targets = AbilityUtils.getDefinedObjects(sa.getHostCard(), sa.getParam("Defined"), sa); targets = AbilityUtils.getDefinedObjects(sa.getHostCard(), sa.getParam("Defined"), sa);
@@ -987,9 +976,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;
}
} }
} }
@@ -1156,8 +1143,9 @@ public class AttachAi extends SpellAbilityAi {
int totToughness = 0; int totToughness = 0;
int totPower = 0; int totPower = 0;
final List<String> keywords = new ArrayList<String>(); final List<String> keywords = new ArrayList<>();
boolean grantingAbilities = false; boolean grantingAbilities = false;
boolean grantingExtraBlock = false;
for (final StaticAbility stAbility : attachSource.getStaticAbilities()) { for (final StaticAbility stAbility : attachSource.getStaticAbilities()) {
final Map<String, String> stabMap = stAbility.getMapParams(); final Map<String, String> stabMap = stAbility.getMapParams();
@@ -1176,18 +1164,15 @@ public class AttachAi extends SpellAbilityAi {
totPower += AbilityUtils.calculateAmount(attachSource, stabMap.get("AddPower"), stAbility); totPower += AbilityUtils.calculateAmount(attachSource, stabMap.get("AddPower"), stAbility);
grantingAbilities |= stabMap.containsKey("AddAbility"); grantingAbilities |= stabMap.containsKey("AddAbility");
grantingExtraBlock |= stabMap.containsKey("CanBlockAmount") || stabMap.containsKey("CanBlockAny");
String kws = stabMap.get("AddKeyword"); String kws = stabMap.get("AddKeyword");
if (kws != null) { if (kws != null) {
for (final String kw : kws.split(" & ")) { keywords.addAll(Arrays.asList(kws.split(" & ")));
keywords.add(kw);
}
} }
kws = stabMap.get("AddHiddenKeyword"); kws = stabMap.get("AddHiddenKeyword");
if (kws != null) { if (kws != null) {
for (final String kw : kws.split(" & ")) { keywords.addAll(Arrays.asList(kws.split(" & ")));
keywords.add(kw);
}
} }
} }
} }
@@ -1209,20 +1194,27 @@ public class AttachAi extends SpellAbilityAi {
} }
//only add useful keywords unless P/T bonus is significant //only add useful keywords unless P/T bonus is significant
if (totToughness + totPower < 4 && !keywords.isEmpty()) { if (totToughness + totPower < 4 && (!keywords.isEmpty() || grantingExtraBlock)) {
final int pow = totPower; final int pow = totPower;
final boolean extraBlock = grantingExtraBlock;
prefList = CardLists.filter(prefList, new Predicate<Card>() { prefList = CardLists.filter(prefList, new Predicate<Card>() {
@Override @Override
public boolean apply(final Card c) { public boolean apply(final Card c) {
for (final String keyword : keywords) { if (!keywords.isEmpty()) {
if (isUsefulAttachKeyword(keyword, c, sa, pow)) { for (final String keyword : keywords) {
return true; if (isUsefulAttachKeyword(keyword, c, sa, pow)) {
} return true;
if (c.hasKeyword(Keyword.INFECT) && pow >= 2) { }
// consider +2 power a significant bonus on Infect creatures
return true;
} }
} }
if (c.hasKeyword(Keyword.INFECT) && pow >= 2) {
// consider +2 power a significant bonus on Infect creatures
return true;
}
if (extraBlock && CombatUtil.canBlock(c, true) && !c.canBlockAny()) {
return true;
}
return false; return false;
} }
}); });
@@ -1353,7 +1345,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 +1549,52 @@ 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.")) {
if (!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.")) {
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 +1615,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 +1628,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 +1657,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;
} }
@@ -484,7 +482,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
} }
} }
List<ZoneType> origin = new ArrayList<ZoneType>(); List<ZoneType> origin = new ArrayList<>();
if (sa.hasParam("Origin")) { if (sa.hasParam("Origin")) {
origin = ZoneType.listValueOf(sa.getParam("Origin")); origin = ZoneType.listValueOf(sa.getParam("Origin"));
} }
@@ -559,7 +557,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
*/ */
private static Card basicManaFixing(final Player ai, final List<Card> list) { // Search for a Basic Land private static Card basicManaFixing(final Player ai, final List<Card> list) { // Search for a Basic Land
final CardCollectionView combined = CardCollection.combine(ai.getCardsIn(ZoneType.Battlefield), ai.getCardsIn(ZoneType.Hand)); final CardCollectionView combined = CardCollection.combine(ai.getCardsIn(ZoneType.Battlefield), ai.getCardsIn(ZoneType.Hand));
final List<String> basics = new ArrayList<String>(); final List<String> basics = new ArrayList<>();
// what types can I go get? // what types can I go get?
for (final String name : MagicColor.Constant.BASIC_LANDS) { for (final String name : MagicColor.Constant.BASIC_LANDS) {
@@ -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();
@@ -1291,7 +1281,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
final List<GameObject> objects = ComputerUtil final List<GameObject> objects = ComputerUtil
.predictThreatenedObjects(ai, sa); .predictThreatenedObjects(ai, sa);
final List<Card> threatenedTargets = new ArrayList<Card>(); final List<Card> threatenedTargets = new ArrayList<>();
for (final Card c : aiPermanents) { for (final Card c : aiPermanents) {
if (objects.contains(c)) { if (objects.contains(c)) {
@@ -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

@@ -103,7 +103,7 @@ public class ChooseGenericEffectAi extends SpellAbilityAi {
Cost unless = new Cost(unlessCost, false); Cost unless = new Cost(unlessCost, false);
SpellAbility paycost = new SpellAbility.EmptySa(sa.getHostCard(), player); SpellAbility paycost = new SpellAbility.EmptySa(sa.getHostCard(), player);
paycost.setPayCosts(unless); paycost.setPayCosts(unless);
if (ComputerUtilCost.willPayUnlessCost(sp, player, unless, false, new FCollection<Player>(player)) if (ComputerUtilCost.willPayUnlessCost(sp, player, unless, false, new FCollection<>(player))
&& ComputerUtilCost.canPayCost(paycost, player)) { && ComputerUtilCost.canPayCost(paycost, player)) {
return sp; return sp;
} }
@@ -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;
} }

View File

@@ -48,6 +48,7 @@ public class CountersMultiplyAi extends SpellAbilityAi {
if (!c.canReceiveCounters(counterType)) { if (!c.canReceiveCounters(counterType)) {
return false; 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
@@ -96,10 +97,7 @@ public class CountersMultiplyAi extends SpellAbilityAi {
@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) {

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;
} }
@@ -595,7 +595,7 @@ public class CountersPutAi extends SpellAbilityAi {
if (ComputerUtil.waitForBlocking(sa)) { if (ComputerUtil.waitForBlocking(sa)) {
return false; return false;
} }
return true; return true;
} }

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

@@ -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);
@@ -952,9 +947,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

@@ -100,7 +100,7 @@ public class DamagePreventAi extends SpellAbilityAi {
tcs.add(ai); tcs.add(ai);
chance = true; chance = true;
} }
final List<Card> threatenedTargets = new ArrayList<Card>(); final List<Card> threatenedTargets = new ArrayList<>();
// filter AIs battlefield by what I can target // filter AIs battlefield by what I can target
List<Card> targetables = CardLists.getValidCards(ai.getCardsIn(ZoneType.Battlefield), tgt.getValidTgts(), ai, hostCard, sa); List<Card> targetables = CardLists.getValidCards(ai.getCardsIn(ZoneType.Battlefield), tgt.getValidTgts(), ai, hostCard, sa);
targetables = CardLists.getTargetableCards(targetables, sa); targetables = CardLists.getTargetableCards(targetables, sa);

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:
@@ -266,7 +266,7 @@ public class DebuffAi extends SpellAbilityAi {
@Override @Override
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) { protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
final List<String> kws = sa.hasParam("Keywords") ? Arrays.asList(sa.getParam("Keywords").split(" & ")) : new ArrayList<String>(); final List<String> kws = sa.hasParam("Keywords") ? Arrays.asList(sa.getParam("Keywords").split(" & ")) : new ArrayList<>();
if (sa.getTargetRestrictions() == null) { if (sa.getTargetRestrictions() == null) {
if (mandatory) { if (mandatory) {

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()) {
@@ -296,8 +296,8 @@ public class DestroyAi extends SpellAbilityAi {
} }
if (list.isEmpty() if (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 false;
} }
} }
@@ -342,7 +342,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 +400,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

@@ -147,7 +147,7 @@ public class DestroyAllAi extends SpellAbilityAi {
block.assignBlockersForCombat(combat); block.assignBlockersForCombat(combat);
if (ComputerUtilCombat.lifeInSeriousDanger(ai, combat)) { if (ComputerUtilCombat.lifeInSeriousDanger(ai, combat)) {
return true; return true;
} }
return false; return false;
} // only lands involved } // only lands involved
@@ -171,7 +171,6 @@ public class DestroyAllAi extends SpellAbilityAi {
else if ((ComputerUtilCard.evaluatePermanentList(ailist) + 3) >= ComputerUtilCard.evaluatePermanentList(opplist)) { else if ((ComputerUtilCard.evaluatePermanentList(ailist) + 3) >= ComputerUtilCard.evaluatePermanentList(opplist)) {
return false; return false;
} }
return true; 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

@@ -69,7 +69,7 @@ public class DrawAi extends SpellAbilityAi {
} }
if (!canLoot(ai, sa)) { if (!canLoot(ai, sa)) {
return false; 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,10 +273,10 @@ 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 if (MyRandom.getRandom().nextInt(20)<(opponent.getCMC() - fighter.getCMC())) { // trade
return true; return true;
} }
} }
} }
return false; return false;
} }

View File

@@ -20,9 +20,9 @@ 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())) { if (!ComputerUtil.predictThreatenedObjects(sa.getActivatingPlayer(), sa).contains(sa.getHostCard())) {
return false; 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

@@ -106,13 +106,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 +300,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;
@@ -184,15 +182,11 @@ public class LifeLoseAi extends SpellAbilityAi {
} }
final List<Player> tgtPlayers = sa.usesTargeting() && !sa.hasParam("Defined") final List<Player> tgtPlayers = sa.usesTargeting() && !sa.hasParam("Defined")
? new FCollection<Player>(sa.getTargets().getTargetPlayers()) ? new FCollection<>(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

@@ -185,10 +185,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,10 +24,10 @@ 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)) { if (aiPlayer.getGame().getPhaseHandler().getPhase().isBefore(PhaseType.MAIN2)) {
return false; 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
return true; return true;

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;
} }
/* /*
@@ -83,7 +80,6 @@ public class PoisonAi extends SpellAbilityAi {
return false; return false;
} }
} }
return true; return true;
} }
@@ -99,7 +95,6 @@ public class PoisonAi extends SpellAbilityAi {
} else if (!input.canReceiveCounters(CounterType.POISON)) { } else if (!input.canReceiveCounters(CounterType.POISON)) {
return false; return false;
} }
return true; return true;
} }
@@ -132,10 +127,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

@@ -30,7 +30,7 @@ import forge.util.MyRandom;
public class ProtectAi extends SpellAbilityAi { public class ProtectAi extends SpellAbilityAi {
private static boolean hasProtectionFrom(final Card card, final String color) { private static boolean hasProtectionFrom(final Card card, final String color) {
final List<String> onlyColors = new ArrayList<String>(MagicColor.Constant.ONLY_COLORS); final List<String> onlyColors = new ArrayList<>(MagicColor.Constant.ONLY_COLORS);
// make sure we have a valid color // make sure we have a valid color
if (!onlyColors.contains(color)) { if (!onlyColors.contains(color)) {
@@ -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,11 +384,11 @@ 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;
} else if (grantsUsefulExtraBlockOpts(ai, card)) {
return true; return true;
} }
} }
@@ -432,7 +421,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 +544,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 +777,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 +834,7 @@ public class PumpAi extends PumpAiBase {
} }
} }
); );
if (sacFodder.size() >= numCreatsToSac) { return sacFodder.size() >= numCreatsToSac;
return true;
}
} }
} }

View File

@@ -37,6 +37,24 @@ public abstract class PumpAiBase extends SpellAbilityAi {
} }
public boolean grantsUsefulExtraBlockOpts(final Player ai, final Card card) {
PhaseHandler ph = ai.getGame().getPhaseHandler();
if (ph.isPlayerTurn(ai) || !ph.getPhase().equals(PhaseType.COMBAT_DECLARE_ATTACKERS)) {
return false;
}
int canBlockNum = 1 + card.canBlockAdditional();
int possibleBlockNum = 0;
for (Card attacker : ai.getGame().getCombat().getAttackers()) {
if (CombatUtil.canBlock(attacker, card)) {
possibleBlockNum++;
if (possibleBlockNum > canBlockNum) {
break;
}
}
}
return possibleBlockNum > canBlockNum;
}
/** /**
* Checks if is useful keyword. * Checks if is useful keyword.
* *
@@ -56,24 +74,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 +101,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 +121,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 +142,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 +180,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 +212,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 +225,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();
@@ -296,13 +274,11 @@ public abstract class PumpAiBase extends SpellAbilityAi {
} }
} }
return false; return false;
} else if (keyword.equals("Bushido")) { } else if (keyword.startsWith("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 +297,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 +329,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,92 +343,52 @@ 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.")) {
if (ph.isPlayerTurn(ai)
|| !ph.getPhase().equals(PhaseType.COMBAT_DECLARE_ATTACKERS)) {
return false;
}
int canBlockNum = 1 + CombatUtil.numberOfAdditionalCreaturesCanBlock(card);
int possibleBlockNum = 0;
for (Card attacker : game.getCombat().getAttackers()) {
if (CombatUtil.canBlock(attacker, card)) {
possibleBlockNum++;
if (possibleBlockNum > canBlockNum) {
break;
}
}
}
if (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 +472,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

@@ -50,7 +50,7 @@ public class PumpAllAi extends PumpAiBase {
final int power = AbilityUtils.calculateAmount(sa.getHostCard(), sa.getParam("NumAtt"), sa); final int power = AbilityUtils.calculateAmount(sa.getHostCard(), sa.getParam("NumAtt"), sa);
final int defense = AbilityUtils.calculateAmount(sa.getHostCard(), sa.getParam("NumDef"), sa); final int defense = AbilityUtils.calculateAmount(sa.getHostCard(), sa.getParam("NumDef"), sa);
final List<String> keywords = sa.hasParam("KW") ? Arrays.asList(sa.getParam("KW").split(" & ")) : new ArrayList<String>(); final List<String> keywords = sa.hasParam("KW") ? Arrays.asList(sa.getParam("KW").split(" & ")) : new ArrayList<>();
final PhaseType phase = game.getPhaseHandler().getPhase(); final PhaseType phase = game.getPhaseHandler().getPhase();
@@ -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

@@ -106,7 +106,7 @@ public class RegenerateAi extends SpellAbilityAi {
// control // control
final List<GameObject> objects = ComputerUtil.predictThreatenedObjects(sa.getActivatingPlayer(), sa, true); final List<GameObject> objects = ComputerUtil.predictThreatenedObjects(sa.getActivatingPlayer(), sa, true);
final List<Card> threatenedTargets = new ArrayList<Card>(); final List<Card> threatenedTargets = new ArrayList<>();
for (final Card c : targetables) { for (final Card c : targetables) {
if (objects.contains(c) && c.getShieldCount() == 0 && !ComputerUtil.canRegenerate(ai, c)) { if (objects.contains(c) && c.getShieldCount() == 0 && !ComputerUtil.canRegenerate(ai, c)) {

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

@@ -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

@@ -92,9 +92,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 +121,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 +250,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 +260,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

@@ -47,7 +47,7 @@ public class StoreSVarAi extends SpellAbilityAi {
possibleBlockers = CardLists.filter(possibleBlockers, Presets.UNTAPPED); possibleBlockers = CardLists.filter(possibleBlockers, Presets.UNTAPPED);
int oppLife = opp.getLife(); int oppLife = opp.getLife();
int potentialDmg = 0; int potentialDmg = 0;
List<Card> currentAttackers = new ArrayList<Card>(); List<Card> currentAttackers = new ArrayList<>();
if (possibleBlockers.size() == 0) { return false; } if (possibleBlockers.size() == 0) { return false; }

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;
} }
@@ -411,7 +403,7 @@ public class TokenAi extends SpellAbilityAi {
String tokenToughness = sa.getParam("TokenToughness"); String tokenToughness = sa.getParam("TokenToughness");
String tokenName = sa.getParam("TokenName"); String tokenName = sa.getParam("TokenName");
String[] tokenTypes = sa.getParam("TokenTypes").split(","); String[] tokenTypes = sa.getParam("TokenTypes").split(",");
String cost = ""; StringBuilder cost = new StringBuilder();
String[] tokenColors = sa.getParam("TokenColors").split(","); String[] tokenColors = sa.getParam("TokenColors").split(",");
String tokenImage = sa.hasParam("TokenImage") ? PaperToken.makeTokenFileName(sa.getParam("TokenImage")) : ""; String tokenImage = sa.hasParam("TokenImage") ? PaperToken.makeTokenFileName(sa.getParam("TokenImage")) : "";
String[] tokenAbilities = sa.hasParam("TokenAbilities") ? sa.getParam("TokenAbilities").split(",") : null; String[] tokenAbilities = sa.hasParam("TokenAbilities") ? sa.getParam("TokenAbilities").split(",") : null;
@@ -426,35 +418,35 @@ public class TokenAi extends SpellAbilityAi {
substitutedColors[i] = host.getChosenColor(); substitutedColors[i] = host.getChosenColor();
} }
} }
String colorDesc = ""; StringBuilder colorDesc = new StringBuilder();
for (final String col : substitutedColors) { for (final String col : substitutedColors) {
if (col.equalsIgnoreCase("White")) { if (col.equalsIgnoreCase("White")) {
colorDesc += "W "; colorDesc.append("W ");
} else if (col.equalsIgnoreCase("Blue")) { } else if (col.equalsIgnoreCase("Blue")) {
colorDesc += "U "; colorDesc.append("U ");
} else if (col.equalsIgnoreCase("Black")) { } else if (col.equalsIgnoreCase("Black")) {
colorDesc += "B "; colorDesc.append("B ");
} else if (col.equalsIgnoreCase("Red")) { } else if (col.equalsIgnoreCase("Red")) {
colorDesc += "R "; colorDesc.append("R ");
} else if (col.equalsIgnoreCase("Green")) { } else if (col.equalsIgnoreCase("Green")) {
colorDesc += "G "; colorDesc.append("G ");
} else if (col.equalsIgnoreCase("Colorless")) { } else if (col.equalsIgnoreCase("Colorless")) {
colorDesc = "C"; colorDesc = new StringBuilder("C");
} }
} }
final List<String> imageNames = new ArrayList<String>(1); final List<String> imageNames = new ArrayList<>(1);
if (tokenImage.equals("")) { if (tokenImage.equals("")) {
imageNames.add(PaperToken.makeTokenFileName(TextUtil.fastReplace(colorDesc, " ", ""), tokenPower, tokenToughness, tokenName)); imageNames.add(PaperToken.makeTokenFileName(TextUtil.fastReplace(colorDesc.toString(), " ", ""), tokenPower, tokenToughness, tokenName));
} else { } else {
imageNames.add(0, tokenImage); imageNames.add(0, tokenImage);
} }
for (final char c : colorDesc.toCharArray()) { for (final char c : colorDesc.toString().toCharArray()) {
cost += c + ' '; cost.append(c + ' ');
} }
cost = colorDesc.replace('C', '1').trim(); cost = new StringBuilder(colorDesc.toString().replace('C', '1').trim());
final int finalPower = AbilityUtils.calculateAmount(host, tokenPower, sa); final int finalPower = AbilityUtils.calculateAmount(host, tokenPower, sa);
final int finalToughness = AbilityUtils.calculateAmount(host, tokenToughness, sa); final int finalToughness = AbilityUtils.calculateAmount(host, tokenToughness, sa);
@@ -468,7 +460,7 @@ public class TokenAi extends SpellAbilityAi {
final String substitutedName = tokenName.equals("ChosenType") ? host.getChosenType() : tokenName; final String substitutedName = tokenName.equals("ChosenType") ? host.getChosenType() : tokenName;
final String imageName = imageNames.get(MyRandom.getRandom().nextInt(imageNames.size())); final String imageName = imageNames.get(MyRandom.getRandom().nextInt(imageNames.size()));
final TokenInfo tokenInfo = new TokenInfo(substitutedName, imageName, final TokenInfo tokenInfo = new TokenInfo(substitutedName, imageName,
cost, substitutedTypes, tokenKeywords, finalPower, finalToughness); cost.toString(), substitutedTypes, tokenKeywords, finalPower, finalToughness);
Card token = tokenInfo.makeOneToken(ai); Card token = tokenInfo.makeOneToken(ai);
if (token == null) { if (token == null) {

View File

@@ -39,7 +39,7 @@ public class TwoPilesAi extends SpellAbilityAi {
} }
final List<Player> tgtPlayers = sa.usesTargeting() && !sa.hasParam("Defined") final List<Player> tgtPlayers = sa.usesTargeting() && !sa.hasParam("Defined")
? new FCollection<Player>(sa.getTargets().getTargetPlayers()) ? new FCollection<>(sa.getTargets().getTargetPlayers())
: AbilityUtils.getDefinedPlayers(sa.getHostCard(), sa.getParam("Defined"), sa); : AbilityUtils.getDefinedPlayers(sa.getHostCard(), sa.getParam("Defined"), sa);
final Player p = tgtPlayers.get(0); final Player p = tgtPlayers.get(0);

View File

@@ -66,7 +66,7 @@ public class UnattachAllAi extends SpellAbilityAi {
final Card card = sa.getHostCard(); final Card card = sa.getHostCard();
final Player opp = ai.getWeakestOpponent(); final Player opp = ai.getWeakestOpponent();
// Check if there are any valid targets // Check if there are any valid targets
List<GameObject> targets = new ArrayList<GameObject>(); List<GameObject> targets = new ArrayList<>();
final TargetRestrictions tgt = sa.getTargetRestrictions(); final TargetRestrictions tgt = sa.getTargetRestrictions();
if (tgt == null) { if (tgt == null) {
targets = AbilityUtils.getDefinedObjects(sa.getHostCard(), sa.getParam("Defined"), sa); targets = AbilityUtils.getDefinedObjects(sa.getHostCard(), sa.getParam("Defined"), sa);
@@ -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

@@ -277,10 +277,7 @@ public class GameCopier {
// TODO: Copy the full list with timestamps. // TODO: Copy the full list with timestamps.
newCard.addNewPT(setPower, setToughness, newGame.getNextTimestamp()); newCard.addNewPT(setPower, setToughness, newGame.getNextTimestamp());
} }
newCard.addTempPowerBoost(c.getTempPowerBoost()); newCard.setPTBoost(c.getPTBoostMap());
newCard.setSemiPermanentPowerBoost(c.getSemiPermanentPowerBoost());
newCard.addTempToughnessBoost(c.getTempToughnessBoost());
newCard.setSemiPermanentToughnessBoost(c.getSemiPermanentToughnessBoost());
newCard.setDamage(c.getDamage()); newCard.setDamage(c.getDamage());
newCard.setChangedCardTypes(c.getChangedCardTypesMap()); newCard.setChangedCardTypes(c.getChangedCardTypesMap());

View File

@@ -38,7 +38,7 @@ public class GameSimulator {
aiPlayer = (Player) copier.find(origAiPlayer); aiPlayer = (Player) copier.find(origAiPlayer);
eval = new GameStateEvaluator(); eval = new GameStateEvaluator();
origLines = new ArrayList<String>(); origLines = new ArrayList<>();
debugLines = origLines; debugLines = origLines;
debugPrint = false; debugPrint = false;
@@ -52,7 +52,7 @@ public class GameSimulator {
// first and get the updated eval score, since this is what we'll // first and get the updated eval score, since this is what we'll
// want to compare to the eval score after simulating. // want to compare to the eval score after simulating.
if (COPY_STACK && !origGame.getStackZone().isEmpty()) { if (COPY_STACK && !origGame.getStackZone().isEmpty()) {
origLines = new ArrayList<String>(); origLines = new ArrayList<>();
debugLines = origLines; debugLines = origLines;
Game copyOrigGame = copier.makeCopy(); Game copyOrigGame = copier.makeCopy();
Player copyOrigAiPlayer = copyOrigGame.getPlayers().get(1); Player copyOrigAiPlayer = copyOrigGame.getPlayers().get(1);
@@ -66,12 +66,12 @@ public class GameSimulator {
private void ensureGameCopyScoreMatches(Game origGame, Player origAiPlayer) { private void ensureGameCopyScoreMatches(Game origGame, Player origAiPlayer) {
eval.setDebugging(true); eval.setDebugging(true);
List<String> simLines = new ArrayList<String>(); List<String> simLines = new ArrayList<>();
debugLines = simLines; debugLines = simLines;
Score simScore = eval.getScoreForGameState(simGame, aiPlayer); Score simScore = eval.getScoreForGameState(simGame, aiPlayer);
if (!simScore.equals(origScore)) { if (!simScore.equals(origScore)) {
// Re-eval orig with debug printing. // Re-eval orig with debug printing.
origLines = new ArrayList<String>(); origLines = new ArrayList<>();
debugLines = origLines; debugLines = origLines;
eval.getScoreForGameState(origGame, origAiPlayer); eval.getScoreForGameState(origGame, origAiPlayer);
// Print debug info. // Print debug info.
@@ -216,7 +216,7 @@ public class GameSimulator {
List<String> simLines = null; List<String> simLines = null;
if (debugPrint) { if (debugPrint) {
debugPrint("SimGame:"); debugPrint("SimGame:");
simLines = new ArrayList<String>(); simLines = new ArrayList<>();
debugLines = simLines; debugLines = simLines;
debugPrint = false; debugPrint = false;
} }
@@ -245,7 +245,7 @@ public class GameSimulator {
opponent.runWithController(new Runnable() { opponent.runWithController(new Runnable() {
@Override @Override
public void run() { public void run() {
final Set<Card> allAffectedCards = new HashSet<Card>(); final Set<Card> allAffectedCards = new HashSet<>();
game.getAction().checkStateEffects(false, allAffectedCards); game.getAction().checkStateEffects(false, allAffectedCards);
game.getStack().addAllTriggeredAbilitiesToStack(); game.getStack().addAllTriggeredAbilitiesToStack();
while (!game.getStack().isEmpty() && !game.isGameOver()) { while (!game.getStack().isEmpty() && !game.isGameOver()) {

View File

@@ -161,17 +161,6 @@ public class GameStateEvaluator {
} }
return super.addValue(value, text); return super.addValue(value, text);
} }
@Override
protected int getEffectivePower(final Card c) {
Card.StatBreakdown breakdown = c.getNetCombatDamageBreakdown();
return breakdown.getTotal() - breakdown.tempBoost;
}
@Override
protected int getEffectiveToughness(final Card c) {
Card.StatBreakdown breakdown = c.getNetToughnessBreakdown();
return breakdown.getTotal() - breakdown.tempBoost;
}
} }
public static class Score { public static class Score {

View File

@@ -51,7 +51,7 @@ public class PossibleTargetSelector {
this.sa = sa; this.sa = sa;
this.targetingSa = targetingSa; this.targetingSa = targetingSa;
this.targetingSaIndex = targetingSaIndex; this.targetingSaIndex = targetingSaIndex;
this.validTargets = new ArrayList<GameObject>(); this.validTargets = new ArrayList<>();
generateValidTargets(sa.getHostCard().getController()); generateValidTargets(sa.getHostCard().getController());
} }
@@ -80,8 +80,8 @@ 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<>();
private HashMap<Card, Integer> creatureScores; private HashMap<Card, Integer> creatureScores;
private int getCreatureScore(Card c) { private int getCreatureScore(Card c) {
@@ -91,7 +91,7 @@ public class PossibleTargetSelector {
return score; return score;
} }
} else { } else {
creatureScores = new HashMap<Card, Integer>(); creatureScores = new HashMap<>();
} }
int score = ComputerUtilCard.evaluateCreature(c); int score = ComputerUtilCard.evaluateCreature(c);

View File

@@ -18,7 +18,7 @@ public class SimulationController {
private List<GameSimulator> simulatorStack; private List<GameSimulator> simulatorStack;
private Plan.Decision bestSequence; // last action of sequence private Plan.Decision bestSequence; // last action of sequence
private Score bestScore; private Score bestScore;
private List<CachedEffect> effectCache = new ArrayList<CachedEffect>(); private List<CachedEffect> effectCache = new ArrayList<>();
private GameObject[] currentHostAndTarget; private GameObject[] currentHostAndTarget;
private static class CachedEffect { private static class CachedEffect {
@@ -39,10 +39,10 @@ public class SimulationController {
public SimulationController(Score score) { public SimulationController(Score score) {
bestScore = score; bestScore = score;
scoreStack = new ArrayList<Score>(); scoreStack = new ArrayList<>();
scoreStack.add(score); scoreStack.add(score);
simulatorStack = new ArrayList<GameSimulator>(); simulatorStack = new ArrayList<>();
currentStack = new ArrayList<Plan.Decision>(); currentStack = new ArrayList<>();
} }
private int getRecursionDepth() { private int getRecursionDepth() {
@@ -97,7 +97,7 @@ public class SimulationController {
throw new RuntimeException("getBestPlan() expects currentStack to be empty!"); throw new RuntimeException("getBestPlan() expects currentStack to be empty!");
} }
ArrayList<Plan.Decision> sequence = new ArrayList<Plan.Decision>(); ArrayList<Plan.Decision> sequence = new ArrayList<>();
Plan.Decision current = bestSequence; Plan.Decision current = bestSequence;
while (current != null) { while (current != null) {
sequence.add(current); sequence.add(current);

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.
@@ -84,7 +84,7 @@ public class SpellAbilityChoicesIterator {
} }
ChoicePoint cp = choicePoints.get(cpIndex); ChoicePoint cp = choicePoints.get(cpIndex);
// Prune duplicates. // Prune duplicates.
HashSet<String> uniqueCards = new HashSet<String>(); HashSet<String> uniqueCards = new HashSet<>();
for (int i = 0; i < fetchList.size(); i++) { for (int i = 0; i < fetchList.size(); i++) {
Card card = fetchList.get(i); Card card = fetchList.get(i);
if (uniqueCards.add(card.getName()) && uniqueCards.size() == cp.nextChoice + 1) { if (uniqueCards.add(card.getName()) && uniqueCards.size() == cp.nextChoice + 1) {
@@ -213,7 +213,7 @@ public class SpellAbilityChoicesIterator {
} }
public static List<AbilitySub> getModeCombination(List<AbilitySub> choices, int[] modeIndexes) { public static List<AbilitySub> getModeCombination(List<AbilitySub> choices, int[] modeIndexes) {
ArrayList<AbilitySub> modes = new ArrayList<AbilitySub>(); ArrayList<AbilitySub> modes = new ArrayList<>();
for (int modeIndex : modeIndexes) { for (int modeIndex : modeIndexes) {
modes.add(choices.get(modeIndex)); modes.add(choices.get(modeIndex));
} }

View File

@@ -61,7 +61,7 @@ public class SpellAbilityPicker {
List<SpellAbility> all = ComputerUtilAbility.getSpellAbilities(cards, player); List<SpellAbility> all = ComputerUtilAbility.getSpellAbilities(cards, player);
CardCollection landsToPlay = ComputerUtilAbility.getAvailableLandsToPlay(game, player); CardCollection landsToPlay = ComputerUtilAbility.getAvailableLandsToPlay(game, player);
if (landsToPlay != null) { if (landsToPlay != null) {
HashMap<String, Card> landsDeDupe = new HashMap<String, Card>(); HashMap<String, Card> landsDeDupe = new HashMap<>();
for (Card land : landsToPlay) { for (Card land : landsToPlay) {
Card previousLand = landsDeDupe.get(land.getName()); Card previousLand = landsDeDupe.get(land.getName());
// Skip identical lands. // Skip identical lands.
@@ -165,7 +165,7 @@ public class SpellAbilityPicker {
PhaseType currentPhase = game.getPhaseHandler().getPhase(); PhaseType currentPhase = game.getPhaseHandler().getPhase();
if (currentPhase.isBefore(PhaseType.COMBAT_DECLARE_BLOCKERS)) { if (currentPhase.isBefore(PhaseType.COMBAT_DECLARE_BLOCKERS)) {
List<SpellAbility> candidateSAs2 = new ArrayList<SpellAbility>(); List<SpellAbility> candidateSAs2 = new ArrayList<>();
for (SpellAbility sa : candidateSAs) { for (SpellAbility sa : candidateSAs) {
if (!isSorcerySpeed(sa, player)) { if (!isSorcerySpeed(sa, player)) {
System.err.println("Not sorcery: " + sa); System.err.println("Not sorcery: " + sa);
@@ -270,28 +270,28 @@ public class SpellAbilityPicker {
return abilityToString(sa, true); return abilityToString(sa, true);
} }
public static String abilityToString(SpellAbility sa, boolean withTargets) { public static String abilityToString(SpellAbility sa, boolean withTargets) {
String saString = "N/A"; StringBuilder saString = new StringBuilder("N/A");
if (sa != null) { if (sa != null) {
saString = sa.toString(); saString = new StringBuilder(sa.toString());
String cardName = sa.getHostCard().getName(); String cardName = sa.getHostCard().getName();
if (!cardName.isEmpty()) { if (!cardName.isEmpty()) {
saString = TextUtil.fastReplace(saString, cardName, "<$>"); saString = new StringBuilder(TextUtil.fastReplace(saString.toString(), cardName, "<$>"));
} }
if (saString.length() > 40) { if (saString.length() > 40) {
saString = saString.substring(0, 40) + "..."; saString = new StringBuilder(saString.substring(0, 40) + "...");
} }
if (withTargets) { if (withTargets) {
SpellAbility saOrSubSa = sa; SpellAbility saOrSubSa = sa;
do { do {
if (saOrSubSa.usesTargeting()) { if (saOrSubSa.usesTargeting()) {
saString += " (targets: " + saOrSubSa.getTargets().getTargetedString() + ")"; saString.append(" (targets: ").append(saOrSubSa.getTargets().getTargetedString()).append(")");
} }
saOrSubSa = saOrSubSa.getSubAbility(); saOrSubSa = saOrSubSa.getSubAbility();
} while (saOrSubSa != null); } while (saOrSubSa != null);
} }
saString = sa.getHostCard() + " -> " + saString; saString.insert(0, sa.getHostCard() + " -> ");
} }
return saString; return saString.toString();
} }
private boolean shouldWaitForLater(final SpellAbility sa) { private boolean shouldWaitForLater(final SpellAbility sa) {
@@ -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

@@ -6,7 +6,7 @@
<parent> <parent>
<artifactId>forge</artifactId> <artifactId>forge</artifactId>
<groupId>forge</groupId> <groupId>forge</groupId>
<version>1.6.28-SNAPSHOT</version> <version>1.6.29-SNAPSHOT</version>
</parent> </parent>
<artifactId>forge-core</artifactId> <artifactId>forge-core</artifactId>

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) {}
}; };
@@ -175,7 +175,7 @@ public class CardStorageReader {
} }
if (zipEntriesMap == null) { if (zipEntriesMap == null) {
zipEntriesMap = new HashMap<String, ZipEntry>(); zipEntriesMap = new HashMap<>();
for (ZipEntry entry : getZipEntries()) { for (ZipEntry entry : getZipEntries()) {
zipEntriesMap.put(entry.getName(), entry); zipEntriesMap.put(entry.getName(), entry);
} }
@@ -256,7 +256,7 @@ public class CardStorageReader {
return result; return result;
} }
final List<File> allFiles = collectCardFiles(new ArrayList<File>(), this.cardsfolder); final List<File> allFiles = collectCardFiles(new ArrayList<>(), this.cardsfolder);
if (!allFiles.isEmpty()) { if (!allFiles.isEmpty()) {
int fileParts = zip == null ? NUMBER_OF_PARTS : 1 + NUMBER_OF_PARTS / 3; int fileParts = zip == null ? NUMBER_OF_PARTS : 1 + NUMBER_OF_PARTS / 3;
if (allFiles.size() < fileParts * 100) { if (allFiles.size() < fileParts * 100) {

View File

@@ -8,7 +8,7 @@ import java.util.Map;
public class FTrace { public class FTrace {
private static long appStartTime; private static long appStartTime;
private static Map<String, FTrace> traces = new HashMap<String, FTrace>(); private static Map<String, FTrace> traces = new HashMap<>();
public static void initialize() { public static void initialize() {
appStartTime = new Date().getTime(); appStartTime = new Date().getTime();

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; }

View File

@@ -110,7 +110,7 @@ public class StaticData {
private List<CardEdition> sortedEditions; private List<CardEdition> sortedEditions;
public final List<CardEdition> getSortedEditions() { public final List<CardEdition> getSortedEditions() {
if (sortedEditions == null) { if (sortedEditions == null) {
sortedEditions = new ArrayList<CardEdition>(); sortedEditions = new ArrayList<>();
for (CardEdition set : editions) { for (CardEdition set : editions) {
sortedEditions.add(set); sortedEditions.add(set);
} }

View File

@@ -41,16 +41,16 @@ 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.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);
private static Map<String, String> artPrefs = new HashMap<String, String>(); private static Map<String, String> artPrefs = new HashMap<>();
private final Map<String, String> alternateName = Maps.newTreeMap(String.CASE_INSENSITIVE_ORDER); private final Map<String, String> alternateName = Maps.newTreeMap(String.CASE_INSENSITIVE_ORDER);
private final Map<String, Integer> artIds = new HashMap<String, Integer>(); private final Map<String, Integer> artIds = new HashMap<>();
private final List<PaperCard> allCards = new ArrayList<PaperCard>(); private final List<PaperCard> allCards = new ArrayList<>();
private final List<PaperCard> roAllCards = Collections.unmodifiableList(allCards); private final List<PaperCard> roAllCards = Collections.unmodifiableList(allCards);
private final CardEdition.Collection editions; private final CardEdition.Collection editions;
@@ -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;
} }
@@ -166,8 +166,8 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
} }
public void initialize(boolean logMissingPerEdition, boolean logMissingSummary) { public void initialize(boolean logMissingPerEdition, boolean logMissingSummary) {
Set<String> allMissingCards = new LinkedHashSet<String>(); Set<String> allMissingCards = new LinkedHashSet<>();
List<String> missingCards = new ArrayList<String>(); List<String> missingCards = new ArrayList<>();
CardEdition upcomingSet = null; CardEdition upcomingSet = null;
Date today = new Date(); Date today = new Date();
@@ -342,10 +342,10 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
if (request.artIndex <= 0) { // this stands for 'random art' if (request.artIndex <= 0) { // this stands for 'random art'
Collection<PaperCard> candidates; Collection<PaperCard> candidates;
if (reqEdition == null) { if (reqEdition == null) {
candidates = new ArrayList<PaperCard>(cards); candidates = new ArrayList<>(cards);
} }
else { else {
candidates = new ArrayList<PaperCard>(); candidates = new ArrayList<>();
for (PaperCard pc : cards) { for (PaperCard pc : cards) {
if (pc.getEdition().equalsIgnoreCase(reqEdition)) { if (pc.getEdition().equalsIgnoreCase(reqEdition)) {
candidates.add(pc); candidates.add(pc);
@@ -452,7 +452,7 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
firstWithoutImage = pc; //ensure first without image returns if none have image firstWithoutImage = pc; //ensure first without image returns if none have image
} }
if (cardsListReadOnly) { //ensure we don't modify a cached collection if (cardsListReadOnly) { //ensure we don't modify a cached collection
cards = new ArrayList<PaperCard>(cards); cards = new ArrayList<>(cards);
cardsListReadOnly = false; cardsListReadOnly = false;
} }
cards.remove(randomIndex); //remove card from collection and try another random card cards.remove(randomIndex); //remove card from collection and try another random card
@@ -686,7 +686,7 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
result = rulesByName.put(cardName, rules); result = rulesByName.put(cardName, rules);
// 1. generate all paper cards from edition data we have (either explicit, or found in res/editions, or add to unknown edition) // 1. generate all paper cards from edition data we have (either explicit, or found in res/editions, or add to unknown edition)
List<PaperCard> paperCards = new ArrayList<PaperCard>(); List<PaperCard> paperCards = new ArrayList<>();
if (null == whenItWasPrinted || whenItWasPrinted.isEmpty()) { if (null == whenItWasPrinted || whenItWasPrinted.isEmpty()) {
for (CardEdition e : editions.getOrderedEditions()) { for (CardEdition e : editions.getOrderedEditions()) {
int artIdx = 1; int artIdx = 1;

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,12 +20,12 @@ 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
} }
private final static List<String> emptyList = Collections.unmodifiableList(new ArrayList<String>()); private final static List<String> emptyList = Collections.unmodifiableList(new ArrayList<>());
private final static Map<String, String> emptyMap = Collections.unmodifiableMap(new TreeMap<String, String>()); private final static Map<String, String> emptyMap = Collections.unmodifiableMap(new TreeMap<>());
private final String name; private final String name;
private String altName = null; private String altName = null;
@@ -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");
@@ -112,12 +112,12 @@ final class CardFace implements ICardFace {
// Raw fields used for Card creation // Raw fields used for Card creation
void setNonAbilityText(String value) { this.nonAbilityText = value; } void setNonAbilityText(String value) { this.nonAbilityText = value; }
void addKeyword(String value) { if (null == this.keywords) { this.keywords = new ArrayList<String>(); } this.keywords.add(value); } void addKeyword(String value) { if (null == this.keywords) { this.keywords = new ArrayList<>(); } this.keywords.add(value); }
void addAbility(String value) { if (null == this.abilities) { this.abilities = new ArrayList<String>(); } this.abilities.add(value);} void addAbility(String value) { if (null == this.abilities) { this.abilities = new ArrayList<>(); } this.abilities.add(value);}
void addTrigger(String value) { if (null == this.triggers) { this.triggers = new ArrayList<String>(); } this.triggers.add(value);} void addTrigger(String value) { if (null == this.triggers) { this.triggers = new ArrayList<>(); } this.triggers.add(value);}
void addStaticAbility(String value) { if (null == this.staticAbilities) { this.staticAbilities = new ArrayList<String>(); } this.staticAbilities.add(value);} void addStaticAbility(String value) { if (null == this.staticAbilities) { this.staticAbilities = new ArrayList<>(); } this.staticAbilities.add(value);}
void addReplacementEffect(String value) { if (null == this.replacements) { this.replacements = new ArrayList<String>(); } this.replacements.add(value);} void addReplacementEffect(String value) { if (null == this.replacements) { this.replacements = new ArrayList<>(); } this.replacements.add(value);}
void addSVar(String key, String value) { if (null == this.variables) { this.variables = new TreeMap<String, String>(String.CASE_INSENSITIVE_ORDER); } this.variables.put(key, value); } void addSVar(String key, String value) { if (null == this.variables) { this.variables = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); } this.variables.put(key, value); }
void assignMissingFields() { // Most scripts do not specify color explicitly void assignMissingFields() { // Most scripts do not specify color explicitly

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