Example #1
0
  /**
   * Handles X mana costs and sets manaCostsToPay.
   *
   * @param game
   * @param noMana
   * @param controller
   * @return variableManaCost for posting to log later
   */
  protected VariableManaCost handleManaXCosts(Game game, boolean noMana, Player controller) {
    // 20121001 - 601.2b
    // If the spell has a variable cost that will be paid as it's being cast (such as an {X} in
    // its mana cost; see rule 107.3), the player announces the value of that variable.
    // TODO: Handle announcing other variable costs here like: RemoveVariableCountersSourceCost
    VariableManaCost variableManaCost = null;
    for (ManaCost cost : manaCostsToPay) {
      if (cost instanceof VariableManaCost) {
        variableManaCost = (VariableManaCost) cost;
        break; // only one VariableManCost per spell (or is it possible to have more?)
      }
    }
    if (variableManaCost != null) {
      int xValue;
      if (!variableManaCost.isPaid()) { // should only happen for human players
        if (!noMana) {
          xValue =
              controller.announceXMana(
                  variableManaCost.getMinX(),
                  variableManaCost.getMaxX(),
                  "Announce the value for " + variableManaCost.getText(),
                  game,
                  this);
          int amountMana = xValue * variableManaCost.getMultiplier();
          StringBuilder manaString = threadLocalBuilder.get();
          if (variableManaCost.getFilter() == null || variableManaCost.getFilter().isGeneric()) {
            manaString.append("{").append(amountMana).append("}");
          } else {
            String manaSymbol = null;
            if (variableManaCost.getFilter().isBlack()) {
              manaSymbol = "B";
            } else if (variableManaCost.getFilter().isRed()) {
              manaSymbol = "R";
            } else if (variableManaCost.getFilter().isBlue()) {
              manaSymbol = "U";
            } else if (variableManaCost.getFilter().isGreen()) {
              manaSymbol = "G";
            } else if (variableManaCost.getFilter().isWhite()) {
              manaSymbol = "W";
            }
            if (manaSymbol == null) {
              throw new UnsupportedOperationException(
                  "ManaFilter is not supported: " + this.toString());
            }
            for (int i = 0; i < amountMana; i++) {
              manaString.append("{").append(manaSymbol).append("}");
            }
          }
          manaCostsToPay.add(new ManaCostsImpl(manaString.toString()));
          manaCostsToPay.setX(amountMana);
        }
        variableManaCost.setPaid();
      }
    }

    return variableManaCost;
  }
Example #2
0
 @Override
 public boolean apply(Game game, Ability source) {
   Player you = game.getPlayer(source.getControllerId());
   ManaCosts cost = new ManaCostsImpl("{X}");
   if (you != null && you.chooseUse(Outcome.Neutral, "Do you want to to pay {X}?", game)) {
     int costX =
         you.announceXMana(0, Integer.MAX_VALUE, "Announce the value for {X}", game, source);
     cost.add(new GenericManaCost(costX));
     if (cost.pay(source, game, source.getId(), source.getControllerId(), false)) {
       Token token = new GoblinSoldierToken();
       return token.putOntoBattlefield(
           costX, game, source.getSourceId(), source.getControllerId());
     }
   }
   return false;
 }
Example #3
0
 @Override
 public int getConvertedManaCost() {
   if (manaCost != null) {
     return manaCost.convertedManaCost();
   }
   return 0;
 }
Example #4
0
  @Override
  public String getRule(boolean all) {
    StringBuilder sbRule = threadLocalBuilder.get();
    if (all || this.abilityType != AbilityType.SPELL) {
      if (manaCosts.size() > 0) {
        sbRule.append(manaCosts.getText());
      }
      if (costs.size() > 0) {
        if (sbRule.length() > 0) {
          sbRule.append(",");
        }
        sbRule.append(costs.getText());
      }
      if (sbRule.length() > 0) {
        sbRule.append(": ");
      }
    }

    String ruleStart = sbRule.toString();
    String text = getModes().getText();
    String rule;
    if (!text.isEmpty()) {
      if (ruleStart.length() > 1) {
        String end = ruleStart.substring(ruleStart.length() - 2).trim();
        if (end.isEmpty() || end.equals(":") || end.equals(".")) {
          rule = ruleStart + Character.toUpperCase(text.charAt(0)) + text.substring(1);
        } else {
          rule = ruleStart + text;
        }
      } else {
        rule = ruleStart + text;
      }
    } else {
      rule = ruleStart;
    }
    if (abilityWord != null) {
      rule =
          "<i>"
              + abilityWord
              + "</i> &mdash; "
              + Character.toUpperCase(rule.charAt(0))
              + rule.substring(1);
    }
    return rule;
  }
