diff --git a/forge-ai/src/main/java/forge/ai/AiAttackController.java b/forge-ai/src/main/java/forge/ai/AiAttackController.java index 7760c900b7d..bba7e2def1f 100644 --- a/forge-ai/src/main/java/forge/ai/AiAttackController.java +++ b/forge-ai/src/main/java/forge/ai/AiAttackController.java @@ -18,7 +18,6 @@ package forge.ai; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import com.google.common.base.Predicate; @@ -44,7 +43,6 @@ import forge.game.combat.CombatUtil; import forge.game.combat.GlobalAttackRestrictions; import forge.game.keyword.Keyword; import forge.game.player.Player; -import forge.game.player.PlayerPredicates; import forge.game.spellability.SpellAbility; import forge.game.trigger.Trigger; import forge.game.trigger.TriggerType; @@ -53,6 +51,7 @@ import forge.util.Aggregates; import forge.util.Expressions; import forge.util.MyRandom; import forge.util.TextUtil; +import forge.util.collect.FCollection; import forge.util.collect.FCollectionView; @@ -207,7 +206,7 @@ public class AiAttackController { * a {@link forge.game.combat.Combat} object. * @return a boolean. */ - public final boolean isEffectiveAttacker(final Player ai, final Card attacker, final Combat combat) { + public final boolean isEffectiveAttacker(final Player ai, final Card attacker, final Combat combat, final GameEntity defender) { // if the attacker will die when attacking don't attack if ((attacker.getNetToughness() + ComputerUtilCombat.predictToughnessBonusOfAttacker(attacker, null, combat, true)) <= 0) { return false; @@ -238,10 +237,8 @@ public class AiAttackController { return true; } - final Player opp = this.defendingOpponent; - // Damage opponent if unblocked - final int dmgIfUnblocked = ComputerUtilCombat.damageIfUnblocked(attacker, opp, combat, true); + final int dmgIfUnblocked = ComputerUtilCombat.damageIfUnblocked(attacker, defender, combat, true); if (dmgIfUnblocked > 0) { boolean onlyIfExalted = false; if (combat.getAttackers().isEmpty() && ai.countExaltedBonus() > 0 @@ -255,14 +252,14 @@ public class AiAttackController { } } // Poison opponent if unblocked - if (ComputerUtilCombat.poisonIfUnblocked(attacker, opp) > 0) { + if (defender instanceof Player && ComputerUtilCombat.poisonIfUnblocked(attacker, (Player) defender) > 0) { return true; } // TODO check if that makes sense int exalted = ai.countExaltedBonus(); if (this.attackers.size() == 1 && exalted > 0 - && ComputerUtilCombat.predictDamageTo(opp, exalted, attacker, true) > 0) { + && ComputerUtilCombat.predictDamageTo(defender, exalted, attacker, true) > 0) { return true; } @@ -315,7 +312,6 @@ public class AiAttackController { } // this checks to make sure that the computer player doesn't lose when the human player attacks - // this method is used by getAttackers() public final List notNeededAsBlockers(final Player ai, final List attackers) { final List notNeededAsBlockers = new ArrayList<>(attackers); int fixedBlockers = 0; @@ -423,7 +419,7 @@ public class AiAttackController { if ((blockersNeeded == 0 || finestHour) && !this.oppList.isEmpty()) { // total attack = biggest creature + exalted, *2 if Rafiq is in play - int humanBasePower = getAttack(this.oppList.get(0)) + humanExaltedBonus; + int humanBasePower = ComputerUtilCombat.getAttack(this.oppList.get(0)) + humanExaltedBonus; if (finestHour) { // For Finest Hour, one creature could attack and get the bonus TWICE humanBasePower = humanBasePower + humanExaltedBonus; @@ -649,9 +645,8 @@ public class AiAttackController { if (-1 == n) { System.out.println("getMustAttackEntity() or getMustAttackEntityThisTurn() returned something not in defenders."); return prefDefender; - } else { - return entity; } + return entity; } else { // 1. assault the opponent if you can kill him if (bAssault) { @@ -688,6 +683,7 @@ public class AiAttackController { boolean tradeIfTappedOut = false; int extraChanceIfOppHasMana = 0; boolean tradeIfLowerLifePressure = false; + boolean predictEvasion = false; if (ai.getController().isAI()) { AiController aic = ((PlayerControllerAi) ai.getController()).getAi(); playAggro = aic.getBooleanProperty(AiProps.PLAY_AGGRO); @@ -695,6 +691,7 @@ public class AiAttackController { tradeIfTappedOut = aic.getBooleanProperty(AiProps.ATTACK_INTO_TRADE_WHEN_TAPPED_OUT); extraChanceIfOppHasMana = aic.getIntProperty(AiProps.CHANCE_TO_ATKTRADE_WHEN_OPP_HAS_MANA); tradeIfLowerLifePressure = aic.getBooleanProperty(AiProps.RANDOMLY_ATKTRADE_ONLY_ON_LOWER_LIFE_PRESSURE); + predictEvasion = aic.getBooleanProperty(AiProps.COMBAT_ATTRITION_ATTACK_EVASION_PREDICTION); } final boolean bAssault = doAssault(ai); @@ -736,7 +733,7 @@ public class AiAttackController { } else if (attacker.getSVar("MustAttack").equals("True")) { mustAttack = true; } else if (attacker.hasSVar("EndOfTurnLeavePlay") - && isEffectiveAttacker(ai, attacker, combat)) { + && isEffectiveAttacker(ai, attacker, combat, defender)) { mustAttack = true; } else if (seasonOfTheWitch) { // TODO: if there are other ways to tap this creature (like mana creature), then don't need to attack @@ -767,7 +764,7 @@ public class AiAttackController { return; } - if (bAssault) { + if (bAssault && defender == this.defendingOpponent) { // in case we are forced to attack someone else if (LOG_AI_ATTACKS) System.out.println("Assault"); CardLists.sortByPowerDesc(attackersLeft); @@ -776,7 +773,7 @@ public class AiAttackController { if (attackMax != -1 && combat.getAttackers().size() >= attackMax) return; - if (canAttackWrapper(attacker, defender) && isEffectiveAttacker(ai, attacker, combat)) { + if (canAttackWrapper(attacker, defender) && isEffectiveAttacker(ai, attacker, combat, defender)) { combat.addAttacker(attacker, defender); } } @@ -817,7 +814,7 @@ public class AiAttackController { System.out.println("Exalted"); this.aiAggression = 6; for (Card attacker : this.attackers) { - if (canAttackWrapper(attacker, defender) && this.shouldAttack(ai, attacker, this.blockers, combat)) { + if (canAttackWrapper(attacker, defender) && shouldAttack(ai, attacker, this.blockers, combat, defender)) { combat.addAttacker(attacker, defender); return; } @@ -833,7 +830,7 @@ public class AiAttackController { // reached max, breakup if (attackMax != -1 && combat.getAttackers().size() >= attackMax) break; - if (canAttackWrapper(attacker, defender) && this.shouldAttack(ai, attacker, this.blockers, combat)) { + if (canAttackWrapper(attacker, defender) && shouldAttack(ai, attacker, this.blockers, combat, defender)) { combat.addAttacker(attacker, defender); } } @@ -853,7 +850,8 @@ public class AiAttackController { final List nextTurnAttackers = new ArrayList<>(); int candidateCounterAttackDamage = 0; - final Player opp = this.defendingOpponent; + final Player opp = defender instanceof Player ? (Player) defender : ((Card)defender).getController(); + this.oppList = getOpponentCreatures(opp); // get the potential damage and strength of the AI forces final List candidateAttackers = new ArrayList<>(); int candidateUnblockedDamage = 0; @@ -867,9 +865,6 @@ public class AiAttackController { } } - boolean predictEvasion = (ai.getController().isAI() - && ((PlayerControllerAi)ai.getController()).getAi().getBooleanProperty(AiProps.COMBAT_ATTRITION_ATTACK_EVASION_PREDICTION)); - CardCollection categorizedOppList = new CardCollection(); if (predictEvasion) { // If predicting evasion, make sure that attackers with evasion are considered first @@ -1059,70 +1054,60 @@ public class AiAttackController { if ( LOG_AI_ATTACKS ) System.out.println("attackersLeft = " + attackersLeft); - for (int i = 0; i < attackersLeft.size(); i++) { - final Card attacker = attackersLeft.get(i); - if (this.aiAggression < 5 && !attacker.hasFirstStrike() && !attacker.hasDoubleStrike() - && ComputerUtilCombat.getTotalFirstStrikeBlockPower(attacker, this.defendingOpponent) - >= ComputerUtilCombat.getDamageToKill(attacker)) { - continue; - } + FCollection possibleDefenders = new FCollection<>(opp); + possibleDefenders.addAll(opp.getPlaneswalkersInPlay()); - if (this.shouldAttack(ai, attacker, this.blockers, combat) && canAttackWrapper(attacker, defender)) { - combat.addAttacker(attacker, defender); - // check if attackers are enough to finish the attacked planeswalker - if (defender instanceof Card) { - Card pw = (Card) defender; - final int blockNum = this.blockers.size(); - int attackNum = 0; - int damage = 0; - List attacking = combat.getAttackersOf(defender); - CardLists.sortByPowerAsc(attacking); - for (Card atta : attacking) { - if (attackNum >= blockNum || !CombatUtil.canBeBlocked(attacker, this.blockers, combat)) { - damage += ComputerUtilCombat.damageIfUnblocked(atta, opp, null, false); - } else if (CombatUtil.canBeBlocked(attacker, this.blockers, combat)) { - attackNum++; - } - } - // if enough damage: switch to next planeswalker or player - if (damage >= pw.getCounters(CounterEnumType.LOYALTY)) { - List pwDefending = combat.getDefendingPlaneswalkers(); - // look for next planeswalker - for (Card walker : Lists.newArrayList(pwDefending)) { - if (!combat.getAttackersOf(walker).isEmpty()) { - pwDefending.remove(walker); + while (!attackersLeft.isEmpty()) { + CardCollection attackersAssigned = new CardCollection(); + for (int i = 0; i < attackersLeft.size(); i++) { + final Card attacker = attackersLeft.get(i); + if (this.aiAggression < 5 && !attacker.hasFirstStrike() && !attacker.hasDoubleStrike() + && ComputerUtilCombat.getTotalFirstStrikeBlockPower(attacker, this.defendingOpponent) + >= ComputerUtilCombat.getDamageToKill(attacker)) { + continue; + } + + if (shouldAttack(ai, attacker, this.blockers, combat, defender) && canAttackWrapper(attacker, defender)) { + combat.addAttacker(attacker, defender); + attackersAssigned.add(attacker); + + // check if attackers are enough to finish the attacked planeswalker + if (i < attackersLeft.size() - 1 && defender instanceof Card) { + final int blockNum = this.blockers.size(); + int attackNum = 0; + int damage = 0; + List attacking = combat.getAttackersOf(defender); + CardLists.sortByPowerDesc(attacking); + for (Card atta : attacking) { + if (attackNum >= blockNum || !CombatUtil.canBeBlocked(atta, this.blockers, combat)) { + damage += ComputerUtilCombat.damageIfUnblocked(atta, defender, null, false); + } else { + attackNum++; } } - if (pwDefending.isEmpty()) { - defender = Collections.min(Lists.newArrayList(combat.getDefendingPlayers()), PlayerPredicates.compareByLife()); - } - else { - final Card pwNearUlti = ComputerUtilCard.getBestPlaneswalkerToDamage(pwDefending); - defender = pwNearUlti != null ? pwNearUlti : ComputerUtilCard.getBestPlaneswalkerAI(pwDefending); + // if enough damage: switch to next planeswalker + if (damage >= ComputerUtilCombat.getDamageToKill((Card) defender)) { + break; } } } } + + attackersLeft.removeAll(attackersAssigned); + possibleDefenders.remove(defender); + if (attackersLeft.isEmpty() || possibleDefenders.isEmpty()) { + break; + } + CardCollection pwDefending = new CardCollection(Iterables.filter(possibleDefenders, Card.class)); + if (pwDefending.isEmpty()) { + // TODO for now only looks at same player as we'd have to check the others from start too + //defender = Collections.min(new PlayerCollection(Iterables.filter(possibleDefenders, Player.class)), PlayerPredicates.compareByLife()); + defender = opp; + } else { + final Card pwNearUlti = ComputerUtilCard.getBestPlaneswalkerToDamage(pwDefending); + defender = pwNearUlti != null ? pwNearUlti : ComputerUtilCard.getBestPlaneswalkerAI(pwDefending); + } } - } // getAttackers() - - /** - *

- * getAttack. - *

- * - * @param c - * a {@link forge.game.card.Card} object. - * @return a int. - */ - public final static int getAttack(final Card c) { - int n = c.getNetCombatDamage(); - - if (c.hasKeyword(Keyword.DOUBLE_STRIKE)) { - n *= 2; - } - - return n; } /** @@ -1138,7 +1123,7 @@ public class AiAttackController { * a {@link forge.game.combat.Combat} object. * @return a boolean. */ - public final boolean shouldAttack(final Player ai, final Card attacker, final List defenders, final Combat combat) { + public final boolean shouldAttack(final Player ai, final Card attacker, final List defenders, final Combat combat, final GameEntity defender) { boolean canBeKilled = false; // indicates if the attacker can be killed boolean canBeKilledByOne = false; // indicates if the attacker can be killed by a single blocker boolean canKillAll = true; // indicates if the attacker can kill all single blockers @@ -1169,7 +1154,7 @@ public class AiAttackController { } } - if (!isEffectiveAttacker(ai, attacker, combat)) { + if (!isEffectiveAttacker(ai, attacker, combat, defender)) { return false; } boolean hasAttackEffect = attacker.getSVar("HasAttackEffect").equals("TRUE") || attacker.hasStartOfKeyword("Annihilator"); @@ -1205,29 +1190,29 @@ public class AiAttackController { // look at the attacker in relation to the blockers to establish a // number of factors about the attacking context that will be relevant // to the attackers decision according to the selected strategy - for (final Card defender : validBlockers) { + for (final Card blocker : validBlockers) { // if both isWorthLessThanAllKillers and canKillAllDangerous are false there's nothing more to check if (isWorthLessThanAllKillers || canKillAllDangerous || numberOfPossibleBlockers < 2) { numberOfPossibleBlockers += 1; - if (isWorthLessThanAllKillers && ComputerUtilCombat.canDestroyAttacker(ai, attacker, defender, combat, false) + if (isWorthLessThanAllKillers && ComputerUtilCombat.canDestroyAttacker(ai, attacker, blocker, combat, false) && !(attacker.hasKeyword(Keyword.UNDYING) && attacker.getCounters(CounterEnumType.P1P1) == 0)) { canBeKilledByOne = true; // there is a single creature on the battlefield that can kill the creature // see if the defending creature is of higher or lower // value. We don't want to attack only to lose value if (isWorthLessThanAllKillers && !attacker.hasSVar("SacMe") - && ComputerUtilCard.evaluateCreature(defender) <= ComputerUtilCard.evaluateCreature(attacker)) { + && ComputerUtilCard.evaluateCreature(blocker) <= ComputerUtilCard.evaluateCreature(attacker)) { isWorthLessThanAllKillers = false; } } // see if this attacking creature can destroy this defender, if // not record that it can't kill everything - if (canKillAllDangerous && !ComputerUtilCombat.canDestroyBlocker(ai, defender, attacker, combat, false)) { + if (canKillAllDangerous && !ComputerUtilCombat.canDestroyBlocker(ai, blocker, attacker, combat, false)) { canKillAll = false; - if (defender.getSVar("HasCombatEffect").equals("TRUE") || defender.getSVar("HasBlockEffect").equals("TRUE")) { + if (blocker.getSVar("HasCombatEffect").equals("TRUE") || blocker.getSVar("HasBlockEffect").equals("TRUE")) { canKillAllDangerous = false; } else { - if (defender.hasKeyword(Keyword.WITHER) || defender.hasKeyword(Keyword.INFECT) - || defender.hasKeyword(Keyword.LIFELINK)) { + if (blocker.hasKeyword(Keyword.WITHER) || blocker.hasKeyword(Keyword.INFECT) + || blocker.hasKeyword(Keyword.LIFELINK)) { canKillAllDangerous = false; // there is a creature that can survive an attack from this creature // and combat will have negative effects @@ -1371,7 +1356,6 @@ public class AiAttackController { break; } } - } if (missTarget) { diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtilCombat.java b/forge-ai/src/main/java/forge/ai/ComputerUtilCombat.java index 3aac8b8d725..0cbd29fb9ed 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtilCombat.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtilCombat.java @@ -167,7 +167,6 @@ public class ComputerUtilCombat { return totalDamageOfBlockers(attacker, list); } - // This function takes Doran and Double Strike into account /** *

@@ -202,10 +201,10 @@ public class ComputerUtilCombat { * a {@link forge.game.combat.Combat} object. * @return a int. */ - public static int damageIfUnblocked(final Card attacker, final Player attacked, final Combat combat, boolean withoutAbilities) { + public static int damageIfUnblocked(final Card attacker, final GameEntity attacked, final Combat combat, boolean withoutAbilities) { int damage = attacker.getNetCombatDamage(); int sum = 0; - if (!attacked.canLoseLife()) { + if (attacked instanceof Player && !((Player) attacked).canLoseLife()) { return 0; } @@ -2139,7 +2138,6 @@ public class ComputerUtilCombat { return damageMap; } // setAssignedDamage() - // how much damage is enough to kill the creature (for AI) /** *

@@ -2175,13 +2173,13 @@ public class ComputerUtilCombat { */ public static final int getEnoughDamageToKill(final Card c, final int maxDamage, final Card source, final boolean isCombat, final boolean noPrevention) { - final int killDamage = c.isPlaneswalker() ? c.getCurrentLoyalty() : getDamageToKill(c); + final int killDamage = getDamageToKill(c); if (c.hasKeyword(Keyword.INDESTRUCTIBLE) || c.getShieldCount() > 0) { if (!(source.hasKeyword(Keyword.WITHER) || source.hasKeyword(Keyword.INFECT))) { return maxDamage + 1; } - } else if (source.hasKeyword(Keyword.DEATHTOUCH)) { + } else if (source.hasKeyword(Keyword.DEATHTOUCH) && !c.isPlaneswalker()) { for (int i = 1; i <= maxDamage; i++) { if (noPrevention) { if (c.staticReplaceDamage(i, source, isCombat) > 0) { @@ -2218,9 +2216,9 @@ public class ComputerUtilCombat { */ public final static int getDamageToKill(final Card c) { int damageShield = c.getPreventNextDamageTotalShields(); - int killDamage = c.getLethalDamage() + damageShield; + int killDamage = (c.isPlaneswalker() ? c.getCurrentLoyalty() : c.getLethalDamage()) + damageShield; - if ((killDamage > damageShield) + if (killDamage > damageShield && c.hasSVar("DestroyWhenDamaged")) { killDamage = 1 + damageShield; } diff --git a/forge-ai/src/main/java/forge/ai/ability/PumpAi.java b/forge-ai/src/main/java/forge/ai/ability/PumpAi.java index 1e6d683bbad..4f068500efa 100644 --- a/forge-ai/src/main/java/forge/ai/ability/PumpAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/PumpAi.java @@ -152,8 +152,8 @@ public class PumpAi extends PumpAiBase { final String sourceName = ComputerUtilAbility.getAbilitySourceName(sa); final List keywords = sa.hasParam("KW") ? Arrays.asList(sa.getParam("KW").split(" & ")) : Lists.newArrayList(); - final String numDefense = sa.hasParam("NumDef") ? sa.getParam("NumDef") : ""; - final String numAttack = sa.hasParam("NumAtt") ? sa.getParam("NumAtt") : ""; + final String numDefense = sa.getParamOrDefault("NumDef", ""); + final String numAttack = sa.getParamOrDefault("NumAtt", ""); final String aiLogic = sa.getParamOrDefault("AILogic", ""); @@ -699,8 +699,8 @@ public class PumpAi extends PumpAiBase { @Override protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) { final SpellAbility root = sa.getRootAbility(); - final String numDefense = sa.hasParam("NumDef") ? sa.getParam("NumDef") : ""; - final String numAttack = sa.hasParam("NumAtt") ? sa.getParam("NumAtt") : ""; + final String numDefense = sa.getParamOrDefault("NumDef", ""); + final String numAttack = sa.getParamOrDefault("NumAtt", ""); if (sa.getSVar("X").equals("Count$xPaid")) { sa.setXManaCostPaid(null); @@ -750,8 +750,8 @@ public class PumpAi extends PumpAiBase { final SpellAbility root = sa.getRootAbility(); final Card source = sa.getHostCard(); - final String numDefense = sa.hasParam("NumDef") ? sa.getParam("NumDef") : ""; - final String numAttack = sa.hasParam("NumAtt") ? sa.getParam("NumAtt") : ""; + final String numDefense = sa.getParamOrDefault("NumDef", ""); + final String numAttack = sa.getParamOrDefault("NumAtt", ""); if (numDefense.equals("-X") && sa.getSVar("X").equals("Count$ChosenNumber")) { int energy = ai.getCounters(CounterEnumType.ENERGY); diff --git a/forge-ai/src/main/java/forge/ai/ability/RepeatEachAi.java b/forge-ai/src/main/java/forge/ai/ability/RepeatEachAi.java index d11f04bc03c..42361a09af0 100644 --- a/forge-ai/src/main/java/forge/ai/ability/RepeatEachAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/RepeatEachAi.java @@ -36,17 +36,17 @@ public class RepeatEachAi extends SpellAbilityAi { return false; } else if ("CloneAllTokens".equals(logic)) { List humTokenCreats = CardLists.filter(aiPlayer.getOpponents().getCreaturesInPlay(), Presets.TOKEN); - List compTokenCreats = CardLists.filter(aiPlayer.getCreaturesInPlay(), Presets.TOKEN); + List compTokenCreats = aiPlayer.getTokensInPlay(); return compTokenCreats.size() > humTokenCreats.size(); } else if ("BalanceLands".equals(logic)) { - if (CardLists.filter(aiPlayer.getCardsIn(ZoneType.Battlefield), Presets.LANDS).size() >= 5) { + if (aiPlayer.getLandsInPlay().size() >= 5) { return false; } List opponents = aiPlayer.getOpponents(); for(Player opp : opponents) { - if (CardLists.filter(opp.getCardsIn(ZoneType.Battlefield), Presets.LANDS).size() < 4) { + if (opp.getLandsInPlay().size() < 4) { return false; } } diff --git a/forge-game/src/main/java/forge/game/GameAction.java b/forge-game/src/main/java/forge/game/GameAction.java index beeef036809..31b57f679d4 100644 --- a/forge-game/src/main/java/forge/game/GameAction.java +++ b/forge-game/src/main/java/forge/game/GameAction.java @@ -1644,7 +1644,7 @@ public class GameAction { private boolean handlePlaneswalkerRule(Player p, CardZoneTable table) { // get all Planeswalkers - final List list = CardLists.filter(p.getCardsIn(ZoneType.Battlefield), CardPredicates.Presets.PLANESWALKERS); + final List list = p.getPlaneswalkersInPlay(); boolean recheck = false; //final Multimap uniqueWalkers = ArrayListMultimap.create(); // Not used as of Ixalan diff --git a/forge-game/src/main/java/forge/game/ability/AbilityUtils.java b/forge-game/src/main/java/forge/game/ability/AbilityUtils.java index 93415c9922d..0427cb86e2f 100644 --- a/forge-game/src/main/java/forge/game/ability/AbilityUtils.java +++ b/forge-game/src/main/java/forge/game/ability/AbilityUtils.java @@ -45,7 +45,6 @@ import forge.game.card.CardPredicates; import forge.game.card.CardState; import forge.game.card.CardUtil; import forge.game.card.CounterType; -import forge.game.card.CardPredicates.Presets; import forge.game.cost.Cost; import forge.game.keyword.Keyword; import forge.game.keyword.KeywordInterface; @@ -2403,7 +2402,7 @@ public class AbilityUtils { if (sq[0].startsWith("Domain")) { int n = 0; Player neededPlayer = sq[0].equals("DomainActivePlayer") ? game.getPhaseHandler().getPlayerTurn() : player; - CardCollection someCards = CardLists.filter(neededPlayer.getCardsIn(ZoneType.Battlefield), Presets.LANDS); + CardCollection someCards = neededPlayer.getLandsInPlay(); for (String basic : MagicColor.Constant.BASIC_LANDS) { if (!CardLists.getType(someCards, basic).isEmpty()) { n++; diff --git a/forge-game/src/main/java/forge/game/ability/effects/CountersPutEffect.java b/forge-game/src/main/java/forge/game/ability/effects/CountersPutEffect.java index 122f854bbe2..f42c21939fe 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/CountersPutEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/CountersPutEffect.java @@ -24,7 +24,6 @@ import forge.game.card.CardCollection; import forge.game.card.CardFactoryUtil; import forge.game.card.CardLists; import forge.game.card.CardPredicates; -import forge.game.card.CardPredicates.Presets; import forge.game.card.CardUtil; import forge.game.card.CounterEnumType; import forge.game.card.CounterType; @@ -132,7 +131,7 @@ public class CountersPutEffect extends SpellAbilityEffect { List tgtObjects = Lists.newArrayList(); int divrem = 0; if (sa.hasParam("Bolster")) { - CardCollection creatsYouCtrl = CardLists.filter(activator.getCardsIn(ZoneType.Battlefield), Presets.CREATURES); + CardCollection creatsYouCtrl = activator.getCreaturesInPlay(); CardCollection leastToughness = new CardCollection(Aggregates.listWithMin(creatsYouCtrl, CardPredicates.Accessors.fnGetDefense)); Map params = Maps.newHashMap(); diff --git a/forge-game/src/main/java/forge/game/ability/effects/DamagePreventEffect.java b/forge-game/src/main/java/forge/game/ability/effects/DamagePreventEffect.java index 173d1318758..b64ec1154ea 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/DamagePreventEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/DamagePreventEffect.java @@ -57,10 +57,10 @@ public class DamagePreventEffect extends DamagePreventEffectBase { return sb.toString(); } - /* (non-Javadoc) - * @see forge.card.abilityfactory.SpellEffect#resolve(java.util.Map, forge.card.spellability.SpellAbility) - */ - @Override + /* (non-Javadoc) + * @see forge.card.abilityfactory.SpellEffect#resolve(java.util.Map, forge.card.spellability.SpellAbility) + */ + @Override public void resolve(SpellAbility sa) { Card host = sa.getHostCard(); int numDam = AbilityUtils.calculateAmount(host, sa.getParam("Amount"), sa); diff --git a/forge-game/src/main/java/forge/game/card/CardLists.java b/forge-game/src/main/java/forge/game/card/CardLists.java index 093010da07d..7308ae667c1 100644 --- a/forge-game/src/main/java/forge/game/card/CardLists.java +++ b/forge-game/src/main/java/forge/game/card/CardLists.java @@ -169,7 +169,6 @@ public class CardLists { Collections.sort(list, Collections.reverseOrder(PowerComparator)); } - /** * * Given a CardCollection c, return a CardCollection that contains a random amount of cards from c. diff --git a/forge-game/src/main/java/forge/game/card/CardProperty.java b/forge-game/src/main/java/forge/game/card/CardProperty.java index 51cadf709c5..ef74c61d242 100644 --- a/forge-game/src/main/java/forge/game/card/CardProperty.java +++ b/forge-game/src/main/java/forge/game/card/CardProperty.java @@ -1479,7 +1479,7 @@ public class CardProperty { if (StringUtils.isEmpty(what)) return combat.isBlocking(card); if (what.startsWith("Source")) return combat.isBlocking(card, source); if (what.startsWith("CreatureYouCtrl")) { - for (final Card c : CardLists.filter(sourceController.getCardsIn(ZoneType.Battlefield), Presets.CREATURES)) + for (final Card c : sourceController.getCreaturesInPlay()) if (combat.isBlocking(card, c)) return true; return false; diff --git a/forge-game/src/main/java/forge/game/combat/AttackRequirement.java b/forge-game/src/main/java/forge/game/combat/AttackRequirement.java index 2d5570323a0..7f9cbc3a886 100644 --- a/forge-game/src/main/java/forge/game/combat/AttackRequirement.java +++ b/forge-game/src/main/java/forge/game/combat/AttackRequirement.java @@ -13,8 +13,6 @@ import forge.game.Game; import forge.game.GameEntity; import forge.game.ability.AbilityUtils; import forge.game.card.Card; -import forge.game.card.CardLists; -import forge.game.card.CardPredicates; import forge.game.keyword.KeywordInterface; import forge.game.player.Player; import forge.game.zone.ZoneType; @@ -88,7 +86,7 @@ public class AttackRequirement { if (c.hasKeyword("Each opponent must attack you or a planeswalker you control with at least one creature each combat if able.")) { if (attacker.getController().isOpponentOf(c.getController()) && !defenderOrPWSpecific.containsKey(c.getController())) { defenderOrPWSpecific.put(c.getController(), 1); - for (Card pw : CardLists.filter(c.getController().getCardsIn(ZoneType.Battlefield), CardPredicates.Presets.PLANESWALKERS)) { + for (Card pw : c.getController().getPlaneswalkersInPlay()) { // Add the attack alternatives that suffice (planeswalkers that can be attacked instead of the player) if (!defenderSpecificAlternatives.containsKey(c.getController())) { defenderSpecificAlternatives.put(c.getController(), Lists.newArrayList()); diff --git a/forge-game/src/main/java/forge/game/combat/Combat.java b/forge-game/src/main/java/forge/game/combat/Combat.java index 9ab7719d0ef..88f41887e1c 100644 --- a/forge-game/src/main/java/forge/game/combat/Combat.java +++ b/forge-game/src/main/java/forge/game/combat/Combat.java @@ -243,7 +243,6 @@ public class Combat { public final void addAttacker(final Card c, GameEntity defender) { addAttacker(c, defender, null); } - public final void addAttacker(final Card c, GameEntity defender, AttackingBand band) { Collection attackersOfDefender = attackedByBands.get(defender); if (attackersOfDefender == null) { @@ -260,8 +259,7 @@ public class Combat { if (band == null || !attackersOfDefender.contains(band)) { band = new AttackingBand(c); attackersOfDefender.add(band); - } - else { + } else { band.addAttacker(c); } c.updateAttackingForView(); diff --git a/forge-game/src/main/java/forge/game/combat/CombatUtil.java b/forge-game/src/main/java/forge/game/combat/CombatUtil.java index dce72ff97d3..156f3686426 100644 --- a/forge-game/src/main/java/forge/game/combat/CombatUtil.java +++ b/forge-game/src/main/java/forge/game/combat/CombatUtil.java @@ -69,7 +69,7 @@ public class CombatUtil { final FCollection defenders = new FCollection<>(); for (final Player defender : playerWhoAttacks.getOpponents()) { defenders.add(defender); - final CardCollection planeswalkers = CardLists.filter(defender.getCardsIn(ZoneType.Battlefield), CardPredicates.Presets.PLANESWALKERS); + final CardCollection planeswalkers = defender.getPlaneswalkersInPlay(); defenders.addAll(planeswalkers); } return defenders; diff --git a/forge-game/src/main/java/forge/game/player/Player.java b/forge-game/src/main/java/forge/game/player/Player.java index 87854f290b5..cb557fac011 100644 --- a/forge-game/src/main/java/forge/game/player/Player.java +++ b/forge-game/src/main/java/forge/game/player/Player.java @@ -2449,6 +2449,10 @@ public class Player extends GameEntity implements Comparable { return CardLists.filter(getCardsIn(ZoneType.Battlefield), Presets.CREATURES); } + public CardCollection getPlaneswalkersInPlay() { + return CardLists.filter(getCardsIn(ZoneType.Battlefield), Presets.PLANESWALKERS); + } + /** * use to get a list of tokens in play for a given player. */ diff --git a/forge-gui/res/cardsfolder/s/sunstreak_phoenix.txt b/forge-gui/res/cardsfolder/s/sunstreak_phoenix.txt index ca3b4d4b8df..43c0591b279 100644 --- a/forge-gui/res/cardsfolder/s/sunstreak_phoenix.txt +++ b/forge-gui/res/cardsfolder/s/sunstreak_phoenix.txt @@ -6,7 +6,7 @@ K:Flying R:Event$ Moved | ValidCard$ Card.Self | Destination$ Battlefield | DayTime$ Neither | ReplaceWith$ DoDay | Description$ If it's neither day nor night, it becomes day as CARDNAME enters the battlefield. SVar:DoDay:DB$ DayTime | Value$ Day | SubAbility$ ETB SVar:ETB:DB$ InternalEtbReplacement -T:Mode$ DayTimeChanges | Execute$ TrigReturn | TriggerZones$ Battlefield | TriggerDescription$ Whenever day becomes night or night becomes day, you may pay {1}{R}. If you do, return CARDNAME from your graveyard to the battlefield tapped. +T:Mode$ DayTimeChanges | Execute$ TrigReturn | TriggerZones$ Graveyard | TriggerDescription$ Whenever day becomes night or night becomes day, you may pay {1}{R}. If you do, return CARDNAME from your graveyard to the battlefield tapped. SVar:TrigReturn:AB$ ChangeZone | Cost$ 1 R | Origin$ Graveyard | Destination$ Battlefield | Tapped$ True DeckHas:Ability$Graveyard Oracle:Flying\nIf it's neither day nor night, it becomes day as Sunstreak Phoenix enters the battlefield.\nWhenever day becomes night or night becomes day, you may pay {1}{R}. If you do, return Sunstreak Phoenix from your graveyard to the battlefield tapped. diff --git a/forge-gui/src/main/java/forge/gamemodes/match/input/InputAttack.java b/forge-gui/src/main/java/forge/gamemodes/match/input/InputAttack.java index 36e2ced0e6d..50dcea8bb78 100644 --- a/forge-gui/src/main/java/forge/gamemodes/match/input/InputAttack.java +++ b/forge-gui/src/main/java/forge/gamemodes/match/input/InputAttack.java @@ -27,8 +27,6 @@ import forge.game.GameEntity; import forge.game.GameEntityView; import forge.game.card.Card; import forge.game.card.CardCollectionView; -import forge.game.card.CardLists; -import forge.game.card.CardPredicates.Presets; import forge.game.card.CardView; import forge.game.combat.AttackingBand; import forge.game.combat.Combat; @@ -128,7 +126,7 @@ public class InputAttack extends InputSyncronizedBase { final List defenders = playerAttacks.getOpponents(); final Set refreshCards = Sets.newHashSet(); - for (final Card c : CardLists.filter(playerAttacks.getCardsIn(ZoneType.Battlefield), Presets.CREATURES)) { + for (final Card c : playerAttacks.getCreaturesInPlay()) { if (combat.isAttacking(c)) { continue; } diff --git a/forge-gui/src/main/java/forge/gamemodes/match/input/InputBlock.java b/forge-gui/src/main/java/forge/gamemodes/match/input/InputBlock.java index a9a1688d3b7..2db4d0e1f67 100644 --- a/forge-gui/src/main/java/forge/gamemodes/match/input/InputBlock.java +++ b/forge-gui/src/main/java/forge/gamemodes/match/input/InputBlock.java @@ -20,8 +20,6 @@ package forge.gamemodes.match.input; import java.util.List; import forge.game.card.Card; -import forge.game.card.CardLists; -import forge.game.card.CardPredicates.Presets; import forge.game.card.CardView; import forge.game.combat.Combat; import forge.game.combat.CombatUtil; @@ -60,7 +58,7 @@ public class InputBlock extends InputSyncronizedBase { //auto-select first attacker to declare blockers for for (final Card attacker : combat.getAttackers()) { - for (final Card c : CardLists.filter(defender.getCardsIn(ZoneType.Battlefield), Presets.CREATURES)) { + for (final Card c : defender.getCreaturesInPlay()) { if (CombatUtil.canBlock(attacker, c, combat)) { FThreads.invokeInEdtNowOrLater(new Runnable() { //must set current attacker on EDT @Override