예제 #1
0
  @Override
  public boolean chkAIDrawback(SpellAbility sa, Player ai) {

    final Card source = sa.getHostCard();

    final String numDefense = sa.hasParam("NumDef") ? sa.getParam("NumDef") : "";
    final String numAttack = sa.hasParam("NumAtt") ? sa.getParam("NumAtt") : "";

    int defense;
    if (numDefense.contains("X") && source.getSVar("X").equals("Count$xPaid")) {
      defense = Integer.parseInt(source.getSVar("PayX"));
    } else {
      defense = AbilityUtils.calculateAmount(sa.getHostCard(), numDefense, sa);
    }

    int attack;
    if (numAttack.contains("X") && source.getSVar("X").equals("Count$xPaid")) {
      if (source.getSVar("PayX").equals("")) {
        // X is not set yet
        final int xPay = ComputerUtilMana.determineLeftoverMana(sa.getRootAbility(), ai);
        source.setSVar("PayX", Integer.toString(xPay));
        attack = xPay;
      } else {
        attack = Integer.parseInt(source.getSVar("PayX"));
      }
    } else {
      attack = AbilityUtils.calculateAmount(sa.getHostCard(), numAttack, sa);
    }

    if ((sa.getTargetRestrictions() == null) || !sa.getTargetRestrictions().doesTarget()) {
      if (source.isCreature()) {
        if (!source.hasKeyword("Indestructible")
            && ((source.getNetDefense() + defense) <= source.getDamage())) {
          return false;
        }
        if ((source.getNetDefense() + defense) <= 0) {
          return false;
        }
      }
    } else {
      // Targeted
      if (!this.pumpTgtAI(ai, sa, defense, attack, false)) {
        return false;
      }
    }

    return true;
  } // pumpDrawbackAI()
예제 #2
0
  @Override
  public PaymentDecision visit(CostExiledMoveToGrave cost) {
    Integer c = cost.convertAmount();
    List<Card> chosen = new ArrayList<Card>();

    if (c == null) {
      c = AbilityUtils.calculateAmount(source, cost.getAmount(), ability);
    }

    List<Card> typeList = player.getGame().getCardsIn(ZoneType.Exile);

    typeList = CardLists.getValidCards(typeList, cost.getType().split(";"), player, source);

    if (typeList.size() < c) {
      return null;
    }

    CardLists.sortByPowerAsc(typeList);
    Collections.reverse(typeList);

    for (int i = 0; i < c; i++) {
      chosen.add(typeList.get(i));
    }

    return chosen.isEmpty() ? null : PaymentDecision.card(chosen);
  }
예제 #3
0
  @Override
  public PaymentDecision visit(CostUntapType cost) {
    final String amount = cost.getAmount();
    Integer c = cost.convertAmount();
    if (c == null) {
      final String sVar = ability.getSVar(amount);
      if (sVar.equals("XChoice")) {
        List<Card> typeList = player.getGame().getCardsIn(ZoneType.Battlefield);
        typeList =
            CardLists.getValidCards(
                typeList, cost.getType().split(";"), player, ability.getHostCard());
        if (!cost.canUntapSource) {
          typeList.remove(source);
        }
        typeList = CardLists.filter(typeList, Presets.TAPPED);
        c = typeList.size();
        source.setSVar("ChosenX", "Number$" + Integer.toString(c));
      } else {
        c = AbilityUtils.calculateAmount(source, amount, ability);
      }
    }

    List<Card> list =
        ComputerUtil.chooseUntapType(player, cost.getType(), source, cost.canUntapSource, c);

    if (list == null) {
      System.out.println("Couldn't find a valid card to untap for: " + source.getName());
      return null;
    }

    return PaymentDecision.card(list);
  }
예제 #4
0
  @Override
  public PaymentDecision visit(CostReveal cost) {

    final String type = cost.getType();
    List<Card> hand = new ArrayList<Card>(player.getCardsIn(ZoneType.Hand));

    if (cost.payCostFromSource()) {
      if (!hand.contains(source)) {
        return null;
      }
      return PaymentDecision.card(source);
    }

    if (cost.getType().equals("Hand"))
      return PaymentDecision.card(player.getCardsIn(ZoneType.Hand));

    if (cost.getType().equals("SameColor")) {
      return null;
    }

    hand = CardLists.getValidCards(hand, type.split(";"), player, source);
    Integer c = cost.convertAmount();
    if (c == null) {
      final String sVar = ability.getSVar(cost.getAmount());
      if (sVar.equals("XChoice")) {
        c = hand.size();
      } else {
        c = AbilityUtils.calculateAmount(source, cost.getAmount(), ability);
      }
    }

    final AiController aic = ((PlayerControllerAi) player.getController()).getAi();
    return PaymentDecision.card(aic.getCardsToDiscard(c, type.split(";"), ability));
  }
