removed some calls to IsComputer in favour of comparision with player passed as parameter

checkConditions inlined
ComputerUtilCombat recieve ai player as parameter
This commit is contained in:
Maxmtg
2013-02-03 17:30:25 +00:00
parent 59df2417b8
commit cd9c815534
18 changed files with 96 additions and 105 deletions

View File

@@ -345,19 +345,6 @@ public class AbilityFactory {
condition.setConditions(mapParams); condition.setConditions(mapParams);
} }
/**
* <p>
* checkConditional.
* </p>
*
* @param sa
* a {@link forge.card.spellability.SpellAbility} object.
* @return a boolean.
*/
public static boolean checkConditional(final SpellAbility sa) {
return sa.getConditions().checkConditions(sa);
}
// Easy creation of SubAbilities // Easy creation of SubAbilities
/** /**
* <p> * <p>
@@ -1579,13 +1566,7 @@ public class AbilityFactory {
public static void passUnlessCost(final SpellAbility sa, final boolean usedStack, final GameState game) { public static void passUnlessCost(final SpellAbility sa, final boolean usedStack, final GameState game) {
final Card source = sa.getSourceCard(); final Card source = sa.getSourceCard();
final ApiType api = sa.getApi(); final ApiType api = sa.getApi();
if (api == null) { if (api == null || sa.getParam("UnlessCost") == null) {
sa.resolve();
AbilityFactory.resolveSubAbilities(sa, usedStack, game);
return;
}
// Nothing to do
if (sa.getParam("UnlessCost") == null) {
sa.resolve(); sa.resolve();
AbilityFactory.resolveSubAbilities(sa, usedStack, game); AbilityFactory.resolveSubAbilities(sa, usedStack, game);
return; return;
@@ -1730,7 +1711,7 @@ public class AbilityFactory {
final GameState game = Singletons.getModel().getGame(); final GameState game = Singletons.getModel().getGame();
// check conditions // check conditions
if (AbilityFactory.checkConditional(sa)) { if (sa.getConditions().areMet(sa)) {
if (sa.isWrapper()) { if (sa.isWrapper()) {
sa.resolve(); sa.resolve();
AbilityFactory.resolveSubAbilities(sa, usedStack, game); AbilityFactory.resolveSubAbilities(sa, usedStack, game);
@@ -1757,12 +1738,12 @@ public class AbilityFactory {
// every resolving spellAbility will end here // every resolving spellAbility will end here
if (usedStack) { if (usedStack) {
SpellAbility root = sa.getRootAbility(); SpellAbility root = sa.getRootAbility();
Singletons.getModel().getGame().getStack().finishResolving(root, false); game.getStack().finishResolving(root, false);
} }
return; return;
} }
// check conditions // check conditions
if (AbilityFactory.checkConditional(abSub)) { if (abSub.getConditions().areMet(abSub)) {
AbilityFactory.passUnlessCost(abSub, usedStack, game); AbilityFactory.passUnlessCost(abSub, usedStack, game);
} else { } else {
AbilityFactory.resolveSubAbilities(abSub, usedStack, game); AbilityFactory.resolveSubAbilities(abSub, usedStack, game);

View File

@@ -816,7 +816,7 @@ public class AttachAi extends SpellAiLogic {
// at some point can support attaching a different card // at some point can support attaching a different card
// Don't equip if already equipping // Don't equip if already equipping
if (attachSource.getEquippingCard() != null && attachSource.getEquippingCard().getController().isComputer()) { if (attachSource.getEquippingCard() != null && attachSource.getEquippingCard().getController() == aiPlayer) {
return null; return null;
} }

View File

@@ -222,10 +222,10 @@ public class ChangeZoneAi extends SpellAiLogic {
} }
// don't play if the conditions aren't met, unless it would trigger a beneficial sub-condition // don't play if the conditions aren't met, unless it would trigger a beneficial sub-condition
if (!AbilityFactory.checkConditional(sa)) { if (!sa.getConditions().areMet(sa)) {
final AbilitySub abSub = sa.getSubAbility(); final AbilitySub abSub = sa.getSubAbility();
if (abSub != null && !sa.isWrapper() && "True".equals(source.getSVar("AIPlayForSub"))) { if (abSub != null && !sa.isWrapper() && "True".equals(source.getSVar("AIPlayForSub"))) {
if (!AbilityFactory.checkConditional(abSub)) { if (!abSub.getConditions().areMet(abSub)) {
return false; return false;
} }
} else { } else {
@@ -269,7 +269,7 @@ public class ChangeZoneAi extends SpellAiLogic {
for (final Player p : pDefined) { for (final Player p : pDefined) {
List<Card> list = p.getCardsIn(origin); List<Card> list = p.getCardsIn(origin);
if ((type != null) && p.isComputer()) { if ((type != null) && p == ai) {
// AI only "knows" about his information // AI only "knows" about his information
list = CardLists.getValidCards(list, type, source.getController(), source); list = CardLists.getValidCards(list, type, source.getController(), source);
} }
@@ -402,7 +402,7 @@ public class ChangeZoneAi extends SpellAiLogic {
List<Card> list = p.getCardsIn(origin); List<Card> list = p.getCardsIn(origin);
// Computer should "know" his deck // Computer should "know" his deck
if (p.isComputer()) { if (p == ai) {
list = AbilityFactory.filterListByType(list, sa.getParam("ChangeType"), sa); list = AbilityFactory.filterListByType(list, sa.getParam("ChangeType"), sa);
} }
@@ -741,7 +741,7 @@ public class ChangeZoneAi extends SpellAiLogic {
CardLists.sortByEvaluateCreature(combatants); CardLists.sortByEvaluateCreature(combatants);
for (final Card c : combatants) { for (final Card c : combatants) {
if (c.getShield() == 0 && ComputerUtilCombat.combatantWouldBeDestroyed(c) && !c.getOwner().isHuman() && !c.isToken()) { if (c.getShield() == 0 && ComputerUtilCombat.combatantWouldBeDestroyed(ai, c) && c.getOwner() == ai && !c.isToken()) {
tgt.addTarget(c); tgt.addTarget(c);
return true; return true;
} }
@@ -812,7 +812,7 @@ public class ChangeZoneAi extends SpellAiLogic {
@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 (c.getOwner().isHuman() && aura.getController().equals(ai)) { if (c.getOwner().isHostileTo(ai) && aura.getController().equals(ai)) {
return false; return false;
} }
} }
@@ -1036,7 +1036,7 @@ public class ChangeZoneAi extends SpellAiLogic {
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().isHuman()) { if (attachedTo.getController().isHostileTo(ai)) {
return false; return false;
} }
} }
@@ -1147,7 +1147,7 @@ public class ChangeZoneAi extends SpellAiLogic {
return true; return true;
} }
}); });
if (player.isHuman() && sa.hasParam("GainControl") && activator.equals(ai)) { if (player.isHostileTo(ai) && sa.hasParam("GainControl") && activator.equals(ai)) {
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) {
@@ -1161,7 +1161,7 @@ public class ChangeZoneAi extends SpellAiLogic {
} }
if (ZoneType.Exile.equals(destination) || origin.contains(ZoneType.Battlefield)) { if (ZoneType.Exile.equals(destination) || origin.contains(ZoneType.Battlefield)) {
// Exiling or bouncing stuff // Exiling or bouncing stuff
if (player.isHuman()) { if (player.isHostileTo(ai)) {
c = CardFactoryUtil.getBestAI(fetchList); c = CardFactoryUtil.getBestAI(fetchList);
} else { } else {
c = CardFactoryUtil.getWorstAI(fetchList); c = CardFactoryUtil.getWorstAI(fetchList);

View File

@@ -155,7 +155,7 @@ public class DamageDealAi extends DamageAiBase {
final List<Card> killables = CardLists.filter(hPlay, new Predicate<Card>() { final List<Card> killables = CardLists.filter(hPlay, new Predicate<Card>() {
@Override @Override
public boolean apply(final Card c) { public boolean apply(final Card c) {
return (c.getEnoughDamageToKill(d, source, false, noPrevention) <= d) && !ComputerUtil.canRegenerate(c) return (c.getEnoughDamageToKill(d, source, false, noPrevention) <= d) && !ComputerUtil.canRegenerate(ai, c)
&& !(c.getSVar("SacMe").length() > 0); && !(c.getSVar("SacMe").length() > 0);
} }
}); });

View File

@@ -68,7 +68,7 @@ public class DamagePreventAi extends SpellAiLogic {
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;
flag |= ComputerUtilCombat.combatantWouldBeDestroyed(c); flag |= ComputerUtilCombat.combatantWouldBeDestroyed(ai, c);
} else if (o instanceof Player) { } else if (o instanceof Player) {
// Don't need to worry about Combat Damage during AI's turn // Don't need to worry about Combat Damage during AI's turn
final Player p = (Player) o; final Player p = (Player) o;
@@ -134,7 +134,7 @@ public class DamagePreventAi extends SpellAiLogic {
CardLists.sortByEvaluateCreature(combatants); CardLists.sortByEvaluateCreature(combatants);
for (final Card c : combatants) { for (final Card c : combatants) {
if (ComputerUtilCombat.combatantWouldBeDestroyed(c)) { if (ComputerUtilCombat.combatantWouldBeDestroyed(ai, c)) {
tgt.addTarget(c); tgt.addTarget(c);
chance = true; chance = true;
break; break;
@@ -195,7 +195,7 @@ public class DamagePreventAi extends SpellAiLogic {
CardLists.sortByEvaluateCreature(combatants); CardLists.sortByEvaluateCreature(combatants);
if (Singletons.getModel().getGame().getPhaseHandler().is(PhaseType.COMBAT_DECLARE_BLOCKERS_INSTANT_ABILITY)) { if (Singletons.getModel().getGame().getPhaseHandler().is(PhaseType.COMBAT_DECLARE_BLOCKERS_INSTANT_ABILITY)) {
for (final Card c : combatants) { for (final Card c : combatants) {
if (ComputerUtilCombat.combatantWouldBeDestroyed(c)) { if (ComputerUtilCombat.combatantWouldBeDestroyed(ai, c)) {
tgt.addTarget(c); tgt.addTarget(c);
return true; return true;
} }

View File

@@ -37,7 +37,7 @@ public class DestroyAi extends SpellAiLogic {
* @see forge.card.abilityfactory.SpellAiLogic#canPlayAI(forge.game.player.Player, java.util.Map, forge.card.spellability.SpellAbility) * @see forge.card.abilityfactory.SpellAiLogic#canPlayAI(forge.game.player.Player, java.util.Map, forge.card.spellability.SpellAbility)
*/ */
@Override @Override
protected boolean canPlayAI(Player ai, SpellAbility sa) { protected boolean canPlayAI(final Player ai, SpellAbility sa) {
// AI needs to be expanded, since this function can be pretty complex // AI needs to be expanded, since this function can be pretty complex
// based on what the expected targets could be // based on what the expected targets could be
final Random r = MyRandom.getRandom(); final Random r = MyRandom.getRandom();
@@ -105,7 +105,7 @@ public class DestroyAi extends SpellAiLogic {
list = CardLists.filter(list, new Predicate<Card>() { list = CardLists.filter(list, new Predicate<Card>() {
@Override @Override
public boolean apply(final Card c) { public boolean apply(final Card c) {
return ((c.getShield() == 0) && !ComputerUtil.canRegenerate(c)); return ((c.getShield() == 0) && !ComputerUtil.canRegenerate(ai, c));
} }
}); });
} }

View File

@@ -52,10 +52,10 @@ public class LifeGainAi extends SpellAiLogic {
} }
// don't play if the conditions aren't met, unless it would trigger a // don't play if the conditions aren't met, unless it would trigger a
// beneficial sub-condition // beneficial sub-condition
if (!AbilityFactory.checkConditional(sa)) { if (!sa.getConditions().areMet(sa)) {
final AbilitySub abSub = sa.getSubAbility(); final AbilitySub abSub = sa.getSubAbility();
if (abSub != null && !sa.isWrapper() && "True".equals(source.getSVar("AIPlayForSub"))) { if (abSub != null && !sa.isWrapper() && "True".equals(source.getSVar("AIPlayForSub"))) {
if (!AbilityFactory.checkConditional(abSub)) { if (!abSub.getConditions().areMet(abSub)) {
return false; return false;
} }
} else { } else {

View File

@@ -34,7 +34,7 @@ public class MustBlockAi extends SpellAiLogic {
} }
@Override @Override
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) { protected boolean doTriggerAINoCost(final Player ai, SpellAbility sa, boolean mandatory) {
final Card source = sa.getSourceCard(); final Card source = sa.getSourceCard();
final Target abTgt = sa.getTarget(); final Target abTgt = sa.getTarget();
@@ -73,10 +73,10 @@ public class MustBlockAi extends SpellAiLogic {
if (!CombatUtil.canBlock(definedAttacker, c)) { if (!CombatUtil.canBlock(definedAttacker, c)) {
return false; return false;
} }
if (ComputerUtilCombat.canDestroyAttacker(definedAttacker, c, null, false)) { if (ComputerUtilCombat.canDestroyAttacker(ai, definedAttacker, c, null, false)) {
return false; return false;
} }
if (!ComputerUtilCombat.canDestroyBlocker(c, definedAttacker, null, false)) { if (!ComputerUtilCombat.canDestroyBlocker(ai, c, definedAttacker, null, false)) {
return false; return false;
} }
c.setTapped(tapped); c.setTapped(tapped);

View File

@@ -15,6 +15,7 @@ import forge.card.cardfactory.CardFactoryUtil;
import forge.card.cost.Cost; import forge.card.cost.Cost;
import forge.card.spellability.SpellAbility; import forge.card.spellability.SpellAbility;
import forge.card.spellability.Target; import forge.card.spellability.Target;
import forge.game.GameState;
import forge.game.ai.ComputerUtilCombat; import forge.game.ai.ComputerUtilCombat;
import forge.game.ai.ComputerUtilCost; import forge.game.ai.ComputerUtilCost;
import forge.game.phase.PhaseType; import forge.game.phase.PhaseType;
@@ -66,6 +67,7 @@ public class ProtectAi extends SpellAiLogic {
*/ */
private static List<Card> getProtectCreatures(final Player ai, final SpellAbility sa) { private static List<Card> getProtectCreatures(final Player ai, final SpellAbility sa) {
final ArrayList<String> gains = AbilityFactory.getProtectionList(sa); final ArrayList<String> gains = AbilityFactory.getProtectionList(sa);
final GameState game = Singletons.getModel().getGame();
List<Card> list = ai.getCreaturesInPlay(); List<Card> list = ai.getCreaturesInPlay();
list = CardLists.filter(list, new Predicate<Card>() { list = CardLists.filter(list, new Predicate<Card>() {
@@ -81,8 +83,8 @@ public class ProtectAi extends SpellAiLogic {
} }
// will the creature attack (only relevant for sorcery speed)? // will the creature attack (only relevant for sorcery speed)?
if (Singletons.getModel().getGame().getPhaseHandler().getPhase().isBefore(PhaseType.COMBAT_DECLARE_ATTACKERS) if (game.getPhaseHandler().getPhase().isBefore(PhaseType.COMBAT_DECLARE_ATTACKERS)
&& Singletons.getModel().getGame().getPhaseHandler().isPlayerTurn(ai) && game.getPhaseHandler().isPlayerTurn(ai)
&& CardFactoryUtil.doesCreatureAttackAI(ai, c)) { && CardFactoryUtil.doesCreatureAttackAI(ai, c)) {
return true; return true;
} }
@@ -90,14 +92,14 @@ public class ProtectAi extends SpellAiLogic {
// is the creature blocking and unable to destroy the attacker // is the creature blocking and unable to destroy the attacker
// or would be destroyed itself? // or would be destroyed itself?
if (c.isBlocking() if (c.isBlocking()
&& (ComputerUtilCombat.blockerWouldBeDestroyed(c))) { && (ComputerUtilCombat.blockerWouldBeDestroyed(ai, c))) {
return true; return true;
} }
// is the creature in blocked and the blocker would survive // is the creature in blocked and the blocker would survive
if (Singletons.getModel().getGame().getPhaseHandler().getPhase().isAfter(PhaseType.COMBAT_DECLARE_BLOCKERS) if (game.getPhaseHandler().getPhase().isAfter(PhaseType.COMBAT_DECLARE_BLOCKERS)
&& Singletons.getModel().getGame().getCombat().isAttacking(c) && Singletons.getModel().getGame().getCombat().isBlocked(c) && game.getCombat().isAttacking(c) && game.getCombat().isBlocked(c)
&& ComputerUtilCombat.blockerWouldBeDestroyed(Singletons.getModel().getGame().getCombat().getBlockers(c).get(0))) { && ComputerUtilCombat.blockerWouldBeDestroyed(ai, game.getCombat().getBlockers(c).get(0))) {
return true; return true;
} }

View File

@@ -16,6 +16,7 @@ import forge.card.abilityfactory.AbilityFactory;
import forge.card.abilityfactory.SpellAiLogic; import forge.card.abilityfactory.SpellAiLogic;
import forge.card.cardfactory.CardFactoryUtil; import forge.card.cardfactory.CardFactoryUtil;
import forge.card.spellability.SpellAbility; import forge.card.spellability.SpellAbility;
import forge.game.GameState;
import forge.game.ai.ComputerUtilCombat; import forge.game.ai.ComputerUtilCombat;
import forge.game.phase.Combat; import forge.game.phase.Combat;
import forge.game.phase.CombatUtil; import forge.game.phase.CombatUtil;
@@ -223,7 +224,7 @@ public abstract class PumpAiBase extends SpellAiLogic {
List<Card> attackers = combat.getAttackers(); List<Card> attackers = combat.getAttackers();
for (Card attacker : attackers) { for (Card attacker : attackers) {
if (CombatUtil.canBlock(attacker, card, combat) if (CombatUtil.canBlock(attacker, card, combat)
&& !ComputerUtilCombat.canDestroyAttacker(attacker, card, combat, false)) { && !ComputerUtilCombat.canDestroyAttacker(ai, attacker, card, combat, false)) {
return true; return true;
} }
} }
@@ -232,7 +233,7 @@ public abstract class PumpAiBase extends SpellAiLogic {
List<Card> blockers = opp.getCreaturesInPlay(); List<Card> blockers = opp.getCreaturesInPlay();
for (Card blocker : blockers) { for (Card blocker : blockers) {
if (CombatUtil.canBlock(card, blocker, combat) if (CombatUtil.canBlock(card, blocker, combat)
&& !ComputerUtilCombat.canDestroyBlocker(blocker, card, combat, false)) { && !ComputerUtilCombat.canDestroyBlocker(ai, blocker, card, combat, false)) {
return true; return true;
} }
} }
@@ -382,7 +383,8 @@ public abstract class PumpAiBase extends SpellAiLogic {
protected boolean shouldPumpCard(final Player ai, final SpellAbility sa, final Card c, final int defense, final int attack, protected boolean shouldPumpCard(final Player ai, final SpellAbility sa, final Card c, final int defense, final int attack,
final List<String> keywords) { final List<String> keywords) {
PhaseHandler phase = Singletons.getModel().getGame().getPhaseHandler(); final GameState game = Singletons.getModel().getGame();
PhaseHandler phase = game.getPhaseHandler();
if (!c.canBeTargetedBy(sa)) { if (!c.canBeTargetedBy(sa)) {
return false; return false;
@@ -408,36 +410,35 @@ public abstract class PumpAiBase extends SpellAiLogic {
// is the creature blocking and unable to destroy the attacker // is the creature blocking and unable to destroy the attacker
// or would be destroyed itself? // or would be destroyed itself?
if (phase.is(PhaseType.COMBAT_DECLARE_BLOCKERS_INSTANT_ABILITY) && c.isBlocking()) { if (phase.is(PhaseType.COMBAT_DECLARE_BLOCKERS_INSTANT_ABILITY) && c.isBlocking()) {
if (defense > 0 && ComputerUtilCombat.blockerWouldBeDestroyed(c)) { if (defense > 0 && ComputerUtilCombat.blockerWouldBeDestroyed(ai, c)) {
return true; return true;
} }
List<Card> blockedBy = Singletons.getModel().getGame().getCombat().getAttackersBlockedBy(c); List<Card> blockedBy = Singletons.getModel().getGame().getCombat().getAttackersBlockedBy(c);
// For now, Only care the first creature blocked by a card. // For now, Only care the first creature blocked by a card.
// TODO Add in better BlockAdditional support // TODO Add in better BlockAdditional support
if (!blockedBy.isEmpty() && attack > 0 && !ComputerUtilCombat.attackerWouldBeDestroyed(blockedBy.get(0))) { if (!blockedBy.isEmpty() && attack > 0 && !ComputerUtilCombat.attackerWouldBeDestroyed(ai, blockedBy.get(0))) {
return true; return true;
} }
} }
// is the creature unblocked and the spell will pump its power? // is the creature unblocked and the spell will pump its power?
if (phase.is(PhaseType.COMBAT_DECLARE_BLOCKERS_INSTANT_ABILITY) if (phase.is(PhaseType.COMBAT_DECLARE_BLOCKERS_INSTANT_ABILITY)
&& Singletons.getModel().getGame().getCombat().isAttacking(c) && game.getCombat().isAttacking(c) && game.getCombat().isUnblocked(c) && attack > 0) {
&& Singletons.getModel().getGame().getCombat().isUnblocked(c) && attack > 0) {
return true; return true;
} }
// is the creature blocked and the blocker would survive // is the creature blocked and the blocker would survive
if (phase.is(PhaseType.COMBAT_DECLARE_BLOCKERS_INSTANT_ABILITY) && attack > 0 if (phase.is(PhaseType.COMBAT_DECLARE_BLOCKERS_INSTANT_ABILITY) && attack > 0
&& Singletons.getModel().getGame().getCombat().isAttacking(c) && game.getCombat().isAttacking(c)
&& Singletons.getModel().getGame().getCombat().isBlocked(c) && game.getCombat().isBlocked(c)
&& Singletons.getModel().getGame().getCombat().getBlockers(c) != null && game.getCombat().getBlockers(c) != null
&& !Singletons.getModel().getGame().getCombat().getBlockers(c).isEmpty() && !game.getCombat().getBlockers(c).isEmpty()
&& !ComputerUtilCombat.blockerWouldBeDestroyed(Singletons.getModel().getGame().getCombat().getBlockers(c).get(0))) { && !ComputerUtilCombat.blockerWouldBeDestroyed(ai, game.getCombat().getBlockers(c).get(0))) {
return true; return true;
} }
// if the life of the computer is in danger, try to pump blockers blocking Tramplers // if the life of the computer is in danger, try to pump blockers blocking Tramplers
List<Card> blockedBy = Singletons.getModel().getGame().getCombat().getAttackersBlockedBy(c); List<Card> blockedBy = game.getCombat().getAttackersBlockedBy(c);
boolean attackerHasTrample = false; boolean attackerHasTrample = false;
for (Card b : blockedBy) { for (Card b : blockedBy) {
attackerHasTrample |= b.hasKeyword("Trample"); attackerHasTrample |= b.hasKeyword("Trample");
@@ -448,7 +449,7 @@ public abstract class PumpAiBase extends SpellAiLogic {
&& c.isBlocking() && c.isBlocking()
&& defense > 0 && defense > 0
&& attackerHasTrample && attackerHasTrample
&& (sa.isAbility() || ComputerUtilCombat.lifeInDanger(ai, Singletons.getModel().getGame().getCombat()))) { && (sa.isAbility() || ComputerUtilCombat.lifeInDanger(ai, game.getCombat()))) {
return true; return true;
} }

View File

@@ -95,7 +95,7 @@ public class RegenerateAi extends SpellAiLogic {
for (final Card c : list) { for (final Card c : list) {
if (c.getShield() == 0) { if (c.getShield() == 0) {
flag |= ComputerUtilCombat.combatantWouldBeDestroyed(c); flag |= ComputerUtilCombat.combatantWouldBeDestroyed(ai, c);
} }
} }
@@ -139,7 +139,7 @@ public class RegenerateAi extends SpellAiLogic {
CardLists.sortByEvaluateCreature(combatants); CardLists.sortByEvaluateCreature(combatants);
for (final Card c : combatants) { for (final Card c : combatants) {
if ((c.getShield() == 0) && ComputerUtilCombat.combatantWouldBeDestroyed(c)) { if ((c.getShield() == 0) && ComputerUtilCombat.combatantWouldBeDestroyed(ai, c)) {
tgt.addTarget(c); tgt.addTarget(c);
chance = true; chance = true;
break; break;
@@ -190,7 +190,7 @@ public class RegenerateAi extends SpellAiLogic {
CardLists.sortByEvaluateCreature(combatants); CardLists.sortByEvaluateCreature(combatants);
if (Singletons.getModel().getGame().getPhaseHandler().is(PhaseType.COMBAT_DECLARE_BLOCKERS_INSTANT_ABILITY)) { if (Singletons.getModel().getGame().getPhaseHandler().is(PhaseType.COMBAT_DECLARE_BLOCKERS_INSTANT_ABILITY)) {
for (final Card c : combatants) { for (final Card c : combatants) {
if ((c.getShield() == 0) && ComputerUtilCombat.combatantWouldBeDestroyed(c)) { if ((c.getShield() == 0) && ComputerUtilCombat.combatantWouldBeDestroyed(ai, c)) {
tgt.addTarget(c); tgt.addTarget(c);
return true; return true;
} }

View File

@@ -68,7 +68,7 @@ public class RegenerateAllAi extends SpellAiLogic {
final List<Card> combatants = CardLists.filter(list, CardPredicates.Presets.CREATURES); final List<Card> combatants = CardLists.filter(list, CardPredicates.Presets.CREATURES);
for (final Card c : combatants) { for (final Card c : combatants) {
if (c.getShield() == 0 && ComputerUtilCombat.combatantWouldBeDestroyed(c)) { if (c.getShield() == 0 && ComputerUtilCombat.combatantWouldBeDestroyed(ai, c)) {
numSaved++; numSaved++;
} }
} }

View File

@@ -124,6 +124,7 @@ public class ChooseTypeEffect extends SpellEffect {
GuiChoose.one("Computer picked: ", new String[]{chosen}); GuiChoose.one("Computer picked: ", new String[]{chosen});
chosenType = chosen; chosenType = chosen;
} }
if (CardUtil.isACreatureType(chosenType) && !invalidTypes.contains(chosenType)) { if (CardUtil.isACreatureType(chosenType) && !invalidTypes.contains(chosenType)) {
valid = true; valid = true;
card.setChosenType(chosenType); card.setChosenType(chosenType);

View File

@@ -166,7 +166,7 @@ public class SpellAbilityCondition extends SpellAbilityVariables {
* a {@link forge.card.spellability.SpellAbility} object. * a {@link forge.card.spellability.SpellAbility} object.
* @return a boolean. * @return a boolean.
*/ */
public final boolean checkConditions(final SpellAbility sa) { public final boolean areMet(final SpellAbility sa) {
Player activator = sa.getActivatingPlayer(); Player activator = sa.getActivatingPlayer();
if (activator == null) { if (activator == null) {

View File

@@ -901,7 +901,7 @@ public class AiAttackController {
if ((isWorthLessThanAllKillers || canKillAllDangerous || numberOfPossibleBlockers < 2) if ((isWorthLessThanAllKillers || canKillAllDangerous || numberOfPossibleBlockers < 2)
&& CombatUtil.canBlock(attacker, defender)) { && CombatUtil.canBlock(attacker, defender)) {
numberOfPossibleBlockers += 1; numberOfPossibleBlockers += 1;
if (isWorthLessThanAllKillers && ComputerUtilCombat.canDestroyAttacker(attacker, defender, combat, false) if (isWorthLessThanAllKillers && ComputerUtilCombat.canDestroyAttacker(ai, attacker, defender, combat, false)
&& !(attacker.hasKeyword("Undying") && attacker.getCounters(CounterType.P1P1) == 0)) { && !(attacker.hasKeyword("Undying") && attacker.getCounters(CounterType.P1P1) == 0)) {
canBeKilledByOne = true; // there is a single creature on canBeKilledByOne = true; // there is a single creature on
// the battlefield that can kill // the battlefield that can kill
@@ -915,7 +915,7 @@ public class AiAttackController {
} }
// see if this attacking creature can destroy this defender, if // see if this attacking creature can destroy this defender, if
// not record that it can't kill everything // not record that it can't kill everything
if (canKillAllDangerous && !ComputerUtilCombat.canDestroyBlocker(defender, attacker, combat, false)) { if (canKillAllDangerous && !ComputerUtilCombat.canDestroyBlocker(ai, defender, attacker, combat, false)) {
canKillAll = false; canKillAll = false;
if (!canKillAllDangerous) { if (!canKillAllDangerous) {
continue; continue;

View File

@@ -415,7 +415,7 @@ public class ComputerUtil {
typeList = CardLists.getNotType(typeList, "Creature"); typeList = CardLists.getNotType(typeList, "Creature");
} }
if ((target != null) && target.getController().isComputer() && typeList.contains(target)) { if ((target != null) && target.getController() == ai && typeList.contains(target)) {
typeList.remove(target); // don't sacrifice the card we're pumping typeList.remove(target); // don't sacrifice the card we're pumping
} }
@@ -473,7 +473,7 @@ public class ComputerUtil {
} else { } else {
typeList = CardLists.getValidCards(ai.getCardsIn(zone), type.split(","), activate.getController(), activate); typeList = CardLists.getValidCards(ai.getCardsIn(zone), type.split(","), activate.getController(), activate);
} }
if ((target != null) && target.getController().isComputer() && typeList.contains(target)) { if ((target != null) && target.getController() == ai && typeList.contains(target)) {
typeList.remove(target); // don't exile the card we're pumping typeList.remove(target); // don't exile the card we're pumping
} }
@@ -588,7 +588,7 @@ public class ComputerUtil {
public static List<Card> chooseReturnType(final Player ai, final String type, final Card activate, final Card target, final int amount) { public static List<Card> chooseReturnType(final Player ai, final String type, final Card activate, final Card target, final int amount) {
final List<Card> typeList = final List<Card> typeList =
CardLists.getValidCards(ai.getCardsIn(ZoneType.Battlefield), type.split(","), activate.getController(), activate); CardLists.getValidCards(ai.getCardsIn(ZoneType.Battlefield), type.split(","), activate.getController(), activate);
if ((target != null) && target.getController().isComputer() && typeList.contains(target)) { if ((target != null) && target.getController() == ai && typeList.contains(target)) {
// don't bounce the card we're pumping // don't bounce the card we're pumping
typeList.remove(target); typeList.remove(target);
} }
@@ -709,12 +709,13 @@ public class ComputerUtil {
* <p> * <p>
* canRegenerate. * canRegenerate.
* </p> * </p>
* @param ai
* *
* @param card * @param card
* a {@link forge.Card} object. * a {@link forge.Card} object.
* @return a boolean. * @return a boolean.
*/ */
public static boolean canRegenerate(final Card card) { public static boolean canRegenerate(Player ai, final Card card) {
if (card.hasKeyword("CARDNAME can't be regenerated.")) { if (card.hasKeyword("CARDNAME can't be regenerated.")) {
return false; return false;
@@ -736,7 +737,7 @@ public class ComputerUtil {
continue; // Can't play ability continue; // Can't play ability
} }
if (controller.isComputer()) { if (controller == ai) {
final Cost abCost = sa.getPayCosts(); final Cost abCost = sa.getPayCosts();
if (abCost != null) { if (abCost != null) {
// AI currently disabled for these costs // AI currently disabled for these costs

View File

@@ -229,11 +229,11 @@ public class ComputerUtilBlock {
* a {@link forge.game.phase.Combat} object. * a {@link forge.game.phase.Combat} object.
* @return a {@link forge.CardList} object. * @return a {@link forge.CardList} object.
*/ */
private static List<Card> getSafeBlockers(final Card attacker, final List<Card> blockersLeft, final Combat combat) { private static List<Card> getSafeBlockers(final Player ai, final Card attacker, final List<Card> blockersLeft, final Combat combat) {
final List<Card> blockers = new ArrayList<Card>(); final List<Card> blockers = new ArrayList<Card>();
for (final Card b : blockersLeft) { for (final Card b : blockersLeft) {
if (!ComputerUtilCombat.canDestroyBlocker(b, attacker, combat, false)) { if (!ComputerUtilCombat.canDestroyBlocker(ai, b, attacker, combat, false)) {
blockers.add(b); blockers.add(b);
} }
} }
@@ -255,11 +255,11 @@ public class ComputerUtilBlock {
* a {@link forge.game.phase.Combat} object. * a {@link forge.game.phase.Combat} object.
* @return a {@link forge.CardList} object. * @return a {@link forge.CardList} object.
*/ */
private static List<Card> getKillingBlockers(final Card attacker, final List<Card> blockersLeft, final Combat combat) { private static List<Card> getKillingBlockers(final Player ai, final Card attacker, final List<Card> blockersLeft, final Combat combat) {
final List<Card> blockers = new ArrayList<Card>(); final List<Card> blockers = new ArrayList<Card>();
for (final Card b : blockersLeft) { for (final Card b : blockersLeft) {
if (ComputerUtilCombat.canDestroyAttacker(attacker, b, combat, false)) { if (ComputerUtilCombat.canDestroyAttacker(ai, attacker, b, combat, false)) {
blockers.add(b); blockers.add(b);
} }
} }
@@ -337,7 +337,7 @@ public class ComputerUtilBlock {
* a {@link forge.game.phase.Combat} object. * a {@link forge.game.phase.Combat} object.
* @return a {@link forge.game.phase.Combat} object. * @return a {@link forge.game.phase.Combat} object.
*/ */
private static Combat makeGoodBlocks(final Combat combat) { private static Combat makeGoodBlocks(final Player ai, final Combat combat) {
List<Card> currentAttackers = new ArrayList<Card>(ComputerUtilBlock.getAttackersLeft()); List<Card> currentAttackers = new ArrayList<Card>(ComputerUtilBlock.getAttackersLeft());
@@ -352,13 +352,13 @@ public class ComputerUtilBlock {
final List<Card> blockers = ComputerUtilBlock.getPossibleBlockers(attacker, final List<Card> blockers = ComputerUtilBlock.getPossibleBlockers(attacker,
ComputerUtilBlock.getBlockersLeft(), combat, true); ComputerUtilBlock.getBlockersLeft(), combat, true);
final List<Card> safeBlockers = ComputerUtilBlock.getSafeBlockers(attacker, blockers, combat); final List<Card> safeBlockers = ComputerUtilBlock.getSafeBlockers(ai, attacker, blockers, combat);
List<Card> killingBlockers; List<Card> killingBlockers;
if (safeBlockers.size() > 0) { if (safeBlockers.size() > 0) {
// 1.Blockers that can destroy the attacker but won't get // 1.Blockers that can destroy the attacker but won't get
// destroyed // destroyed
killingBlockers = ComputerUtilBlock.getKillingBlockers(attacker, safeBlockers, combat); killingBlockers = ComputerUtilBlock.getKillingBlockers(ai, attacker, safeBlockers, combat);
if (killingBlockers.size() > 0) { if (killingBlockers.size() > 0) {
blocker = CardFactoryUtil.getWorstCreatureAI(killingBlockers); blocker = CardFactoryUtil.getWorstCreatureAI(killingBlockers);
} else if (!attacker.hasKeyword("You may have CARDNAME assign its combat damage as though it weren't blocked.")) { } else if (!attacker.hasKeyword("You may have CARDNAME assign its combat damage as though it weren't blocked.")) {
@@ -368,7 +368,7 @@ public class ComputerUtilBlock {
} // no safe blockers } // no safe blockers
else { else {
// 3.Blockers that can destroy the attacker and have an upside when dying // 3.Blockers that can destroy the attacker and have an upside when dying
killingBlockers = ComputerUtilBlock.getKillingBlockers(attacker, blockers, combat); killingBlockers = ComputerUtilBlock.getKillingBlockers(ai, attacker, blockers, combat);
for (Card b : killingBlockers) { for (Card b : killingBlockers) {
if ((b.hasKeyword("Undying") && b.getCounters(CounterType.P1P1) == 0) if ((b.hasKeyword("Undying") && b.getCounters(CounterType.P1P1) == 0)
|| !b.getSVar("SacMe").equals("")) { || !b.getSVar("SacMe").equals("")) {
@@ -542,7 +542,7 @@ public class ComputerUtilBlock {
continue; continue;
} }
killingBlockers = ComputerUtilBlock.getKillingBlockers(attacker, killingBlockers = ComputerUtilBlock.getKillingBlockers(ai, attacker,
ComputerUtilBlock.getPossibleBlockers(attacker, ComputerUtilBlock.getBlockersLeft(), combat, true), ComputerUtilBlock.getPossibleBlockers(attacker, ComputerUtilBlock.getBlockersLeft(), combat, true),
combat); combat);
if ((killingBlockers.size() > 0) && ComputerUtilCombat.lifeInDanger(ai, combat)) { if ((killingBlockers.size() > 0) && ComputerUtilCombat.lifeInDanger(ai, combat)) {
@@ -648,7 +648,7 @@ public class ComputerUtilBlock {
* a {@link forge.game.phase.Combat} object. * a {@link forge.game.phase.Combat} object.
* @return a {@link forge.game.phase.Combat} object. * @return a {@link forge.game.phase.Combat} object.
*/ */
private static Combat reinforceBlockersToKill(final Combat combat) { private static Combat reinforceBlockersToKill(final Player ai, final Combat combat) {
List<Card> safeBlockers; List<Card> safeBlockers;
List<Card> blockers; List<Card> blockers;
@@ -664,7 +664,7 @@ public class ComputerUtilBlock {
blockers.removeAll(combat.getBlockers(attacker)); blockers.removeAll(combat.getBlockers(attacker));
// Try to use safe blockers first // Try to use safe blockers first
safeBlockers = ComputerUtilBlock.getSafeBlockers(attacker, blockers, combat); safeBlockers = ComputerUtilBlock.getSafeBlockers(ai, attacker, blockers, combat);
for (final Card blocker : safeBlockers) { for (final Card blocker : safeBlockers) {
final int damageNeeded = attacker.getKillDamage() final int damageNeeded = attacker.getKillDamage()
+ ComputerUtilCombat.predictToughnessBonusOfAttacker(attacker, blocker, combat, false); + ComputerUtilCombat.predictToughnessBonusOfAttacker(attacker, blocker, combat, false);
@@ -808,7 +808,7 @@ public class ComputerUtilBlock {
CardLists.sortAttackLowFirst(ComputerUtilBlock.getBlockersLeft()); CardLists.sortAttackLowFirst(ComputerUtilBlock.getBlockersLeft());
// == 1. choose best blocks first == // == 1. choose best blocks first ==
combat = ComputerUtilBlock.makeGoodBlocks(combat); combat = ComputerUtilBlock.makeGoodBlocks(ai, combat);
combat = ComputerUtilBlock.makeGangBlocks(ai, combat); combat = ComputerUtilBlock.makeGangBlocks(ai, combat);
if (ComputerUtilCombat.lifeInDanger(ai, combat)) { if (ComputerUtilCombat.lifeInDanger(ai, combat)) {
combat = ComputerUtilBlock.makeTradeBlocks(ai, combat); // choose combat = ComputerUtilBlock.makeTradeBlocks(ai, combat); // choose
@@ -830,7 +830,7 @@ public class ComputerUtilBlock {
// Support blockers not destroying the attacker with more blockers to // Support blockers not destroying the attacker with more blockers to
// try to kill the attacker // try to kill the attacker
if (!ComputerUtilCombat.lifeInDanger(ai, combat)) { if (!ComputerUtilCombat.lifeInDanger(ai, combat)) {
combat = ComputerUtilBlock.reinforceBlockersToKill(combat); combat = ComputerUtilBlock.reinforceBlockersToKill(ai, combat);
} }
// == 2. If the AI life would still be in danger make a safer approach // == 2. If the AI life would still be in danger make a safer approach
@@ -845,7 +845,7 @@ public class ComputerUtilBlock {
// necessary // necessary
// trade blocks // trade blocks
// if life is in danger // if life is in danger
combat = ComputerUtilBlock.makeGoodBlocks(combat); combat = ComputerUtilBlock.makeGoodBlocks(ai, combat);
if (ComputerUtilCombat.lifeInDanger(ai, combat)) { if (ComputerUtilCombat.lifeInDanger(ai, combat)) {
combat = ComputerUtilBlock.makeChumpBlocks(ai, combat); // choose combat = ComputerUtilBlock.makeChumpBlocks(ai, combat); // choose
// necessary // necessary
@@ -859,7 +859,7 @@ public class ComputerUtilBlock {
combat = ComputerUtilBlock.reinforceBlockersAgainstTrample(ai, combat); combat = ComputerUtilBlock.reinforceBlockersAgainstTrample(ai, combat);
} }
combat = ComputerUtilBlock.makeGangBlocks(ai, combat); combat = ComputerUtilBlock.makeGangBlocks(ai, combat);
combat = ComputerUtilBlock.reinforceBlockersToKill(combat); combat = ComputerUtilBlock.reinforceBlockersToKill(ai, combat);
} }
// == 3. If the AI life would be in serious danger make an even safer // == 3. If the AI life would be in serious danger make an even safer
@@ -878,7 +878,7 @@ public class ComputerUtilBlock {
} }
if (!ComputerUtilCombat.lifeInDanger(ai, combat)) { if (!ComputerUtilCombat.lifeInDanger(ai, combat)) {
combat = ComputerUtilBlock.makeGoodBlocks(combat); combat = ComputerUtilBlock.makeGoodBlocks(ai, combat);
} }
// Reinforce blockers blocking attackers with trample if life is // Reinforce blockers blocking attackers with trample if life is
// still in danger // still in danger
@@ -888,7 +888,7 @@ public class ComputerUtilBlock {
combat = ComputerUtilBlock.makeGangBlocks(ai, combat); combat = ComputerUtilBlock.makeGangBlocks(ai, combat);
// Support blockers not destroying the attacker with more blockers // Support blockers not destroying the attacker with more blockers
// to try to kill the attacker // to try to kill the attacker
combat = ComputerUtilBlock.reinforceBlockersToKill(combat); combat = ComputerUtilBlock.reinforceBlockersToKill(ai, combat);
} }
// assign blockers that have to block // assign blockers that have to block

View File

@@ -517,18 +517,19 @@ public class ComputerUtilCombat {
* <p> * <p>
* combatantWouldBeDestroyed. * combatantWouldBeDestroyed.
* </p> * </p>
* @param ai
* *
* @param combatant * @param combatant
* a {@link forge.Card} object. * a {@link forge.Card} object.
* @return a boolean. * @return a boolean.
*/ */
public static boolean combatantWouldBeDestroyed(final Card combatant) { public static boolean combatantWouldBeDestroyed(Player ai, final Card combatant) {
if (combatant.isAttacking()) { if (combatant.isAttacking()) {
return ComputerUtilCombat.attackerWouldBeDestroyed(combatant); return ComputerUtilCombat.attackerWouldBeDestroyed(ai, combatant);
} }
if (combatant.isBlocking()) { if (combatant.isBlocking()) {
return ComputerUtilCombat.blockerWouldBeDestroyed(combatant); return ComputerUtilCombat.blockerWouldBeDestroyed(ai, combatant);
} }
return false; return false;
} }
@@ -538,16 +539,17 @@ public class ComputerUtilCombat {
* <p> * <p>
* attackerWouldBeDestroyed. * attackerWouldBeDestroyed.
* </p> * </p>
* @param ai
* *
* @param attacker * @param attacker
* a {@link forge.Card} object. * a {@link forge.Card} object.
* @return a boolean. * @return a boolean.
*/ */
public static boolean attackerWouldBeDestroyed(final Card attacker) { public static boolean attackerWouldBeDestroyed(Player ai, final Card attacker) {
final List<Card> blockers = Singletons.getModel().getGame().getCombat().getBlockers(attacker); final List<Card> blockers = Singletons.getModel().getGame().getCombat().getBlockers(attacker);
for (final Card defender : blockers) { for (final Card defender : blockers) {
if (ComputerUtilCombat.canDestroyAttacker(attacker, defender, Singletons.getModel().getGame().getCombat(), true) if (ComputerUtilCombat.canDestroyAttacker(ai, attacker, defender, Singletons.getModel().getGame().getCombat(), true)
&& !(defender.hasKeyword("Wither") || defender.hasKeyword("Infect"))) { && !(defender.hasKeyword("Wither") || defender.hasKeyword("Infect"))) {
return true; return true;
} }
@@ -1343,6 +1345,7 @@ public class ComputerUtilCombat {
* <p> * <p>
* canDestroyAttacker. * canDestroyAttacker.
* </p> * </p>
* @param ai
* *
* @param attacker * @param attacker
* a {@link forge.Card} object. * a {@link forge.Card} object.
@@ -1354,7 +1357,7 @@ public class ComputerUtilCombat {
* a boolean. * a boolean.
* @return a boolean. * @return a boolean.
*/ */
public static boolean canDestroyAttacker(final Card attacker, final Card defender, final Combat combat, public static boolean canDestroyAttacker(Player ai, final Card attacker, final Card defender, final Combat combat,
final boolean withoutAbilities) { final boolean withoutAbilities) {
if (attacker.getName().equals("Sylvan Basilisk") && !defender.hasKeyword("Indestructible")) { if (attacker.getName().equals("Sylvan Basilisk") && !defender.hasKeyword("Indestructible")) {
@@ -1375,7 +1378,7 @@ public class ComputerUtilCombat {
} }
} // flanking } // flanking
if (((attacker.hasKeyword("Indestructible") || (ComputerUtil.canRegenerate(attacker) && !withoutAbilities)) && !(defender if (((attacker.hasKeyword("Indestructible") || (ComputerUtil.canRegenerate(ai, attacker) && !withoutAbilities)) && !(defender
.hasKeyword("Wither") || defender.hasKeyword("Infect"))) .hasKeyword("Wither") || defender.hasKeyword("Infect")))
|| (attacker.hasKeyword("Persist") && !attacker.canHaveCountersPlacedOnIt(CounterType.M1M1) && (attacker || (attacker.hasKeyword("Persist") && !attacker.canHaveCountersPlacedOnIt(CounterType.M1M1) && (attacker
.getCounters(CounterType.M1M1) == 0)) .getCounters(CounterType.M1M1) == 0))
@@ -1471,18 +1474,19 @@ public class ComputerUtilCombat {
* <p> * <p>
* blockerWouldBeDestroyed. * blockerWouldBeDestroyed.
* </p> * </p>
* @param ai
* *
* @param blocker * @param blocker
* a {@link forge.Card} object. * a {@link forge.Card} object.
* @return a boolean. * @return a boolean.
*/ */
public static boolean blockerWouldBeDestroyed(final Card blocker) { public static boolean blockerWouldBeDestroyed(Player ai, final Card blocker) {
// TODO THis function only checks if a single attacker at a time would destroy a blocker // TODO THis function only checks if a single attacker at a time would destroy a blocker
// This needs to expand to tally up damage // This needs to expand to tally up damage
final List<Card> attackers = Singletons.getModel().getGame().getCombat().getAttackersBlockedBy(blocker); final List<Card> attackers = Singletons.getModel().getGame().getCombat().getAttackersBlockedBy(blocker);
for (Card attacker : attackers) { for (Card attacker : attackers) {
if (ComputerUtilCombat.canDestroyBlocker(blocker, attacker, Singletons.getModel().getGame().getCombat(), true) if (ComputerUtilCombat.canDestroyBlocker(ai, blocker, attacker, Singletons.getModel().getGame().getCombat(), true)
&& !(attacker.hasKeyword("Wither") || attacker.hasKeyword("Infect"))) { && !(attacker.hasKeyword("Wither") || attacker.hasKeyword("Infect"))) {
return true; return true;
} }
@@ -1495,6 +1499,7 @@ public class ComputerUtilCombat {
* <p> * <p>
* canDestroyBlocker. * canDestroyBlocker.
* </p> * </p>
* @param ai
* *
* @param defender * @param defender
* a {@link forge.Card} object. * a {@link forge.Card} object.
@@ -1506,7 +1511,7 @@ public class ComputerUtilCombat {
* a boolean. * a boolean.
* @return a boolean. * @return a boolean.
*/ */
public static boolean canDestroyBlocker(final Card defender, final Card attacker, final Combat combat, public static boolean canDestroyBlocker(Player ai, final Card defender, final Card attacker, final Combat combat,
final boolean withoutAbilities) { final boolean withoutAbilities) {
int flankingMagnitude = 0; int flankingMagnitude = 0;
@@ -1522,7 +1527,7 @@ public class ComputerUtilCombat {
} }
} // flanking } // flanking
if (((defender.hasKeyword("Indestructible") || (ComputerUtil.canRegenerate(defender) && !withoutAbilities)) && !(attacker if (((defender.hasKeyword("Indestructible") || (ComputerUtil.canRegenerate(ai, defender) && !withoutAbilities)) && !(attacker
.hasKeyword("Wither") || attacker.hasKeyword("Infect"))) .hasKeyword("Wither") || attacker.hasKeyword("Infect")))
|| (defender.hasKeyword("Persist") && !defender.canHaveCountersPlacedOnIt(CounterType.M1M1) && (defender || (defender.hasKeyword("Persist") && !defender.canHaveCountersPlacedOnIt(CounterType.M1M1) && (defender
.getCounters(CounterType.M1M1) == 0)) .getCounters(CounterType.M1M1) == 0))