diff --git a/forge-ai/src/main/java/forge/ai/AiAttackController.java b/forge-ai/src/main/java/forge/ai/AiAttackController.java index 1b07b0f4d5a..e1dd250beac 100644 --- a/forge-ai/src/main/java/forge/ai/AiAttackController.java +++ b/forge-ai/src/main/java/forge/ai/AiAttackController.java @@ -394,7 +394,7 @@ public class AiAttackController { } for (Card attacker : oppList) { - if (!CombatUtil.canAttackNextTurn(attacker)) { + if (!ComputerUtil.canAttackNextTurn(attacker)) { continue; } if (blockersLeft > 0 && CombatUtil.canBeBlocked(attacker, ai)) { @@ -657,7 +657,7 @@ public class AiAttackController { for (final Card pCard : this.oppList) { // if the creature can attack next turn add it to counter attackers // list - if (CombatUtil.canAttackNextTurn(pCard)) { + if (ComputerUtil.canAttackNextTurn(pCard)) { nextTurnAttackers.add(pCard); if (pCard.getNetCombatDamage() > 0) { candidateCounterAttackDamage += pCard.getNetCombatDamage(); @@ -684,7 +684,7 @@ public class AiAttackController { for (final Card pCard : this.myList) { // if the creature can attack then it's a potential attacker this // turn, assume summoning sickness creatures will be able to - if (CombatUtil.canAttackNextTurn(pCard)) { + if (ComputerUtil.canAttackNextTurn(pCard)) { candidateAttackers.add(pCard); if (pCard.getNetCombatDamage() > 0) { candidateUnblockedDamage += ComputerUtilCombat.damageIfUnblocked(pCard, opp, null); diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtil.java b/forge-ai/src/main/java/forge/ai/ComputerUtil.java index 6f6ceea1985..a0b5f377480 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtil.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtil.java @@ -879,7 +879,7 @@ public class ComputerUtil { playNow = false; break; } - if (!playNow && c.isCreature() && CombatUtil.canAttackNextTurn(c) && c.canBeEquippedBy(card)) { + if (!playNow && c.isCreature() && ComputerUtil.canAttackNextTurn(c) && c.canBeEquippedBy(card)) { playNow = true; } } @@ -1980,5 +1980,18 @@ public class ComputerUtil { return bestBoardPosition; } + + /** + *

+ * canAttackNextTurn. + *

+ * + * @param c + * a {@link forge.game.card.Card} object. + * @return a boolean. + */ + public static boolean canAttackNextTurn(final Card c) { + return CombatUtil.canAttackNextTurn(c, c.getController().getOpponent()); + } } diff --git a/forge-ai/src/main/java/forge/ai/ability/AttachAi.java b/forge-ai/src/main/java/forge/ai/ability/AttachAi.java index d01f459877f..9fa5c4a62a2 100644 --- a/forge-ai/src/main/java/forge/ai/ability/AttachAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/AttachAi.java @@ -616,7 +616,7 @@ public class AttachAi extends SpellAbilityAi { prefList = CardLists.filter(prefList, new Predicate() { @Override public boolean apply(final Card c) { - return CombatUtil.canAttackNextTurn(c) && c.getNetAttack() > 0; + return ComputerUtil.canAttackNextTurn(c) && c.getNetAttack() > 0; } }); } @@ -945,7 +945,7 @@ public class AttachAi extends SpellAbilityAi { if (!c.isCreature()) { return true; } - return CombatUtil.canAttackNextTurn(c) && powerBonus + c.getNetAttack() > 0; + return ComputerUtil.canAttackNextTurn(c) && powerBonus + c.getNetAttack() > 0; } }); } @@ -1133,7 +1133,7 @@ public class AttachAi extends SpellAbilityAi { // give evasive keywords to creatures that can attack and deal damage if (evasive) { if (card.getNetCombatDamage() + powerBonus <= 0 - || !CombatUtil.canAttackNextTurn(card) + || !ComputerUtil.canAttackNextTurn(card) || !CombatUtil.canBeBlocked(card, opponent)) { return false; } @@ -1142,20 +1142,20 @@ public class AttachAi extends SpellAbilityAi { || card.getNetCombatDamage() + powerBonus <= 0 || card.hasKeyword("CARDNAME can attack as though it had haste.") || ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS) - || !CombatUtil.canAttackNextTurn(card)) { + || !ComputerUtil.canAttackNextTurn(card)) { return false; } } else if (keyword.endsWith("Indestructible")) { return true; } else if (keyword.endsWith("Deathtouch") || keyword.endsWith("Wither")) { if (card.getNetCombatDamage() + powerBonus <= 0 - || ((!CombatUtil.canBeBlocked(card, opponent) || !CombatUtil.canAttackNextTurn(card)) + || ((!CombatUtil.canBeBlocked(card, opponent) || !ComputerUtil.canAttackNextTurn(card)) && !CombatUtil.canBlock(card, true))) { return false; } } else if (keyword.equals("Double Strike") || keyword.equals("Lifelink")) { if (card.getNetCombatDamage() + powerBonus <= 0 - || (!CombatUtil.canAttackNextTurn(card) && !CombatUtil.canBlock(card, true))) { + || (!ComputerUtil.canAttackNextTurn(card) && !CombatUtil.canBlock(card, true))) { return false; } } else if (keyword.equals("First Strike")) { @@ -1164,29 +1164,29 @@ public class AttachAi extends SpellAbilityAi { } } else if (keyword.startsWith("Flanking")) { if (card.getNetCombatDamage() + powerBonus <= 0 - || !CombatUtil.canAttackNextTurn(card) + || !ComputerUtil.canAttackNextTurn(card) || !CombatUtil.canBeBlocked(card, opponent)) { return false; } } else if (keyword.startsWith("Bushido")) { - if ((!CombatUtil.canBeBlocked(card, opponent) || !CombatUtil.canAttackNextTurn(card)) + if ((!CombatUtil.canBeBlocked(card, opponent) || !ComputerUtil.canAttackNextTurn(card)) && !CombatUtil.canBlock(card, true)) { return false; } } else if (keyword.equals("Trample")) { if (card.getNetCombatDamage() + powerBonus <= 1 || !CombatUtil.canBeBlocked(card, opponent) - || !CombatUtil.canAttackNextTurn(card)) { + || !ComputerUtil.canAttackNextTurn(card)) { return false; } } else if (keyword.equals("Infect")) { if (card.getNetCombatDamage() + powerBonus <= 0 - || !CombatUtil.canAttackNextTurn(card)) { + || !ComputerUtil.canAttackNextTurn(card)) { return false; } } else if (keyword.equals("Vigilance")) { if (card.getNetCombatDamage() + powerBonus <= 0 - || !CombatUtil.canAttackNextTurn(card) + || !ComputerUtil.canAttackNextTurn(card) || !CombatUtil.canBlock(card, true)) { return false; } @@ -1231,11 +1231,11 @@ public class AttachAi extends SpellAbilityAi { if (keyword.endsWith("CARDNAME can't attack.") || keyword.equals("Defender") || keyword.endsWith("CARDNAME can't attack or block.")) { - if (!CombatUtil.canAttackNextTurn(card) || card.getNetCombatDamage() < 1) { + if (!ComputerUtil.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.")) { - if (!CombatUtil.canAttackNextTurn(card) || !CombatUtil.canBlock(card, true) || ai.getCreaturesInPlay().isEmpty()) { + if (!ComputerUtil.canAttackNextTurn(card) || !CombatUtil.canBlock(card, true) || ai.getCreaturesInPlay().isEmpty()) { return false; } } else if (keyword.endsWith("CARDNAME can't block.") || keyword.contains("CantBlock")) { @@ -1250,12 +1250,12 @@ public class AttachAi extends SpellAbilityAi { } return false; } else if (keyword.endsWith("Prevent all combat damage that would be dealt by CARDNAME.")) { - if (!CombatUtil.canAttackNextTurn(card) || card.getNetCombatDamage() < 1) { + if (!ComputerUtil.canAttackNextTurn(card) || card.getNetCombatDamage() < 1) { return false; } } 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.")) { - if (!CombatUtil.canAttackNextTurn(card) || card.getNetCombatDamage() < 2) { + if (!ComputerUtil.canAttackNextTurn(card) || card.getNetCombatDamage() < 2) { return false; } } else if (keyword.endsWith("CARDNAME doesn't untap during your untap step.")) { diff --git a/forge-ai/src/main/java/forge/ai/ability/EncodeAi.java b/forge-ai/src/main/java/forge/ai/ability/EncodeAi.java index 2b534df5d4a..25132e88b78 100644 --- a/forge-ai/src/main/java/forge/ai/ability/EncodeAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/EncodeAi.java @@ -18,6 +18,8 @@ package forge.ai.ability; import com.google.common.base.Predicate; + +import forge.ai.ComputerUtil; import forge.ai.ComputerUtilCard; import forge.ai.SpellAbilityAi; import forge.game.card.Card; @@ -78,7 +80,7 @@ public final class EncodeAi extends SpellAbilityAi { final List attackers = CardLists.filter(options, new Predicate() { @Override public boolean apply(final Card c) { - return CombatUtil.canAttackNextTurn(c); + return ComputerUtil.canAttackNextTurn(c); } }); final List unblockables = CardLists.filter(attackers, new Predicate() { diff --git a/forge-ai/src/main/java/forge/ai/ability/PumpAiBase.java b/forge-ai/src/main/java/forge/ai/ability/PumpAiBase.java index fea78825b9c..18cbc5f7e77 100644 --- a/forge-ai/src/main/java/forge/ai/ability/PumpAiBase.java +++ b/forge-ai/src/main/java/forge/ai/ability/PumpAiBase.java @@ -235,7 +235,7 @@ public abstract class PumpAiBase extends SpellAbilityAi { || newPower <= 0 || card.hasKeyword("CARDNAME can attack as though it had haste.") || ph.getPhase().isAfter(PhaseType.COMBAT_DECLARE_ATTACKERS) - || !CombatUtil.canAttackNextTurn(card)) { + || !ComputerUtil.canAttackNextTurn(card)) { return false; } } else if (keyword.endsWith("Indestructible")) { diff --git a/forge-ai/src/main/java/forge/ai/ability/TapAllAi.java b/forge-ai/src/main/java/forge/ai/ability/TapAllAi.java index f946ec986d0..f788af69a22 100644 --- a/forge-ai/src/main/java/forge/ai/ability/TapAllAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/TapAllAi.java @@ -2,6 +2,8 @@ package forge.ai.ability; import com.google.common.base.Predicate; import com.google.common.collect.Iterables; + +import forge.ai.ComputerUtil; import forge.ai.SpellAbilityAi; import forge.game.Game; import forge.game.card.Card; @@ -83,7 +85,7 @@ public class TapAllAi extends SpellAbilityAi { final boolean any = Iterables.any(validTappables, new Predicate() { @Override public boolean apply(final Card c) { - return CombatUtil.canAttack(c) && CombatUtil.canAttackNextTurn(c); + return CombatUtil.canAttack(c) && ComputerUtil.canAttackNextTurn(c); } }); if(!any) { 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 0ef7795ee36..9d83785c529 100644 --- a/forge-game/src/main/java/forge/game/combat/CombatUtil.java +++ b/forge-game/src/main/java/forge/game/combat/CombatUtil.java @@ -731,7 +731,7 @@ public class CombatUtil { * @return a boolean. */ public static boolean canAttack(final Card c, final GameEntity def, final Combat combat) { - int cntAttackers = combat.getAttackers().size(); + final int cntAttackers = combat.getAttackers().size(); final Game game = c.getGame(); if (cntAttackers > 0) { @@ -741,10 +741,6 @@ public class CombatUtil { if (keyword.equals("No more than two creatures can attack each combat.")) { return false; } - if (keyword.equals("No more than two creatures can attack you each combat.") && - card.getController().getOpponent().equals(c.getController())) { - return false; - } } if (keyword.equals("CARDNAME can only attack alone.") && combat.isAttacking(card)) { return false; @@ -761,6 +757,18 @@ public class CombatUtil { } } + final int cntAttackersToDef = combat.getAttackersOf(def).size(); + if (cntAttackersToDef > 1) { + for (final Card card : game.getCardsIn(ZoneType.Battlefield)) { + for (final String keyword : card.getKeyword()) { + if (keyword.equals("No more than two creatures can attack you each combat.") && + card.getController().equals(def)) { + return false; + } + } + } + } + if ((c.hasKeyword("CARDNAME can't attack or block alone.") || c.hasKeyword("CARDNAME can't attack alone.")) && c.getController().getCreaturesInPlay().size() < 2) { return false; @@ -832,20 +840,8 @@ public class CombatUtil { * * @param c * a {@link forge.game.card.Card} object. - * @return a boolean. - */ - public static boolean canAttackNextTurn(final Card c) { - return canAttackNextTurn(c, c.getController().getOpponent()); - } - - // can a creature attack if untapped and without summoning sickness? - /** - *

- * canAttackNextTurn. - *

- * - * @param c - * a {@link forge.game.card.Card} object. + * @param def + * the defending {@link GameEntity}. * @return a boolean. */ public static boolean canAttackNextTurn(final Card c, final GameEntity defender) {