예제 #5
0
  @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);
          }
        }
      }
    }
  }
예제 #6
0
  @Override
  public PaymentDecision visit(CostSacrifice cost) {
    if (cost.payCostFromSource()) {
      return PaymentDecision.card(source);
    }
    if (cost.getAmount().equals("All")) {
      /*List<Card> typeList = new ArrayList<Card>(activator.getCardsIn(ZoneType.Battlefield));
      typeList = CardLists.getValidCards(typeList, cost.getType().split(";"), activator, source);
      if (activator.hasKeyword("You can't sacrifice creatures to cast spells or activate abilities.")) {
          typeList = CardLists.getNotType(typeList, "Creature");
      }*/
      // Does the AI want to use Sacrifice All?
      return null;
    }

    Integer c = cost.convertAmount();
    if (c == null) {
      if (ability.getSVar(cost.getAmount()).equals("XChoice")) {
        return null;
      }

      c = AbilityUtils.calculateAmount(source, cost.getAmount(), ability);
    }
    List<Card> list =
        ComputerUtil.chooseSacrificeType(
            player, cost.getType(), source, ability.getTargetCard(), c);
    return PaymentDecision.card(list);
  }
예제 #7
0
  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;
  }
예제 #8
0
  @Override
  public PaymentDecision visit(CostTapType cost) {
    final String amount = cost.getAmount();
    Integer c = cost.convertAmount();
    if (c == null) {
      final String sVar = ability.getSVar(amount);
      if (sVar.equals("XChoice")) {
        List<Card> typeList =
            CardLists.getValidCards(
                player.getCardsIn(ZoneType.Battlefield),
                cost.getType().split(";"),
                ability.getActivatingPlayer(),
                ability.getHostCard());
        typeList = CardLists.filter(typeList, Presets.UNTAPPED);
        c = typeList.size();
        source.setSVar("ChosenX", "Number$" + Integer.toString(c));
      } else {
        c = AbilityUtils.calculateAmount(source, amount, ability);
      }
    }
    if (cost.getType().contains("sharesCreatureTypeWith")
        || cost.getType().contains("withTotalPowerGE")) {
      return null;
    }

    List<Card> totap =
        ComputerUtil.chooseTapType(player, cost.getType(), source, !cost.canTapSource, c);

    if (totap == null) {
      System.out.println("Couldn't find a valid card to tap for: " + source.getName());
      return null;
    }

    return PaymentDecision.card(totap);
  }
  /*
   * (non-Javadoc)
   *
   * @see
   * forge.card.cost.CostPart#canPay(forge.card.spellability.SpellAbility,
   * forge.Card, forge.Player, forge.card.cost.Cost)
   */
  @Override
  public final boolean canPay(final SpellAbility ability) {
    final Player activator = ability.getActivatingPlayer();
    final Card source = ability.getHostCard();
    CardCollectionView validCards = activator.getCardsIn(ZoneType.Battlefield);
    validCards = CardLists.getValidCards(validCards, this.getType().split(";"), activator, source);
    validCards =
        CardLists.filter(
            validCards,
            new Predicate<Card>() {
              @Override
              public boolean apply(final Card c) {
                return c.hasCounters();
              }
            });
    if (validCards.isEmpty()) {
      return false;
    }
    Integer i = this.convertAmount();

    if (i == null) {
      i = AbilityUtils.calculateAmount(source, this.getAmount(), ability);
    }
    int allCounters = 0;
    for (Card c : validCards) {
      final Map<CounterType, Integer> tgtCounters = c.getCounters();
      for (Integer value : tgtCounters.values()) {
        allCounters += value;
      }
    }

    return i <= allCounters;
  }
