@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 protected boolean canPlayAI(Player ai, SpellAbility sa) { final TargetRestrictions tgt = sa.getTargetRestrictions(); final Card source = sa.getHostCard(); final Game game = source.getGame(); boolean useAbility = true; // if (card.getController().isComputer()) { // final List<Card> creatures = AllZoneUtil.getCreaturesInPlay(); // if (!creatures.isEmpty()) { // cardToCopy = CardFactoryUtil.getBestCreatureAI(creatures); // } // } // TODO - add some kind of check to answer // "Am I going to attack with this?" // TODO - add some kind of check for during human turn to answer // "Can I use this to block something?" PhaseHandler phase = game.getPhaseHandler(); // don't use instant speed clone abilities outside computers // Combat_Begin step if (!phase.is(PhaseType.COMBAT_BEGIN) && phase.isPlayerTurn(ai) && !SpellAbilityAi.isSorcerySpeed(sa) && !sa.hasParam("ActivationPhases") && !sa.hasParam("Permanent")) { return false; } // don't use instant speed clone abilities outside humans // Combat_Declare_Attackers_InstantAbility step if (!phase.is(PhaseType.COMBAT_DECLARE_ATTACKERS) || phase.isPlayerTurn(ai) || game.getCombat().getAttackers().isEmpty()) { return false; } // don't activate during main2 unless this effect is permanent if (phase.is(PhaseType.MAIN2) && !sa.hasParam("Permanent")) { return false; } if (null == tgt) { final List<Card> defined = AbilityUtils.getDefinedCards(source, sa.getParam("Defined"), sa); boolean bFlag = false; for (final Card c : defined) { bFlag |= (!c.isCreature() && !c.isTapped() && !(c.getTurnInZone() == phase.getTurn())); // for creatures that could be improved (like Figure of Destiny) if (c.isCreature() && (sa.hasParam("Permanent") || (!c.isTapped() && !c.isSick()))) { int power = -5; if (sa.hasParam("Power")) { power = AbilityUtils.calculateAmount(source, sa.getParam("Power"), sa); } int toughness = -5; if (sa.hasParam("Toughness")) { toughness = AbilityUtils.calculateAmount(source, sa.getParam("Toughness"), sa); } if ((power + toughness) > (c.getCurrentPower() + c.getCurrentToughness())) { bFlag = true; } } } if (!bFlag) { // All of the defined stuff is cloned, not very // useful return false; } } else { sa.resetTargets(); useAbility &= cloneTgtAI(sa); } return useAbility; } // end cloneCanPlayAI()
/* (non-Javadoc) * @see forge.card.cost.CostPartWithList#executePayment(forge.card.spellability.SpellAbility, forge.Card) */ @Override protected Card doPayment(SpellAbility ability, Card targetCard) { return targetCard.getGame().getAction().moveToHand(targetCard); }
@Override public void resolve(SpellAbility sa) { final Card card = sa.getHostCard(); final Game game = card.getGame(); final boolean remDestroyed = sa.hasParam("RememberDestroyed"); final boolean remAttached = sa.hasParam("RememberAttached"); if (remDestroyed || remAttached) { card.clearRemembered(); } final boolean noRegen = sa.hasParam("NoRegen"); final boolean sac = sa.hasParam("Sacrifice"); final List<Card> tgtCards = getTargetCards(sa); final List<Card> untargetedCards = new ArrayList<Card>(); final TargetRestrictions tgt = sa.getTargetRestrictions(); if (sa.hasParam("Radiance")) { for (final Card c : CardUtil.getRadiance(card, tgtCards.get(0), sa.getParam("ValidTgts").split(","))) { untargetedCards.add(c); } } for (final Card tgtC : tgtCards) { if (tgtC.isInPlay() && ((tgt == null) || tgtC.canBeTargetedBy(sa))) { boolean destroyed = false; final Card lki = CardUtil.getLKICopy(tgtC); if (remAttached) { card.addRemembered(tgtC.getEnchantedBy(false)); card.addRemembered(tgtC.getEquippedBy(false)); card.addRemembered(tgtC.getFortifiedBy(false)); } if (sac) { destroyed = game.getAction().sacrifice(tgtC, sa) != null; } else if (noRegen) { destroyed = game.getAction().destroyNoRegeneration(tgtC, sa); } else { destroyed = game.getAction().destroy(tgtC, sa); } if (destroyed && remDestroyed) { card.addRemembered(tgtC); } if (destroyed && sa.hasParam("RememberLKI")) { card.addRemembered(lki); } } } for (final Card unTgtC : untargetedCards) { if (unTgtC.isInPlay()) { boolean destroyed = false; if (sac) { destroyed = game.getAction().sacrifice(unTgtC, sa) != null; } else if (noRegen) { destroyed = game.getAction().destroyNoRegeneration(unTgtC, sa); } else { destroyed = game.getAction().destroy(unTgtC, sa); } if (destroyed && remDestroyed) { card.addRemembered(unTgtC); } } } }
/* (non-Javadoc) * @see forge.card.abilityfactory.SpellEffect#resolve(java.util.Map, forge.card.spellability.SpellAbility) */ @Override public void resolve(final SpellAbility sa) { final Card source = sa.getHostCard(); final Game game = source.getGame(); game.reverseTurnOrder(); }
@Override public void resolve(SpellAbility sa) { final Card card = sa.getHostCard(); // final int min = sa.containsKey("Min") ? Integer.parseInt(sa.get("Min")) : 0; // final int max = sa.containsKey("Max") ? Integer.parseInt(sa.get("Max")) : 99; final boolean random = sa.hasParam("Random"); final boolean anyNumber = sa.hasParam("ChooseAnyNumber"); final boolean secretlyChoose = sa.hasParam("SecretlyChoose"); final String sMin = sa.getParamOrDefault("Min", "0"); final int min = AbilityUtils.calculateAmount(card, sMin, sa); final String sMax = sa.getParamOrDefault("Max", "99"); final int max = AbilityUtils.calculateAmount(card, sMax, sa); final List<Player> tgtPlayers = getTargetPlayers(sa); final TargetRestrictions tgt = sa.getTargetRestrictions(); final Map<Player, Integer> chooseMap = new HashMap<Player, Integer>(); for (final Player p : tgtPlayers) { if ((tgt == null) || p.canBeTargetedBy(sa)) { int chosen; if (random) { final Random randomGen = new Random(); chosen = randomGen.nextInt(max - min) + min; p.getGame().getAction().nofityOfValue(sa, p, Integer.toString(chosen), null); } else { String title = sa.hasParam("ListTitle") ? sa.getParam("ListTitle") : "Choose a number"; if (anyNumber) { chosen = p.getController().announceRequirements(sa, title, true); } else { chosen = p.getController().chooseNumber(sa, title, min, max); } // don't notify here, because most scripts I've seen don't store that number in a long // term } if (secretlyChoose) { chooseMap.put(p, chosen); } else { card.setChosenNumber(chosen); } if (sa.hasParam("Notify")) { p.getGame().getAction().nofityOfValue(sa, card, p.getName() + " picked " + chosen, p); } } } if (secretlyChoose) { StringBuilder sb = new StringBuilder(); List<Player> highestNum = new ArrayList<Player>(); List<Player> lowestNum = new ArrayList<Player>(); int highest = 0; int lowest = Integer.MAX_VALUE; for (Entry<Player, Integer> ev : chooseMap.entrySet()) { int num = ev.getValue(); Player player = ev.getKey(); sb.append(player).append(" chose ").append(num); sb.append("\r\n"); if (num > highest) { highestNum.clear(); highest = num; } if (num == highest) { highestNum.add(player); } if (num < lowest) { lowestNum.clear(); lowest = num; } if (num == lowest) { lowestNum.add(player); } } card.getGame().getAction().nofityOfValue(sa, card, sb.toString(), null); if (sa.hasParam("ChooseNumberSubAbility")) { SpellAbility sub = AbilityFactory.getAbility(card.getSVar(sa.getParam("ChooseNumberSubAbility")), card); sub.setActivatingPlayer(sa.getActivatingPlayer()); ((AbilitySub) sub).setParent(sa); for (Player p : chooseMap.keySet()) { card.addRemembered(p); card.setChosenNumber(chooseMap.get(p)); AbilityUtils.resolve(sub); card.clearRemembered(); } } if (sa.hasParam("Lowest")) { SpellAbility action = AbilityFactory.getAbility(card.getSVar(sa.getParam("Lowest")), card); action.setActivatingPlayer(sa.getActivatingPlayer()); ((AbilitySub) action).setParent(sa); for (Player p : lowestNum) { card.addRemembered(p); card.setChosenNumber(lowest); AbilityUtils.resolve(action); card.clearRemembered(); } } if (sa.hasParam("Highest")) { SpellAbility action = AbilityFactory.getAbility(card.getSVar(sa.getParam("Highest")), card); action.setActivatingPlayer(sa.getActivatingPlayer()); ((AbilitySub) action).setParent(sa); for (Player p : highestNum) { card.addRemembered(p); card.setChosenNumber(highest); AbilityUtils.resolve(action); card.clearRemembered(); } if (sa.hasParam("RememberHighest")) { card.addRemembered(highestNum); } } } }
/** {@inheritDoc} */ @Override public final boolean performTest(final java.util.Map<String, Object> runParams2) { final SpellAbility spellAbility = (SpellAbility) runParams2.get("CastSA"); if (spellAbility == null) { System.out.println( "TriggerSpellAbilityCast performTest encountered spellAbility == null. runParams2 = " + runParams2); return false; } final Card cast = spellAbility.getHostCard(); final Game game = cast.getGame(); final SpellAbilityStackInstance si = game.getStack().getInstanceFromSpellAbility(spellAbility); if (this.getMode() == TriggerType.SpellCast) { if (!spellAbility.isSpell()) { return false; } } else if (this.getMode() == TriggerType.AbilityCast) { if (!spellAbility.isAbility()) { return false; } } else if (this.getMode() == TriggerType.SpellAbilityCast) { // Empty block for readability. } if (this.mapParams.containsKey("ActivatedOnly")) { if (spellAbility.isTrigger()) { return false; } } if (this.mapParams.containsKey("ValidControllingPlayer")) { if (!matchesValid( cast.getController(), this.mapParams.get("ValidControllingPlayer").split(","), this.getHostCard())) { return false; } } if (this.mapParams.containsKey("ValidActivatingPlayer")) { if (si == null || !matchesValid( si.getSpellAbility(true).getActivatingPlayer(), this.mapParams.get("ValidActivatingPlayer").split(","), this.getHostCard())) { return false; } if (this.mapParams.containsKey("ActivatorThisTurnCast")) { String compare = this.mapParams.get("ActivatorThisTurnCast"); List<Card> thisTurnCast = CardUtil.getThisTurnCast( this.mapParams.containsKey("ValidCard") ? this.mapParams.get("ValidCard") : "Card", this.getHostCard()); thisTurnCast = CardLists.filterControlledBy( thisTurnCast, si.getSpellAbility(true).getActivatingPlayer()); int left = thisTurnCast.size(); int right = Integer.parseInt(compare.substring(2)); if (!Expressions.compare(left, compare, right)) { return false; } } } if (this.mapParams.containsKey("ValidCard")) { if (!matchesValid(cast, this.mapParams.get("ValidCard").split(","), this.getHostCard())) { return false; } } if (this.mapParams.containsKey("TargetsValid")) { SpellAbility sa = spellAbility; if (si != null) { sa = si.getSpellAbility(true); } boolean validTgtFound = false; while (sa != null && !validTgtFound) { for (final Card tgt : sa.getTargets().getTargetCards()) { if (tgt.isValid( this.mapParams.get("TargetsValid").split(","), this.getHostCard().getController(), this.getHostCard())) { validTgtFound = true; break; } } for (final Player p : sa.getTargets().getTargetPlayers()) { if (matchesValid(p, this.mapParams.get("TargetsValid").split(","), this.getHostCard())) { validTgtFound = true; break; } } sa = sa.getSubAbility(); } if (!validTgtFound) { return false; } } if (this.mapParams.containsKey("NonTapCost")) { final Cost cost = (Cost) (runParams2.get("Cost")); if (cost.hasTapCost()) { return false; } } if (this.mapParams.containsKey("Conspire")) { if (!spellAbility.isOptionalCostPaid(OptionalCost.Conspire)) { return false; } if (spellAbility.getConspireInstances() == 0) { return false; } else { spellAbility.subtractConspireInstance(); // System.out.println("Conspire instances left = " + spellAbility.getConspireInstances()); } } if (this.mapParams.containsKey("Outlast")) { if (!spellAbility.isOutlast()) { return false; } } if (this.mapParams.containsKey("IsSingleTarget")) { int numTargeted = 0; for (TargetChoices tc : spellAbility.getAllTargetChoices()) { numTargeted += tc.getNumTargeted(); } if (numTargeted != 1) { return false; } } if (this.mapParams.containsKey("SpellSpeed")) { if (this.mapParams.get("SpellSpeed").equals("NotSorcerySpeed")) { if (this.getHostCard().getController().couldCastSorcery(spellAbility)) { return false; } if (this.getHostCard() .hasKeyword( "You may cast CARDNAME as though it had flash. If you cast it any time a " + "sorcery couldn't have been cast, the controller of the permanent it becomes sacrifices it at the beginning" + " of the next cleanup step.")) { // for these cards the trigger must only fire if using their own ability to cast at // instant speed if (this.getHostCard().hasKeyword("Flash") || this.getHostCard() .getController() .hasKeyword("You may cast nonland cards as though they had flash.")) { return false; } } return true; } } return true; }
/* (non-Javadoc) * @see forge.card.ability.SpellAbilityEffect#resolve(forge.card.spellability.SpellAbility) */ @Override public void resolve(final SpellAbility sa) { final Card hostCard = sa.getHostCard(); final Game game = hostCard.getGame(); final List<String> keywords = new ArrayList<String>(); final List<String> types = new ArrayList<String>(); final List<String> svars = new ArrayList<String>(); final List<String> triggers = new ArrayList<String>(); if (sa.hasParam("Optional")) { if (!sa.getActivatingPlayer() .getController() .confirmAction(sa, null, "Copy this permanent?")) { return; } } if (sa.hasParam("Keywords")) { keywords.addAll(Arrays.asList(sa.getParam("Keywords").split(" & "))); } if (sa.hasParam("AddTypes")) { types.addAll(Arrays.asList(sa.getParam("AddTypes").split(" & "))); } if (sa.hasParam("AddSVars")) { svars.addAll(Arrays.asList(sa.getParam("AddSVars").split(" & "))); } if (sa.hasParam("Triggers")) { triggers.addAll(Arrays.asList(sa.getParam("Triggers").split(" & "))); } final int numCopies = sa.hasParam("NumCopies") ? AbilityUtils.calculateAmount(hostCard, sa.getParam("NumCopies"), sa) : 1; Player controller = null; if (sa.hasParam("Controller")) { final FCollectionView<Player> defined = AbilityUtils.getDefinedPlayers(hostCard, sa.getParam("Controller"), sa); if (!defined.isEmpty()) { controller = defined.getFirst(); } } if (controller == null) { controller = sa.getActivatingPlayer(); } List<Card> tgtCards = getTargetCards(sa); final TargetRestrictions tgt = sa.getTargetRestrictions(); if (sa.hasParam("ValidSupportedCopy")) { List<PaperCard> cards = Lists.newArrayList(StaticData.instance().getCommonCards().getUniqueCards()); String valid = sa.getParam("ValidSupportedCopy"); if (valid.contains("X")) { valid = valid.replace("X", Integer.toString(AbilityUtils.calculateAmount(hostCard, "X", sa))); } if (StringUtils.containsIgnoreCase(valid, "creature")) { Predicate<PaperCard> cpp = Predicates.compose(CardRulesPredicates.Presets.IS_CREATURE, PaperCard.FN_GET_RULES); cards = Lists.newArrayList(Iterables.filter(cards, cpp)); } if (StringUtils.containsIgnoreCase(valid, "equipment")) { Predicate<PaperCard> cpp = Predicates.compose(CardRulesPredicates.Presets.IS_EQUIPMENT, PaperCard.FN_GET_RULES); cards = Lists.newArrayList(Iterables.filter(cards, cpp)); } if (sa.hasParam("RandomCopied")) { List<PaperCard> copysource = new ArrayList<PaperCard>(cards); List<Card> choice = new ArrayList<Card>(); final String num = sa.hasParam("RandomNum") ? sa.getParam("RandomNum") : "1"; int ncopied = AbilityUtils.calculateAmount(hostCard, num, sa); while (ncopied > 0) { final PaperCard cp = Aggregates.random(copysource); Card possibleCard = Card.fromPaperCard( cp, sa.getActivatingPlayer()); // Need to temporarily set the Owner so the Game is set if (possibleCard.isValid(valid, hostCard.getController(), hostCard)) { choice.add(possibleCard); copysource.remove(cp); ncopied -= 1; } } tgtCards = choice; } else if (sa.hasParam("DefinedName")) { String name = sa.getParam("DefinedName"); if (name.equals("NamedCard")) { if (!hostCard.getNamedCard().isEmpty()) { name = hostCard.getNamedCard(); } } Predicate<PaperCard> cpp = Predicates.compose( CardRulesPredicates.name(StringOp.EQUALS, name), PaperCard.FN_GET_RULES); cards = Lists.newArrayList(Iterables.filter(cards, cpp)); tgtCards.clear(); if (!cards.isEmpty()) { tgtCards.add(Card.fromPaperCard(cards.get(0), controller)); } } } hostCard.clearClones(); for (final Card c : tgtCards) { if ((tgt == null) || c.canBeTargetedBy(sa)) { int multiplier = numCopies * hostCard.getController().getTokenDoublersMagnitude(); final List<Card> crds = new ArrayList<Card>(multiplier); for (int i = 0; i < multiplier; i++) { final Card copy = CardFactory.copyCopiableCharacteristics(c, sa.getActivatingPlayer()); copy.setToken(true); copy.setCopiedPermanent(c); CardFactory.copyCopiableAbilities(c, copy); // add keywords from sa for (final String kw : keywords) { copy.addIntrinsicKeyword(kw); } for (final String type : types) { copy.addType(type); } for (final String svar : svars) { String actualsVar = hostCard.getSVar(svar); String name = svar; if (actualsVar.startsWith("SVar:")) { actualsVar = actualsVar.split("SVar:")[1]; name = actualsVar.split(":")[0]; actualsVar = actualsVar.split(":")[1]; } copy.setSVar(name, actualsVar); } for (final String s : triggers) { final String actualTrigger = hostCard.getSVar(s); final Trigger parsedTrigger = TriggerHandler.parseTrigger(actualTrigger, copy, true); copy.addTrigger(parsedTrigger); } // Temporarily register triggers of an object created with CopyPermanent // game.getTriggerHandler().registerActiveTrigger(copy, false); final Card copyInPlay = game.getAction().moveToPlay(copy); // when copying something stolen: copyInPlay.setController(controller, 0); copyInPlay.setSetCode(c.getSetCode()); copyInPlay.setCloneOrigin(hostCard); sa.getHostCard().addClone(copyInPlay); crds.add(copyInPlay); if (sa.hasParam("RememberCopied")) { hostCard.addRemembered(copyInPlay); } if (sa.hasParam("Tapped")) { copyInPlay.setTapped(true); } if (sa.hasParam("CopyAttacking") && game.getPhaseHandler().inCombat()) { final String attacked = sa.getParam("CopyAttacking"); GameEntity defender; if ("True".equals(attacked)) { FCollectionView<GameEntity> defs = game.getCombat().getDefenders(); defender = c.getController() .getController() .chooseSingleEntityForEffect( defs, sa, "Choose which defender to attack with " + c, false); } else { defender = AbilityUtils.getDefinedPlayers(hostCard, sa.getParam("CopyAttacking"), sa).get(0); } game.getCombat().addAttacker(copyInPlay, defender); game.fireEvent(new GameEventCombatChanged()); } if (sa.hasParam("AttachedTo")) { CardCollectionView list = AbilityUtils.getDefinedCards(hostCard, sa.getParam("AttachedTo"), sa); if (list.isEmpty()) { list = copyInPlay.getController().getGame().getCardsIn(ZoneType.Battlefield); list = CardLists.getValidCards( list, sa.getParam("AttachedTo"), copyInPlay.getController(), copyInPlay); } if (!list.isEmpty()) { Card attachedTo = sa.getActivatingPlayer() .getController() .chooseSingleEntityForEffect( list, sa, copyInPlay + " - Select a card to attach to."); if (copyInPlay.isAura()) { if (attachedTo.canBeEnchantedBy(copyInPlay)) { copyInPlay.enchantEntity(attachedTo); } else { // can't enchant continue; } } else if (copyInPlay.isEquipment()) { // Equipment if (attachedTo.canBeEquippedBy(copyInPlay)) { copyInPlay.equipCard(attachedTo); } else { continue; } } else { // Fortification copyInPlay.fortifyCard(attachedTo); } } else { continue; } } } if (sa.hasParam("AtEOT")) { final String location = sa.getParam("AtEOT"); registerDelayedTrigger(sa, location, crds); } } // end canBeTargetedBy } // end foreach Card } // end resolve