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_Pump class.</p> * * @author Forge * @version $Id: $ */ public class AbilityFactory_Pump { private final ArrayList<String> Keywords = new ArrayList<String>(); private String numAttack; private String numDefense; private AbilityFactory AF = null; private HashMap<String, String> params = null; private Card hostCard = null; /** * <p>Constructor for AbilityFactory_Pump.</p> * * @param newAF a {@link forge.card.abilityFactory.AbilityFactory} object. */ public AbilityFactory_Pump(AbilityFactory newAF) { AF = newAF; params = AF.getMapParams(); hostCard = AF.getHostCard(); numAttack = (params.containsKey("NumAtt")) ? params.get("NumAtt") : "0"; numDefense = (params.containsKey("NumDef")) ? params.get("NumDef") : "0"; // Start with + sign now optional if (numAttack.startsWith("+")) numAttack = numAttack.substring(1); if (numDefense.startsWith("+")) numDefense = numDefense.substring(1); if (params.containsKey("KW")) { String tmp = params.get("KW"); String kk[] = tmp.split(" & "); Keywords.clear(); for (int i = 0; i < kk.length; i++) Keywords.add(kk[i]); } else Keywords.add("none"); } /** * <p>getSpellPump.</p> * * @return a {@link forge.card.spellability.SpellAbility} object. */ public SpellAbility getSpellPump() { SpellAbility spPump = new Spell(hostCard, AF.getAbCost(), AF.getAbTgt()) { private static final long serialVersionUID = 42244224L; @Override public boolean canPlayAI() { return pumpPlayAI(this); } @Override public String getStackDescription() { return pumpStackDescription(AF, this); } @Override public void resolve() { pumpResolve(this); }//resolve };//SpellAbility return spPump; } /** * <p>getAbilityPump.</p> * * @return a {@link forge.card.spellability.SpellAbility} object. */ public SpellAbility getAbilityPump() { final SpellAbility abPump = new Ability_Activated(hostCard, AF.getAbCost(), AF.getAbTgt()) { private static final long serialVersionUID = -1118592153328758083L; @Override public boolean canPlayAI() { return pumpPlayAI(this); } @Override public String getStackDescription() { return pumpStackDescription(AF, this); } @Override public void resolve() { pumpResolve(this); hostCard.setAbilityUsed(hostCard.getAbilityUsed() + 1); }//resolve() @Override public boolean doTrigger(boolean mandatory) { return pumpTriggerAI(AF, this, mandatory); } };//SpellAbility return abPump; } /** * <p>getDrawbackPump.</p> * * @return a {@link forge.card.spellability.SpellAbility} object. */ public SpellAbility getDrawbackPump() { SpellAbility dbPump = new Ability_Sub(hostCard, AF.getAbTgt()) { private static final long serialVersionUID = 42244224L; @Override public boolean canPlayAI() { return pumpPlayAI(this); } @Override public String getStackDescription() { return pumpStackDescription(AF, this); } @Override public void resolve() { pumpResolve(this); }//resolve @Override public boolean chkAI_Drawback() { return pumpDrawbackAI(this); } @Override public boolean doTrigger(boolean mandatory) { return pumpTriggerAI(AF, this, mandatory); } };//SpellAbility return dbPump; } /** * <p>Getter for the field <code>numAttack</code>.</p> * * @param sa a {@link forge.card.spellability.SpellAbility} object. * @return a int. */ private int getNumAttack(SpellAbility sa) { return AbilityFactory.calculateAmount(hostCard, numAttack, sa); } /** * <p>Getter for the field <code>numDefense</code>.</p> * * @param sa a {@link forge.card.spellability.SpellAbility} object. * @return a int. */ private int getNumDefense(SpellAbility sa) { return AbilityFactory.calculateAmount(hostCard, numDefense, sa); } /** * <p>getPumpCreatures.</p> * * @param defense a int. * @param attack a int. * @return a {@link forge.CardList} object. */ private CardList getPumpCreatures(final int defense, final int attack) { final boolean kHaste = Keywords.contains("Haste"); final boolean evasive = (Keywords.contains("Flying") || Keywords.contains("Horsemanship") || Keywords.contains("HIDDEN Unblockable") || Keywords.contains("Fear") || Keywords.contains("Intimidate")); final boolean kSize = !Keywords.get(0).equals("none"); String KWpump[] = {"none"}; if (!Keywords.get(0).equals("none")) KWpump = Keywords.toArray(new String[Keywords.size()]); final String KWs[] = KWpump; CardList list = AllZoneUtil.getCreaturesInPlay(AllZone.getComputerPlayer()); list = list.filter(new CardListFilter() { public boolean addCard(Card c) { if (!CardFactoryUtil.canTarget(hostCard, c)) return false; if (c.getNetDefense() + defense <= 0) //don't kill the creature return false; //Don't add duplicate keywords boolean hKW = c.hasAnyKeyword(KWs); if (kSize && hKW) return false; //give haste to creatures that could attack with it if (c.hasSickness() && kHaste && AllZone.getPhase().isPlayerTurn(AllZone.getComputerPlayer()) && CombatUtil.canAttackNextTurn(c) && AllZone.getPhase().isBefore(Constant.Phase.Combat_Declare_Attackers)) return true; //give evasive keywords to creatures that can attack if (evasive && AllZone.getPhase().isPlayerTurn(AllZone.getComputerPlayer()) && CombatUtil.canAttack(c) && AllZone.getPhase().isBefore(Constant.Phase.Combat_Declare_Attackers) && c.getNetCombatDamage() > 0) return true; //will the creature attack (only relevant for sorcery speed)? if (CardFactoryUtil.AI_doesCreatureAttack(c) && AllZone.getPhase().isBefore(Constant.Phase.Combat_Declare_Attackers) && AllZone.getPhase().isPlayerTurn(AllZone.getComputerPlayer())) return true; //is the creature blocking and unable to destroy the attacker or would be destroyed itself? if (c.isBlocking() && (CombatUtil.blockerWouldBeDestroyed(c) || !CombatUtil.attackerWouldBeDestroyed(AllZone.getCombat().getAttackerBlockedBy(c)))) return true; //is the creature unblocked and the spell will pump its power? if (AllZone.getPhase().isAfter(Constant.Phase.Combat_Declare_Blockers) && AllZone.getCombat().isAttacking(c) && AllZone.getCombat().isUnblocked(c) && attack > 0) return true; //is the creature in blocked and the blocker would survive if (AllZone.getPhase().isAfter(Constant.Phase.Combat_Declare_Blockers) && AllZone.getCombat().isAttacking(c) && AllZone.getCombat().isBlocked(c) && CombatUtil.blockerWouldBeDestroyed(AllZone.getCombat().getBlockers(c).get(0))) return true; //if the life of the computer is in danger, try to pump potential blockers before declaring blocks if (CombatUtil.lifeInDanger(AllZone.getCombat()) && AllZone.getPhase().isAfter(Constant.Phase.Combat_Declare_Attackers) && AllZone.getPhase().isBefore(Constant.Phase.Main2) && CombatUtil.canBlock(c, AllZone.getCombat()) && AllZone.getPhase().isPlayerTurn(AllZone.getHumanPlayer())) return true; return false; } }); return list; }//getPumpCreatures() /** * <p>getCurseCreatures.</p> * * @param sa a {@link forge.card.spellability.SpellAbility} object. * @param defense a int. * @param attack a int. * @return a {@link forge.CardList} object. */ private CardList getCurseCreatures(SpellAbility sa, final int defense, int attack) { CardList list = AllZoneUtil.getCreaturesInPlay(AllZone.getHumanPlayer()); list = list.filter(AllZoneUtil.getCanTargetFilter(hostCard)); if (defense < 0 && !list.isEmpty()) { // with spells that give -X/-X, compi will try to destroy a creature list = list.filter(new CardListFilter() { public boolean addCard(Card c) { if (c.getNetDefense() <= -defense) return true; // can kill indestructible creatures return (c.getKillDamage() <= -defense && !c.hasKeyword("Indestructible")); } }); // leaves all creatures that will be destroyed } // -X/-X end else if (!list.isEmpty()) { String KWpump[] = {"none"}; if (!Keywords.get(0).equals("none")) KWpump = Keywords.toArray(new String[Keywords.size()]); final String KWs[] = KWpump; final boolean addsKeywords = Keywords.size() > 0; if (addsKeywords) { list = list.filter(new CardListFilter() { public boolean addCard(Card c) { return !c.hasAnyKeyword(KWs); // don't add duplicate negative keywords } }); } } return list; }//getCurseCreatures() /** * <p>pumpPlayAI.</p> * * @param sa a {@link forge.card.spellability.SpellAbility} object. * @return a boolean. */ private boolean pumpPlayAI(SpellAbility sa) { // if there is no target and host card isn't in play, don't activate if (AF.getAbTgt() == null && !AllZoneUtil.isCardInPlay(hostCard)) return false; // temporarily disabled until AI is improved if (AF.getAbCost().getSacCost() && sa.getSourceCard().isCreature()) return false; if (AF.getAbCost().getLifeCost()) { if (!AF.isCurse()) return false; //Use life only to kill creatures if (AllZone.getComputerPlayer().getLife() - AF.getAbCost().getLifeAmount() < 4) return false; } if (AF.getAbCost().getDiscardCost() && !AF.isCurse()) { return false; } if (AF.getAbCost().getSubCounter()) { // instead of never removing counters, we will have a random possibility of failure. // all the other tests still need to pass if a counter will be removed Counters count = AF.getAbCost().getCounterType(); double chance = .66; if (count.equals(Counters.P1P1)) { // 10% chance to remove +1/+1 to pump chance = .1; } else if (count.equals(Counters.CHARGE)) { // 50% chance to remove charge to pump chance = .5; } Random r = MyRandom.random; if (r.nextFloat() > chance) return false; } if (!ComputerUtil.canPayCost(sa)) return false; SpellAbility_Restriction restrict = sa.getRestrictions(); // Phase Restrictions if (AllZone.getStack().size() == 0 && AllZone.getPhase().isBefore(Constant.Phase.Combat_Begin)) { // Instant-speed pumps should not be cast outside of combat when the stack is empty if (!AF.isCurse()) { if (!AbilityFactory.isSorcerySpeed(sa)) return false; } } else if (AllZone.getStack().size() > 0) { // TODO: pump something only if the top thing on the stack will kill it via damage // or if top thing on stack will pump it/enchant it and I want to kill it return false; } int activations = restrict.getNumberTurnActivations(); int sacActivations = restrict.getActivationNumberSacrifice(); //don't risk sacrificing a creature just to pump it if (sacActivations != -1 && activations >= (sacActivations - 1)) { return false; } Card source = sa.getSourceCard(); if (source.getSVar("X").equals("Count$xPaid")) source.setSVar("PayX", ""); int defense; if (numDefense.contains("X") && source.getSVar("X").equals("Count$xPaid")) { // Set PayX here to maximum value. int xPay = ComputerUtil.determineLeftoverMana(sa); source.setSVar("PayX", Integer.toString(xPay)); defense = xPay; } else defense = getNumDefense(sa); int attack; if (numAttack.contains("X") && source.getSVar("X").equals("Count$xPaid")) { // Set PayX here to maximum value. String toPay = source.getSVar("PayX"); if (toPay.equals("")) { int xPay = ComputerUtil.determineLeftoverMana(sa); source.setSVar("PayX", Integer.toString(xPay)); attack = xPay; } else attack = Integer.parseInt(toPay); } else attack = getNumAttack(sa); if (AF.getAbTgt() == null || !AF.getAbTgt().doesTarget()) { ArrayList<Card> cards = AbilityFactory.getDefinedCards(sa.getSourceCard(), params.get("Defined"), sa); if (cards.size() == 0) return false; // when this happens we need to expand AI to consider if its ok for everything? for (Card card : cards) { // TODO: if AI doesn't control Card and Pump is a Curse, than maybe use? if ((card.getNetDefense() + defense > 0) && (!card.hasAnyKeyword(Keywords))) { if (card.hasSickness() && Keywords.contains("Haste")) return true; else if (card.hasSickness() ^ Keywords.contains("Haste")) return false; else if (hostCard.equals(card)) { Random r = MyRandom.random; if (r.nextFloat() <= Math.pow(.6667, activations)) return CardFactoryUtil.AI_doesCreatureAttack(card) && !sa.getPayCosts().getTap(); } else { Random r = MyRandom.random; return (r.nextFloat() <= Math.pow(.6667, activations)); } } } } else return pumpTgtAI(sa, defense, attack, false); return false; }//pumpPlayAI() /** * <p>pumpTgtAI.</p> * * @param sa a {@link forge.card.spellability.SpellAbility} object. * @param defense a int. * @param attack a int. * @param mandatory a boolean. * @return a boolean. */ private boolean pumpTgtAI(SpellAbility sa, int defense, int attack, boolean mandatory) { if (!mandatory && AllZone.getPhase().isAfter(Constant.Phase.Combat_Declare_Blockers_InstantAbility) && !(AF.isCurse() && defense < 0)) return false; Target tgt = AF.getAbTgt(); tgt.resetTargets(); CardList list; if (AF.isCurse()) // Curse means spells with negative effect list = getCurseCreatures(sa, defense, attack); else list = getPumpCreatures(defense, attack); list = list.getValidCards(tgt.getValidTgts(), sa.getActivatingPlayer(), sa.getSourceCard()); if (AllZone.getStack().size() == 0) { // If the cost is tapping, don't activate before declare attack/block if (sa.getPayCosts() != null && sa.getPayCosts().getTap()) { if (AllZone.getPhase().isBefore(Constant.Phase.Combat_Declare_Attackers) && AllZone.getPhase().isPlayerTurn(AllZone.getComputerPlayer())) list.remove(sa.getSourceCard()); if (AllZone.getPhase().isBefore(Constant.Phase.Combat_Declare_Blockers) && AllZone.getPhase().isPlayerTurn(AllZone.getHumanPlayer())) list.remove(sa.getSourceCard()); } } if (list.isEmpty()) return mandatory && pumpMandatoryTarget(AF, sa, mandatory); while (tgt.getNumTargeted() < tgt.getMaxTargets(sa.getSourceCard(), sa)) { Card t = null; //boolean goodt = false; if (list.isEmpty()) { if (tgt.getNumTargeted() < tgt.getMinTargets(sa.getSourceCard(), sa) || tgt.getNumTargeted() == 0) { if (mandatory) return pumpMandatoryTarget(AF, sa, mandatory); tgt.resetTargets(); return false; } else { // TODO is this good enough? for up to amounts? break; } } /*Not needed if (AF.isCurse()){ t = CardFactoryUtil.AI_getBestCreature(list); goodt = true; } else{ while(!goodt && !list.isEmpty()) { t = CardFactoryUtil.AI_getBestCreature(list); if((t.getNetDefense() + defense) > t.getDamage()) goodt = true; else list.remove(t); } }*/ t = CardFactoryUtil.AI_getBestCreature(list); tgt.addTarget(t); list.remove(t); } return true; }//pumpTgtAI() /** * <p>pumpMandatoryTarget.</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 pumpMandatoryTarget(AbilityFactory af, SpellAbility sa, boolean mandatory) { CardList list = AllZoneUtil.getCardsInPlay(); Target tgt = sa.getTarget(); list = list.getValidCards(tgt.getValidTgts(), sa.getActivatingPlayer(), sa.getSourceCard()); if (list.size() < tgt.getMinTargets(sa.getSourceCard(), sa)) { tgt.resetTargets(); return false; } // Remove anything that's already been targeted for (Card c : tgt.getTargetCards()) list.remove(c); CardList pref; CardList forced; Card source = sa.getSourceCard(); if (af.isCurse()) { pref = list.getController(AllZone.getHumanPlayer()); forced = list.getController(AllZone.getComputerPlayer()); } else { pref = list.getController(AllZone.getComputerPlayer()); forced = list.getController(AllZone.getHumanPlayer()); } while (tgt.getNumTargeted() < tgt.getMaxTargets(source, sa)) { if (pref.isEmpty()) break; Card c; if (pref.getNotType("Creature").size() == 0) c = CardFactoryUtil.AI_getBestCreature(pref); else c = CardFactoryUtil.AI_getMostExpensivePermanent(pref, source, true); pref.remove(c); tgt.addTarget(c); } while (tgt.getNumTargeted() < tgt.getMinTargets(sa.getSourceCard(), sa)) { if (forced.isEmpty()) break; Card c; if (forced.getNotType("Creature").size() == 0) c = CardFactoryUtil.AI_getWorstCreature(forced); else c = CardFactoryUtil.AI_getCheapestPermanent(forced, source, true); forced.remove(c); tgt.addTarget(c); } if (tgt.getNumTargeted() < tgt.getMinTargets(sa.getSourceCard(), sa)) { tgt.resetTargets(); return false; } return true; }//pumpMandatoryTarget() /** * <p>pumpTriggerAI.</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 pumpTriggerAI(AbilityFactory af, SpellAbility sa, boolean mandatory) { if (!ComputerUtil.canPayCost(sa)) return false; Card source = sa.getSourceCard(); int defense; if (numDefense.contains("X") && source.getSVar("X").equals("Count$xPaid")) { // Set PayX here to maximum value. int xPay = ComputerUtil.determineLeftoverMana(sa); source.setSVar("PayX", Integer.toString(xPay)); defense = xPay; } else defense = getNumDefense(sa); int attack; if (numAttack.contains("X") && source.getSVar("X").equals("Count$xPaid")) { // Set PayX here to maximum value. String toPay = source.getSVar("PayX"); if (toPay.equals("")) { int xPay = ComputerUtil.determineLeftoverMana(sa); source.setSVar("PayX", Integer.toString(xPay)); attack = xPay; } else attack = Integer.parseInt(toPay); } else attack = getNumAttack(sa); if (sa.getTarget() == null) { if (mandatory) return true; } else { return pumpTgtAI(sa, defense, attack, mandatory); } return true; }//pumpTriggerAI /** * <p>pumpDrawbackAI.</p> * * @param sa a {@link forge.card.spellability.SpellAbility} object. * @return a boolean. */ private boolean pumpDrawbackAI(SpellAbility sa) { Card source = sa.getSourceCard(); int defense; if (numDefense.contains("X") && source.getSVar("X").equals("Count$xPaid")) { defense = Integer.parseInt(source.getSVar("PayX")); } else defense = getNumDefense(sa); int attack; if (numAttack.contains("X") && source.getSVar("X").equals("Count$xPaid")) { attack = Integer.parseInt(source.getSVar("PayX")); } else attack = getNumAttack(sa); if (AF.getAbTgt() == null || !AF.getAbTgt().doesTarget()) { if (hostCard.isCreature()) { if (!hostCard.hasKeyword("Indestructible") && hostCard.getNetDefense() + defense <= hostCard.getDamage()) return false; if (hostCard.getNetDefense() + defense <= 0) return false; } } else return pumpTgtAI(sa, defense, attack, false); return true; }//pumpDrawbackAI() /** * <p>pumpStackDescription.</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 pumpStackDescription(AbilityFactory af, SpellAbility sa) { // when damageStackDescription is called, just build exactly what is happening StringBuilder sb = new StringBuilder(); String name = af.getHostCard().getName(); ArrayList<Card> tgtCards; Target tgt = AF.getAbTgt(); if (tgt != null) tgtCards = tgt.getTargetCards(); else tgtCards = AbilityFactory.getDefinedCards(sa.getSourceCard(), params.get("Defined"), sa); if (tgtCards.size() > 0) { if (sa instanceof Ability_Sub) sb.append(" "); else sb.append(name).append(" - "); for (Card c : tgtCards) sb.append(c.getName()).append(" "); final int atk = getNumAttack(sa); final int def = getNumDefense(sa); sb.append("gains "); if (atk != 0 || def != 0) { if (atk >= 0) sb.append("+"); sb.append(atk); sb.append("/"); if (def >= 0) sb.append("+"); sb.append(def); sb.append(" "); } for (int i = 0; i < Keywords.size(); i++) { if (!Keywords.get(i).equals("none")) sb.append(Keywords.get(i)).append(" "); } if (!params.containsKey("Permanent")) sb.append("until end of turn."); } Ability_Sub abSub = sa.getSubAbility(); if (abSub != null) { sb.append(abSub.getStackDescription()); } return sb.toString(); }//pumpStackDescription() /** * <p>pumpResolve.</p> * * @param sa a {@link forge.card.spellability.SpellAbility} object. */ private void pumpResolve(SpellAbility sa) { Player activator = sa.getActivatingPlayer(); ArrayList<Card> tgtCards; Target tgt = AF.getAbTgt(); if (tgt != null) tgtCards = tgt.getTargetCards(); else tgtCards = AbilityFactory.getDefinedCards(hostCard, params.get("Defined"), sa); int size = tgtCards.size(); for (int j = 0; j < size; j++) { final Card tgtC = tgtCards.get(j); // only pump things in play if (!AllZoneUtil.isCardInPlay(tgtC)) continue; // if pump is a target, make sure we can still target now if (tgt != null && !CardFactoryUtil.canTarget(AF.getHostCard(), tgtC)) continue; final int a = getNumAttack(sa); final int d = getNumDefense(sa); tgtC.addTempAttackBoost(a); tgtC.addTempDefenseBoost(d); for (int i = 0; i < Keywords.size(); i++) { if (!Keywords.get(i).equals("none")) tgtC.addExtrinsicKeyword(Keywords.get(i)); } if (!params.containsKey("Permanent")) { // If not Permanent, remove Pumped at EOT final Command untilEOT = new Command() { private static final long serialVersionUID = -42244224L; public void execute() { if (AllZoneUtil.isCardInPlay(tgtC)) { tgtC.addTempAttackBoost(-1 * a); tgtC.addTempDefenseBoost(-1 * d); if (Keywords.size() > 0) { for (int i = 0; i < Keywords.size(); i++) { if (!Keywords.get(i).equals("none")) tgtC.removeExtrinsicKeyword(Keywords.get(i)); } } } } }; if (params.containsKey("UntilEndOfCombat")) AllZone.getEndOfCombat().addUntil(untilEOT); else if(params.containsKey("UntilYourNextUpkeep")) AllZone.getUpkeep().addUntil(activator, untilEOT); else AllZone.getEndOfTurn().addUntil(untilEOT); } } }//pumpResolve() ///////////////////////////////////// // // PumpAll // ////////////////////////////////////// /** * <p>getAbilityPumpAll.</p> * * @return a {@link forge.card.spellability.SpellAbility} object. */ public SpellAbility getAbilityPumpAll() { final SpellAbility abPumpAll = new Ability_Activated(hostCard, AF.getAbCost(), AF.getAbTgt()) { private static final long serialVersionUID = -8299417521903307630L; @Override public boolean canPlayAI() { return pumpAllCanPlayAI(this); } @Override public String getStackDescription() { return pumpAllStackDescription(AF, this); } @Override public void resolve() { pumpAllResolve(this); hostCard.setAbilityUsed(hostCard.getAbilityUsed() + 1); }//resolve() @Override public boolean doTrigger(boolean mandatory) { return pumpAllTriggerAI(AF, this, mandatory); } };//SpellAbility return abPumpAll; } /** * <p>getSpellPumpAll.</p> * * @return a {@link forge.card.spellability.SpellAbility} object. */ public SpellAbility getSpellPumpAll() { SpellAbility spPumpAll = new Spell(hostCard, AF.getAbCost(), AF.getAbTgt()) { private static final long serialVersionUID = -4055467978660824703L; public boolean canPlayAI() { return pumpAllCanPlayAI(this); } @Override public String getStackDescription() { return pumpAllStackDescription(AF, this); } public void resolve() { pumpAllResolve(this); }//resolve };//SpellAbility return spPumpAll; } /** * <p>getDrawbackPumpAll.</p> * * @return a {@link forge.card.spellability.SpellAbility} object. */ public SpellAbility getDrawbackPumpAll() { SpellAbility dbPumpAll = new Ability_Sub(hostCard, AF.getAbTgt()) { private static final long serialVersionUID = 6411531984691660342L; @Override public String getStackDescription() { return pumpAllStackDescription(AF, this); } @Override public void resolve() { pumpAllResolve(this); }//resolve @Override public boolean chkAI_Drawback() { return pumpAllChkDrawbackAI(this); } @Override public boolean doTrigger(boolean mandatory) { return pumpAllTriggerAI(AF, this, mandatory); } };//SpellAbility return dbPumpAll; } /** * <p>pumpAllCanPlayAI.</p> * * @param sa a {@link forge.card.spellability.SpellAbility} object. * @return a boolean. */ private boolean pumpAllCanPlayAI(SpellAbility sa) { String valid = ""; Random r = MyRandom.random; final Card source = sa.getSourceCard(); params = AF.getMapParams(); final int defense = getNumDefense(sa); boolean chance = r.nextFloat() <= Math.pow(.6667, source.getAbilityUsed()); //to prevent runaway activations if (params.containsKey("ValidCards")) { valid = params.get("ValidCards"); } CardList comp = AllZoneUtil.getPlayerCardsInPlay(AllZone.getComputerPlayer()); comp = comp.getValidCards(valid, hostCard.getController(), hostCard); CardList human = AllZoneUtil.getPlayerCardsInPlay(AllZone.getHumanPlayer()); human = human.getValidCards(valid, hostCard.getController(), hostCard); //only count creatures that can attack human = human.filter(new CardListFilter() { public boolean addCard(Card c) { return CombatUtil.canAttack(c) && !AF.isCurse(); } }); if (AF.isCurse()) { if (defense < 0) { // try to destroy creatures comp = comp.filter(new CardListFilter() { public boolean addCard(Card c) { if (c.getNetDefense() <= -defense) return true; // can kill indestructible creatures return (c.getKillDamage() <= -defense && !c.hasKeyword("Indestructible")); } }); // leaves all creatures that will be destroyed human = human.filter(new CardListFilter() { public boolean addCard(Card c) { if (c.getNetDefense() <= -defense) return true; // can kill indestructible creatures return (c.getKillDamage() <= -defense && !c.hasKeyword("Indestructible")); } }); // leaves all creatures that will be destroyed } // -X/-X end //evaluate both lists and pass only if human creatures are more valuable if (CardFactoryUtil.evaluateCreatureList(comp) + 200 >= CardFactoryUtil.evaluateCreatureList(human)) return false; return chance; }//end Curse //don't use non curse PumpAll after Combat_Begin until AI is improved if (AllZone.getPhase().isAfter(Constant.Phase.Combat_Begin)) return false; if (comp.size() <= human.size() || comp.size() <= 1) return false; return (r.nextFloat() < .6667) && chance; }//pumpAllCanPlayAI() /** * <p>pumpAllResolve.</p> * * @param sa a {@link forge.card.spellability.SpellAbility} object. */ private void pumpAllResolve(SpellAbility sa) { AbilityFactory af = sa.getAbilityFactory(); CardList list; ArrayList<Player> tgtPlayers = null; Target tgt = af.getAbTgt(); if (tgt != null) tgtPlayers = tgt.getTargetPlayers(); else if (params.containsKey("Defined")) // Make sure Defined exists to use it tgtPlayers = AbilityFactory.getDefinedPlayers(sa.getSourceCard(), params.get("Defined"), sa); if (tgtPlayers == null || tgtPlayers.isEmpty()) list = AllZoneUtil.getCardsInPlay(); else list = AllZoneUtil.getPlayerCardsInPlay(tgtPlayers.get(0)); String valid = ""; if (params.containsKey("ValidCards")) valid = params.get("ValidCards"); list = list.getValidCards(valid.split(","), hostCard.getController(), hostCard); final int a = getNumAttack(sa); final int d = getNumDefense(sa); for (Card c : list) { final Card tgtC = c; // only pump things in play if (!AllZoneUtil.isCardInPlay(tgtC)) continue; tgtC.addTempAttackBoost(a); tgtC.addTempDefenseBoost(d); for (int i = 0; i < Keywords.size(); i++) { if (!Keywords.get(i).equals("none")) tgtC.addExtrinsicKeyword(Keywords.get(i)); } if (!params.containsKey("Permanent")) { // If not Permanent, remove Pumped at EOT final Command untilEOT = new Command() { private static final long serialVersionUID = 5415795460189457660L; public void execute() { if (AllZoneUtil.isCardInPlay(tgtC)) { tgtC.addTempAttackBoost(-1 * a); tgtC.addTempDefenseBoost(-1 * d); if (Keywords.size() > 0) { for (int i = 0; i < Keywords.size(); i++) { if (!Keywords.get(i).equals("none")) { tgtC.removeExtrinsicKeyword(Keywords.get(i)); } } } } } }; AllZone.getEndOfTurn().addUntil(untilEOT); } } }//pumpAllResolve() /** * <p>pumpAllTriggerAI.</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 pumpAllTriggerAI(AbilityFactory af, SpellAbility sa, boolean mandatory) { if (!ComputerUtil.canPayCost(sa)) return false; // TODO: add targeting consideration such as "Creatures target player controls gets" return true; } /** * <p>pumpAllChkDrawbackAI.</p> * * @param sa a {@link forge.card.spellability.SpellAbility} object. * @return a boolean. */ private boolean pumpAllChkDrawbackAI(SpellAbility sa) { return true; } /** * <p>pumpAllStackDescription.</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 pumpAllStackDescription(AbilityFactory af, SpellAbility sa) { StringBuilder sb = new StringBuilder(); String desc = ""; if (params.containsKey("SpellDescription")) { desc = params.get("SpellDescription"); } else if (params.containsKey("PumpAllDescription")) { desc = params.get("PumpAllDescription"); } if (!(sa instanceof Ability_Sub)) sb.append(sa.getSourceCard()).append(" - "); else sb.append(" "); sb.append(desc); Ability_Sub abSub = sa.getSubAbility(); if (abSub != null) { sb.append(abSub.getStackDescription()); } return sb.toString(); }//pumpAllStackDescription() }//end class AbilityFactory_Pump