예제 #10
0
  @Override
  public PaymentDecision visit(CostGainControl cost) {
    if (cost.payCostFromSource()) return PaymentDecision.card(source);

    Integer c = cost.convertAmount();
    if (c == null) {
      c = AbilityUtils.calculateAmount(source, cost.getAmount(), ability);
    }

    final List<Card> typeList =
        CardLists.getValidCards(
            player.getGame().getCardsIn(ZoneType.Battlefield),
            cost.getType().split(";"),
            player,
            source);

    if (typeList.size() < c) {
      return null;
    }

    CardLists.sortByPowerAsc(typeList);
    final List<Card> res = new ArrayList<Card>();

    for (int i = 0; i < c; i++) {
      res.add(typeList.get(i));
    }
    return res.isEmpty() ? null : PaymentDecision.card(res);
  }
예제 #11
0
  @Override
  public PaymentDecision visit(CostExile cost) {
    if (cost.payCostFromSource()) {
      return PaymentDecision.card(source);
    }

    if (cost.getType().equals("All")) {
      return PaymentDecision.card(player.getCardsIn(cost.getFrom()));
    } else if (cost.getType().contains("FromTopGrave")) {
      return null;
    }

    Integer c = cost.convertAmount();
    if (c == null) {
      final String sVar = ability.getSVar(cost.getAmount());
      // Generalize cost
      if (sVar.equals("XChoice")) {
        return null;
      }
      c = AbilityUtils.calculateAmount(source, cost.getAmount(), ability);
    }

    if (cost.getFrom().equals(ZoneType.Library)) {
      return PaymentDecision.card(player.getCardsIn(ZoneType.Library, c));
    } else if (cost.sameZone) {
      // TODO Determine exile from same zone for AI
      return null;
    } else {
      List<Card> chosen =
          ComputerUtil.chooseExileFrom(
              player, cost.getFrom(), cost.getType(), source, ability.getTargetCard(), c);
      return null == chosen ? null : PaymentDecision.card(chosen);
    }
  }
예제 #12
0
  @Override
  public PaymentDecision visit(CostAddMana cost) {
    Integer c = cost.convertAmount();

    if (c == null) {
      c = AbilityUtils.calculateAmount(source, cost.getAmount(), ability);
    }

    return PaymentDecision.number(c);
  }
예제 #13
0
  @Override
  protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
    final Card source = sa.getHostCard();
    final String numDefense = sa.hasParam("NumDef") ? sa.getParam("NumDef") : "";
    final String numAttack = sa.hasParam("NumAtt") ? sa.getParam("NumAtt") : "";

    int defense;
    if (numDefense.contains("X") && source.getSVar("X").equals("Count$xPaid")) {
      // Set PayX here to maximum value.
      final int xPay = ComputerUtilMana.determineLeftoverMana(sa, ai);
      source.setSVar("PayX", Integer.toString(xPay));
      defense = xPay;
    } else {
      defense = AbilityUtils.calculateAmount(sa.getHostCard(), numDefense, sa);
    }

    int attack;
    if (numAttack.contains("X") && source.getSVar("X").equals("Count$xPaid")) {
      // Set PayX here to maximum value.
      final String toPay = source.getSVar("PayX");

      if (toPay.equals("")) {
        final int xPay = ComputerUtilMana.determineLeftoverMana(sa, ai);
        source.setSVar("PayX", Integer.toString(xPay));
        attack = xPay;
      } else {
        attack = Integer.parseInt(toPay);
      }
    } else {
      attack = AbilityUtils.calculateAmount(sa.getHostCard(), numAttack, sa);
    }

    if (sa.getTargetRestrictions() == null) {
      if (mandatory) {
        return true;
      }
    } else {
      return this.pumpTgtAI(ai, sa, defense, attack, mandatory);
    }

    return true;
  } // pumpTriggerAI
