/** {@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 resolve(SpellAbility sa) {
    CardCollectionView tgtCards;
    final Game game = sa.getActivatingPlayer().getGame();
    final Card source = sa.getHostCard();
    final boolean phaseInOrOut = sa.hasParam("PhaseInOrOutAll");

    if (sa.hasParam("AllValid")) {
      if (phaseInOrOut) {
        tgtCards = game.getCardsIncludePhasingIn(ZoneType.Battlefield);
      } else {
        tgtCards = game.getCardsIn(ZoneType.Battlefield);
      }
      tgtCards = AbilityUtils.filterListByType(tgtCards, sa.getParam("AllValid"), sa);
    } else if (sa.hasParam("Defined")) {
      tgtCards = AbilityUtils.getDefinedCards(source, sa.getParam("Defined"), sa);
    } else {
      tgtCards = getTargetCards(sa);
    }
    if (phaseInOrOut) { // Time and Tide
      for (final Card tgtC : tgtCards) {
        tgtC.phase();
      }
    } else { // just phase out
      for (final Card tgtC : tgtCards) {
        if (!tgtC.isPhasedOut()) {
          tgtC.phase();
        }
      }
    }
  }
  /* (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();
    }
  }
Exemple #5
0
 @Override
 public void selectButtonCancel() {
   // cancel auto pass for all players
   for (final Player player : game.getPlayers()) {
     player.getController().autoPassCancel();
   }
 }
  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;
  }
Exemple #7
0
  /* (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);
        }
      }
    }
  }
Exemple #8
0
  /** {@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()
Exemple #9
0
 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;
 }
Exemple #10
0
 public static RegisteredPlayer getGuiRegisteredPlayer(Game game) {
   LobbyPlayer guiPlayer = GuiBase.getInterface().getGuiPlayer();
   for (RegisteredPlayer player : game.getMatch().getPlayers()) {
     if (player.getPlayer() == guiPlayer) {
       return player;
     }
   }
   return null;
 }
Exemple #11
0
  @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
  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);
    }
  }
  private boolean phasesUnpreferredTargeting(
      final Game game, final SpellAbility sa, final boolean mandatory) {
    final Card source = sa.getHostCard();
    final TargetRestrictions tgt = sa.getTargetRestrictions();

    CardCollectionView list = game.getCardsIn(ZoneType.Battlefield);
    list =
        CardLists.getTargetableCards(
            CardLists.getValidCards(list, tgt.getValidTgts(), source.getController(), source), sa);

    return false;
  }
 // returns a List<Card> that is a subset of list with cards that share a name
 // with a permanent on the battlefield
 private static CardCollection sharesNameWithCardOnBattlefield(
     final Game game, final List<Card> list) {
   final CardCollection toReturn = new CardCollection();
   final CardCollectionView play = game.getCardsIn(ZoneType.Battlefield);
   for (final Card c : list) {
     for (final Card p : play) {
       if (p.getName().equals(c.getName()) && !toReturn.contains(c)) {
         toReturn.add(c);
       }
     }
   }
   return toReturn;
 }
Exemple #15
0
 @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;
 }
 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
 }
  /* (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
Exemple #18
0
  /* (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()
Exemple #19
0
  private boolean pumpMandatoryTarget(
      final Player ai, final SpellAbility sa, final boolean mandatory) {
    final Game game = ai.getGame();
    List<Card> list = game.getCardsIn(ZoneType.Battlefield);
    final TargetRestrictions tgt = sa.getTargetRestrictions();
    final Player opp = ai.getOpponent();
    list =
        CardLists.getValidCards(
            list, tgt.getValidTgts(), sa.getActivatingPlayer(), sa.getHostCard());
    list = CardLists.getTargetableCards(list, sa);

    if (list.size() < tgt.getMinTargets(sa.getHostCard(), sa)) {
      sa.resetTargets();
      return false;
    }

    // Remove anything that's already been targeted
    for (final Card c : sa.getTargets().getTargetCards()) {
      list.remove(c);
    }

    List<Card> pref;
    List<Card> forced;
    final Card source = sa.getHostCard();

    if (sa.isCurse()) {
      pref = CardLists.filterControlledBy(list, opp);
      forced = CardLists.filterControlledBy(list, ai);
    } else {
      pref = CardLists.filterControlledBy(list, ai);
      forced = CardLists.filterControlledBy(list, opp);
    }

    while (sa.getTargets().getNumTargeted() < tgt.getMaxTargets(source, sa)) {
      if (pref.isEmpty()) {
        break;
      }

      Card c;
      if (CardLists.getNotType(pref, "Creature").isEmpty()) {
        c = ComputerUtilCard.getBestCreatureAI(pref);
      } else {
        c = ComputerUtilCard.getMostExpensivePermanentAI(pref, sa, true);
      }

      pref.remove(c);

      sa.getTargets().add(c);
    }

    while (sa.getTargets().getNumTargeted() < tgt.getMinTargets(source, sa)) {
      if (forced.isEmpty()) {
        break;
      }

      Card c;
      if (CardLists.getNotType(forced, "Creature").isEmpty()) {
        c = ComputerUtilCard.getWorstCreatureAI(forced);
      } else {
        c = ComputerUtilCard.getCheapestPermanentAI(forced, sa, true);
      }

      forced.remove(c);

      sa.getTargets().add(c);
    }

    if (sa.getTargets().getNumTargeted() < tgt.getMinTargets(source, sa)) {
      sa.resetTargets();
      return false;
    }

    return true;
  } // pumpMandatoryTarget()
Exemple #20
0
  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
  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()
  @Override
  public void resolve(final SpellAbility sa) {
    final Card host = sa.getHostCard();
    final Map<String, String> svars = host.getSVars();

    // AF specific sa
    int power = -1;
    if (sa.hasParam("Power")) {
      power = AbilityUtils.calculateAmount(host, sa.getParam("Power"), sa);
    }
    int toughness = -1;
    if (sa.hasParam("Toughness")) {
      toughness = AbilityUtils.calculateAmount(host, sa.getParam("Toughness"), sa);
    }
    final Game game = sa.getActivatingPlayer().getGame();

    // Every Animate event needs a unique time stamp
    final long timestamp = game.getNextTimestamp();

    final boolean permanent = sa.hasParam("Permanent");

    final CardType types = new CardType();
    if (sa.hasParam("Types")) {
      types.addAll(Arrays.asList(sa.getParam("Types").split(",")));
    }

    final CardType removeTypes = new CardType();
    if (sa.hasParam("RemoveTypes")) {
      removeTypes.addAll(Arrays.asList(sa.getParam("RemoveTypes").split(",")));
    }

    // allow ChosenType - overrides anything else specified
    if (types.hasSubtype("ChosenType")) {
      types.clear();
      types.add(host.getChosenType());
    }

    final List<String> keywords = new ArrayList<String>();
    if (sa.hasParam("Keywords")) {
      keywords.addAll(Arrays.asList(sa.getParam("Keywords").split(" & ")));
    }

    final List<String> removeKeywords = new ArrayList<String>();
    if (sa.hasParam("RemoveKeywords")) {
      removeKeywords.addAll(Arrays.asList(sa.getParam("RemoveKeywords").split(" & ")));
    }

    final List<String> hiddenKeywords = new ArrayList<String>();
    if (sa.hasParam("HiddenKeywords")) {
      hiddenKeywords.addAll(Arrays.asList(sa.getParam("HiddenKeywords").split(" & ")));
    }
    // allow SVar substitution for keywords
    for (int i = 0; i < keywords.size(); i++) {
      final String k = keywords.get(i);
      if (svars.containsKey(k)) {
        keywords.add(svars.get(k));
        keywords.remove(k);
      }
    }

    // colors to be added or changed to
    String tmpDesc = "";
    if (sa.hasParam("Colors")) {
      final String colors = sa.getParam("Colors");
      if (colors.equals("ChosenColor")) {
        tmpDesc = CardUtil.getShortColorsString(host.getChosenColors());
      } else {
        tmpDesc =
            CardUtil.getShortColorsString(new ArrayList<String>(Arrays.asList(colors.split(","))));
      }
    }
    final String finalDesc = tmpDesc;

    // abilities to add to the animated being
    final List<String> abilities = new ArrayList<String>();
    if (sa.hasParam("Abilities")) {
      abilities.addAll(Arrays.asList(sa.getParam("Abilities").split(",")));
    }
    // replacement effects to add to the animated being
    final List<String> replacements = new ArrayList<String>();
    if (sa.hasParam("Replacements")) {
      replacements.addAll(Arrays.asList(sa.getParam("Replacements").split(",")));
    }
    // triggers to add to the animated being
    final List<String> triggers = new ArrayList<String>();
    if (sa.hasParam("Triggers")) {
      triggers.addAll(Arrays.asList(sa.getParam("Triggers").split(",")));
    }

    // sVars to add to the animated being
    final List<String> sVars = new ArrayList<String>();
    if (sa.hasParam("sVars")) {
      sVars.addAll(Arrays.asList(sa.getParam("sVars").split(",")));
    }

    String valid = "";

    if (sa.hasParam("ValidCards")) {
      valid = sa.getParam("ValidCards");
    }

    CardCollectionView list;
    List<Player> tgtPlayers = getTargetPlayers(sa);

    if (!sa.usesTargeting() && !sa.hasParam("Defined")) {
      list = game.getCardsIn(ZoneType.Battlefield);
    } else {
      list = tgtPlayers.get(0).getCardsIn(ZoneType.Battlefield);
    }

    list = CardLists.getValidCards(list, valid.split(","), host.getController(), host);

    for (final Card c : list) {
      doAnimate(
          c,
          sa,
          power,
          toughness,
          types,
          removeTypes,
          finalDesc,
          keywords,
          removeKeywords,
          hiddenKeywords,
          timestamp);

      // give abilities
      final List<SpellAbility> addedAbilities = new ArrayList<SpellAbility>();
      if (abilities.size() > 0) {
        for (final String s : abilities) {
          final String actualAbility = host.getSVar(s);
          final SpellAbility grantedAbility = AbilityFactory.getAbility(actualAbility, c);
          addedAbilities.add(grantedAbility);
          c.addSpellAbility(grantedAbility);
        }
      }

      // remove abilities
      final List<SpellAbility> removedAbilities = new ArrayList<SpellAbility>();
      if (sa.hasParam("OverwriteAbilities") || sa.hasParam("RemoveAllAbilities")) {
        for (final SpellAbility ab : c.getSpellAbilities()) {
          if (ab.isAbility()) {
            c.removeSpellAbility(ab);
            removedAbilities.add(ab);
          }
        }
      }
      // give replacement effects
      final List<ReplacementEffect> addedReplacements = new ArrayList<ReplacementEffect>();
      if (replacements.size() > 0) {
        for (final String s : replacements) {
          final String actualReplacement = host.getSVar(s);
          final ReplacementEffect parsedReplacement =
              ReplacementHandler.parseReplacement(actualReplacement, c, false);
          addedReplacements.add(c.addReplacementEffect(parsedReplacement));
        }
      }
      // Grant triggers
      final List<Trigger> addedTriggers = new ArrayList<Trigger>();
      if (triggers.size() > 0) {
        for (final String s : triggers) {
          final String actualTrigger = host.getSVar(s);
          final Trigger parsedTrigger = TriggerHandler.parseTrigger(actualTrigger, c, false);
          addedTriggers.add(c.addTrigger(parsedTrigger));
        }
      }

      // suppress triggers from the animated card
      final List<Trigger> removedTriggers = new ArrayList<Trigger>();
      if (sa.hasParam("OverwriteTriggers") || sa.hasParam("RemoveAllAbilities")) {
        final FCollectionView<Trigger> triggersToRemove = c.getTriggers();
        for (final Trigger trigger : triggersToRemove) {
          trigger.setSuppressed(true);
          removedTriggers.add(trigger);
        }
      }

      // suppress static abilities from the animated card
      final List<StaticAbility> removedStatics = new ArrayList<StaticAbility>();
      if (sa.hasParam("OverwriteStatics") || sa.hasParam("RemoveAllAbilities")) {
        final FCollectionView<StaticAbility> staticsToRemove = c.getStaticAbilities();
        for (final StaticAbility stAb : staticsToRemove) {
          stAb.setTemporarilySuppressed(true);
          removedStatics.add(stAb);
        }
      }

      // suppress static abilities from the animated card
      final List<ReplacementEffect> removedReplacements = new ArrayList<ReplacementEffect>();
      if (sa.hasParam("OverwriteReplacements") || sa.hasParam("RemoveAllAbilities")) {
        final FCollectionView<ReplacementEffect> replacementsToRemove = c.getReplacementEffects();
        for (final ReplacementEffect re : replacementsToRemove) {
          re.setTemporarilySuppressed(true);
          removedReplacements.add(re);
        }
      }

      // give sVars
      if (sVars.size() > 0) {
        for (final String s : sVars) {
          final String actualsVar = host.getSVar(s);
          c.setSVar(s, actualsVar);
        }
      }
      game.fireEvent(new GameEventCardStatsChanged(c));

      final GameCommand unanimate =
          new GameCommand() {
            private static final long serialVersionUID = -5861759814760561373L;

            @Override
            public void run() {
              doUnanimate(
                  c,
                  sa,
                  finalDesc,
                  hiddenKeywords,
                  addedAbilities,
                  addedTriggers,
                  addedReplacements,
                  false,
                  removedAbilities,
                  timestamp);

              // give back suppressed triggers
              for (final Trigger t : removedTriggers) {
                t.setSuppressed(false);
              }

              // give back suppressed static abilities
              for (final StaticAbility s : removedStatics) {
                s.setTemporarilySuppressed(false);
              }

              // give back suppressed replacement effects
              for (final ReplacementEffect re : removedReplacements) {
                re.setTemporarilySuppressed(false);
              }
              game.fireEvent(new GameEventCardStatsChanged(c));
            }
          };

      if (!permanent) {
        if (sa.hasParam("UntilEndOfCombat")) {
          game.getEndOfCombat().addUntil(unanimate);
        } else {
          game.getEndOfTurn().addUntil(unanimate);
        }
      }
    }
  } // animateAllResolve
  @Override
  public void resolve(SpellAbility sa) {
    final Card host = sa.getHostCard();
    final Player player = sa.getActivatingPlayer();
    final Game game = player.getGame();
    Player chooser = player;
    int numToDig = AbilityUtils.calculateAmount(host, sa.getParam("DigNum"), sa);

    final ZoneType srcZone =
        sa.hasParam("SourceZone")
            ? ZoneType.smartValueOf(sa.getParam("SourceZone"))
            : ZoneType.Library;

    final ZoneType destZone1 =
        sa.hasParam("DestinationZone")
            ? ZoneType.smartValueOf(sa.getParam("DestinationZone"))
            : ZoneType.Hand;
    final ZoneType destZone2 =
        sa.hasParam("DestinationZone2")
            ? ZoneType.smartValueOf(sa.getParam("DestinationZone2"))
            : ZoneType.Library;

    int libraryPosition =
        sa.hasParam("LibraryPosition") ? Integer.parseInt(sa.getParam("LibraryPosition")) : -1;
    int destZone1ChangeNum = 1;
    final boolean mitosis = sa.hasParam("Mitosis");
    String changeValid = sa.hasParam("ChangeValid") ? sa.getParam("ChangeValid") : "";
    // andOrValid is for cards with "creature card and/or a land card"
    String andOrValid = sa.hasParam("AndOrValid") ? sa.getParam("AndOrValid") : "";
    final boolean anyNumber = sa.hasParam("AnyNumber");

    final int libraryPosition2 =
        sa.hasParam("LibraryPosition2") ? Integer.parseInt(sa.getParam("LibraryPosition2")) : -1;
    final boolean optional = sa.hasParam("Optional");
    final boolean noMove = sa.hasParam("NoMove");
    final boolean skipReorder = sa.hasParam("SkipReorder");

    // A hack for cards like Explorer's Scope that need to ensure that a card is revealed to the
    // player activating the ability
    final boolean forceRevealToController = sa.hasParam("ForceRevealToController");

    // These parameters are used to indicate that a dialog box must be show to the player asking if
    // the player wants to proceed
    // with an optional ability, otherwise the optional ability is skipped.
    final boolean mayBeSkipped = sa.hasParam("PromptToSkipOptionalAbility");
    final String optionalAbilityPrompt =
        sa.hasParam("OptionalAbilityPrompt") ? sa.getParam("OptionalAbilityPrompt") : "";

    boolean changeAll = false;
    boolean allButOne = false;
    final List<String> keywords = new ArrayList<String>();
    if (sa.hasParam("Keywords")) {
      keywords.addAll(Arrays.asList(sa.getParam("Keywords").split(" & ")));
    }

    if (sa.hasParam("ChangeNum")) {
      if (sa.getParam("ChangeNum").equalsIgnoreCase("All")) {
        changeAll = true;
      } else if (sa.getParam("ChangeNum").equalsIgnoreCase("AllButOne")) {
        allButOne = true;
      } else {
        destZone1ChangeNum = AbilityUtils.calculateAmount(host, sa.getParam("ChangeNum"), sa);
      }
    }

    final TargetRestrictions tgt = sa.getTargetRestrictions();
    final List<Player> tgtPlayers = getTargetPlayers(sa);

    if (sa.hasParam("Choser")) {
      final List<Player> choosers =
          AbilityUtils.getDefinedPlayers(sa.getHostCard(), sa.getParam("Choser"), sa);
      if (!choosers.isEmpty()) {
        chooser = choosers.get(0);
      }
    }

    for (final Player p : tgtPlayers) {
      if (tgt != null && !p.canBeTargetedBy(sa)) {
        continue;
      }
      final CardCollection top = new CardCollection();
      final CardCollection rest = new CardCollection();
      final PlayerZone sourceZone = p.getZone(srcZone);

      numToDig = Math.min(numToDig, sourceZone.size());
      for (int i = 0; i < numToDig; i++) {
        top.add(sourceZone.get(i));
      }

      if (!top.isEmpty()) {
        DelayedReveal delayedReveal = null;
        boolean hasRevealed = true;
        if (sa.hasParam("Reveal")) {
          game.getAction().reveal(top, p, false);
        } else if (sa.hasParam("RevealOptional")) {
          String question = "Reveal: " + Lang.joinHomogenous(top) + "?";

          hasRevealed = p.getController().confirmAction(sa, null, question);
          if (hasRevealed) {
            game.getAction().reveal(top, p);
          }
        } else if (sa.hasParam("RevealValid")) {
          final String revealValid = sa.getParam("RevealValid");
          final CardCollection toReveal =
              CardLists.getValidCards(top, revealValid, host.getController(), host);
          if (!toReveal.isEmpty()) {
            game.getAction().reveal(toReveal, host.getController());
            if (sa.hasParam("RememberRevealed")) {
              for (final Card one : toReveal) {
                host.addRemembered(one);
              }
            }
          }
          // Singletons.getModel().getGameAction().revealToCopmuter(top.toArray());
          // - for when it exists
        } else if (!sa.hasParam("NoLooking")) {
          // show the user the revealed cards
          delayedReveal = new DelayedReveal(top, srcZone, PlayerView.get(p));

          if (noMove) {
            // Let the activating player see the cards even if they're not moved
            game.getAction().revealTo(top, player);
          }
        }

        if (sa.hasParam("RememberRevealed") && !sa.hasParam("RevealValid") && hasRevealed) {
          for (final Card one : top) {
            host.addRemembered(one);
          }
        }

        if (!noMove) {
          CardCollection movedCards;
          CardCollection andOrCards;
          for (final Card c : top) {
            rest.add(c);
          }
          CardCollection valid;
          if (mitosis) {
            valid = sharesNameWithCardOnBattlefield(game, top);
            andOrCards = new CardCollection();
          } else if (!changeValid.isEmpty()) {
            if (changeValid.contains("ChosenType")) {
              changeValid = changeValid.replace("ChosenType", host.getChosenType());
            }
            valid =
                CardLists.getValidCards(top, changeValid.split(","), host.getController(), host);
            if (!andOrValid.equals("")) {
              andOrCards =
                  CardLists.getValidCards(top, andOrValid.split(","), host.getController(), host);
              andOrCards.removeAll((Collection<?>) valid);
              valid.addAll(andOrCards);
            } else {
              andOrCards = new CardCollection();
            }
          } else {
            valid = top;
            andOrCards = new CardCollection();
          }

          if (forceRevealToController) {
            // Force revealing the card to the player activating the ability (e.g. Explorer's Scope)
            game.getAction().revealTo(top, player);
          }

          // Optional abilities that use a dialog box to prompt the user to skip the ability (e.g.
          // Explorer's Scope, Quest for Ula's Temple)
          if (optional && mayBeSkipped && !valid.isEmpty()) {
            String prompt =
                !optionalAbilityPrompt.isEmpty()
                    ? optionalAbilityPrompt
                    : "Would you like to proceed with the optional ability for "
                        + sa.getHostCard()
                        + "?\n\n("
                        + sa.getDescription()
                        + ")";
            if (!p.getController()
                .confirmAction(sa, null, prompt.replace("CARDNAME", sa.getHostCard().getName()))) {
              return;
            }
          }

          if (changeAll) {
            movedCards = new CardCollection(valid);
          } else if (sa.hasParam("RandomChange")) {
            int numChanging = Math.min(destZone1ChangeNum, valid.size());
            movedCards = CardLists.getRandomSubList(valid, numChanging);
          } else if (allButOne) {
            movedCards = new CardCollection(valid);
            String prompt;
            if (destZone2.equals(ZoneType.Library) && libraryPosition2 == 0) {
              prompt = "Choose a card to leave on top of {player's} library";
            } else {
              prompt = "Choose a card to leave in {player's} " + destZone2.name();
            }

            Card chosen =
                chooser
                    .getController()
                    .chooseSingleEntityForEffect(valid, delayedReveal, sa, prompt, false, p);
            movedCards.remove(chosen);
            if (sa.hasParam("RandomOrder")) {
              CardLists.shuffle(movedCards);
            }
          } else {
            int j = 0;
            String prompt;

            if (sa.hasParam("PrimaryPrompt")) {
              prompt = sa.getParam("PrimaryPrompt");
            } else {
              prompt = "Choose a card to put into " + destZone1.name();
              if (destZone1.equals(ZoneType.Library)) {
                if (libraryPosition == -1) {
                  prompt = "Choose a card to put on the bottom of {player's} library";
                } else if (libraryPosition == 0) {
                  prompt = "Choose a card to put on top of {player's} library";
                }
              }
            }

            movedCards = new CardCollection();
            while (j < destZone1ChangeNum || (anyNumber && j < numToDig)) {
              // let user get choice
              Card chosen = null;
              if (!valid.isEmpty()) {
                chosen =
                    chooser
                        .getController()
                        .chooseSingleEntityForEffect(
                            valid, delayedReveal, sa, prompt, anyNumber || optional, p);
              } else {
                chooser.getController().notifyOfValue(sa, null, "No valid cards");
              }

              if (chosen == null) {
                break;
              }

              movedCards.add(chosen);
              valid.remove(chosen);
              if (!andOrValid.equals("")) {
                andOrCards.remove(chosen);
                if (!chosen.isValid(andOrValid.split(","), host.getController(), host)) {
                  valid = new CardCollection(andOrCards);
                } else if (!chosen.isValid(changeValid.split(","), host.getController(), host)) {
                  valid.removeAll((Collection<?>) andOrCards);
                }
              }
              j++;
            }

            if (!changeValid.isEmpty()) {
              game.getAction()
                  .reveal(
                      movedCards,
                      chooser,
                      true,
                      chooser
                          + " picked "
                          + (movedCards.size() == 1 ? "this card" : "these cards")
                          + " from ");
            }
          }
          if (sa.hasParam("ForgetOtherRemembered")) {
            host.clearRemembered();
          }
          Collections.reverse(movedCards);
          for (Card c : movedCards) {
            final PlayerZone zone = c.getOwner().getZone(destZone1);

            if (zone.is(ZoneType.Library)
                || zone.is(ZoneType.PlanarDeck)
                || zone.is(ZoneType.SchemeDeck)) {
              if (libraryPosition == -1 || libraryPosition > zone.size()) {
                libraryPosition = zone.size();
              }
              c = game.getAction().moveTo(zone, c, libraryPosition);
            } else {
              c = game.getAction().moveTo(zone, c);
              if (destZone1.equals(ZoneType.Battlefield)) {
                for (final String kw : keywords) {
                  c.addExtrinsicKeyword(kw);
                }
                if (sa.hasParam("Tapped")) {
                  c.setTapped(true);
                }
              }
            }

            if (sa.hasParam("ExileFaceDown")) {
              c.setState(CardStateName.FaceDown, true);
            }
            if (sa.hasParam("Imprint")) {
              host.addImprintedCard(c);
            }
            if (sa.hasParam("ForgetOtherRemembered")) {
              host.clearRemembered();
            }
            if (sa.hasParam("RememberChanged")) {
              host.addRemembered(c);
            }
            rest.remove(c);
          }

          // now, move the rest to destZone2
          if (destZone2 == ZoneType.Library
              || destZone2 == ZoneType.PlanarDeck
              || destZone2 == ZoneType.SchemeDeck) {
            CardCollection afterOrder = rest;
            if (sa.hasParam("RestRandomOrder")) {
              CardLists.shuffle(afterOrder);
            } else if (!skipReorder && rest.size() > 1) {
              afterOrder =
                  (CardCollection) chooser.getController().orderMoveToZoneList(rest, destZone2);
            }
            if (libraryPosition2 != -1) {
              // Closest to top
              Collections.reverse(afterOrder);
            }
            for (final Card c : afterOrder) {
              if (destZone2 == ZoneType.Library) {
                game.getAction().moveToLibrary(c, libraryPosition2);
              } else {
                game.getAction().moveToVariantDeck(c, destZone2, libraryPosition2);
              }
            }
          } else {
            // just move them randomly
            for (int i = 0; i < rest.size(); i++) {
              Card c = rest.get(i);
              final PlayerZone toZone = c.getOwner().getZone(destZone2);
              c = game.getAction().moveTo(toZone, c);
              if (destZone2.equals(ZoneType.Battlefield) && !keywords.isEmpty()) {
                for (final String kw : keywords) {
                  c.addExtrinsicKeyword(kw);
                }
              }
            }
          }
        }
      }
    }
  }
  /** {@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.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 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);
        }
      }
    }
  }
Exemple #27
0
  @Override
  public void resolve(SpellAbility sa) {
    final Card host = sa.getHostCard();
    final Player player = sa.getActivatingPlayer();
    final Game game = player.getGame();

    // make list of creatures that controller has on Battlefield
    CardCollectionView choices = game.getCardsIn(ZoneType.Battlefield);
    choices = CardLists.getValidCards(choices, "Creature.YouCtrl", host.getController(), host);

    // if no creatures on battlefield, cannot encoded
    if (choices.isEmpty()) {
      return;
    }
    // Handle choice of whether or not to encoded

    final StringBuilder sb = new StringBuilder();
    sb.append("Do you want to exile " + host + " and encode it onto a creature you control?");
    if (!player.getController().confirmAction(sa, null, sb.toString())) {
      return;
    }

    // move host card to exile
    Card movedCard = game.getAction().moveTo(ZoneType.Exile, host);

    // choose a creature
    Card choice =
        player
            .getController()
            .chooseSingleEntityForEffect(
                choices, sa, "Choose a creature you control to encode ", true);

    if (choice == null) {
      return;
    }

    StringBuilder codeLog = new StringBuilder();
    codeLog.append("Encoding ").append(host.toString()).append(" to ").append(choice.toString());
    game.getGameLog().add(GameLogEntryType.STACK_RESOLVE, codeLog.toString());

    // store hostcard in encoded array
    choice.addEncodedCard(movedCard);

    // add trigger
    final int numEncoded = choice.getEncodedCards().size();
    final StringBuilder cipherTrigger = new StringBuilder();
    cipherTrigger
        .append(
            "Mode$ DamageDone | ValidSource$ Card.Self | ValidTarget$ Player | Execute$ PlayEncoded")
        .append(numEncoded);
    cipherTrigger.append(" | CombatDamage$ True | OptionalDecider$ You | TriggerDescription$ ");
    cipherTrigger.append(
        "Whenever CARDNAME deals combat damage to a player, its controller may cast a copy of ");
    cipherTrigger.append(movedCard).append(" without paying its mana cost.");
    final String abName = "PlayEncoded" + numEncoded;
    final String abString =
        "AB$ Play | Cost$ 0 | Encoded$ " + numEncoded + " | WithoutManaCost$ True | CopyCard$ True";
    final Trigger parsedTrigger =
        TriggerHandler.parseTrigger(cipherTrigger.toString(), choice, false);
    choice.addTrigger(parsedTrigger);
    choice.setSVar(abName, abString);
    return;
  }
  /* (non-Javadoc)
   * @see forge.card.abilityfactory.SpellAiLogic#canPlayAI(forge.game.player.Player, java.util.Map, forge.card.spellability.SpellAbility)
   */
  @Override
  protected boolean canPlayAI(final Player ai, SpellAbility sa) {
    // TODO: AI Support! Currently this is copied from AF ChooseCard.
    //       When implementing AI, I believe AI also needs to be made aware of the damage sources
    // chosen
    //       to be prevented (e.g. so the AI doesn't attack with a creature that will not deal any
    // damage
    //       to the player because a CoP was pre-activated on it - unless, of course, there's
    // another
    //       possible reason to attack with that creature).
    final Card host = sa.getHostCard();
    final Cost abCost = sa.getPayCosts();
    final Card source = sa.getHostCard();

    if (abCost != null) {
      // AI currently disabled for these costs
      if (!ComputerUtilCost.checkLifeCost(ai, abCost, source, 4, null)) {
        return false;
      }

      if (!ComputerUtilCost.checkDiscardCost(ai, abCost, source)) {
        return false;
      }

      if (!ComputerUtilCost.checkSacrificeCost(ai, abCost, source)) {
        return false;
      }

      if (!ComputerUtilCost.checkRemoveCounterCost(abCost, source)) {
        return false;
      }
    }

    final TargetRestrictions tgt = sa.getTargetRestrictions();
    if (tgt != null) {
      sa.resetTargets();
      if (sa.canTarget(ai.getOpponent())) {
        sa.getTargets().add(ai.getOpponent());
      } else {
        return false;
      }
    }
    if (sa.hasParam("AILogic")) {
      final Game game = ai.getGame();
      if (sa.getParam("AILogic").equals("NeedsPrevention")) {
        if (!game.getStack().isEmpty()) {
          final SpellAbility topStack = game.getStack().peekAbility();
          if (sa.hasParam("Choices")
              && !topStack.getHostCard().isValid(sa.getParam("Choices"), ai, source)) {
            return false;
          }
          final ApiType threatApi = topStack.getApi();
          if (threatApi != ApiType.DealDamage && threatApi != ApiType.DamageAll) {
            return false;
          }

          final Card threatSource = topStack.getHostCard();
          List<? extends GameObject> objects = getTargets(topStack);
          if (!topStack.usesTargeting()
              && topStack.hasParam("ValidPlayers")
              && !topStack.hasParam("Defined")) {
            objects =
                AbilityUtils.getDefinedPlayers(
                    threatSource, topStack.getParam("ValidPlayers"), topStack);
          }

          if (!objects.contains(ai) || topStack.hasParam("NoPrevention")) {
            return false;
          }
          int dmg =
              AbilityUtils.calculateAmount(threatSource, topStack.getParam("NumDmg"), topStack);
          if (ComputerUtilCombat.predictDamageTo(ai, dmg, threatSource, false) <= 0) {
            return false;
          }
          return true;
        }
        if (game.getPhaseHandler().getPhase() != PhaseType.COMBAT_DECLARE_BLOCKERS) {
          return false;
        }
        CardCollectionView choices = game.getCardsIn(ZoneType.Battlefield);
        if (sa.hasParam("Choices")) {
          choices =
              CardLists.getValidCards(choices, sa.getParam("Choices"), host.getController(), host);
        }
        final Combat combat = game.getCombat();
        choices =
            CardLists.filter(
                choices,
                new Predicate<Card>() {
                  @Override
                  public boolean apply(final Card c) {
                    if (combat == null || !combat.isAttacking(c, ai) || !combat.isUnblocked(c)) {
                      return false;
                    }
                    return ComputerUtilCombat.damageIfUnblocked(c, ai, combat, true) > 0;
                  }
                });
        if (choices.isEmpty()) {
          return false;
        }
      }
    }

    return true;
  }