@Override public boolean chkAIDrawback(SpellAbility sa, Player ai) { final Card source = sa.getHostCard(); final String numDefense = sa.hasParam("NumDef") ? sa.getParam("NumDef") : ""; final String numAttack = sa.hasParam("NumAtt") ? sa.getParam("NumAtt") : ""; int defense; if (numDefense.contains("X") && source.getSVar("X").equals("Count$xPaid")) { defense = Integer.parseInt(source.getSVar("PayX")); } else { defense = AbilityUtils.calculateAmount(sa.getHostCard(), numDefense, sa); } int attack; if (numAttack.contains("X") && source.getSVar("X").equals("Count$xPaid")) { if (source.getSVar("PayX").equals("")) { // X is not set yet final int xPay = ComputerUtilMana.determineLeftoverMana(sa.getRootAbility(), ai); source.setSVar("PayX", Integer.toString(xPay)); attack = xPay; } else { attack = Integer.parseInt(source.getSVar("PayX")); } } else { attack = AbilityUtils.calculateAmount(sa.getHostCard(), numAttack, sa); } if ((sa.getTargetRestrictions() == null) || !sa.getTargetRestrictions().doesTarget()) { if (source.isCreature()) { if (!source.hasKeyword("Indestructible") && ((source.getNetDefense() + defense) <= source.getDamage())) { return false; } if ((source.getNetDefense() + defense) <= 0) { return false; } } } else { // Targeted if (!this.pumpTgtAI(ai, sa, defense, attack, false)) { return false; } } return true; } // pumpDrawbackAI()
@Override public PaymentDecision visit(CostExiledMoveToGrave cost) { Integer c = cost.convertAmount(); List<Card> chosen = new ArrayList<Card>(); if (c == null) { c = AbilityUtils.calculateAmount(source, cost.getAmount(), ability); } List<Card> typeList = player.getGame().getCardsIn(ZoneType.Exile); typeList = CardLists.getValidCards(typeList, cost.getType().split(";"), player, source); if (typeList.size() < c) { return null; } CardLists.sortByPowerAsc(typeList); Collections.reverse(typeList); for (int i = 0; i < c; i++) { chosen.add(typeList.get(i)); } return chosen.isEmpty() ? null : PaymentDecision.card(chosen); }
@Override public PaymentDecision visit(CostUntapType cost) { final String amount = cost.getAmount(); Integer c = cost.convertAmount(); if (c == null) { final String sVar = ability.getSVar(amount); if (sVar.equals("XChoice")) { List<Card> typeList = player.getGame().getCardsIn(ZoneType.Battlefield); typeList = CardLists.getValidCards( typeList, cost.getType().split(";"), player, ability.getHostCard()); if (!cost.canUntapSource) { typeList.remove(source); } typeList = CardLists.filter(typeList, Presets.TAPPED); c = typeList.size(); source.setSVar("ChosenX", "Number$" + Integer.toString(c)); } else { c = AbilityUtils.calculateAmount(source, amount, ability); } } List<Card> list = ComputerUtil.chooseUntapType(player, cost.getType(), source, cost.canUntapSource, c); if (list == null) { System.out.println("Couldn't find a valid card to untap for: " + source.getName()); return null; } return PaymentDecision.card(list); }
@Override public PaymentDecision visit(CostReveal cost) { final String type = cost.getType(); List<Card> hand = new ArrayList<Card>(player.getCardsIn(ZoneType.Hand)); if (cost.payCostFromSource()) { if (!hand.contains(source)) { return null; } return PaymentDecision.card(source); } if (cost.getType().equals("Hand")) return PaymentDecision.card(player.getCardsIn(ZoneType.Hand)); if (cost.getType().equals("SameColor")) { return null; } hand = CardLists.getValidCards(hand, type.split(";"), player, source); Integer c = cost.convertAmount(); if (c == null) { final String sVar = ability.getSVar(cost.getAmount()); if (sVar.equals("XChoice")) { c = hand.size(); } else { c = AbilityUtils.calculateAmount(source, cost.getAmount(), ability); } } final AiController aic = ((PlayerControllerAi) player.getController()).getAi(); return PaymentDecision.card(aic.getCardsToDiscard(c, type.split(";"), ability)); }
@Override public void resolve(SpellAbility sa) { final int numTurns = AbilityUtils.calculateAmount(sa.getHostCard(), sa.getParam("NumTurns"), sa); List<Player> tgtPlayers = getTargetPlayers(sa); for (final Player p : tgtPlayers) { if ((sa.getTargetRestrictions() == null) || p.canBeTargetedBy(sa)) { for (int i = 0; i < numTurns; i++) { ExtraTurn extra = p.getGame().getPhaseHandler().addExtraTurn(p); if (sa.hasParam("LoseAtEndStep")) { extra.setLoseAtEndStep(true); } if (sa.hasParam("SkipUntap")) { extra.setSkipUntap(true); } if (sa.hasParam("NoSchemes")) { extra.setCantSetSchemesInMotion(true); } if (sa.hasParam("ShowMessage")) { p.getGame().getAction().nofityOfValue(sa, p, p + " takes an extra turn.", null); } } } } }
@Override public PaymentDecision visit(CostSacrifice cost) { if (cost.payCostFromSource()) { return PaymentDecision.card(source); } if (cost.getAmount().equals("All")) { /*List<Card> typeList = new ArrayList<Card>(activator.getCardsIn(ZoneType.Battlefield)); typeList = CardLists.getValidCards(typeList, cost.getType().split(";"), activator, source); if (activator.hasKeyword("You can't sacrifice creatures to cast spells or activate abilities.")) { typeList = CardLists.getNotType(typeList, "Creature"); }*/ // Does the AI want to use Sacrifice All? return null; } Integer c = cost.convertAmount(); if (c == null) { if (ability.getSVar(cost.getAmount()).equals("XChoice")) { return null; } c = AbilityUtils.calculateAmount(source, cost.getAmount(), ability); } List<Card> list = ComputerUtil.chooseSacrificeType( player, cost.getType(), source, ability.getTargetCard(), c); return PaymentDecision.card(list); }
private Card chooseCardOnStack(SpellAbility sa, Player ai, Game game) { for (SpellAbilityStackInstance si : game.getStack()) { final Card source = si.getSourceCard(); final SpellAbility abilityOnStack = si.getSpellAbility(true); if (sa.hasParam("Choices") && !abilityOnStack.getHostCard().isValid(sa.getParam("Choices"), ai, sa.getHostCard())) { continue; } final ApiType threatApi = abilityOnStack.getApi(); if (threatApi != ApiType.DealDamage && threatApi != ApiType.DamageAll) { continue; } List<? extends GameObject> objects = getTargets(abilityOnStack); if (!abilityOnStack.usesTargeting() && !abilityOnStack.hasParam("Defined") && abilityOnStack.hasParam("ValidPlayers")) objects = AbilityUtils.getDefinedPlayers( source, abilityOnStack.getParam("ValidPlayers"), abilityOnStack); if (!objects.contains(ai) || abilityOnStack.hasParam("NoPrevention")) { continue; } int dmg = AbilityUtils.calculateAmount(source, abilityOnStack.getParam("NumDmg"), abilityOnStack); if (ComputerUtilCombat.predictDamageTo(ai, dmg, source, false) <= 0) { continue; } return source; } return null; }
@Override public PaymentDecision visit(CostTapType cost) { final String amount = cost.getAmount(); Integer c = cost.convertAmount(); if (c == null) { final String sVar = ability.getSVar(amount); if (sVar.equals("XChoice")) { List<Card> typeList = CardLists.getValidCards( player.getCardsIn(ZoneType.Battlefield), cost.getType().split(";"), ability.getActivatingPlayer(), ability.getHostCard()); typeList = CardLists.filter(typeList, Presets.UNTAPPED); c = typeList.size(); source.setSVar("ChosenX", "Number$" + Integer.toString(c)); } else { c = AbilityUtils.calculateAmount(source, amount, ability); } } if (cost.getType().contains("sharesCreatureTypeWith") || cost.getType().contains("withTotalPowerGE")) { return null; } List<Card> totap = ComputerUtil.chooseTapType(player, cost.getType(), source, !cost.canTapSource, c); if (totap == null) { System.out.println("Couldn't find a valid card to tap for: " + source.getName()); return null; } return PaymentDecision.card(totap); }
/* * (non-Javadoc) * * @see * forge.card.cost.CostPart#canPay(forge.card.spellability.SpellAbility, * forge.Card, forge.Player, forge.card.cost.Cost) */ @Override public final boolean canPay(final SpellAbility ability) { final Player activator = ability.getActivatingPlayer(); final Card source = ability.getHostCard(); CardCollectionView validCards = activator.getCardsIn(ZoneType.Battlefield); validCards = CardLists.getValidCards(validCards, this.getType().split(";"), activator, source); validCards = CardLists.filter( validCards, new Predicate<Card>() { @Override public boolean apply(final Card c) { return c.hasCounters(); } }); if (validCards.isEmpty()) { return false; } Integer i = this.convertAmount(); if (i == null) { i = AbilityUtils.calculateAmount(source, this.getAmount(), ability); } int allCounters = 0; for (Card c : validCards) { final Map<CounterType, Integer> tgtCounters = c.getCounters(); for (Integer value : tgtCounters.values()) { allCounters += value; } } return i <= allCounters; }
@Override public PaymentDecision visit(CostGainControl cost) { if (cost.payCostFromSource()) return PaymentDecision.card(source); Integer c = cost.convertAmount(); if (c == null) { c = AbilityUtils.calculateAmount(source, cost.getAmount(), ability); } final List<Card> typeList = CardLists.getValidCards( player.getGame().getCardsIn(ZoneType.Battlefield), cost.getType().split(";"), player, source); if (typeList.size() < c) { return null; } CardLists.sortByPowerAsc(typeList); final List<Card> res = new ArrayList<Card>(); for (int i = 0; i < c; i++) { res.add(typeList.get(i)); } return res.isEmpty() ? null : PaymentDecision.card(res); }
@Override public PaymentDecision visit(CostExile cost) { if (cost.payCostFromSource()) { return PaymentDecision.card(source); } if (cost.getType().equals("All")) { return PaymentDecision.card(player.getCardsIn(cost.getFrom())); } else if (cost.getType().contains("FromTopGrave")) { return null; } Integer c = cost.convertAmount(); if (c == null) { final String sVar = ability.getSVar(cost.getAmount()); // Generalize cost if (sVar.equals("XChoice")) { return null; } c = AbilityUtils.calculateAmount(source, cost.getAmount(), ability); } if (cost.getFrom().equals(ZoneType.Library)) { return PaymentDecision.card(player.getCardsIn(ZoneType.Library, c)); } else if (cost.sameZone) { // TODO Determine exile from same zone for AI return null; } else { List<Card> chosen = ComputerUtil.chooseExileFrom( player, cost.getFrom(), cost.getType(), source, ability.getTargetCard(), c); return null == chosen ? null : PaymentDecision.card(chosen); } }
@Override public PaymentDecision visit(CostAddMana cost) { Integer c = cost.convertAmount(); if (c == null) { c = AbilityUtils.calculateAmount(source, cost.getAmount(), ability); } return PaymentDecision.number(c); }
@Override protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) { final Card source = sa.getHostCard(); final String numDefense = sa.hasParam("NumDef") ? sa.getParam("NumDef") : ""; final String numAttack = sa.hasParam("NumAtt") ? sa.getParam("NumAtt") : ""; int defense; if (numDefense.contains("X") && source.getSVar("X").equals("Count$xPaid")) { // Set PayX here to maximum value. final int xPay = ComputerUtilMana.determineLeftoverMana(sa, ai); source.setSVar("PayX", Integer.toString(xPay)); defense = xPay; } else { defense = AbilityUtils.calculateAmount(sa.getHostCard(), numDefense, sa); } int attack; if (numAttack.contains("X") && source.getSVar("X").equals("Count$xPaid")) { // Set PayX here to maximum value. final String toPay = source.getSVar("PayX"); if (toPay.equals("")) { final int xPay = ComputerUtilMana.determineLeftoverMana(sa, ai); source.setSVar("PayX", Integer.toString(xPay)); attack = xPay; } else { attack = Integer.parseInt(toPay); } } else { attack = AbilityUtils.calculateAmount(sa.getHostCard(), numAttack, sa); } if (sa.getTargetRestrictions() == null) { if (mandatory) { return true; } } else { return this.pumpTgtAI(ai, sa, defense, attack, mandatory); } return true; } // pumpTriggerAI
@Override public PaymentDecision visit(CostRemoveCounter cost) { final String amount = cost.getAmount(); Integer c = cost.convertAmount(); final String type = cost.getType(); if (c == null) { final String sVar = ability.getSVar(amount); if (sVar.equals("XChoice")) { c = AbilityUtils.calculateAmount(source, "ChosenX", ability); } else if (amount.equals("All")) { c = source.getCounters(cost.counter); } else { c = AbilityUtils.calculateAmount(source, amount, ability); } } if (!cost.payCostFromSource()) { List<Card> typeList; if (type.equals("OriginalHost")) { typeList = Lists.newArrayList(ability.getOriginalHost()); } else { typeList = CardLists.getValidCards(player.getCardsIn(cost.zone), type.split(";"), player, source); } for (Card card : typeList) { if (card.getCounters(cost.counter) >= c) { return PaymentDecision.card(card, c); } } return null; } if (c > source.getCounters(cost.counter)) { System.out.println("Not enough " + cost.counter + " on " + source.getName()); return null; } return PaymentDecision.card(source, c); }
@Override public PaymentDecision visit(CostReturn cost) { if (cost.payCostFromSource()) return PaymentDecision.card(source); Integer c = cost.convertAmount(); if (c == null) { c = AbilityUtils.calculateAmount(source, cost.getAmount(), ability); } List<Card> res = ComputerUtil.chooseReturnType(player, cost.getType(), source, ability.getTargetCard(), c); return res.isEmpty() ? null : PaymentDecision.card(res); }
@Override public PaymentDecision visit(CostFlipCoin cost) { Integer c = cost.convertAmount(); if (c == null) { final String sVar = ability.getSVar(cost.getAmount()); // Generalize cost if (sVar.equals("XChoice")) { return null; } c = AbilityUtils.calculateAmount(source, cost.getAmount(), ability); } return PaymentDecision.number(c); }
/* (non-Javadoc) * @see forge.card.abilityfactory.SpellAiLogic#canPlayAI(forge.game.player.Player, java.util.Map, forge.card.spellability.SpellAbility) */ @Override protected boolean canPlayAI(Player ai, SpellAbility sa) { final TargetRestrictions tgt = sa.getTargetRestrictions(); if (tgt != null && sa.canTarget(ai.getOpponent())) { sa.resetTargets(); sa.getTargets().add(ai.getOpponent()); } final String damage = sa.getParam("NumDmg"); final int iDmg = AbilityUtils.calculateAmount(sa.getHostCard(), damage, sa); return this.shouldTgtP(ai, sa, iDmg, false); }
@Override public PaymentDecision visit(CostMill cost) { Integer c = cost.convertAmount(); if (c == null) { final String sVar = ability.getSVar(cost.getAmount()); // Generalize cost if (sVar.equals("XChoice")) { return null; } c = AbilityUtils.calculateAmount(source, cost.getAmount(), ability); } List<Card> topLib = player.getCardsIn(ZoneType.Library, c); return topLib.size() < c ? null : PaymentDecision.card(topLib); }
@Override public boolean payAsDecided(Player ai, PaymentDecision decision, SpellAbility ability) { final String amount = this.getAmount(); final Card source = ability.getHostCard(); Integer c = this.convertAmount(); if (c == null) { c = AbilityUtils.calculateAmount(source, amount, ability); } Card valid = decision.cards.get(0); counterType = decision.ct; for (int i = 0; i < c; i++) { executePayment(ability, valid); } source.setSVar("CostCountersRemoved", Integer.toString(c)); return true; }
/* * (non-Javadoc) * * @see * forge.card.cost.CostPart#canPay(forge.card.spellability.SpellAbility, * forge.Card, forge.Player, forge.card.cost.Cost) */ @Override public final boolean canPay(final SpellAbility ability) { final Player activator = ability.getActivatingPlayer(); final Card source = ability.getHostCard(); CardCollectionView typeList = activator.getGame().getCardsIn(ZoneType.Battlefield); typeList = CardLists.getValidCards(typeList, this.getType().split(";"), activator, source); Integer amount = this.convertAmount(); if (amount == null) { amount = AbilityUtils.calculateAmount(source, this.getAmount(), ability); } if (typeList.size() < amount) { return false; } return true; }
@Override public PaymentDecision visit(CostPayLife cost) { Integer c = cost.convertAmount(); if (c == null) { final String sVar = ability.getSVar(cost.getAmount()); // Generalize cost if (sVar.equals("XChoice")) { return null; } else { c = AbilityUtils.calculateAmount(source, cost.getAmount(), ability); } } if (!player.canPayLife(c)) { return null; } // activator.payLife(c, null); return PaymentDecision.number(c); }
@Override public PaymentDecision visit(CostPutCardToLib cost) { Integer c = cost.convertAmount(); final Game game = player.getGame(); List<Card> chosen = new ArrayList<Card>(); List<Card> list; if (cost.isSameZone()) { list = new ArrayList<Card>(game.getCardsIn(cost.getFrom())); } else { list = new ArrayList<Card>(player.getCardsIn(cost.getFrom())); } if (c == null) { final String sVar = ability.getSVar(cost.getAmount()); // Generalize cost if (sVar.equals("XChoice")) { return null; } c = AbilityUtils.calculateAmount(source, cost.getAmount(), ability); } list = CardLists.getValidCards(list, cost.getType().split(";"), player, source); if (cost.isSameZone()) { // Jotun Grunt // TODO: improve AI final List<Player> players = game.getPlayers(); for (Player p : players) { List<Card> enoughType = CardLists.filter(list, CardPredicates.isOwner(p)); if (enoughType.size() >= c) { chosen.addAll(enoughType); break; } } chosen = chosen.subList(0, c); } else { chosen = ComputerUtil.choosePutToLibraryFrom( player, cost.getFrom(), cost.getType(), source, ability.getTargetCard(), c); } return chosen.isEmpty() ? null : PaymentDecision.card(chosen); }
@Override protected String getStackDescription(SpellAbility sa) { final Card host = sa.getHostCard(); final StringBuilder sb = new StringBuilder(); final int numToDig = AbilityUtils.calculateAmount(host, sa.getParam("DigNum"), sa); final List<Player> tgtPlayers = getTargetPlayers(sa); sb.append(host.getController()).append(" looks at the top "); sb.append(Lang.nounWithAmount(numToDig, "card")).append(" of "); if (tgtPlayers.contains(host.getController())) { sb.append("his or her "); } else { for (final Player p : tgtPlayers) { sb.append(Lang.getPossesive(p.getName())).append(" "); } } sb.append("library."); return sb.toString(); }
@Override public PaymentDecision visit(CostExileFromStack cost) { Integer c = cost.convertAmount(); if (c == null) { final String sVar = ability.getSVar(cost.getAmount()); // Generalize cost if (sVar.equals("XChoice")) { return null; } c = AbilityUtils.calculateAmount(source, cost.getAmount(), ability); } List<SpellAbility> chosen = new ArrayList<SpellAbility>(); for (SpellAbilityStackInstance si : source.getGame().getStack()) { SpellAbility sp = si.getSpellAbility().getRootAbility(); if (si.getSourceCard().isValid(cost.getType().split(";"), source.getController(), source)) { chosen.add(sp); } } return chosen.isEmpty() ? null : PaymentDecision.spellabilities(chosen); }
@Override public PaymentDecision visit(CostDiscard cost) { final String type = cost.getType(); final List<Card> hand = player.getCardsIn(ZoneType.Hand); if (type.equals("LastDrawn")) { if (!hand.contains(player.getLastDrawnCard())) { return null; } return PaymentDecision.card(player.getLastDrawnCard()); } else if (cost.payCostFromSource()) { if (!hand.contains(source)) { return null; } return PaymentDecision.card(source); } else if (type.equals("Hand")) { return PaymentDecision.card(hand); } if (type.contains("WithSameName")) { return null; } Integer c = cost.convertAmount(); if (c == null) { final String sVar = ability.getSVar(cost.getAmount()); if (sVar.equals("XChoice")) { return null; } c = AbilityUtils.calculateAmount(source, cost.getAmount(), ability); } if (type.equals("Random")) { return PaymentDecision.card(CardLists.getRandomSubList(hand, c)); } else { final AiController aic = ((PlayerControllerAi) player.getController()).getAi(); return PaymentDecision.card(aic.getCardsToDiscard(c, type.split(";"), ability)); } }
@Override public PaymentDecision visit(CostRemoveAnyCounter cost) { final String amount = cost.getAmount(); final int c = AbilityUtils.calculateAmount(source, amount, ability); final String type = cost.getType(); List<Card> typeList = CardLists.getValidCards( player.getCardsIn(ZoneType.Battlefield), type.split(";"), player, source); List<Card> hperms = CardLists.filter( typeList, new Predicate<Card>() { @Override public boolean apply(final Card crd) { for (final CounterType c1 : CounterType.values()) { if (crd.getCounters(c1) >= c && ComputerUtil.isNegativeCounter(c1, crd)) { return true; } } return false; } }); if (hperms.isEmpty()) return null; PaymentDecision result = PaymentDecision.card(hperms); Card valid = hperms.get(0); for (CounterType c1 : valid.getCounters().keySet()) { if (valid.getCounters(c1) >= c && ComputerUtil.isNegativeCounter(c1, valid)) { result.ct = c1; break; } } // Only find cards with enough negative counters // TODO: add ai for Chisei, Heart of Oceans return result; }
@Override protected String getStackDescription(SpellAbility sa) { final StringBuilder sb = new StringBuilder(); final int numTurns = AbilityUtils.calculateAmount(sa.getHostCard(), sa.getParam("NumTurns"), sa); List<Player> tgtPlayers = getTargetPlayers(sa); for (final Player player : tgtPlayers) { sb.append(player).append(" "); } sb.append("takes "); sb.append(numTurns > 1 ? numTurns : "an"); sb.append(" extra turn"); if (numTurns > 1) { sb.append("s"); } sb.append(" after this one."); return sb.toString(); }
/* (non-Javadoc) * @see forge.card.abilityfactory.SpellAiLogic#canPlayAI(forge.game.player.Player, java.util.Map, forge.card.spellability.SpellAbility) */ @Override protected boolean canPlayAI(Player ai, SpellAbility sa) { final Cost cost = sa.getPayCosts(); final Game game = ai.getGame(); final PhaseHandler ph = game.getPhaseHandler(); final List<String> keywords = sa.hasParam("KW") ? Arrays.asList(sa.getParam("KW").split(" & ")) : new ArrayList<String>(); final String numDefense = sa.hasParam("NumDef") ? sa.getParam("NumDef") : ""; final String numAttack = sa.hasParam("NumAtt") ? sa.getParam("NumAtt") : ""; if (!ComputerUtilCost.checkLifeCost(ai, cost, sa.getHostCard(), 4, null)) { return false; } if (!ComputerUtilCost.checkDiscardCost(ai, cost, sa.getHostCard())) { return false; } if (!ComputerUtilCost.checkCreatureSacrificeCost(ai, cost, sa.getHostCard())) { return false; } if (!ComputerUtilCost.checkRemoveCounterCost(cost, sa.getHostCard())) { return false; } if (!ComputerUtilCost.checkTapTypeCost(ai, cost, sa.getHostCard())) { return false; } if (game.getStack().isEmpty() && hasTapCost(cost, sa.getHostCard())) { if (ph.getPhase().isBefore(PhaseType.COMBAT_DECLARE_ATTACKERS) && ph.isPlayerTurn(ai)) { return false; } if (ph.getPhase().isBefore(PhaseType.COMBAT_DECLARE_BLOCKERS) && ph.isPlayerTurn(ai.getOpponent())) { return false; } } if (ComputerUtil.preventRunAwayActivations(sa)) { return false; } // Phase Restrictions if (game.getStack().isEmpty() && ph.getPhase().isBefore(PhaseType.COMBAT_BEGIN)) { // Instant-speed pumps should not be cast outside of combat when the // stack is empty if (!sa.isCurse() && !SpellAbilityAi.isSorcerySpeed(sa)) { return false; } } else if (!game.getStack().isEmpty() && !sa.isCurse()) { return pumpAgainstRemoval(ai, sa); } if (sa.hasParam("ActivationNumberSacrifice")) { final SpellAbilityRestriction restrict = sa.getRestrictions(); final int sacActivations = Integer.parseInt(sa.getParam("ActivationNumberSacrifice").substring(2)); final int activations = restrict.getNumberTurnActivations(); // don't risk sacrificing a creature just to pump it if (activations >= sacActivations - 1) { return false; } } final Card source = sa.getHostCard(); 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. final int xPay = ComputerUtilMana.determineLeftoverMana(sa, ai); source.setSVar("PayX", Integer.toString(xPay)); defense = xPay; if (numDefense.equals("-X")) { defense = -xPay; } } else { defense = AbilityUtils.calculateAmount(sa.getHostCard(), numDefense, sa); } int attack; if (numAttack.contains("X") && source.getSVar("X").equals("Count$xPaid")) { // Set PayX here to maximum value. final String toPay = source.getSVar("PayX"); if (toPay.equals("")) { final int xPay = ComputerUtilMana.determineLeftoverMana(sa, ai); source.setSVar("PayX", Integer.toString(xPay)); attack = xPay; } else { attack = Integer.parseInt(toPay); } } else { attack = AbilityUtils.calculateAmount(sa.getHostCard(), numAttack, sa); } if ((numDefense.contains("X") && defense == 0) || (numAttack.contains("X") && attack == 0)) { return false; } // Untargeted if ((sa.getTargetRestrictions() == null) || !sa.getTargetRestrictions().doesTarget()) { final List<Card> cards = AbilityUtils.getDefinedCards(sa.getHostCard(), sa.getParam("Defined"), sa); if (cards.size() == 0) { return false; } // when this happens we need to expand AI to consider if its ok for // everything? for (final Card card : cards) { if (sa.isCurse()) { if (!card.getController().isOpponentOf(ai)) { return false; } if (!containsUsefulKeyword(ai, keywords, card, sa, attack)) { continue; } return true; } if (!card.getController().isOpponentOf(ai) && shouldPumpCard(ai, sa, card, defense, attack, keywords)) { return true; } } return false; } // Targeted if (!this.pumpTgtAI(ai, sa, defense, attack, false)) { return false; } return true; } // pumpPlayAI()
private boolean pumpTgtAI( final Player ai, final SpellAbility sa, final int defense, final int attack, final boolean mandatory) { final List<String> keywords = sa.hasParam("KW") ? Arrays.asList(sa.getParam("KW").split(" & ")) : new ArrayList<String>(); final Game game = ai.getGame(); final Card source = sa.getHostCard(); if (!mandatory && !sa.isTrigger() && game.getPhaseHandler().getPhase().isAfter(PhaseType.COMBAT_DECLARE_BLOCKERS) && !(sa.isCurse() && defense < 0) && !this.containsNonCombatKeyword(keywords) && !sa.hasParam("UntilYourNextTurn")) { return false; } final Player opp = ai.getOpponent(); final TargetRestrictions tgt = sa.getTargetRestrictions(); sa.resetTargets(); if (sa.hasParam("TargetingPlayer") && sa.getActivatingPlayer().equals(ai) && !sa.isTrigger()) { Player targetingPlayer = AbilityUtils.getDefinedPlayers(source, sa.getParam("TargetingPlayer"), sa).get(0); sa.setTargetingPlayer(targetingPlayer); return targetingPlayer.getController().chooseTargetsFor(sa); } List<Card> list = new ArrayList<Card>(); if (sa.hasParam("AILogic")) { if (sa.getParam("AILogic").equals("HighestPower")) { list = CardLists.getValidCards( CardLists.filter(game.getCardsIn(ZoneType.Battlefield), Presets.CREATURES), tgt.getValidTgts(), ai, source); list = CardLists.getTargetableCards(list, sa); CardLists.sortByPowerDesc(list); if (!list.isEmpty()) { sa.getTargets().add(list.get(0)); return true; } else { return false; } } if (sa.getParam("AILogic").equals("Fight") || sa.getParam("AILogic").equals("PowerDmg")) { final AbilitySub tgtFight = sa.getSubAbility(); List<Card> aiCreatures = ai.getCreaturesInPlay(); aiCreatures = CardLists.getTargetableCards(aiCreatures, sa); aiCreatures = ComputerUtil.getSafeTargets(ai, sa, aiCreatures); ComputerUtilCard.sortByEvaluateCreature(aiCreatures); // sort is suboptimal due to conflicting needs depending on game state: // -deathtouch for deal damage // -max power for damage to player // -survivability for generic "fight" // -no support for "heroic" List<Card> humCreatures = ai.getOpponent().getCreaturesInPlay(); humCreatures = CardLists.getTargetableCards(humCreatures, tgtFight); ComputerUtilCard.sortByEvaluateCreature(humCreatures); if (humCreatures.isEmpty() || aiCreatures.isEmpty()) { return false; } int buffedAtk = attack, buffedDef = defense; for (Card humanCreature : humCreatures) { for (Card aiCreature : aiCreatures) { if (sa.isSpell()) { // heroic triggers adding counters for (Trigger t : aiCreature.getTriggers()) { if (t.getMode() == TriggerType.SpellCast) { final Map<String, String> params = t.getMapParams(); if ("Card.Self".equals(params.get("TargetsValid")) && "You".equals(params.get("ValidActivatingPlayer")) && params.containsKey("Execute")) { SpellAbility heroic = AbilityFactory.getAbility( aiCreature.getSVar(params.get("Execute")), aiCreature); if ("Self".equals(heroic.getParam("Defined")) && "P1P1".equals(heroic.getParam("CounterType"))) { int amount = AbilityUtils.calculateAmount( aiCreature, heroic.getParam("CounterNum"), heroic); buffedAtk += amount; buffedDef += amount; } break; } } } } if (sa.getParam("AILogic").equals("PowerDmg")) { if (FightAi.canKill(aiCreature, humanCreature, buffedAtk)) { sa.getTargets().add(aiCreature); tgtFight.getTargets().add(humanCreature); return true; } } else { if (FightAi.shouldFight(aiCreature, humanCreature, buffedAtk, buffedDef)) { sa.getTargets().add(aiCreature); tgtFight.getTargets().add(humanCreature); return true; } } } } } return false; } else if (sa.isCurse()) { if (sa.canTarget(opp)) { sa.getTargets().add(opp); return true; } list = this.getCurseCreatures(ai, sa, defense, attack, keywords); } else { if (!tgt.canTgtCreature()) { ZoneType zone = tgt.getZone().get(0); list = game.getCardsIn(zone); } else { list = this.getPumpCreatures(ai, sa, defense, attack, keywords); } if (sa.canTarget(ai)) { sa.getTargets().add(ai); return true; } } list = CardLists.getValidCards(list, tgt.getValidTgts(), ai, source); if (game.getStack().isEmpty()) { // If the cost is tapping, don't activate before declare // attack/block if (sa.getPayCosts() != null && sa.getPayCosts().hasTapCost()) { if (game.getPhaseHandler().getPhase().isBefore(PhaseType.COMBAT_DECLARE_ATTACKERS) && game.getPhaseHandler().isPlayerTurn(ai)) { list.remove(sa.getHostCard()); } if (game.getPhaseHandler().getPhase().isBefore(PhaseType.COMBAT_DECLARE_BLOCKERS) && game.getPhaseHandler().isPlayerTurn(opp)) { list.remove(sa.getHostCard()); } } } if (list.isEmpty()) { return mandatory && this.pumpMandatoryTarget(ai, sa, mandatory); } if (!sa.isCurse()) { // Don't target cards that will die. list = ComputerUtil.getSafeTargets(ai, sa, list); } while (sa.getTargets().getNumTargeted() < tgt.getMaxTargets(source, sa)) { Card t = null; // boolean goodt = false; if (list.isEmpty()) { if ((sa.getTargets().getNumTargeted() < tgt.getMinTargets(source, sa)) || (sa.getTargets().getNumTargeted() == 0)) { if (mandatory) { return this.pumpMandatoryTarget(ai, sa, mandatory); } sa.resetTargets(); return false; } else { // TODO is this good enough? for up to amounts? break; } } t = ComputerUtilCard.getBestAI(list); // option to hold removal instead only applies for single targeted removal if (!sa.isTrigger() && tgt.getMaxTargets(source, sa) == 1 && sa.isCurse()) { if (!ComputerUtilCard.useRemovalNow(sa, t, -defense, ZoneType.Graveyard)) { return false; } } sa.getTargets().add(t); list.remove(t); } return true; } // pumpTgtAI()
@Override public void resolve(SpellAbility sa) { final Card card = sa.getHostCard(); AbilityManaPart abMana = sa.getManaPart(); // Spells are not undoable sa.setUndoable(sa.isAbility() && sa.isUndoable()); final List<Player> tgtPlayers = getTargetPlayers(sa); final TargetRestrictions tgt = sa.getTargetRestrictions(); final boolean optional = sa.hasParam("Optional"); final Game game = sa.getActivatingPlayer().getGame(); if (optional && !sa.getActivatingPlayer() .getController() .confirmAction(sa, null, "Do you want to add mana to your mana pool?")) { return; } if (abMana.isComboMana()) { for (Player p : tgtPlayers) { int amount = sa.hasParam("Amount") ? AbilityUtils.calculateAmount(card, sa.getParam("Amount"), sa) : 1; if (tgt == null || p.canBeTargetedBy(sa)) { Player activator = sa.getActivatingPlayer(); // String colorsNeeded = abMana.getExpressChoice(); String[] colorsProduced = abMana.getComboColors().split(" "); final StringBuilder choiceString = new StringBuilder(); ColorSet colorOptions = null; if (!abMana.isAnyMana()) { colorOptions = ColorSet.fromNames(colorsProduced); } else { colorOptions = ColorSet.fromNames(MagicColor.Constant.ONLY_COLORS); } for (int nMana = 1; nMana <= amount; nMana++) { String choice = ""; byte chosenColor = activator.getController().chooseColor("Select Mana to Produce", sa, colorOptions); if (chosenColor == 0) throw new RuntimeException( "ManaEffect::resolve() /*combo mana*/ - " + activator + " color mana choice is empty for " + card.getName()); choice = MagicColor.toShortString(chosenColor); if (nMana != 1) { choiceString.append(" "); } choiceString.append(choice); } game.action.nofityOfValue(sa, card, activator + " picked " + choiceString, activator); abMana.setExpressChoice(choiceString.toString()); } } } else if (abMana.isAnyMana()) { for (Player p : tgtPlayers) { if (tgt == null || p.canBeTargetedBy(sa)) { Player act = sa.getActivatingPlayer(); // AI color choice is set in ComputerUtils so only human players need to make a choice String colorsNeeded = abMana.getExpressChoice(); String choice = ""; ColorSet colorMenu = null; byte mask = 0; // loop through colors to make menu for (int nChar = 0; nChar < colorsNeeded.length(); nChar++) { mask |= MagicColor.fromName(colorsNeeded.charAt(nChar)); } colorMenu = mask == 0 ? ColorSet.ALL_COLORS : ColorSet.fromMask(mask); byte val = act.getController().chooseColor("Select Mana to Produce", sa, colorMenu); if (0 == val) { throw new RuntimeException( "ManaEffect::resolve() /*any mana*/ - " + act + " color mana choice is empty for " + card.getName()); } choice = MagicColor.toShortString(val); game.action.nofityOfValue(sa, card, act + " picked " + choice, act); abMana.setExpressChoice(choice); } } } else if (abMana.isSpecialMana()) { for (Player p : tgtPlayers) { if (tgt == null || p.canBeTargetedBy(sa)) { String type = abMana.getOrigProduced().split("Special ")[1]; if (type.equals("EnchantedManaCost")) { Card enchanted = card.getEnchantingCard(); if (enchanted == null) continue; StringBuilder sb = new StringBuilder(); int generic = enchanted.getManaCost().getGenericCost(); if (generic > 0) sb.append(generic); for (ManaCostShard s : enchanted.getManaCost()) { ColorSet cs = ColorSet.fromMask(s.getColorMask()); if (cs.isColorless()) continue; sb.append(' '); if (cs.isMonoColor()) sb.append(MagicColor.toShortString(s.getColorMask())); else /* (cs.isMulticolor()) */ { byte chosenColor = sa.getActivatingPlayer() .getController() .chooseColor("Choose a single color from " + s.toString(), sa, cs); sb.append(MagicColor.toShortString(chosenColor)); } } abMana.setExpressChoice(sb.toString().trim()); } else if (type.equals("LastNotedType")) { Mana manaType = (Mana) Iterables.getFirst(card.getRemembered(), null); if (manaType == null) { return; } String cs = manaType.toString(); abMana.setExpressChoice(cs); } if (abMana.getExpressChoice().isEmpty()) { System.out.println( "AbilityFactoryMana::manaResolve() - special mana effect is empty for " + sa.getHostCard().getName()); } } } } for (final Player player : tgtPlayers) { abMana.produceMana(GameActionUtil.generatedMana(sa), player, sa); } // Only clear express choice after mana has been produced abMana.clearExpressChoice(); // resolveDrawback(sa); }