package forge.card.abilityFactory; import forge.*; import forge.card.cardFactory.CardFactoryUtil; import forge.card.spellability.*; import java.util.ArrayList; import java.util.HashMap; import java.util.Random; /** * <p>AbilityFactory_DealDamage class.</p> * * @author Forge * @version $Id: $ */ public class AbilityFactory_DealDamage { private AbilityFactory AF = null; private String damage; /** * <p>Constructor for AbilityFactory_DealDamage.</p> * * @param newAF a {@link forge.card.abilityFactory.AbilityFactory} object. */ public AbilityFactory_DealDamage(AbilityFactory newAF) { AF = newAF; damage = AF.getMapParams().get("NumDmg"); // Note: TgtOpp should not be used, Please use ValidTgts$ Opponent instead } // ****************************************************************************************************** // ***************************** DAMAGE ***************************************************************** // ****************************************************************************************************** /** * <p>getAbility.</p> * * @return a {@link forge.card.spellability.SpellAbility} object. */ public SpellAbility getAbility() { final SpellAbility abDamage = new Ability_Activated(AF.getHostCard(), AF.getAbCost(), AF.getAbTgt()) { private static final long serialVersionUID = -7560349014757367722L; @Override public boolean canPlayAI() { return doCanPlayAI(this); } @Override public String getStackDescription() { return damageStackDescription(AF, this); } @Override public void resolve() { doResolve(this); AF.getHostCard().setAbilityUsed(AF.getHostCard().getAbilityUsed() + 1); } @Override public boolean doTrigger(boolean mandatory) { return damageDoTriggerAI(AF, this, mandatory); } };// Ability_Activated return abDamage; } /** * <p>getSpell.</p> * * @return a {@link forge.card.spellability.SpellAbility} object. */ public SpellAbility getSpell() { final SpellAbility spDealDamage = new Spell(AF.getHostCard(), AF.getAbCost(), AF.getAbTgt()) { private static final long serialVersionUID = 7239608350643325111L; @Override public boolean canPlayAI() { return doCanPlayAI(this); } @Override public String getStackDescription() { return damageStackDescription(AF, this); } @Override public void resolve() { doResolve(this); } }; // Spell return spDealDamage; } /** * <p>getDrawback.</p> * * @return a {@link forge.card.spellability.SpellAbility} object. */ public SpellAbility getDrawback() { final SpellAbility dbDealDamage = new Ability_Sub(AF.getHostCard(), AF.getAbTgt()) { private static final long serialVersionUID = 7239608350643325111L; @Override public boolean chkAI_Drawback() { // Make sure there is a valid target return damageDrawback(this); } @Override public String getStackDescription() { return damageStackDescription(AF, this); } @Override public void resolve() { doResolve(this); } @Override public boolean doTrigger(boolean mandatory) { return damageDoTriggerAI(AF, this, mandatory); } }; // Drawback return dbDealDamage; } /** * <p>damageStackDescription.</p> * * @param af a {@link forge.card.abilityFactory.AbilityFactory} object. * @param sa a {@link forge.card.spellability.SpellAbility} object. * @return a {@link java.lang.String} object. */ private String damageStackDescription(AbilityFactory af, SpellAbility sa) { // when damageStackDescription is called, just build exactly what is happening StringBuilder sb = new StringBuilder(); String name = af.getHostCard().toString(); int dmg = getNumDamage(sa); ArrayList<Object> tgts; if (sa.getTarget() == null) tgts = AbilityFactory.getDefinedObjects(sa.getSourceCard(), af.getMapParams().get("Defined"), sa); else tgts = sa.getTarget().getTargets(); if (!(sa instanceof Ability_Sub)) sb.append(name).append(" -"); sb.append(" "); String conditionDesc = af.getMapParams().get("ConditionDescription"); if (conditionDesc != null) sb.append(conditionDesc).append(" "); ArrayList<Card> definedSources = AbilityFactory.getDefinedCards(sa.getSourceCard(), af.getMapParams().get("DamageSource"), sa); Card source = definedSources.get(0); if (source != sa.getSourceCard()) sb.append(source.toString()).append(" deals"); else sb.append("Deals"); sb.append(" ").append(dmg).append(" damage to "); for (int i = 0; i < tgts.size(); i++) { if (i != 0) sb.append(" "); Object o = tgts.get(i); if (o instanceof Card || o instanceof Player) sb.append(o.toString()); } sb.append(". "); if (sa.getSubAbility() != null) { sb.append(sa.getSubAbility().getStackDescription()); } return sb.toString(); } /** * <p>getNumDamage.</p> * * @param saMe a {@link forge.card.spellability.SpellAbility} object. * @return a int. */ private int getNumDamage(SpellAbility saMe) { return AbilityFactory.calculateAmount(saMe.getSourceCard(), damage, saMe); } /** * <p>damageDrawback.</p> * * @param sa a {@link forge.card.spellability.SpellAbility} object. * @return a boolean. */ private boolean damageDrawback(SpellAbility sa) { Card source = sa.getSourceCard(); int dmg; if (damage.equals("X") && source.getSVar(damage).equals("Count$xPaid")) { // Set PayX here to maximum value. dmg = ComputerUtil.determineLeftoverMana(sa); source.setSVar("PayX", Integer.toString(dmg)); } else dmg = getNumDamage(sa); return damageTargetAI(sa, dmg); } /** * <p>doCanPlayAI.</p> * * @param saMe a {@link forge.card.spellability.SpellAbility} object. * @return a boolean. */ private boolean doCanPlayAI(SpellAbility saMe) { Cost abCost = AF.getAbCost(); Card source = saMe.getSourceCard(); int dmg = 0; if (damage.equals("X") && source.getSVar(damage).equals("Count$xPaid")) { // Set PayX here to maximum value. dmg = ComputerUtil.determineLeftoverMana(saMe); source.setSVar("PayX", Integer.toString(dmg)); } else dmg = getNumDamage(saMe); boolean rr = AF.isSpell(); // temporarily disabled until better AI if (abCost.getSacCost() && !abCost.getSacThis() && AllZone.getHumanPlayer().getLife() - dmg > 0) { //only sacrifice something that's supposed to be sacrificed String sacType = abCost.getSacType(); CardList typeList = AllZoneUtil.getPlayerCardsInPlay(AllZone.getComputerPlayer()); typeList = typeList.getValidCards(sacType.split(","), source.getController(), source); if (ComputerUtil.getCardPreference(source, "SacCost", typeList) == null) return false; } if (AF.getAbCost().getSubCounter()) { // +1/+1 counters only if damage from this ability would kill the human, otherwise ok if (AllZone.getHumanPlayer().getLife() - dmg > 0 && AF.getAbCost().getCounterType().equals(Counters.P1P1)) return false; } if (AF.getAbCost().getLifeCost()) { if (AllZone.getHumanPlayer().getLife() - dmg > 0) // only if damage from this ability would kill the human return false; } if (source.getName().equals("Stuffy Doll")) { // Now stuffy sits around for blocking // TODO(sol): this should also happen if Stuffy is going to die if (AllZone.getPhase().is(Constant.Phase.End_Of_Turn, AllZone.getHumanPlayer())) return true; else return false; } if (AF.isAbility()) { Random r = MyRandom.random; // prevent run-away activations if (r.nextFloat() <= Math.pow(.6667, AF.getHostCard().getAbilityUsed())) rr = true; } boolean bFlag = damageTargetAI(saMe, dmg); if (!bFlag) return false; if (damage.equals("X") && source.getSVar(damage).equals("Count$xPaid")) { // If I can kill my target by paying less mana, do it Target tgt = saMe.getTarget(); if (tgt != null) { int actualPay = 0; boolean noPrevention = AF.getMapParams().containsKey("NoPrevention"); ArrayList<Card> cards = tgt.getTargetCards(); for (Card c : cards) { int adjDamage = c.getEnoughDamageToKill(dmg, source, false, noPrevention); if (adjDamage > actualPay && adjDamage <= dmg) actualPay = adjDamage; } source.setSVar("PayX", Integer.toString(actualPay)); } } Ability_Sub subAb = saMe.getSubAbility(); if (subAb != null) rr &= subAb.chkAI_Drawback(); return rr; } /** * <p>shouldTgtP.</p> * * @param sa a {@link forge.card.spellability.SpellAbility} object. * @param d a int. * @param noPrevention a boolean. * @return a boolean. */ private boolean shouldTgtP(SpellAbility sa, int d, final boolean noPrevention) { int restDamage = d; Player human = AllZone.getHumanPlayer(); Player comp = AllZone.getComputerPlayer(); if (!noPrevention) restDamage = human.predictDamage(restDamage, AF.getHostCard(), false); else restDamage = human.staticReplaceDamage(restDamage, AF.getHostCard(), false); if (restDamage == 0) return false; if (!human.canLoseLife()) return false; CardList hand = AllZoneUtil.getPlayerHand(comp); if (AF.isSpell()) { // If this is a spell, cast it instead of discarding if ((AllZone.getPhase().is(Constant.Phase.End_Of_Turn) || AllZone.getPhase().is(Constant.Phase.Main2)) && AllZone.getPhase().isPlayerTurn(comp) && (hand.size() > comp.getMaxHandSize())) return true; } if (human.getLife() - restDamage < 5) // if damage from this spell would drop the human to less than 5 life return true; return false; } /** * <p>chooseTgtC.</p> * * @param d a int. * @param noPrevention a boolean. * @param pl a {@link forge.Player} object. * @param mandatory a boolean. * @return a {@link forge.Card} object. */ private Card chooseTgtC(final int d, final boolean noPrevention, final Player pl, final boolean mandatory) { Target tgt = AF.getAbTgt(); final Card source = AF.getHostCard(); CardList hPlay = AllZoneUtil.getPlayerCardsInPlay(pl); hPlay = hPlay.getValidCards(tgt.getValidTgts(), AllZone.getComputerPlayer(), source); ArrayList<Object> objects = tgt.getTargets(); for (Object o : objects) { if (o instanceof Card) { Card c = (Card) o; if (hPlay.contains(c)) hPlay.remove(c); } } hPlay = hPlay.getTargetableCards(source); CardList killables = hPlay.filter(new CardListFilter() { public boolean addCard(Card c) { return (c.getEnoughDamageToKill(d, source, false, noPrevention) <= d) && !ComputerUtil.canRegenerate(c) && !(c.getSVar("SacMe").length() > 0); } }); Card targetCard; if (pl.isHuman() && killables.size() > 0) { targetCard = CardFactoryUtil.AI_getBestCreature(killables); return targetCard; } if (!mandatory) return null; if (hPlay.size() > 0) { if (pl.isHuman()) targetCard = CardFactoryUtil.AI_getBestCreature(hPlay); else targetCard = CardFactoryUtil.AI_getWorstCreature(hPlay); return targetCard; } return null; } /** * <p>damageTargetAI.</p> * * @param saMe a {@link forge.card.spellability.SpellAbility} object. * @param dmg a int. * @return a boolean. */ private boolean damageTargetAI(SpellAbility saMe, int dmg) { Target tgt = AF.getAbTgt(); if (tgt == null) return damageChooseNontargeted(saMe, dmg); return damageChoosingTargets(saMe, tgt, dmg, false); } /** * <p>damageChoosingTargets.</p> * * @param saMe a {@link forge.card.spellability.SpellAbility} object. * @param tgt a {@link forge.card.spellability.Target} object. * @param dmg a int. * @param mandatory a boolean. * @return a boolean. */ private boolean damageChoosingTargets(SpellAbility saMe, Target tgt, int dmg, boolean mandatory) { boolean noPrevention = AF.getMapParams().containsKey("NoPrevention"); // target loop tgt.resetTargets(); while (tgt.getNumTargeted() < tgt.getMaxTargets(saMe.getSourceCard(), saMe)) { // TODO: Consider targeting the planeswalker if (tgt.canTgtCreatureAndPlayer()) { if (shouldTgtP(saMe, dmg, noPrevention)) { if (tgt.addTarget(AllZone.getHumanPlayer())) continue; } Card c = chooseTgtC(dmg, noPrevention, AllZone.getHumanPlayer(), mandatory); if (c != null) { tgt.addTarget(c); continue; } // When giving priority to targeting Creatures for mandatory triggers // feel free to add the Human after we run out of good targets // TODO: add check here if card is about to die from something on the stack // or from taking combat damage boolean freePing = mandatory || AbilityFactory.playReusable(saMe); if (freePing && tgt.addTarget(AllZone.getHumanPlayer())) continue; } else if (tgt.canTgtCreature()) { Card c = chooseTgtC(dmg, noPrevention, AllZone.getHumanPlayer(), mandatory); if (c != null) { tgt.addTarget(c); continue; } } // TODO: Improve Damage, we shouldn't just target the player just because we can else if (tgt.canTgtPlayer()) { if (tgt.addTarget(AllZone.getHumanPlayer())) continue; } // fell through all the choices, no targets left? if ((tgt.getNumTargeted() < tgt.getMinTargets(saMe.getSourceCard(), saMe) || tgt.getNumTargeted() == 0)) { if (!mandatory) { tgt.resetTargets(); return false; } else { // If the trigger is mandatory, gotta choose my own stuff now return damageChooseRequiredTargets(saMe, tgt, dmg, mandatory); } } else { // TODO is this good enough? for up to amounts? break; } } return true; } /** * <p>damageChooseNontargeted.</p> * * @param saMe a {@link forge.card.spellability.SpellAbility} object. * @param dmg a int. * @return a boolean. */ private boolean damageChooseNontargeted(SpellAbility saMe, int dmg) { // TODO: Improve circumstances where the Defined Damage is unwanted ArrayList<Object> objects = AbilityFactory.getDefinedObjects(saMe.getSourceCard(), AF.getMapParams().get("Defined"), saMe); for (Object o : objects) { if (o instanceof Card) { //Card c = (Card)o; } else if (o instanceof Player) { Player p = (Player) o; int restDamage = p.predictDamage(dmg, AF.getHostCard(), false); if (p.isComputer() && p.canLoseLife() && restDamage + 3 >= p.getLife() && restDamage > 0) // Damage from this spell will kill me return false; if (p.isHuman() && !p.canLoseLife()) return false; } } return true; } /** * <p>damageChooseRequiredTargets.</p> * * @param saMe a {@link forge.card.spellability.SpellAbility} object. * @param tgt a {@link forge.card.spellability.Target} object. * @param dmg a int. * @param mandatory a boolean. * @return a boolean. */ private boolean damageChooseRequiredTargets(SpellAbility saMe, Target tgt, int dmg, boolean mandatory) { // this is for Triggered targets that are mandatory boolean noPrevention = AF.getMapParams().containsKey("NoPrevention"); while (tgt.getNumTargeted() < tgt.getMinTargets(saMe.getSourceCard(), saMe)) { // TODO: Consider targeting the planeswalker if (tgt.canTgtCreature()) { Card c = chooseTgtC(dmg, noPrevention, AllZone.getComputerPlayer(), mandatory); if (c != null) { tgt.addTarget(c); continue; } } if (tgt.canTgtPlayer()) { if (tgt.addTarget(AllZone.getComputerPlayer())) continue; } // if we get here then there isn't enough targets, this is the only time we can return false return false; } return true; } /** * <p>damageDoTriggerAI.</p> * * @param af a {@link forge.card.abilityFactory.AbilityFactory} object. * @param sa a {@link forge.card.spellability.SpellAbility} object. * @param mandatory a boolean. * @return a boolean. */ private boolean damageDoTriggerAI(AbilityFactory af, SpellAbility sa, boolean mandatory) { if (!ComputerUtil.canPayCost(sa) && !mandatory) return false; Card source = sa.getSourceCard(); int dmg; if (damage.equals("X") && source.getSVar(damage).equals("Count$xPaid")) { // Set PayX here to maximum value. dmg = ComputerUtil.determineLeftoverMana(sa); source.setSVar("PayX", Integer.toString(dmg)); } else dmg = getNumDamage(sa); Target tgt = sa.getTarget(); if (tgt == null) { // If it's not mandatory check a few things if (!mandatory && !damageChooseNontargeted(sa, dmg)) { return false; } } else { if (!damageChoosingTargets(sa, tgt, dmg, mandatory) && !mandatory) return false; if (damage.equals("X") && source.getSVar(damage).equals("Count$xPaid")) { // If I can kill my target by paying less mana, do it int actualPay = 0; boolean noPrevention = AF.getMapParams().containsKey("NoPrevention"); ArrayList<Card> cards = tgt.getTargetCards(); for (Card c : cards) { int adjDamage = c.getEnoughDamageToKill(dmg, source, false, noPrevention); if (adjDamage > actualPay) actualPay = adjDamage; } source.setSVar("PayX", Integer.toString(actualPay)); } } if (sa.getSubAbility() != null) return sa.getSubAbility().doTrigger(mandatory); return true; } /** * <p>doResolve.</p> * * @param saMe a {@link forge.card.spellability.SpellAbility} object. */ private void doResolve(SpellAbility saMe) { HashMap<String, String> params = AF.getMapParams(); int dmg = getNumDamage(saMe); boolean noPrevention = params.containsKey("NoPrevention"); ArrayList<Object> tgts; if (saMe.getTarget() == null) tgts = AbilityFactory.getDefinedObjects(saMe.getSourceCard(), params.get("Defined"), saMe); else tgts = saMe.getTarget().getTargets(); boolean targeted = (AF.getAbTgt() != null); ArrayList<Card> definedSources = AbilityFactory.getDefinedCards(saMe.getSourceCard(), params.get("DamageSource"), saMe); Card source = definedSources.get(0); for (Object o : tgts) { if (o instanceof Card) { Card c = (Card) o; if (AllZoneUtil.isCardInPlay(c) && (!targeted || CardFactoryUtil.canTarget(AF.getHostCard(), c))) { if (noPrevention) c.addDamageWithoutPrevention(dmg, source); else c.addDamage(dmg, source); } } else if (o instanceof Player) { Player p = (Player) o; if (!targeted || p.canTarget(AF.getHostCard())) { if (noPrevention) p.addDamageWithoutPrevention(dmg, source); else p.addDamage(dmg, source); } } } } // ****************************************************************************************************** // ***************************** DAMAGEALL ************************************************************** // ****************************************************************************************************** /** * <p>getAbilityDamageAll.</p> * * @return a {@link forge.card.spellability.SpellAbility} object. */ public SpellAbility getAbilityDamageAll() { final SpellAbility abDamageAll = new Ability_Activated(AF.getHostCard(), AF.getAbCost(), AF.getAbTgt()) { private static final long serialVersionUID = -1831356710492849854L; final AbilityFactory af = AF; @Override public String getStackDescription() { return damageAllStackDescription(af, this); } @Override public boolean canPlayAI() { return damageAllCanPlayAI(af, this); } @Override public void resolve() { damageAllResolve(af, this); } @Override public boolean doTrigger(boolean mandatory) { return damageAllDoTriggerAI(AF, this, mandatory); } }; return abDamageAll; } /** * <p>getSpellDamageAll.</p> * * @return a {@link forge.card.spellability.SpellAbility} object. */ public SpellAbility getSpellDamageAll() { final SpellAbility spDamageAll = new Spell(AF.getHostCard(), AF.getAbCost(), AF.getAbTgt()) { private static final long serialVersionUID = 8004957182752984818L; final AbilityFactory af = AF; final HashMap<String, String> params = af.getMapParams(); @Override public String getStackDescription() { if (params.containsKey("SpellDescription")) return AF.getHostCard().getName() + " - " + params.get("SpellDescription"); else return damageAllStackDescription(af, this); } public boolean canPlayAI() { return damageAllCanPlayAI(af, this); } @Override public void resolve() { damageAllResolve(af, this); } }; return spDamageAll; } /** * <p>getDrawbackDamageAll.</p> * * @return a {@link forge.card.spellability.SpellAbility} object. */ public SpellAbility getDrawbackDamageAll() { final SpellAbility dbDamageAll = new Ability_Sub(AF.getHostCard(), AF.getAbTgt()) { private static final long serialVersionUID = -6169562107675964474L; final AbilityFactory af = AF; @Override public String getStackDescription() { return damageAllStackDescription(af, this); } @Override public void resolve() { damageAllResolve(af, this); } @Override public boolean chkAI_Drawback() { //check AI life before playing this drawback? return true; } @Override public boolean doTrigger(boolean mandatory) { return damageAllDoTriggerAI(AF, this, mandatory); } }; return dbDamageAll; } /** * <p>damageAllStackDescription.</p> * * @param af a {@link forge.card.abilityFactory.AbilityFactory} object. * @param sa a {@link forge.card.spellability.SpellAbility} object. * @return a {@link java.lang.String} object. */ private String damageAllStackDescription(final AbilityFactory af, SpellAbility sa) { StringBuilder sb = new StringBuilder(); String name = af.getHostCard().getName(); HashMap<String, String> params = af.getMapParams(); String desc = ""; if (params.containsKey("ValidDescription")) desc = params.get("ValidDescription"); int dmg = getNumDamage(sa); sb.append(name).append(" - Deals " + dmg + " damage to " + desc); Ability_Sub abSub = sa.getSubAbility(); if (abSub != null) { sb.append(abSub.getStackDescription()); } return sb.toString(); } /** * <p>damageAllCanPlayAI.</p> * * @param af a {@link forge.card.abilityFactory.AbilityFactory} object. * @param sa a {@link forge.card.spellability.SpellAbility} object. * @return a boolean. */ private boolean damageAllCanPlayAI(final AbilityFactory af, final SpellAbility sa) { // AI needs to be expanded, since this function can be pretty complex based on what the expected targets could be Random r = MyRandom.random; Cost abCost = sa.getPayCosts(); final Card source = sa.getSourceCard(); final HashMap<String, String> params = af.getMapParams(); String validP = ""; int dmg; if (damage.equals("X") && source.getSVar(damage).equals("Count$xPaid")) { // Set PayX here to maximum value. dmg = ComputerUtil.determineLeftoverMana(sa); source.setSVar("PayX", Integer.toString(dmg)); } else dmg = getNumDamage(sa); if (params.containsKey("ValidPlayers")) validP = params.get("ValidPlayers"); CardList humanList = getKillableCreatures(af, sa, AllZone.getHumanPlayer(), dmg); CardList computerList = getKillableCreatures(af, sa, AllZone.getComputerPlayer(), dmg); Target tgt = af.getAbTgt(); if (tgt != null) { tgt.resetTargets(); sa.getTarget().addTarget(AllZone.getHumanPlayer()); computerList = new CardList(); } //abCost stuff that should probably be centralized... if (abCost != null) { // AI currently disabled for some costs if (abCost.getSacCost()) { //OK } if (abCost.getLifeCost()) { if (AllZone.getComputerPlayer().getLife() - abCost.getLifeAmount() < 4) return false; } if (abCost.getDiscardCost()) ; //OK if (abCost.getSubCounter()) { // OK } } if (!ComputerUtil.canPayCost(sa)) return false; ///// // TODO: if damage is dependant on mana paid, maybe have X be human's max life //Don't kill yourself if (validP.contains("Each") && AllZone.getComputerPlayer().getLife() <= AllZone.getComputerPlayer().predictDamage(dmg, source, false)) return false; //if we can kill human, do it if ((validP.contains("Each") || validP.contains("EachOpponent")) && AllZone.getHumanPlayer().getLife() <= AllZone.getHumanPlayer().predictDamage(dmg, source, false)) return true; // prevent run-away activations - first time will always return true boolean chance = r.nextFloat() <= Math.pow(.6667, source.getAbilityUsed()); int minGain = 200; //The minimum gain in destroyed creatures if (sa.getPayCosts().isReusuableResource()) minGain = 100; // evaluate both lists and pass only if human creatures are more valuable if (CardFactoryUtil.evaluateCreatureList(computerList) + minGain >= CardFactoryUtil.evaluateCreatureList(humanList)) return false; Ability_Sub subAb = sa.getSubAbility(); if (subAb != null) chance &= subAb.chkAI_Drawback(); return ((r.nextFloat() < .6667) && chance); } /** * <p>getKillableCreatures.</p> * * @param af a {@link forge.card.abilityFactory.AbilityFactory} object. * @param sa a {@link forge.card.spellability.SpellAbility} object. * @param player a {@link forge.Player} object. * @param dmg a int. * @return a {@link forge.CardList} object. */ private CardList getKillableCreatures(final AbilityFactory af, final SpellAbility sa, Player player, final int dmg) { final HashMap<String, String> params = af.getMapParams(); final Card source = af.getHostCard(); String validC = ""; if (params.containsKey("ValidCards")) validC = params.get("ValidCards"); //TODO: X may be something different than X paid CardList list = AllZoneUtil.getPlayerCardsInPlay(player); list = list.getValidCards(validC.split(","), source.getController(), source); CardListFilter filterKillable = new CardListFilter() { public boolean addCard(Card c) { return (c.predictDamage(dmg, source, false) >= c.getKillDamage()); } }; list = list.getNotKeyword("Indestructible"); list = list.filter(filterKillable); return list; } /** * <p>damageAllDoTriggerAI.</p> * * @param af a {@link forge.card.abilityFactory.AbilityFactory} object. * @param sa a {@link forge.card.spellability.SpellAbility} object. * @param mandatory a boolean. * @return a boolean. */ private boolean damageAllDoTriggerAI(AbilityFactory af, SpellAbility sa, boolean mandatory) { if (!ComputerUtil.canPayCost(sa) && !mandatory) return false; final Card source = sa.getSourceCard(); final HashMap<String, String> params = af.getMapParams(); String validP = ""; int dmg; if (damage.equals("X") && source.getSVar(damage).equals("Count$xPaid")) { // Set PayX here to maximum value. dmg = ComputerUtil.determineLeftoverMana(sa); source.setSVar("PayX", Integer.toString(dmg)); } else dmg = getNumDamage(sa); if (params.containsKey("ValidPlayers")) validP = params.get("ValidPlayers"); Target tgt = sa.getTarget(); do { // A little trick to still check the SubAbilities, once we know we want to play it if (tgt == null) { // If it's not mandatory check a few things if (mandatory) return true; else { // Don't get yourself killed if (validP.contains("Each") && AllZone.getComputerPlayer().getLife() <= AllZone.getComputerPlayer().predictDamage(dmg, source, false)) return false; //if we can kill human, do it if ((validP.contains("Each") || validP.contains("EachOpponent") || validP.contains("Targeted")) && AllZone.getHumanPlayer().getLife() <= AllZone.getHumanPlayer().predictDamage(dmg, source, false)) break; // Evaluate creatures getting killed CardList humanList = getKillableCreatures(af, sa, AllZone.getHumanPlayer(), dmg); CardList computerList = getKillableCreatures(af, sa, AllZone.getComputerPlayer(), dmg); if (CardFactoryUtil.evaluateCreatureList(computerList) + 50 >= CardFactoryUtil.evaluateCreatureList(humanList)) return false; } } else { // DamageAll doesn't really target right now } } while (false); if (sa.getSubAbility() != null) return sa.getSubAbility().doTrigger(mandatory); return true; } /** * <p>damageAllResolve.</p> * * @param af a {@link forge.card.abilityFactory.AbilityFactory} object. * @param sa a {@link forge.card.spellability.SpellAbility} object. */ private void damageAllResolve(final AbilityFactory af, final SpellAbility sa) { HashMap<String, String> params = af.getMapParams(); Card card = sa.getSourceCard(); int dmg = getNumDamage(sa); Target tgt = af.getAbTgt(); Player targetPlayer = null; if (tgt != null) targetPlayer = tgt.getTargetPlayers().get(0); String players = ""; if (params.containsKey("ValidPlayers")) players = params.get("ValidPlayers"); CardList list = AllZoneUtil.getCardsInPlay(); if (targetPlayer != null) list = list.getController(targetPlayer); list = AbilityFactory.filterListByType(list, params.get("ValidCards"), sa); for (Card c : list) c.addDamage(dmg, card); if (players.equals("Each")) { for (Player p : AllZoneUtil.getPlayersInGame()) p.addDamage(dmg, card); } else if (players.equals("EachOpponent")) { for (Player p : AllZoneUtil.getOpponents(card.getController())) p.addDamage(dmg, card); } else if (players.equals("Self")) card.getController().addDamage(dmg, card); else if (players.equals("Targeted")) targetPlayer.addDamage(dmg, card); } }