Example #5
0
 @Override
 public boolean apply(Game game, Ability source) {
   Player player = game.getPlayer(source.getControllerId());
   if (player != null) {
     ManaCosts cost = new ManaCostsImpl("{5}{R}{R}");
     if (player.chooseUse(Outcome.Damage, "Pay " + cost.getText() + "?", game)) {
       cost.clearPaid();
       if (cost.pay(source, game, source.getId(), source.getControllerId(), false)) {
         new UntapAllControllerEffect(new FilterAttackingCreature(), "").apply(game, source);
         game.getState()
             .getTurnMods()
             .add(new TurnMod(source.getControllerId(), Constants.TurnPhase.COMBAT, null, false));
         return true;
       }
     }
   }
   return false;
 }
Example #6
0
 private Choice buildChoice(ManaCosts manaCosts, ManaCosts manaCostsSpell) {
   Choice choice = new ChoiceImpl();
   String creatureCosts = manaCosts.getText();
   String spellCosts = manaCostsSpell.getText();
   if (creatureCosts.contains("B") && spellCosts.contains("B")) {
     choice.getChoices().add("Black");
   }
   if (creatureCosts.contains("U") && spellCosts.contains("U")) {
     choice.getChoices().add("Blue");
   }
   if (creatureCosts.contains("G") && spellCosts.contains("G")) {
     choice.getChoices().add("Green");
   }
   if (creatureCosts.contains("R") && spellCosts.contains("R")) {
     choice.getChoices().add("Red");
   }
   if (creatureCosts.contains("W") && spellCosts.contains("W")) {
     choice.getChoices().add("White");
   }
   return choice;
 }
  public SpellCostReductionSourceEffect(
      ManaCosts<ManaCost> manaCostsToReduce, Condition condition) {
    super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.REDUCE_COST);
    this.amount = 0;
    this.manaCostsToReduce = manaCostsToReduce;
    this.condition = condition;

    StringBuilder sb = new StringBuilder();
    sb.append("{this} costs ");
    for (String manaSymbol : manaCostsToReduce.getSymbols()) {
      sb.append(manaSymbol);
    }
    sb.append(" less to if ").append(this.condition.toString());
    this.staticText = sb.toString();
  }
  public SpellsCostReductionControllerEffect(
      FilterCard filter, ManaCosts<ManaCost> manaCostsToReduce) {
    super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.REDUCE_COST);
    this.filter = filter;
    this.amount = 0;
    this.manaCostsToReduce = manaCostsToReduce;
    this.upTo = false;

    StringBuilder sb = new StringBuilder();
    sb.append(filter.getMessage()).append(" you cast cost ");
    for (String manaSymbol : manaCostsToReduce.getSymbols()) {
      sb.append(manaSymbol);
    }
    sb.append(" less to cast. This effect reduces only the amount of colored mana you pay.");
    this.staticText = sb.toString();
  }
Example #9
0
  @Override
  public void adjustCosts(Ability ability, Game game) {
    Player player = game.getPlayer(controllerId);
    if (player == null || !(ability instanceof SpellAbility)) {
      return;
    }
    Target target = new TargetControlledCreaturePermanent(1, Integer.MAX_VALUE, filter, true);
    target.setTargetName("creatures to convoke");
    if (!target.canChoose(sourceId, controllerId, game)) {
      return;
    }
    if (player.chooseUse(Outcome.Detriment, "Convoke creatures?", game)) {
      player.chooseTarget(Outcome.Tap, target, ability, game);
      if (target.getTargets().size() > 0) {
        int adjCost = 0;
        for (UUID creatureId : target.getTargets()) {
          Permanent perm = game.getPermanent(creatureId);
          if (perm == null) {
            continue;
          }
          ManaCosts manaCostsCreature = perm.getSpellAbility().getManaCosts();
          if (manaCostsCreature != null
              && manaCostsCreature.convertedManaCost() > 0
              && perm.tap(game)) {
            Choice chooseManaType = buildChoice(manaCostsCreature, ability.getManaCostsToPay());
            if (chooseManaType.getChoices().size() > 0) {
              if (chooseManaType.getChoices().size() > 1) {
                chooseManaType.getChoices().add("Colorless");
                chooseManaType.setMessage("Choose mana color to reduce from " + perm.getName());
                while (!chooseManaType.isChosen()) {
                  player.choose(Outcome.Benefit, chooseManaType, game);
                }
              } else {
                chooseManaType.setChoice(chooseManaType.getChoices().iterator().next());
              }

              ManaCosts manaCostsToReduce = new ManaCostsImpl();
              if (chooseManaType.getChoice().equals("Black")) {
                manaCostsToReduce.load("{B}");
              }
              if (chooseManaType.getChoice().equals("Blue")) {
                manaCostsToReduce.load("{U}");
              }
              if (chooseManaType.getChoice().equals("Green")) {
                manaCostsToReduce.load("{G}");
              }
              if (chooseManaType.getChoice().equals("White")) {
                manaCostsToReduce.load("{W}");
              }
              if (chooseManaType.getChoice().equals("Red")) {
                manaCostsToReduce.load("{R}");
              }
              if (chooseManaType.getChoice().equals("Colorless")) {
                ++adjCost;
              }
              CardUtil.adjustCost((SpellAbility) ability, manaCostsToReduce);
            } else {
              ++adjCost;
            }
          }
        }
        this.getTargets().add(target);
        CardUtil.adjustCost((SpellAbility) ability, adjCost);
      }
    }
  }