예제 #14
0
  @Override
  public PaymentDecision visit(CostRemoveCounter cost) {
    final String amount = cost.getAmount();
    Integer c = cost.convertAmount();
    final String type = cost.getType();

    if (c == null) {
      final String sVar = ability.getSVar(amount);
      if (sVar.equals("XChoice")) {
        c = AbilityUtils.calculateAmount(source, "ChosenX", ability);
      } else if (amount.equals("All")) {
        c = source.getCounters(cost.counter);
      } else {
        c = AbilityUtils.calculateAmount(source, amount, ability);
      }
    }

    if (!cost.payCostFromSource()) {
      List<Card> typeList;
      if (type.equals("OriginalHost")) {
        typeList = Lists.newArrayList(ability.getOriginalHost());
      } else {
        typeList =
            CardLists.getValidCards(player.getCardsIn(cost.zone), type.split(";"), player, source);
      }
      for (Card card : typeList) {
        if (card.getCounters(cost.counter) >= c) {
          return PaymentDecision.card(card, c);
        }
      }
      return null;
    }

    if (c > source.getCounters(cost.counter)) {
      System.out.println("Not enough " + cost.counter + " on " + source.getName());
      return null;
    }

    return PaymentDecision.card(source, c);
  }
예제 #15
0
  @Override
  public PaymentDecision visit(CostReturn cost) {
    if (cost.payCostFromSource()) return PaymentDecision.card(source);

    Integer c = cost.convertAmount();
    if (c == null) {
      c = AbilityUtils.calculateAmount(source, cost.getAmount(), ability);
    }

    List<Card> res =
        ComputerUtil.chooseReturnType(player, cost.getType(), source, ability.getTargetCard(), c);
    return res.isEmpty() ? null : PaymentDecision.card(res);
  }
예제 #16
0
 @Override
 public PaymentDecision visit(CostFlipCoin cost) {
   Integer c = cost.convertAmount();
   if (c == null) {
     final String sVar = ability.getSVar(cost.getAmount());
     // Generalize cost
     if (sVar.equals("XChoice")) {
       return null;
     }
     c = AbilityUtils.calculateAmount(source, cost.getAmount(), ability);
   }
   return PaymentDecision.number(c);
 }
예제 #17
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 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);
  }
예제 #18
0
  @Override
  public PaymentDecision visit(CostMill cost) {
    Integer c = cost.convertAmount();
    if (c == null) {
      final String sVar = ability.getSVar(cost.getAmount());
      // Generalize cost
      if (sVar.equals("XChoice")) {
        return null;
      }

      c = AbilityUtils.calculateAmount(source, cost.getAmount(), ability);
    }

    List<Card> topLib = player.getCardsIn(ZoneType.Library, c);
    return topLib.size() < c ? null : PaymentDecision.card(topLib);
  }
 @Override
 public boolean payAsDecided(Player ai, PaymentDecision decision, SpellAbility ability) {
   final String amount = this.getAmount();
   final Card source = ability.getHostCard();
   Integer c = this.convertAmount();
   if (c == null) {
     c = AbilityUtils.calculateAmount(source, amount, ability);
   }
   Card valid = decision.cards.get(0);
   counterType = decision.ct;
   for (int i = 0; i < c; i++) {
     executePayment(ability, valid);
   }
   source.setSVar("CostCountersRemoved", Integer.toString(c));
   return true;
 }
