/* * (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 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); } } } } }
/** {@inheritDoc} */ @Override public final void showMessage() { final Game game = player.getGame(); final StringBuilder sb = new StringBuilder(); if (startingPlayer == player) { sb.append(player).append(", you are going first!\n\n"); } else { sb.append(startingPlayer.getName()).append(" is going first.\n"); sb.append(player) .append(", you are going ") .append(Lang.getOrdinal(game.getPosition(player, startingPlayer))) .append(".\n\n"); } if (isCommander) { getController().getGui().updateButtons(getOwner(), "Keep", "Exile", true, false, true); sb.append( "Will you keep your hand or choose some cards to exile those and draw one less card?"); } else { getController().getGui().updateButtons(getOwner(), "Keep", "Mulligan", true, true, true); sb.append("Do you want to keep your hand?"); } showMessage(sb.toString()); }
@Override public void selectButtonCancel() { // cancel auto pass for all players for (final Player player : game.getPlayers()) { player.getController().autoPassCancel(); } }
/* (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); }
/* (non-Javadoc) * @see forge.card.abilityfactory.SpellAiLogic#canPlayAI(forge.game.player.Player, forge.card.spellability.SpellAbility) */ @Override protected boolean canPlayAI(Player aiPlayer, SpellAbility sa) { String logic = sa.getParam("AILogic"); Game game = aiPlayer.getGame(); if ("ZeroToughness".equals(logic)) { // If Creature has Zero Toughness, make sure some static ability is in play // That will grant a toughness bonus final List<Card> list = aiPlayer.getCardsIn(ZoneType.Battlefield); if (!Iterables.any( list, Predicates.or( CardPredicates.nameEquals("Glorious Anthem"), CardPredicates.nameEquals("Gaea's Anthem")))) { return false; } // TODO See if card ETB will survive after Static Effects /* List<Card> cards = game.getCardsIn(ZoneType.Battlefield); for(Card c : cards) { ArrayList<StaticAbility> statics = c.getStaticAbilities(); for(StaticAbility s : statics) { final Map<String, String> stabMap = s.parseParams(); if (!stabMap.get("Mode").equals("Continuous")) { continue; } final String affected = stabMap.get("Affected"); if (affected == null) { continue; } } } */ } // Wait for Main2 if possible if (game.getPhaseHandler().is(PhaseType.MAIN1) && !ComputerUtil.castPermanentInMain1(aiPlayer, sa)) { return false; } // AI shouldn't be retricted all that much for Creatures for now return true; }
@Override public void resolve(SpellAbility sa) { Game game = sa.getActivatingPlayer().getGame(); for (Player p : game.getPlayers()) { p.leaveCurrentPlane(); } if (sa.hasParam("Defined")) { CardCollectionView destinations = AbilityUtils.getDefinedCards(sa.getHostCard(), sa.getParam("Defined"), sa); sa.getActivatingPlayer().planeswalkTo(destinations); } else { sa.getActivatingPlayer().planeswalk(); } }
@Override public PaymentDecision visit(CostGainLife cost) { final List<Player> oppsThatCanGainLife = new ArrayList<Player>(); for (final Player opp : cost.getPotentialTargets(player, source)) { if (opp.canGainLife()) { oppsThatCanGainLife.add(opp); } } if (oppsThatCanGainLife.size() == 0) { return null; } return PaymentDecision.players(oppsThatCanGainLife); }
/* * (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 protected boolean allowAwaitNextInput() { return chosenSa == null && !player .getController() .mayAutoPass(); // don't allow awaiting next input if player chose to end the turn or if // a spell/ability is chosen }
/** {@inheritDoc} */ @Override public boolean canPlay() { final Card card = this.getHostCard(); Player activator = this.getActivatingPlayer(); if (activator == null) { activator = card.getController(); if (activator == null) { return false; } } final Game game = activator.getGame(); if (game.getStack().isSplitSecondOnStack()) { return false; } if (!(card.isInstant() || activator.canCastSorcery() || card.hasKeyword("Flash") || this.getRestrictions().isInstantSpeed() || activator.hasKeyword("You may cast nonland cards as though they had flash.") || card.hasStartOfKeyword("You may cast CARDNAME as though it had flash."))) { return false; } if (!this.getRestrictions().canPlay(card, this)) { return false; } // for uncastables like lotus bloom, check if manaCost is blank (except for morph spells) if (!isCastFaceDown() && isBasicSpell() && card.getState(card.isFaceDown() ? CardStateName.Original : card.getCurrentStateName()) .getManaCost() .isNoCost()) { return false; } if (this.getPayCosts() != null) { if (!CostPayment.canPayAdditionalCosts(this.getPayCosts(), this)) { return false; } } return checkOtherRestrictions(); } // canPlay()
@Override protected boolean canPlayAI(Player ai, SpellAbility sa) { final String svarName = sa.getParam("Execute"); final SpellAbility trigsa = AbilityFactory.getAbility(sa.getSVar(svarName), sa.getHostCard()); trigsa.setActivatingPlayer(ai); return AiPlayDecision.WillPlay == ((PlayerControllerAi) ai.getController()).getAi().canPlaySa(trigsa); }
public boolean checkOtherRestrictions() { final Card source = this.getHostCard(); Player activator = getActivatingPlayer(); final Game game = activator.getGame(); // CantBeCast static abilities final CardCollection allp = new CardCollection(game.getCardsIn(ZoneType.listValueOf("Battlefield,Command"))); allp.add(source); for (final Card ca : allp) { final FCollectionView<StaticAbility> staticAbilities = ca.getStaticAbilities(); for (final StaticAbility stAb : staticAbilities) { if (stAb.applyAbility("CantBeCast", source, activator)) { return false; } } } return true; }
@Override public Card chooseSingleCard( final Player aiChoser, SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer) { if ("NeedsPrevention".equals(sa.getParam("AILogic"))) { final Player ai = sa.getActivatingPlayer(); final Game game = ai.getGame(); if (!game.getStack().isEmpty()) { Card choseCard = chooseCardOnStack(sa, ai, game); if (choseCard != null) { return choseCard; } } final Combat combat = game.getCombat(); List<Card> permanentSources = CardLists.filter( options, new Predicate<Card>() { @Override public boolean apply(final Card c) { if (c == null || c.getZone() == null || c.getZone().getZoneType() != ZoneType.Battlefield || combat == null || !combat.isAttacking(c, ai) || !combat.isUnblocked(c)) { return false; } return ComputerUtilCombat.damageIfUnblocked(c, ai, combat, true) > 0; } }); return ComputerUtilCard.getBestCreatureAI(permanentSources); } else { return ComputerUtilCard.getBestAI(options); } }
@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(); }
/** {@inheritDoc} */ @Override public final void showMessage() { showMessage(getTurnPhasePriorityMessage(player.getGame())); chosenSa = null; if (getController() .canUndoLastAction()) { // allow undoing with cancel button if can undo last action ButtonUtil.update(getOwner(), "OK", "Undo", true, true, true); } else { // otherwise allow ending turn with cancel button ButtonUtil.update(getOwner(), "OK", "End Turn", true, true, true); } }
void setPlayerMayLook(Player p, boolean mayLook, boolean temp) { TrackableProperty prop = temp ? TrackableProperty.PlayerMayLookTemp : TrackableProperty.PlayerMayLook; TrackableCollection<PlayerView> col = get(prop); if (mayLook) { if (col == null) { col = new TrackableCollection<PlayerView>(p.getView()); set(prop, col); } else if (col.add(p.getView())) { flagAsChanged(prop); } } else if (col != null) { if (col.remove(p.getView())) { if (col.isEmpty()) { set(prop, null); } else { flagAsChanged(prop); } } } }
/* * (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(); if (!this.payCostFromSource()) { boolean needsAnnoucement = ability.hasParam("Announce") && this.getType().contains(ability.getParam("Announce")); CardCollectionView typeList = activator.getCardsIn(ZoneType.Battlefield); typeList = CardLists.getValidCards(typeList, this.getType().split(";"), activator, source); final Integer amount = this.convertAmount(); if (!needsAnnoucement && amount != null && typeList.size() < amount) { return false; } } else if (!source.isInPlay()) { return false; } return true; }
@Override public void resolve(SpellAbility sa) { final Card host = sa.getHostCard(); final String[] choices = sa.getParam("Choices").split(","); final List<SpellAbility> abilities = new ArrayList<SpellAbility>(); for (String s : choices) { abilities.add(AbilityFactory.getAbility(host.getSVar(s), host)); } final List<Player> tgtPlayers = getDefinedPlayersOrTargeted(sa); final TargetRestrictions tgt = sa.getTargetRestrictions(); for (final Player p : tgtPlayers) { if (tgt != null && !p.canBeTargetedBy(sa)) { continue; } int idxChosen = 0; String chosenName; if (sa.hasParam("AtRandom")) { idxChosen = MyRandom.getRandom().nextInt(choices.length); chosenName = choices[idxChosen]; } else { SpellAbility saChosen = p.getController().chooseSingleSpellForEffect(abilities, sa, "Choose one"); idxChosen = abilities.indexOf(saChosen); chosenName = choices[idxChosen]; } SpellAbility chosenSA = AbilityFactory.getAbility(host.getSVar(chosenName), host); if (sa.hasParam("ShowChoice")) { p.getGame() .getAction() .nofityOfValue(sa, p, abilities.get(idxChosen).getDescription(), null); } chosenSA.setActivatingPlayer(sa.getActivatingPlayer()); ((AbilitySub) chosenSA).setParent(sa); AbilityUtils.resolve(chosenSA); } }
@Override protected boolean onCardSelected(final Card card, final ITriggerEvent triggerEvent) { // remove unplayable unless triggerEvent specified, in which case unplayable may be shown as // disabled options List<SpellAbility> abilities = card.getAllPossibleAbilities(player, triggerEvent == null); if (abilities.isEmpty()) { flashIncorrectAction(); return false; } selectAbility(player.getController().getAbilityToPlay(abilities, triggerEvent)); return true; }
@Override protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) { final String svarName = sa.getParam("Execute"); final SpellAbility trigsa = AbilityFactory.getAbility(sa.getHostCard().getSVar(svarName), sa.getHostCard()); AiController aic = ((PlayerControllerAi) ai.getController()).getAi(); trigsa.setActivatingPlayer(ai); if (!sa.hasParam("OptionalDecider")) { return aic.doTrigger(trigsa, true); } else { return aic.doTrigger(trigsa, !sa.getParam("OptionalDecider").equals("You")); } }
protected boolean revealHandTargetAI(final Player ai, final SpellAbility sa) { final TargetRestrictions tgt = sa.getTargetRestrictions(); Player opp = ai.getOpponent(); final int humanHandSize = opp.getCardsIn(ZoneType.Hand).size(); if (tgt != null) { // ability is targeted sa.resetTargets(); final boolean canTgtHuman = opp.canBeTargetedBy(sa); if (!canTgtHuman || (humanHandSize == 0)) { return false; } else { sa.getTargets().add(opp); } } else { // if it's just defined, no big deal } return true; } // revealHandTargetAI()
private void passPriority(final Runnable runnable) { if (FModel.getPreferences().getPrefBoolean(FPref.UI_MANA_LOST_PROMPT)) { // if gui player has mana floating that will be lost if phase ended right now, prompt before // passing priority final Game game = player.getGame(); if (game.getStack().isEmpty()) { // phase can't end right now if stack isn't empty Player player = game.getPhaseHandler().getPriorityPlayer(); if (player != null && player.getManaPool().willManaBeLostAtEndOfPhase() && player.getLobbyPlayer() == GamePlayerUtil.getGuiPlayer()) { ThreadUtil.invokeInGameThread( new Runnable() { // must invoke in game thread so dialog can be shown on mobile game @Override public void run() { String message = "You have mana floating in your mana pool that could be lost if you pass priority now."; if (FModel.getPreferences().getPrefBoolean(FPref.UI_MANABURN)) { message += " You will take mana burn damage equal to the amount of floating mana lost this way."; } if (SOptionPane.showOptionDialog( message, "Mana Floating", SOptionPane.WARNING_ICON, new String[] {"OK", "Cancel"}) == 0) { runnable.run(); } } }); return; } } } runnable.run(); // just pass priority immediately if no mana floating that would be lost }
@Override public Player chooseSinglePlayer(Player ai, SpellAbility sa, Iterable<Player> choices) { Player chosen = null; if ("Curse".equals(sa.getParam("AILogic"))) { for (Player pc : choices) { if (pc.isOpponentOf(ai)) { chosen = pc; break; } } if (chosen == null) { chosen = Iterables.getFirst(choices, null); System.out.println("No good curse choices. Picking first available: " + chosen); } } else if ("Pump".equals(sa.getParam("AILogic"))) { chosen = Iterables.contains(choices, ai) ? ai : Iterables.getFirst(choices, null); } else if ("BestAllyBoardPosition".equals(sa.getParam("AILogic"))) { List<Player> prefChoices = Lists.newArrayList(choices); prefChoices.removeAll(ai.getOpponents()); if (!prefChoices.isEmpty()) { chosen = ComputerUtil.evaluateBoardPosition(prefChoices); } if (chosen == null) { chosen = Iterables.getFirst(choices, null); System.out.println("No good curse choices. Picking first available: " + chosen); } } else if ("MostCardsInHand".equals(sa.getParam("AILogic"))) { int cardsInHand = 0; for (final Player p : choices) { int hand = p.getCardsIn(ZoneType.Hand).size(); if (hand >= cardsInHand) { chosen = p; cardsInHand = hand; } } } else if ("LeastCreatures".equals(sa.getParam("AILogic"))) { int creats = 50; for (final Player p : choices) { int curr = p.getCreaturesInPlay().size(); if (curr <= creats) { chosen = p; creats = curr; } } } else { System.out.println("Default player choice logic."); chosen = Iterables.contains(choices, ai) ? ai : Iterables.getFirst(choices, null); } return chosen; }
@Override protected boolean onCardSelected( final Card c0, final List<Card> otherCardsToSelect, final ITriggerEvent triggerEvent) { // the only place that would cause troubles - input is supposed only to // confirm, not to fire abilities final boolean fromHand = player.getZone(ZoneType.Hand).contains(c0); final boolean isSerumPowder = c0.getName().equals("Serum Powder"); final boolean isLegalChoice = fromHand && (isCommander || isSerumPowder); if (!isLegalChoice || cardSelectLocked) { return false; } final CardView cView = c0.getView(); if (isSerumPowder && getController().getGui().confirm(cView, "Use " + cView + "'s ability?")) { cardSelectLocked = true; ThreadUtil.invokeInGameThread( new Runnable() { @Override public void run() { final CardCollection hand = new CardCollection(c0.getController().getCardsIn(ZoneType.Hand)); for (final Card c : hand) { player.getGame().getAction().exile(c); } c0.getController().drawCards(hand.size()); cardSelectLocked = false; } }); return true; } if (isCommander) { // allow to choose cards for partial paris if (selected.contains(c0)) { getController().getGui().setUsedToPay(c0.getView(), false); selected.remove(c0); } else { getController().getGui().setUsedToPay(c0.getView(), true); selected.add(c0); } getController() .getGui() .updateButtons(getOwner(), "Keep", "Exile", true, !selected.isEmpty(), true); } return true; }
@Override protected boolean doTriggerAINoCost(Player aiPlayer, SpellAbility sa, boolean mandatory) { final TargetRestrictions tgt = sa.getTargetRestrictions(); if (tgt == null) { return mandatory; } if (phasesPrefTargeting(tgt, sa, mandatory)) { return true; } else if (mandatory) { // not enough preferred targets, but mandatory so keep going: return phasesUnpreferredTargeting(aiPlayer.getGame(), sa, mandatory); } return false; }
@Override public boolean chkAIDrawback(SpellAbility sa, Player ai) { if ("Always".equals(sa.getParam("AILogic"))) { // TODO: improve ai return true; } final String svarName = sa.getParam("Execute"); final SpellAbility trigsa = AbilityFactory.getAbility(sa.getHostCard().getSVar(svarName), sa.getHostCard()); trigsa.setActivatingPlayer(ai); if (trigsa instanceof AbilitySub) { return SpellApiToAi.Converter.get(((AbilitySub) trigsa).getApi()) .chkDrawbackWithSubs(ai, (AbilitySub) trigsa); } else { return AiPlayDecision.WillPlay == ((PlayerControllerAi) ai.getController()).getAi().canPlaySa(trigsa); } }
@Override protected int evaluate(Player player, Game game) { if (player.getOutcome().hasWon()) { for (Player p : game.getRegisteredPlayers()) { if (p.isOpponentOf(player) && p.getOutcome().lossState == GameLossReason.CommanderDamage) { Integer damage = p.getCommanderDamage(player.getCommander()); if (damage != null && damage >= THRESHOLD) { return damage; } } } } return 0; }
boolean pumpAgainstRemoval(Player ai, SpellAbility sa) { final List<GameObject> objects = ComputerUtil.predictThreatenedObjects(sa.getActivatingPlayer(), sa); final List<Card> threatenedTargets = new ArrayList<Card>(); final TargetRestrictions tgt = sa.getTargetRestrictions(); if (tgt == null) { // For pumps without targeting restrictions, just return immediately until this is fleshed // out. return false; } List<Card> targetables = CardLists.getValidCards( ai.getCardsIn(ZoneType.Battlefield), tgt.getValidTgts(), ai, sa.getHostCard()); targetables = CardLists.getTargetableCards(targetables, sa); targetables = ComputerUtil.getSafeTargets(ai, sa, targetables); for (final Card c : targetables) { if (objects.contains(c)) { threatenedTargets.add(c); } } if (!threatenedTargets.isEmpty()) { ComputerUtilCard.sortByEvaluateCreature(threatenedTargets); for (Card c : threatenedTargets) { sa.getTargets().add(c); if (sa.getTargets().getNumTargeted() >= tgt.getMaxTargets(sa.getHostCard(), sa)) { break; } } if (sa.getTargets().getNumTargeted() > tgt.getMaxTargets(sa.getHostCard(), sa) || sa.getTargets().getNumTargeted() < tgt.getMinTargets(sa.getHostCard(), sa)) { sa.resetTargets(); return false; } return true; } return false; }
/* (non-Javadoc) * @see forge.card.ability.SpellAbilityEffect#resolve(forge.card.spellability.SpellAbility) */ @Override public void resolve(SpellAbility sa) { Player activator = sa.getActivatingPlayer(); Card source = sa.getHostCard(); Game game = activator.getGame(); String valid = sa.hasParam("Valid") ? sa.getParam("Valid") : "Card"; ZoneType zone = sa.hasParam("Zone") ? ZoneType.smartValueOf(sa.getParam("Zone")) : ZoneType.Battlefield; int min = Integer.MAX_VALUE; final FCollectionView<Player> players = game.getPlayersInTurnOrder(); final List<CardCollection> validCards = new ArrayList<CardCollection>(players.size()); for (int i = 0; i < players.size(); i++) { // Find the minimum of each Valid per player validCards.add( CardLists.getValidCards(players.get(i).getCardsIn(zone), valid, activator, source)); min = Math.min(min, validCards.get(i).size()); } for (int i = 0; i < players.size(); i++) { Player p = players.get(i); int numToBalance = validCards.get(i).size() - min; if (numToBalance == 0) { continue; } if (zone.equals(ZoneType.Hand)) { for (Card card : p.getController() .chooseCardsToDiscardFrom(p, sa, validCards.get(i), numToBalance, numToBalance)) { if (null == card) continue; p.discard(card, sa); } } else { // Battlefield // TODO: "can'e be sacrificed" for (Card card : p.getController() .choosePermanentsToSacrifice( sa, numToBalance, numToBalance, validCards.get(i), valid)) { if (null == card) continue; game.getAction().sacrifice(card, sa); } } } }