Example #10
0
  @Override
  public boolean activate(Game game, boolean noMana) {
    Player controller = game.getPlayer(this.getControllerId());
    if (controller == null) {
      return false;
    }
    game.applyEffects();

    /* 20130201 - 601.2b
     * If the spell is modal the player announces the mode choice (see rule 700.2).
     */
    if (!getModes().choose(game, this)) {
      return false;
    }
    if (controller.isTestMode()) {
      if (!controller.addTargets(this, game)) {
        return false;
      }
    }

    getSourceObject(game);

    /* 20130201 - 601.2b
     * If the player wishes to splice any cards onto the spell (see rule 702.45), he
     * or she reveals those cards in his or her hand.
     */
    if (this.abilityType.equals(AbilityType.SPELL)) {
      game.getContinuousEffects().applySpliceEffects(this, game);
    }

    // if ability can be cast for no mana, clear the mana costs now, because additional mana costs
    // must be paid.
    // For Flashback ability can be set X before, so the X costs have to be restored for the
    // flashbacked ability
    if (noMana) {
      if (this.getManaCostsToPay().getVariableCosts().size() > 0) {
        int xValue = this.getManaCostsToPay().getX();
        this.getManaCostsToPay().clear();
        VariableManaCost xCosts = new VariableManaCost();
        xCosts.setAmount(xValue);
        this.getManaCostsToPay().add(xCosts);
      } else {
        this.getManaCostsToPay().clear();
      }
    }
    // 20130201 - 601.2b
    // If the spell has alternative or additional costs that will be paid as it's being cast such
    // as buyback, kicker, or convoke costs (see rules 117.8 and 117.9), the player announces his
    // or her intentions to pay any or all of those costs (see rule 601.2e).
    // A player can't apply two alternative methods of casting or two alternative costs to a single
    // spell.
    if (!activateAlternateOrAdditionalCosts(sourceObject, noMana, controller, game)) {
      if (getAbilityType().equals(AbilityType.SPELL)
          && ((SpellAbility) this)
              .getSpellAbilityType()
              .equals(SpellAbilityType.FACE_DOWN_CREATURE)) {
        return false;
      }
    }

    // 20121001 - 601.2b
    // If the spell has a variable cost that will be paid as it's being cast (such as an {X} in
    // its mana cost; see rule 107.3), the player announces the value of that variable.
    VariableManaCost variableManaCost = handleManaXCosts(game, noMana, controller);
    String announceString = handleOtherXCosts(game, controller);
    // For effects from cards like Void Winnower x costs have to be set
    if (this.getAbilityType().equals(AbilityType.SPELL)
        && game.replaceEvent(
            GameEvent.getEvent(
                GameEvent.EventType.CAST_SPELL_LATE, getId(), getSourceId(), getControllerId()),
            this)) {
      return false;
    }
    for (Mode mode : this.getModes().getSelectedModes()) {
      this.getModes().setActiveMode(mode);
      // 20121001 - 601.2c
      // 601.2c The player announces his or her choice of an appropriate player, object, or zone for
      // each target the spell requires. A spell may require some targets only if an alternative or
      // additional cost (such as a buyback or kicker cost), or a particular mode, was chosen for
      // it;
      // otherwise, the spell is cast as though it did not require those targets. If the spell has a
      // variable number of targets, the player announces how many targets he or she will choose
      // before
      // he or she announces those targets. The same target can't be chosen multiple times for any
      // one
      // instance of the word "target" on the spell. However, if the spell uses the word "target" in
      // multiple places, the same object, player, or zone can be chosen once for each instance of
      // the
      // word "target" (as long as it fits the targeting criteria). If any effects say that an
      // object
      // or player must be chosen as a target, the player chooses targets so that he or she obeys
      // the
      // maximum possible number of such effects without violating any rules or effects that say
      // that
      // an object or player can't be chosen as a target. The chosen players, objects, and/or zones
      // each become a target of that spell. (Any abilities that trigger when those players,
      // objects,
      // and/or zones become the target of a spell trigger at this point; they'll wait to be put on
      // the stack until the spell has finished being cast.)

      if (sourceObject != null
          && !this.getAbilityType()
              .equals(AbilityType.TRIGGERED)) { // triggered abilities check this already in
        // playerImpl.triggerAbility
        sourceObject.adjustTargets(this, game);
      }
      if (mode.getTargets().size() > 0
          && mode.getTargets()
                  .chooseTargets(
                      getEffects().get(0).getOutcome(), this.controllerId, this, noMana, game)
              == false) {
        if ((variableManaCost != null || announceString != null) && !game.isSimulation()) {
          game.informPlayer(
              controller,
              (sourceObject != null ? sourceObject.getIdName() : "")
                  + ": no valid targets with this value of X");
        }
        return false; // when activation of ability is canceled during target selection
      }
    } // end modes

    // TODO: Handle optionalCosts at the same time as already OptionalAdditionalSourceCosts are
    // handled.
    for (Cost cost : optionalCosts) {
      if (cost instanceof ManaCost) {
        cost.clearPaid();
        if (controller.chooseUse(
            Outcome.Benefit, "Pay optional cost " + cost.getText() + "?", this, game)) {
          manaCostsToPay.add((ManaCost) cost);
        }
      }
    }
    // 20100716 - 601.2e
    if (sourceObject != null) {
      sourceObject.adjustCosts(this, game);
      if (sourceObject instanceof Card) {
        for (Ability ability : ((Card) sourceObject).getAbilities(game)) {
          if (ability instanceof AdjustingSourceCosts) {
            ((AdjustingSourceCosts) ability).adjustCosts(this, game);
          }
        }
      } else {
        for (Ability ability : sourceObject.getAbilities()) {
          if (ability instanceof AdjustingSourceCosts) {
            ((AdjustingSourceCosts) ability).adjustCosts(this, game);
          }
        }
      }
    }

    // this is a hack to prevent mana abilities with mana costs from causing endless loops - pay
    // other costs first
    if (this instanceof ManaAbility
        && !costs.pay(this, game, sourceId, controllerId, noMana, null)) {
      logger.debug("activate mana ability failed - non mana costs");
      return false;
    }

    // 20101001 - 601.2e
    if (costModificationActive) {
      game.getContinuousEffects().costModification(this, game);
    } else {
      costModificationActive = true;
    }

    UUID activatorId = controllerId;
    if ((this instanceof ActivatedAbilityImpl)
        && ((ActivatedAbilityImpl) this).getActivatorId() != null) {
      activatorId = ((ActivatedAbilityImpl) this).getActivatorId();
    }

    // 20100716 - 601.2f  (noMana is not used here, because mana costs were cleared for this ability
    // before adding additional costs and applying cost modification effects)
    if (!manaCostsToPay.pay(this, game, sourceId, activatorId, false, null)) {
      return false; // cancel during mana payment
    }

    // 20100716 - 601.2g
    if (!costs.pay(this, game, sourceId, activatorId, noMana, null)) {
      logger.debug("activate failed - non mana costs");
      return false;
    }
    if (!game.isSimulation()) {
      // inform about x costs now, so canceled announcements are not shown in the log
      if (announceString != null) {
        game.informPlayers(announceString);
      }
      if (variableManaCost != null) {
        int xValue = getManaCostsToPay().getX();
        game.informPlayers(
            controller.getLogName()
                + " announces a value of "
                + xValue
                + " for "
                + variableManaCost.getText());
      }
    }
    activated = true;
    // fire if tapped for mana (may only fire now because else costs of ability itself can be payed
    // with mana of abilities that trigger for that event
    if (this.getAbilityType().equals(AbilityType.MANA)) {
      for (Cost cost : costs) {
        if (cost instanceof TapSourceCost) {
          Mana mana = null;
          Effect effect = getEffects().get(0);
          if (effect instanceof DynamicManaEffect) {
            mana = ((DynamicManaEffect) effect).getMana(game, this);
          } else if (effect instanceof BasicManaEffect) {
            mana = ((BasicManaEffect) effect).getMana(game, this);
          }
          if (mana != null
              && mana.getAny()
                  == 0) { // if mana == null or Any > 0 the event has to be fired in the mana effect
            // to know which mana was produced
            ManaEvent event =
                new ManaEvent(
                    GameEvent.EventType.TAPPED_FOR_MANA, sourceId, sourceId, controllerId, mana);
            if (!game.replaceEvent(event)) {
              game.fireEvent(event);
            }
          }
          break;
        }
      }
    }
    return true;
  }