예제 #20
0
  /*
   * (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;
  }
예제 #21
0
 @Override
 public PaymentDecision visit(CostPayLife cost) {
   Integer c = cost.convertAmount();
   if (c == null) {
     final String sVar = ability.getSVar(cost.getAmount());
     // Generalize cost
     if (sVar.equals("XChoice")) {
       return null;
     } else {
       c = AbilityUtils.calculateAmount(source, cost.getAmount(), ability);
     }
   }
   if (!player.canPayLife(c)) {
     return null;
   }
   // activator.payLife(c, null);
   return PaymentDecision.number(c);
 }
예제 #22
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);
  }
예제 #23
0
  @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();
  }
예제 #24
0
  @Override
  public PaymentDecision visit(CostExileFromStack cost) {

    Integer c = cost.convertAmount();
    if (c == null) {
      final String sVar = ability.getSVar(cost.getAmount());
      // Generalize cost
      if (sVar.equals("XChoice")) {
        return null;
      }
      c = AbilityUtils.calculateAmount(source, cost.getAmount(), ability);
    }
    List<SpellAbility> chosen = new ArrayList<SpellAbility>();
    for (SpellAbilityStackInstance si : source.getGame().getStack()) {
      SpellAbility sp = si.getSpellAbility().getRootAbility();
      if (si.getSourceCard().isValid(cost.getType().split(";"), source.getController(), source)) {
        chosen.add(sp);
      }
    }
    return chosen.isEmpty() ? null : PaymentDecision.spellabilities(chosen);
  }
예제 #25
0
  @Override
  public PaymentDecision visit(CostDiscard cost) {
    final String type = cost.getType();

    final List<Card> hand = player.getCardsIn(ZoneType.Hand);
    if (type.equals("LastDrawn")) {
      if (!hand.contains(player.getLastDrawnCard())) {
        return null;
      }
      return PaymentDecision.card(player.getLastDrawnCard());
    } else if (cost.payCostFromSource()) {
      if (!hand.contains(source)) {
        return null;
      }

      return PaymentDecision.card(source);
    } else if (type.equals("Hand")) {
      return PaymentDecision.card(hand);
    }

    if (type.contains("WithSameName")) {
      return null;
    }
    Integer c = cost.convertAmount();
    if (c == null) {
      final String sVar = ability.getSVar(cost.getAmount());
      if (sVar.equals("XChoice")) {
        return null;
      }
      c = AbilityUtils.calculateAmount(source, cost.getAmount(), ability);
    }

    if (type.equals("Random")) {
      return PaymentDecision.card(CardLists.getRandomSubList(hand, c));
    } else {
      final AiController aic = ((PlayerControllerAi) player.getController()).getAi();
      return PaymentDecision.card(aic.getCardsToDiscard(c, type.split(";"), ability));
    }
  }
예제 #26
0
  @Override
  public PaymentDecision visit(CostRemoveAnyCounter cost) {
    final String amount = cost.getAmount();
    final int c = AbilityUtils.calculateAmount(source, amount, ability);
    final String type = cost.getType();

    List<Card> typeList =
        CardLists.getValidCards(
            player.getCardsIn(ZoneType.Battlefield), type.split(";"), player, source);
    List<Card> hperms =
        CardLists.filter(
            typeList,
            new Predicate<Card>() {
              @Override
              public boolean apply(final Card crd) {
                for (final CounterType c1 : CounterType.values()) {
                  if (crd.getCounters(c1) >= c && ComputerUtil.isNegativeCounter(c1, crd)) {
                    return true;
                  }
                }
                return false;
              }
            });

    if (hperms.isEmpty()) return null;

    PaymentDecision result = PaymentDecision.card(hperms);
    Card valid = hperms.get(0);
    for (CounterType c1 : valid.getCounters().keySet()) {
      if (valid.getCounters(c1) >= c && ComputerUtil.isNegativeCounter(c1, valid)) {
        result.ct = c1;
        break;
      }
    }
    // Only find cards with enough negative counters
    // TODO: add ai for Chisei, Heart of Oceans
    return result;
  }
예제 #27
0
  @Override
  protected String getStackDescription(SpellAbility sa) {

    final StringBuilder sb = new StringBuilder();
    final int numTurns =
        AbilityUtils.calculateAmount(sa.getHostCard(), sa.getParam("NumTurns"), sa);

    List<Player> tgtPlayers = getTargetPlayers(sa);

    for (final Player player : tgtPlayers) {
      sb.append(player).append(" ");
    }

    sb.append("takes ");
    sb.append(numTurns > 1 ? numTurns : "an");
    sb.append(" extra turn");

    if (numTurns > 1) {
      sb.append("s");
    }
    sb.append(" after this one.");
    return sb.toString();
  }
예제 #28
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()
예제 #29
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()
예제 #30
0
  @Override
  public void resolve(SpellAbility sa) {
    final Card card = sa.getHostCard();

    AbilityManaPart abMana = sa.getManaPart();

    // Spells are not undoable
    sa.setUndoable(sa.isAbility() && sa.isUndoable());

    final List<Player> tgtPlayers = getTargetPlayers(sa);
    final TargetRestrictions tgt = sa.getTargetRestrictions();
    final boolean optional = sa.hasParam("Optional");
    final Game game = sa.getActivatingPlayer().getGame();

    if (optional
        && !sa.getActivatingPlayer()
            .getController()
            .confirmAction(sa, null, "Do you want to add mana to your mana pool?")) {
      return;
    }
    if (abMana.isComboMana()) {
      for (Player p : tgtPlayers) {
        int amount =
            sa.hasParam("Amount")
                ? AbilityUtils.calculateAmount(card, sa.getParam("Amount"), sa)
                : 1;
        if (tgt == null || p.canBeTargetedBy(sa)) {
          Player activator = sa.getActivatingPlayer();
          // String colorsNeeded = abMana.getExpressChoice();
          String[] colorsProduced = abMana.getComboColors().split(" ");

          final StringBuilder choiceString = new StringBuilder();
          ColorSet colorOptions = null;
          if (!abMana.isAnyMana()) {
            colorOptions = ColorSet.fromNames(colorsProduced);
          } else {
            colorOptions = ColorSet.fromNames(MagicColor.Constant.ONLY_COLORS);
          }
          for (int nMana = 1; nMana <= amount; nMana++) {
            String choice = "";
            byte chosenColor =
                activator.getController().chooseColor("Select Mana to Produce", sa, colorOptions);
            if (chosenColor == 0)
              throw new RuntimeException(
                  "ManaEffect::resolve() /*combo mana*/ - "
                      + activator
                      + " color mana choice is empty for "
                      + card.getName());

            choice = MagicColor.toShortString(chosenColor);
            if (nMana != 1) {
              choiceString.append(" ");
            }
            choiceString.append(choice);
          }
          game.action.nofityOfValue(sa, card, activator + " picked " + choiceString, activator);
          abMana.setExpressChoice(choiceString.toString());
        }
      }
    } else if (abMana.isAnyMana()) {
      for (Player p : tgtPlayers) {
        if (tgt == null || p.canBeTargetedBy(sa)) {
          Player act = sa.getActivatingPlayer();
          // AI color choice is set in ComputerUtils so only human players need to make a choice

          String colorsNeeded = abMana.getExpressChoice();
          String choice = "";

          ColorSet colorMenu = null;
          byte mask = 0;
          // loop through colors to make menu
          for (int nChar = 0; nChar < colorsNeeded.length(); nChar++) {
            mask |= MagicColor.fromName(colorsNeeded.charAt(nChar));
          }
          colorMenu = mask == 0 ? ColorSet.ALL_COLORS : ColorSet.fromMask(mask);
          byte val = act.getController().chooseColor("Select Mana to Produce", sa, colorMenu);
          if (0 == val) {
            throw new RuntimeException(
                "ManaEffect::resolve() /*any mana*/ - "
                    + act
                    + " color mana choice is empty for "
                    + card.getName());
          }
          choice = MagicColor.toShortString(val);

          game.action.nofityOfValue(sa, card, act + " picked " + choice, act);
          abMana.setExpressChoice(choice);
        }
      }
    } else if (abMana.isSpecialMana()) {
      for (Player p : tgtPlayers) {
        if (tgt == null || p.canBeTargetedBy(sa)) {
          String type = abMana.getOrigProduced().split("Special ")[1];

          if (type.equals("EnchantedManaCost")) {
            Card enchanted = card.getEnchantingCard();
            if (enchanted == null) continue;

            StringBuilder sb = new StringBuilder();
            int generic = enchanted.getManaCost().getGenericCost();
            if (generic > 0) sb.append(generic);

            for (ManaCostShard s : enchanted.getManaCost()) {
              ColorSet cs = ColorSet.fromMask(s.getColorMask());
              if (cs.isColorless()) continue;
              sb.append(' ');
              if (cs.isMonoColor()) sb.append(MagicColor.toShortString(s.getColorMask()));
              else /* (cs.isMulticolor()) */ {
                byte chosenColor =
                    sa.getActivatingPlayer()
                        .getController()
                        .chooseColor("Choose a single color from " + s.toString(), sa, cs);
                sb.append(MagicColor.toShortString(chosenColor));
              }
            }
            abMana.setExpressChoice(sb.toString().trim());
          } else if (type.equals("LastNotedType")) {
            Mana manaType = (Mana) Iterables.getFirst(card.getRemembered(), null);
            if (manaType == null) {
              return;
            }
            String cs = manaType.toString();
            abMana.setExpressChoice(cs);
          }

          if (abMana.getExpressChoice().isEmpty()) {
            System.out.println(
                "AbilityFactoryMana::manaResolve() - special mana effect is empty for "
                    + sa.getHostCard().getName());
          }
        }
      }
    }

    for (final Player player : tgtPlayers) {
      abMana.produceMana(GameActionUtil.generatedMana(sa), player, sa);
    }

    // Only clear express choice after mana has been produced
    abMana.clearExpressChoice();

    // resolveDrawback(sa);
  }