mirror of
https://github.com/magefree/mage.git
synced 2025-12-26 13:32:06 -08:00
* Additional costs - added support of X costs on free cast (example: Kicker X, see Thieving Skydiver and Etali, Primal Storm combo);
* As an additional cost discard X cards - fixed wrong text (example: Channeled Force, Firestorm);
This commit is contained in:
parent
d62cf17422
commit
53aababd44
65 changed files with 483 additions and 417 deletions
|
|
@ -259,7 +259,7 @@ public abstract class AbilityImpl implements Ability {
|
|||
if (!this.getManaCostsToPay().getVariableCosts().isEmpty()) {
|
||||
int xValue = this.getManaCostsToPay().getX();
|
||||
this.getManaCostsToPay().clear();
|
||||
VariableManaCost xCosts = new VariableManaCost();
|
||||
VariableManaCost xCosts = new VariableManaCost(VariableCostType.ADDITIONAL);
|
||||
// no x events - rules from Unbound Flourishing:
|
||||
// - Spells with additional costs that include X won't be affected by Unbound Flourishing. X must be in the spell's mana cost.
|
||||
xCosts.setAmount(xValue, xValue, false);
|
||||
|
|
@ -555,9 +555,17 @@ public abstract class AbilityImpl implements Ability {
|
|||
* @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.
|
||||
// 20210723 - 601.2b
|
||||
// If the spell has alternative or additional costs that will
|
||||
// be paid as it’s being cast such as buyback or kicker costs (see rules 118.8 and 118.9),
|
||||
// the player announces their intentions to pay any or all of those costs (see rule 601.2f).
|
||||
// A player can’t apply two alternative methods of casting or two alternative costs to a
|
||||
// single spell. 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. If the value of that variable is defined in the text of the spell by a choice
|
||||
// that player would make later in the announcement or resolution of the spell, that player
|
||||
// makes that choice at this time instead of that later time.
|
||||
|
||||
// TODO: Handle announcing other variable costs here like: RemoveVariableCountersSourceCost
|
||||
VariableManaCost variableManaCost = null;
|
||||
for (ManaCost cost : manaCostsToPay) {
|
||||
|
|
@ -574,7 +582,7 @@ public abstract class AbilityImpl implements Ability {
|
|||
if (!variableManaCost.isPaid()) { // should only happen for human players
|
||||
int xValue;
|
||||
int xValueMultiplier = handleManaXMultiplier(game, 1);
|
||||
if (!noMana) {
|
||||
if (!noMana || variableManaCost.getCostType().canUseAnnounceOnFreeCast()) {
|
||||
xValue = controller.announceXMana(variableManaCost.getMinX(), variableManaCost.getMaxX(), xValueMultiplier,
|
||||
"Announce the value for " + variableManaCost.getText(), game, this);
|
||||
int amountMana = xValue * variableManaCost.getXInstancesCount();
|
||||
|
|
|
|||
|
|
@ -1,8 +1,11 @@
|
|||
package mage.abilities.costs;
|
||||
|
||||
import mage.util.Copyable;
|
||||
|
||||
/**
|
||||
* Virtual optional/additional cost, it must be tranformed to simple cost on resolve in your custom ability.
|
||||
* Don't forget to set up cost type for variable costs
|
||||
* <p>
|
||||
* Example: KickerAbility.
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public interface OptionalAdditionalCost extends Cost {
|
||||
|
|
@ -77,6 +80,13 @@ public interface OptionalAdditionalCost extends Cost {
|
|||
*/
|
||||
int getActivateCount();
|
||||
|
||||
/**
|
||||
* Set cost type to variable costs like additional or normal (example: Kicker)
|
||||
*
|
||||
* @param costType
|
||||
*/
|
||||
void setCostType(VariableCostType costType);
|
||||
|
||||
@Override
|
||||
OptionalAdditionalCost copy();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -166,6 +166,12 @@ public class OptionalAdditionalCostImpl extends CostsImpl<Cost> implements Optio
|
|||
return activatedCounter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCostType(VariableCostType costType) {
|
||||
this.getVariableCosts().forEach(cost -> {
|
||||
cost.setCostType(costType);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public OptionalAdditionalCostImpl copy() {
|
||||
|
|
|
|||
|
|
@ -13,6 +13,12 @@ import mage.game.Game;
|
|||
*/
|
||||
public interface OptionalAdditionalSourceCosts {
|
||||
|
||||
/**
|
||||
* Warning, don't forget to set up cost type for costs, it can help with X announce
|
||||
*
|
||||
* @param ability
|
||||
* @param game
|
||||
*/
|
||||
// TODO: add AI support to use buyback, replicate and other additional costs (current version can't calc available mana before buyback use)
|
||||
void addOptionalAdditionalCosts(Ability ability, Game game);
|
||||
|
||||
|
|
|
|||
|
|
@ -64,4 +64,8 @@ public interface VariableCost {
|
|||
* @return
|
||||
*/
|
||||
Cost getFixedCostsFromAnnouncedValue(int xValue);
|
||||
|
||||
VariableCostType getCostType();
|
||||
|
||||
void setCostType(VariableCostType costType);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import java.util.UUID;
|
|||
public abstract class VariableCostImpl implements Cost, VariableCost {
|
||||
|
||||
protected UUID id;
|
||||
protected VariableCostType costType;
|
||||
protected String text;
|
||||
protected boolean paid;
|
||||
protected Targets targets;
|
||||
|
|
@ -23,8 +24,8 @@ public abstract class VariableCostImpl implements Cost, VariableCost {
|
|||
protected String xText;
|
||||
protected String actionText;
|
||||
|
||||
public VariableCostImpl(String actionText) {
|
||||
this("X", actionText);
|
||||
public VariableCostImpl(VariableCostType costType, String actionText) {
|
||||
this(costType, "X", actionText);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -32,17 +33,19 @@ public abstract class VariableCostImpl implements Cost, VariableCost {
|
|||
* @param actionText what happens with the value (e.g. "to tap", "to exile
|
||||
* from your graveyard")
|
||||
*/
|
||||
public VariableCostImpl(String xText, String actionText) {
|
||||
id = UUID.randomUUID();
|
||||
paid = false;
|
||||
targets = new Targets();
|
||||
amountPaid = 0;
|
||||
public VariableCostImpl(VariableCostType costType, String xText, String actionText) {
|
||||
this.id = UUID.randomUUID();
|
||||
this.costType = costType;
|
||||
this.paid = false;
|
||||
this.targets = new Targets();
|
||||
this.amountPaid = 0;
|
||||
this.xText = xText;
|
||||
this.actionText = actionText;
|
||||
}
|
||||
|
||||
public VariableCostImpl(final VariableCostImpl cost) {
|
||||
this.id = cost.id;
|
||||
this.costType = cost.costType;
|
||||
this.text = cost.text;
|
||||
this.paid = cost.paid;
|
||||
this.targets = cost.targets.copy();
|
||||
|
|
@ -107,14 +110,12 @@ public abstract class VariableCostImpl implements Cost, VariableCost {
|
|||
public boolean canPay(Ability ability, Ability source, UUID controllerId, Game game) {
|
||||
return true;
|
||||
/* not used */
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean pay(Ability ability, Game game, Ability source, UUID controllerId, boolean noMana, Cost costToPay) {
|
||||
return true;
|
||||
/* not used */
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -150,4 +151,14 @@ public abstract class VariableCostImpl implements Cost, VariableCost {
|
|||
}
|
||||
return xValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VariableCostType getCostType() {
|
||||
return this.costType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCostType(VariableCostType costType) {
|
||||
this.costType = costType;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,24 @@
|
|||
package mage.abilities.costs;
|
||||
|
||||
/**
|
||||
* See rules 601.2b
|
||||
*
|
||||
* @author JayDi85
|
||||
*/
|
||||
public enum VariableCostType {
|
||||
|
||||
NORMAL(false),
|
||||
ALTERNATIVE(false),
|
||||
ADDITIONAL(true);
|
||||
|
||||
// allows announcing X value on free cast (noMana) for additional costs, example: Kicker X
|
||||
private final boolean canUseAnnounceOnFreeCast;
|
||||
|
||||
VariableCostType(boolean canUseAnnounceOnFreeCast) {
|
||||
this.canUseAnnounceOnFreeCast = canUseAnnounceOnFreeCast;
|
||||
}
|
||||
|
||||
public boolean canUseAnnounceOnFreeCast() {
|
||||
return this.canUseAnnounceOnFreeCast;
|
||||
}
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@ package mage.abilities.costs.common;
|
|||
import mage.abilities.Ability;
|
||||
import mage.abilities.costs.Cost;
|
||||
import mage.abilities.costs.VariableCostImpl;
|
||||
import mage.abilities.costs.VariableCostType;
|
||||
import mage.filter.FilterCard;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
|
|
@ -19,9 +20,10 @@ public class DiscardXTargetCost extends VariableCostImpl {
|
|||
this(filter, false);
|
||||
}
|
||||
|
||||
public DiscardXTargetCost(FilterCard filter, boolean additionalCostText) {
|
||||
super(filter.getMessage() + " to discard");
|
||||
this.text = (additionalCostText ? "as an additional cost to cast this spell, discard " : "Discard ") + xText + ' ' + filter.getMessage();
|
||||
public DiscardXTargetCost(FilterCard filter, boolean useAsAdditionalCost) {
|
||||
super(useAsAdditionalCost ? VariableCostType.ADDITIONAL : VariableCostType.NORMAL,
|
||||
filter.getMessage() + " to discard");
|
||||
this.text = (useAsAdditionalCost ? "as an additional cost to cast this spell, discard " : "Discard ") + xText + ' ' + filter.getMessage();
|
||||
this.filter = filter;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package mage.abilities.costs.common;
|
|||
import mage.abilities.Ability;
|
||||
import mage.abilities.costs.Cost;
|
||||
import mage.abilities.costs.CostImpl;
|
||||
import mage.abilities.costs.VariableCostType;
|
||||
import mage.abilities.costs.mana.VariableManaCost;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.Cards;
|
||||
|
|
@ -23,7 +24,7 @@ import java.util.UUID;
|
|||
public class ExileFromHandCost extends CostImpl {
|
||||
|
||||
List<Card> cards = new ArrayList<>();
|
||||
private boolean setXFromCMC;
|
||||
private final boolean setXFromCMC;
|
||||
|
||||
public ExileFromHandCost(TargetCardInHand target) {
|
||||
this(target, false);
|
||||
|
|
@ -32,7 +33,7 @@ public class ExileFromHandCost extends CostImpl {
|
|||
/**
|
||||
* @param target
|
||||
* @param setXFromCMC the spells X value on the stack is set to the
|
||||
* converted mana costs of the exiled card
|
||||
* converted mana costs of the exiled card (alternative cost)
|
||||
*/
|
||||
public ExileFromHandCost(TargetCardInHand target, boolean setXFromCMC) {
|
||||
this.addTarget(target);
|
||||
|
|
@ -66,9 +67,10 @@ public class ExileFromHandCost extends CostImpl {
|
|||
player.moveCards(cardsToExile, Zone.EXILED, ability, game);
|
||||
paid = true;
|
||||
if (setXFromCMC) {
|
||||
VariableManaCost vmc = new VariableManaCost();
|
||||
VariableManaCost vmc = new VariableManaCost(VariableCostType.ALTERNATIVE);
|
||||
// no x events - rules from Unbound Flourishing:
|
||||
// - Spells with additional costs that include X won't be affected by Unbound Flourishing. X must be in the spell's mana cost.
|
||||
// TODO: wtf, look at setXFromCMC usage -- it used in cards with alternative costs, not additional... need to fix?
|
||||
vmc.setAmount(cmc, cmc, false);
|
||||
vmc.setPaid();
|
||||
ability.getManaCostsToPay().add(vmc);
|
||||
|
|
|
|||
|
|
@ -1,16 +1,15 @@
|
|||
|
||||
package mage.abilities.costs.common;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.costs.Cost;
|
||||
import mage.abilities.costs.VariableCostImpl;
|
||||
import mage.abilities.costs.VariableCostType;
|
||||
import mage.filter.FilterCard;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
import mage.target.common.TargetCardInYourGraveyard;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public class ExileXFromYourGraveCost extends VariableCostImpl {
|
||||
|
|
@ -21,10 +20,11 @@ public class ExileXFromYourGraveCost extends VariableCostImpl {
|
|||
this(filter, false);
|
||||
}
|
||||
|
||||
public ExileXFromYourGraveCost(FilterCard filter, boolean additionalCostText) {
|
||||
super(filter.getMessage() + " to exile");
|
||||
public ExileXFromYourGraveCost(FilterCard filter, boolean useAsAdditionalCost) {
|
||||
super(useAsAdditionalCost ? VariableCostType.ADDITIONAL : VariableCostType.NORMAL,
|
||||
filter.getMessage() + " to exile");
|
||||
this.filter = filter;
|
||||
this.text = (additionalCostText ? "as an additional cost to cast this spell, exile " : "Exile ") + xText + ' ' + filter.getMessage();
|
||||
this.text = (useAsAdditionalCost ? "as an additional cost to cast this spell, exile " : "Exile ") + xText + ' ' + filter.getMessage();
|
||||
}
|
||||
|
||||
public ExileXFromYourGraveCost(final ExileXFromYourGraveCost cost) {
|
||||
|
|
|
|||
|
|
@ -3,11 +3,11 @@ package mage.abilities.costs.common;
|
|||
import mage.abilities.Ability;
|
||||
import mage.abilities.costs.Cost;
|
||||
import mage.abilities.costs.VariableCostImpl;
|
||||
import mage.abilities.costs.VariableCostType;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public class PayVariableLifeCost extends VariableCostImpl {
|
||||
|
|
@ -16,9 +16,10 @@ public class PayVariableLifeCost extends VariableCostImpl {
|
|||
this(false);
|
||||
}
|
||||
|
||||
public PayVariableLifeCost(boolean additionalCostText) {
|
||||
super("life to pay");
|
||||
this.text = new StringBuilder(additionalCostText ? "as an additional cost to cast this spell, pay " : "Pay ")
|
||||
public PayVariableLifeCost(boolean useAsAdditionalCost) {
|
||||
super(useAsAdditionalCost ? VariableCostType.ADDITIONAL : VariableCostType.NORMAL,
|
||||
"life to pay");
|
||||
this.text = new StringBuilder(useAsAdditionalCost ? "as an additional cost to cast this spell, pay " : "Pay ")
|
||||
.append(xText).append(' ').append("life").toString();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import mage.abilities.Ability;
|
|||
import mage.abilities.LoyaltyAbility;
|
||||
import mage.abilities.costs.Cost;
|
||||
import mage.abilities.costs.VariableCostImpl;
|
||||
import mage.abilities.costs.VariableCostType;
|
||||
import mage.counters.CounterType;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
|
|
@ -24,7 +25,7 @@ public class PayVariableLoyaltyCost extends VariableCostImpl {
|
|||
private int costModification = 0;
|
||||
|
||||
public PayVariableLoyaltyCost() {
|
||||
super("loyality counters to remove");
|
||||
super(VariableCostType.NORMAL, "loyality counters to remove");
|
||||
this.text = "-X";
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,21 +1,20 @@
|
|||
|
||||
package mage.abilities.costs.common;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.costs.Cost;
|
||||
import mage.abilities.costs.VariableCostImpl;
|
||||
import mage.abilities.costs.VariableCostType;
|
||||
import mage.counters.Counter;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public class RemoveVariableCountersSourceCost extends VariableCostImpl {
|
||||
|
||||
protected int minimalCountersToPay = 0;
|
||||
private String counterName;
|
||||
private final String counterName;
|
||||
|
||||
public RemoveVariableCountersSourceCost(Counter counter) {
|
||||
this(counter, 0);
|
||||
|
|
@ -30,7 +29,7 @@ public class RemoveVariableCountersSourceCost extends VariableCostImpl {
|
|||
}
|
||||
|
||||
public RemoveVariableCountersSourceCost(Counter counter, int minimalCountersToPay, String text) {
|
||||
super(counter.getName() + " counters to remove");
|
||||
super(VariableCostType.NORMAL, counter.getName() + " counters to remove");
|
||||
this.minimalCountersToPay = minimalCountersToPay;
|
||||
this.counterName = counter.getName();
|
||||
if (text == null || text.isEmpty()) {
|
||||
|
|
|
|||
|
|
@ -1,11 +1,9 @@
|
|||
|
||||
|
||||
package mage.abilities.costs.common;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.costs.Cost;
|
||||
import mage.abilities.costs.VariableCostImpl;
|
||||
import mage.abilities.costs.VariableCostType;
|
||||
import mage.counters.Counter;
|
||||
import mage.counters.CounterType;
|
||||
import mage.filter.FilterPermanent;
|
||||
|
|
@ -13,11 +11,12 @@ import mage.game.Game;
|
|||
import mage.game.permanent.Permanent;
|
||||
import mage.target.TargetPermanent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX
|
||||
*/
|
||||
public class RemoveVariableCountersTargetCost extends VariableCostImpl {
|
||||
public class RemoveVariableCountersTargetCost extends VariableCostImpl {
|
||||
|
||||
protected FilterPermanent filter;
|
||||
protected CounterType counterTypeToRemove;
|
||||
|
|
@ -32,7 +31,7 @@ public class RemoveVariableCountersTargetCost extends VariableCostImpl {
|
|||
}
|
||||
|
||||
public RemoveVariableCountersTargetCost(FilterPermanent filter, CounterType counterTypeToRemove, String xText, int minValue) {
|
||||
super(xText, new StringBuilder(counterTypeToRemove != null ? counterTypeToRemove.getName() + ' ' :"").append("counters to remove").toString());
|
||||
super(VariableCostType.NORMAL, xText, new StringBuilder(counterTypeToRemove != null ? counterTypeToRemove.getName() + ' ' : "").append("counters to remove").toString());
|
||||
this.filter = filter;
|
||||
this.counterTypeToRemove = counterTypeToRemove;
|
||||
this.text = setText();
|
||||
|
|
@ -67,11 +66,11 @@ public class RemoveVariableCountersTargetCost extends VariableCostImpl {
|
|||
@Override
|
||||
public int getMaxValue(Ability source, Game game) {
|
||||
int maxValue = 0;
|
||||
for (Permanent permanent :game.getBattlefield().getAllActivePermanents(filter, source.getControllerId(), game)) {
|
||||
for (Permanent permanent : game.getBattlefield().getAllActivePermanents(filter, source.getControllerId(), game)) {
|
||||
if (counterTypeToRemove != null) {
|
||||
maxValue += permanent.getCounters(game).getCount(counterTypeToRemove);
|
||||
} else {
|
||||
for(Counter counter :permanent.getCounters(game).values()){
|
||||
for (Counter counter : permanent.getCounters(game).values()) {
|
||||
maxValue += counter.getCount();
|
||||
}
|
||||
}
|
||||
|
|
@ -81,7 +80,7 @@ public class RemoveVariableCountersTargetCost extends VariableCostImpl {
|
|||
|
||||
@Override
|
||||
public Cost getFixedCostsFromAnnouncedValue(int xValue) {
|
||||
return new RemoveCounterCost(new TargetPermanent(minValue,Integer.MAX_VALUE, filter, true), counterTypeToRemove, xValue);
|
||||
return new RemoveCounterCost(new TargetPermanent(minValue, Integer.MAX_VALUE, filter, true), counterTypeToRemove, xValue);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,15 +1,14 @@
|
|||
|
||||
package mage.abilities.costs.common;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.costs.Cost;
|
||||
import mage.abilities.costs.VariableCostImpl;
|
||||
import mage.abilities.costs.VariableCostType;
|
||||
import mage.filter.common.FilterControlledPermanent;
|
||||
import mage.game.Game;
|
||||
import mage.target.common.TargetControlledPermanent;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public class SacrificeXTargetCost extends VariableCostImpl {
|
||||
|
|
@ -20,9 +19,10 @@ public class SacrificeXTargetCost extends VariableCostImpl {
|
|||
this(filter, false);
|
||||
}
|
||||
|
||||
public SacrificeXTargetCost(FilterControlledPermanent filter, boolean additionalCostText) {
|
||||
super(filter.getMessage() + " to sacrifice");
|
||||
this.text = (additionalCostText ? "as an additional cost to cast this spell, sacrifice " : "Sacrifice ") + xText + ' ' + filter.getMessage();
|
||||
public SacrificeXTargetCost(FilterControlledPermanent filter, boolean useAsAdditionalCost) {
|
||||
super(useAsAdditionalCost ? VariableCostType.ADDITIONAL : VariableCostType.NORMAL,
|
||||
filter.getMessage() + " to sacrifice");
|
||||
this.text = (useAsAdditionalCost ? "as an additional cost to cast this spell, sacrifice " : "Sacrifice ") + xText + ' ' + filter.getMessage();
|
||||
this.filter = filter;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,19 +1,17 @@
|
|||
|
||||
|
||||
package mage.abilities.costs.common;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.costs.Cost;
|
||||
import mage.abilities.costs.VariableCostImpl;
|
||||
import mage.abilities.costs.VariableCostType;
|
||||
import mage.filter.common.FilterControlledPermanent;
|
||||
import mage.game.Game;
|
||||
import mage.target.common.TargetControlledPermanent;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
*/
|
||||
public class TapVariableTargetCost extends VariableCostImpl {
|
||||
public class TapVariableTargetCost extends VariableCostImpl {
|
||||
|
||||
protected FilterControlledPermanent filter;
|
||||
|
||||
|
|
@ -21,10 +19,11 @@ public class TapVariableTargetCost extends VariableCostImpl {
|
|||
this(filter, false, "X");
|
||||
}
|
||||
|
||||
public TapVariableTargetCost(FilterControlledPermanent filter, boolean additionalCostText, String xText) {
|
||||
super(xText, new StringBuilder(filter.getMessage()).append(" to tap").toString());
|
||||
public TapVariableTargetCost(FilterControlledPermanent filter, boolean useAsAdditionalCost, String xText) {
|
||||
super(useAsAdditionalCost ? VariableCostType.ADDITIONAL : VariableCostType.NORMAL,
|
||||
xText, new StringBuilder(filter.getMessage()).append(" to tap").toString());
|
||||
this.filter = filter;
|
||||
this.text = new StringBuilder(additionalCostText ? "as an additional cost to cast this spell, tap ":"Tap ")
|
||||
this.text = new StringBuilder(useAsAdditionalCost ? "as an additional cost to cast this spell, tap " : "Tap ")
|
||||
.append(this.xText).append(' ').append(filter.getMessage()).toString();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,10 +2,7 @@ package mage.abilities.costs.mana;
|
|||
|
||||
import mage.Mana;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.costs.Cost;
|
||||
import mage.abilities.costs.Costs;
|
||||
import mage.abilities.costs.CostsImpl;
|
||||
import mage.abilities.costs.VariableCost;
|
||||
import mage.abilities.costs.*;
|
||||
import mage.abilities.costs.common.PayLifeCost;
|
||||
import mage.abilities.mana.ManaOptions;
|
||||
import mage.constants.ColoredManaSymbol;
|
||||
|
|
@ -32,7 +29,7 @@ public class ManaCostsImpl<T extends ManaCost> extends ArrayList<T> implements M
|
|||
protected final UUID id;
|
||||
protected String text = null;
|
||||
|
||||
private static Map<String, ManaCosts> costsCache = new ConcurrentHashMap<>(); // must be thread safe, can't use nulls
|
||||
private static final Map<String, ManaCosts> costsCache = new ConcurrentHashMap<>(); // must be thread safe, can't use nulls
|
||||
|
||||
public ManaCostsImpl() {
|
||||
this.id = UUID.randomUUID();
|
||||
|
|
@ -471,7 +468,7 @@ public class ManaCostsImpl<T extends ManaCost> extends ArrayList<T> implements M
|
|||
modifierForX++;
|
||||
}
|
||||
}
|
||||
this.add(new VariableManaCost(modifierForX));
|
||||
this.add(new VariableManaCost(VariableCostType.NORMAL, modifierForX));
|
||||
} //TODO: handle multiple {X} and/or {Y} symbols
|
||||
} else if (Character.isDigit(symbol.charAt(0))) {
|
||||
MonoHybridManaCost cost;
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import mage.Mana;
|
|||
import mage.abilities.Ability;
|
||||
import mage.abilities.costs.Cost;
|
||||
import mage.abilities.costs.VariableCost;
|
||||
import mage.abilities.costs.VariableCostType;
|
||||
import mage.constants.ColoredManaSymbol;
|
||||
import mage.filter.FilterMana;
|
||||
import mage.game.Game;
|
||||
|
|
@ -18,6 +19,7 @@ public final class VariableManaCost extends ManaCostImpl implements VariableCost
|
|||
// 1. as X value in spell/ability cast (announce X, set VariableManaCost as paid and add generic mana to pay instead)
|
||||
// 2. as X value in direct pay (X already announced, cost is unpaid, need direct pay)
|
||||
|
||||
protected VariableCostType costType;
|
||||
protected int xInstancesCount; // number of {X} instances in cost like {X} or {X}{X}
|
||||
protected int xValue = 0; // final X value after announce and replace events
|
||||
protected int xPay = 0; // final/total need pay after announce and replace events (example: {X}{X}, X=3, xPay = 6)
|
||||
|
|
@ -27,11 +29,12 @@ public final class VariableManaCost extends ManaCostImpl implements VariableCost
|
|||
protected int minX = 0;
|
||||
protected int maxX = Integer.MAX_VALUE;
|
||||
|
||||
public VariableManaCost() {
|
||||
this(1);
|
||||
public VariableManaCost(VariableCostType costType) {
|
||||
this(costType, 1);
|
||||
}
|
||||
|
||||
public VariableManaCost(int xInstancesCount) {
|
||||
public VariableManaCost(VariableCostType costType, int xInstancesCount) {
|
||||
this.costType = costType;
|
||||
this.xInstancesCount = xInstancesCount;
|
||||
this.cost = new Mana();
|
||||
options.add(new Mana());
|
||||
|
|
@ -39,6 +42,7 @@ public final class VariableManaCost extends ManaCostImpl implements VariableCost
|
|||
|
||||
public VariableManaCost(final VariableManaCost manaCost) {
|
||||
super(manaCost);
|
||||
this.costType = manaCost.costType;
|
||||
this.xInstancesCount = manaCost.xInstancesCount;
|
||||
this.xValue = manaCost.xValue;
|
||||
this.xPay = manaCost.xPay;
|
||||
|
|
@ -171,4 +175,14 @@ public final class VariableManaCost extends ManaCostImpl implements VariableCost
|
|||
public void setFilter(FilterMana filter) {
|
||||
this.filter = filter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VariableCostType getCostType() {
|
||||
return this.costType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCostType(VariableCostType costType) {
|
||||
this.costType = costType;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,25 +36,31 @@ public class BuybackAbility extends StaticAbility implements OptionalAdditionalS
|
|||
private static final String keywordText = "Buyback";
|
||||
private static final String reminderTextCost = "You may {cost} in addition to any other costs as you cast this spell. If you do, put this card into your hand as it resolves.";
|
||||
private static final String reminderTextMana = "You may pay an additional {cost} as you cast this spell. If you do, put this card into your hand as it resolves.";
|
||||
|
||||
protected OptionalAdditionalCost buybackCost;
|
||||
private int amountToReduceBy = 0;
|
||||
|
||||
public BuybackAbility(String manaString) {
|
||||
super(Zone.STACK, new BuybackEffect());
|
||||
this.buybackCost = new OptionalAdditionalCostImpl(keywordText, reminderTextMana, new ManaCostsImpl(manaString));
|
||||
addBuybackCostAndSetup(new OptionalAdditionalCostImpl(keywordText, reminderTextMana, new ManaCostsImpl(manaString)));
|
||||
setRuleAtTheTop(true);
|
||||
}
|
||||
|
||||
public BuybackAbility(Cost cost) {
|
||||
super(Zone.STACK, new BuybackEffect());
|
||||
this.buybackCost = new OptionalAdditionalCostImpl(keywordText, "—", reminderTextCost, cost);
|
||||
addBuybackCostAndSetup(new OptionalAdditionalCostImpl(keywordText, "—", reminderTextCost, cost));
|
||||
setRuleAtTheTop(true);
|
||||
}
|
||||
|
||||
private void addBuybackCostAndSetup(OptionalAdditionalCost newCost) {
|
||||
this.buybackCost = newCost;
|
||||
this.buybackCost.setCostType(VariableCostType.ADDITIONAL);
|
||||
}
|
||||
|
||||
public BuybackAbility(final BuybackAbility ability) {
|
||||
super(ability);
|
||||
buybackCost = new OptionalAdditionalCostImpl((OptionalAdditionalCostImpl) ability.buybackCost);
|
||||
amountToReduceBy = ability.amountToReduceBy;
|
||||
this.buybackCost = ability.buybackCost.copy();
|
||||
this.amountToReduceBy = ability.amountToReduceBy;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -4,11 +4,9 @@ import mage.abilities.Ability;
|
|||
import mage.abilities.SpellAbility;
|
||||
import mage.abilities.StaticAbility;
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.costs.Cost;
|
||||
import mage.abilities.costs.Costs;
|
||||
import mage.abilities.costs.OptionalAdditionalCostImpl;
|
||||
import mage.abilities.costs.OptionalAdditionalSourceCosts;
|
||||
import mage.abilities.costs.*;
|
||||
import mage.abilities.costs.common.TapTargetCost;
|
||||
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.cards.Card;
|
||||
|
|
@ -62,9 +60,9 @@ public class ConspireAbility extends StaticAbility implements OptionalAdditional
|
|||
MORE
|
||||
}
|
||||
|
||||
private UUID conspireId;
|
||||
private final UUID conspireId;
|
||||
private String reminderText;
|
||||
private OptionalAdditionalCostImpl conspireCost;
|
||||
private OptionalAdditionalCost conspireCost;
|
||||
|
||||
/**
|
||||
* Unique Id for a ConspireAbility but may not change while a continuous
|
||||
|
|
@ -87,12 +85,19 @@ public class ConspireAbility extends StaticAbility implements OptionalAdditional
|
|||
reminderText = "As you cast this spell, you may tap two untapped creatures you control that share a color with it. When you do, copy it and you may choose new targets for the copy.";
|
||||
break;
|
||||
}
|
||||
|
||||
Cost cost = new TapTargetCost(new TargetControlledPermanent(2, 2, filter, true));
|
||||
cost.setText("");
|
||||
conspireCost = new OptionalAdditionalCostImpl(keywordText, " ", reminderText, cost);
|
||||
addConspireCostAndSetup(new OptionalAdditionalCostImpl(keywordText, " ", reminderText, cost));
|
||||
|
||||
addSubAbility(new ConspireTriggeredAbility(conspireId));
|
||||
}
|
||||
|
||||
private void addConspireCostAndSetup(OptionalAdditionalCost newCost) {
|
||||
this.conspireCost = newCost;
|
||||
this.conspireCost.setCostType(VariableCostType.ADDITIONAL);
|
||||
}
|
||||
|
||||
public ConspireAbility(final ConspireAbility ability) {
|
||||
super(ability);
|
||||
this.conspireId = ability.conspireId;
|
||||
|
|
@ -139,9 +144,13 @@ public class ConspireAbility extends StaticAbility implements OptionalAdditional
|
|||
if (conspireCost.canPay(ability, this, getControllerId(), game)
|
||||
&& player.chooseUse(Outcome.Benefit, "Pay " + conspireCost.getText(false) + " ?", ability, game)) {
|
||||
activateConspire(ability, game);
|
||||
for (Iterator it = conspireCost.iterator(); it.hasNext(); ) {
|
||||
for (Iterator it = ((Costs) conspireCost).iterator(); it.hasNext(); ) {
|
||||
Cost cost = (Cost) it.next();
|
||||
ability.getCosts().add(cost.copy());
|
||||
if (cost instanceof ManaCostsImpl) {
|
||||
ability.getManaCostsToPay().add((ManaCostsImpl) cost.copy());
|
||||
} else {
|
||||
ability.getCosts().add(cost.copy());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -194,7 +203,7 @@ public class ConspireAbility extends StaticAbility implements OptionalAdditional
|
|||
|
||||
class ConspireTriggeredAbility extends TriggeredAbilityImpl {
|
||||
|
||||
private UUID conspireId;
|
||||
private final UUID conspireId;
|
||||
|
||||
public ConspireTriggeredAbility(UUID conspireId) {
|
||||
super(Zone.STACK, new ConspireEffect());
|
||||
|
|
|
|||
|
|
@ -3,10 +3,7 @@ package mage.abilities.keyword;
|
|||
import mage.abilities.Ability;
|
||||
import mage.abilities.SpellAbility;
|
||||
import mage.abilities.StaticAbility;
|
||||
import mage.abilities.costs.Cost;
|
||||
import mage.abilities.costs.OptionalAdditionalCost;
|
||||
import mage.abilities.costs.OptionalAdditionalCostImpl;
|
||||
import mage.abilities.costs.OptionalAdditionalModeSourceCosts;
|
||||
import mage.abilities.costs.*;
|
||||
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.Zone;
|
||||
|
|
@ -14,6 +11,7 @@ import mage.game.Game;
|
|||
import mage.players.Player;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
|
|
@ -35,12 +33,12 @@ public class EntwineAbility extends StaticAbility implements OptionalAdditionalM
|
|||
private static final String keywordText = "Entwine";
|
||||
protected static final String reminderText = "You may {cost} in addition to any other costs to use all modes.";
|
||||
|
||||
protected OptionalAdditionalCost additionalCost;
|
||||
protected OptionalAdditionalCost entwineCost;
|
||||
protected Set<String> activations = new HashSet<>(); // same logic as KickerAbility: activations per zoneChangeCounter
|
||||
|
||||
public EntwineAbility(String manaString) {
|
||||
super(Zone.STACK, null);
|
||||
this.additionalCost = new OptionalAdditionalCostImpl(keywordText, reminderText, new ManaCostsImpl(manaString));
|
||||
addEntwineCostAndSetup(new OptionalAdditionalCostImpl(keywordText, reminderText, new ManaCostsImpl(manaString)));
|
||||
}
|
||||
|
||||
public EntwineAbility(Cost cost) {
|
||||
|
|
@ -49,14 +47,20 @@ public class EntwineAbility extends StaticAbility implements OptionalAdditionalM
|
|||
|
||||
public EntwineAbility(Cost cost, String reminderText) {
|
||||
super(Zone.STACK, null);
|
||||
this.additionalCost = new OptionalAdditionalCostImpl(keywordText, "—", reminderText, cost);
|
||||
|
||||
addEntwineCostAndSetup(new OptionalAdditionalCostImpl(keywordText, "—", reminderText, cost));
|
||||
setRuleAtTheTop(true);
|
||||
}
|
||||
|
||||
private void addEntwineCostAndSetup(OptionalAdditionalCost newCost) {
|
||||
this.entwineCost = newCost;
|
||||
this.entwineCost.setCostType(VariableCostType.ADDITIONAL);
|
||||
}
|
||||
|
||||
public EntwineAbility(final EntwineAbility ability) {
|
||||
super(ability);
|
||||
if (ability.additionalCost != null) {
|
||||
this.additionalCost = ability.additionalCost.copy();
|
||||
if (ability.entwineCost != null) {
|
||||
this.entwineCost = ability.entwineCost.copy();
|
||||
}
|
||||
this.activations.addAll(ability.activations);
|
||||
}
|
||||
|
|
@ -77,32 +81,40 @@ public class EntwineAbility extends StaticAbility implements OptionalAdditionalM
|
|||
return;
|
||||
}
|
||||
|
||||
this.resetCosts(game, ability);
|
||||
if (additionalCost == null) {
|
||||
this.resetEntwine(game, ability);
|
||||
if (entwineCost == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (additionalCost.canPay(ability, this, ability.getControllerId(), game)
|
||||
&& player.chooseUse(Outcome.Benefit, "Pay " + additionalCost.getText(false) + " ?", ability, game)) {
|
||||
addCostsToAbility(additionalCost, ability);
|
||||
activateCost(game, ability);
|
||||
// AI can use it
|
||||
if (entwineCost.canPay(ability, this, ability.getControllerId(), game)
|
||||
&& player.chooseUse(Outcome.Benefit, "Pay " + entwineCost.getText(false) + " ?", ability, game)) {
|
||||
for (Iterator it = ((Costs) entwineCost).iterator(); it.hasNext(); ) {
|
||||
Cost cost = (Cost) it.next();
|
||||
if (cost instanceof ManaCostsImpl) {
|
||||
ability.getManaCostsToPay().add((ManaCostsImpl) cost.copy());
|
||||
} else {
|
||||
ability.getCosts().add(cost.copy());
|
||||
}
|
||||
}
|
||||
activateEntwine(game, ability);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRule() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (additionalCost != null) {
|
||||
sb.append(additionalCost.getText(false));
|
||||
sb.append(' ').append(additionalCost.getReminderText());
|
||||
if (entwineCost != null) {
|
||||
sb.append(entwineCost.getText(false));
|
||||
sb.append(' ').append(entwineCost.getReminderText());
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCastMessageSuffix() {
|
||||
if (additionalCost != null) {
|
||||
return additionalCost.getCastSuffixMessage(0);
|
||||
if (entwineCost != null) {
|
||||
return entwineCost.getCastSuffixMessage(0);
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
|
|
@ -119,20 +131,16 @@ public class EntwineAbility extends StaticAbility implements OptionalAdditionalM
|
|||
ability.getModes().setMaxModes(maxModes);
|
||||
}
|
||||
|
||||
private void addCostsToAbility(Cost cost, Ability ability) {
|
||||
ability.addCost(cost.copy());
|
||||
}
|
||||
|
||||
private void resetCosts(Game game, Ability source) {
|
||||
if (additionalCost != null) {
|
||||
additionalCost.reset();
|
||||
private void resetEntwine(Game game, Ability source) {
|
||||
if (entwineCost != null) {
|
||||
entwineCost.reset();
|
||||
}
|
||||
|
||||
String key = getActivationKey(source, game);
|
||||
this.activations.remove(key);
|
||||
}
|
||||
|
||||
private void activateCost(Game game, Ability source) {
|
||||
private void activateEntwine(Game game, Ability source) {
|
||||
String key = getActivationKey(source, game);
|
||||
this.activations.add(key);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -95,17 +95,24 @@ public class KickerAbility extends StaticAbility implements OptionalAdditionalSo
|
|||
}
|
||||
|
||||
public final OptionalAdditionalCost addKickerCost(String manaString) {
|
||||
OptionalAdditionalCost kickerCost = new OptionalAdditionalCostImpl(
|
||||
OptionalAdditionalCost newCost = new OptionalAdditionalCostImpl(
|
||||
keywordText, reminderText, new ManaCostsImpl(manaString));
|
||||
kickerCosts.add(kickerCost);
|
||||
return kickerCost;
|
||||
addKickerCostAndSetup(newCost);
|
||||
return newCost;
|
||||
}
|
||||
|
||||
public final OptionalAdditionalCost addKickerCost(Cost cost) {
|
||||
OptionalAdditionalCost kickerCost = new OptionalAdditionalCostImpl(
|
||||
OptionalAdditionalCost newCost = new OptionalAdditionalCostImpl(
|
||||
keywordText, "-", reminderText, cost);
|
||||
kickerCosts.add(kickerCost);
|
||||
return kickerCost;
|
||||
addKickerCostAndSetup(newCost);
|
||||
return newCost;
|
||||
}
|
||||
|
||||
private void addKickerCostAndSetup(OptionalAdditionalCost newCost) {
|
||||
this.kickerCosts.add(newCost);
|
||||
this.kickerCosts.forEach(cost -> {
|
||||
cost.setCostType(VariableCostType.ADDITIONAL);
|
||||
});
|
||||
}
|
||||
|
||||
public void resetKicker(Game game, Ability source) {
|
||||
|
|
@ -250,6 +257,7 @@ public class KickerAbility extends StaticAbility implements OptionalAdditionalSo
|
|||
"Pay " + times + kickerCost.getText(false) + " ?", ability, game)) {
|
||||
this.activateKicker(kickerCost, ability, game);
|
||||
if (kickerCost instanceof Costs) {
|
||||
// as multiple costs
|
||||
for (Iterator itKickerCost = ((Costs) kickerCost).iterator(); itKickerCost.hasNext(); ) {
|
||||
Object kickerCostObject = itKickerCost.next();
|
||||
if ((kickerCostObject instanceof Costs)) {
|
||||
|
|
@ -262,6 +270,7 @@ public class KickerAbility extends StaticAbility implements OptionalAdditionalSo
|
|||
}
|
||||
}
|
||||
} else {
|
||||
// as single cost
|
||||
addKickerCostsToAbility(kickerCost, ability, game);
|
||||
}
|
||||
again = kickerCost.isRepeatable();
|
||||
|
|
@ -275,7 +284,8 @@ public class KickerAbility extends StaticAbility implements OptionalAdditionalSo
|
|||
}
|
||||
|
||||
private void addKickerCostsToAbility(Cost cost, Ability ability, Game game) {
|
||||
// can contains multiple costs from multikicker ability
|
||||
// can contain multiple costs from multikicker ability
|
||||
// must be additional cost type
|
||||
if (cost instanceof ManaCostsImpl) {
|
||||
ability.getManaCostsToPay().add((ManaCostsImpl) cost.copy());
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -1,11 +1,13 @@
|
|||
package mage.abilities.keyword;
|
||||
|
||||
import mage.ApprovingObject;
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.SpecialAction;
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.common.BeginningOfUpkeepTriggeredAbility;
|
||||
import mage.abilities.condition.common.SuspendedCondition;
|
||||
import mage.abilities.costs.VariableCostType;
|
||||
import mage.abilities.costs.mana.ManaCost;
|
||||
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||
import mage.abilities.costs.mana.VariableManaCost;
|
||||
|
|
@ -26,7 +28,6 @@ import mage.target.targetpointer.FixedTarget;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import mage.ApprovingObject;
|
||||
|
||||
/**
|
||||
* 502.59. Suspend
|
||||
|
|
@ -108,16 +109,16 @@ import mage.ApprovingObject;
|
|||
*/
|
||||
public class SuspendAbility extends SpecialAction {
|
||||
|
||||
private String ruleText;
|
||||
private final String ruleText;
|
||||
private boolean gainedTemporary;
|
||||
|
||||
/**
|
||||
* Gives the card the SuspendAbility
|
||||
*
|
||||
* @param suspend - amount of time counters, if Integer.MAX_VALUE is set
|
||||
* there will be {X} costs and X counters added
|
||||
* @param cost - null is used for temporary gained suspend ability
|
||||
* @param card - card that has the suspend ability
|
||||
* there will be {X} costs and X counters added
|
||||
* @param cost - null is used for temporary gained suspend ability
|
||||
* @param card - card that has the suspend ability
|
||||
*/
|
||||
public SuspendAbility(int suspend, ManaCost cost, Card card) {
|
||||
this(suspend, cost, card, false);
|
||||
|
|
@ -129,7 +130,7 @@ public class SuspendAbility extends SpecialAction {
|
|||
this.addEffect(new SuspendExileEffect(suspend));
|
||||
this.usesStack = false;
|
||||
if (suspend == Integer.MAX_VALUE) {
|
||||
VariableManaCost xCosts = new VariableManaCost();
|
||||
VariableManaCost xCosts = new VariableManaCost(VariableCostType.ALTERNATIVE);
|
||||
xCosts.setMinX(1);
|
||||
this.addManaCost(xCosts);
|
||||
cost = new ManaCostsImpl("{X}" + cost.getText());
|
||||
|
|
@ -138,7 +139,7 @@ public class SuspendAbility extends SpecialAction {
|
|||
if (cost != null) {
|
||||
sb.append(suspend == Integer.MAX_VALUE ? "X" : suspend).append("—")
|
||||
.append(cost.getText()).append(suspend
|
||||
== Integer.MAX_VALUE ? ". X can't be 0." : "");
|
||||
== Integer.MAX_VALUE ? ". X can't be 0." : "");
|
||||
if (!shortRule) {
|
||||
sb.append(" <i>(Rather than cast this card from your hand, pay ")
|
||||
.append(cost.getText())
|
||||
|
|
@ -191,7 +192,7 @@ public class SuspendAbility extends SpecialAction {
|
|||
UUID exileId = (UUID) game.getState().getValue("SuspendExileId" + controllerId.toString());
|
||||
if (exileId == null) {
|
||||
exileId = UUID.randomUUID();
|
||||
game.getState().setValue("SuspendExileId" + controllerId.toString(), exileId);
|
||||
game.getState().setValue("SuspendExileId" + controllerId, exileId);
|
||||
}
|
||||
return exileId;
|
||||
}
|
||||
|
|
@ -212,7 +213,7 @@ public class SuspendAbility extends SpecialAction {
|
|||
return new ActivationStatus(object.isInstant(game)
|
||||
|| object.hasAbility(FlashAbility.getInstance(), game)
|
||||
|| null != game.getContinuousEffects().asThough(sourceId,
|
||||
AsThoughEffectType.CAST_AS_INSTANT, this, playerId, game)
|
||||
AsThoughEffectType.CAST_AS_INSTANT, this, playerId, game)
|
||||
|| game.canPlaySorcery(playerId), null);
|
||||
}
|
||||
|
||||
|
|
@ -306,7 +307,7 @@ class SuspendPlayCardAbility extends TriggeredAbilityImpl {
|
|||
@Override
|
||||
public String getRule() {
|
||||
return "When the last time counter is removed from this card ({this}), "
|
||||
+ "if it's removed from the game, " ;
|
||||
+ "if it's removed from the game, ";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -428,7 +429,7 @@ class SuspendBeginningOfUpkeepInterveningIfTriggeredAbility extends ConditionalI
|
|||
|
||||
public SuspendBeginningOfUpkeepInterveningIfTriggeredAbility() {
|
||||
super(new BeginningOfUpkeepTriggeredAbility(Zone.EXILED, new RemoveCounterSourceEffect(CounterType.TIME.createInstance()),
|
||||
TargetController.YOU, false),
|
||||
TargetController.YOU, false),
|
||||
SuspendedCondition.instance,
|
||||
"At the beginning of your upkeep, if this card ({this}) is suspended, remove a time counter from it.");
|
||||
this.setRuleVisible(false);
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package mage.game.command.emblems;
|
|||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.LimitedTimesPerTurnActivatedAbility;
|
||||
import mage.abilities.costs.VariableCostType;
|
||||
import mage.abilities.costs.common.DiscardCardCost;
|
||||
import mage.abilities.costs.mana.VariableManaCost;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
|
|
@ -18,12 +19,12 @@ import mage.constants.Zone;
|
|||
import mage.game.Game;
|
||||
import mage.game.command.Emblem;
|
||||
import mage.game.permanent.token.EmptyToken;
|
||||
import mage.game.permanent.token.Token;
|
||||
import mage.game.permanent.token.custom.CreatureToken;
|
||||
import mage.util.CardUtil;
|
||||
import mage.util.RandomUtil;
|
||||
|
||||
import java.util.List;
|
||||
import mage.game.permanent.token.Token;
|
||||
import mage.game.permanent.token.custom.CreatureToken;
|
||||
|
||||
/**
|
||||
* @author spjspj
|
||||
|
|
@ -37,7 +38,7 @@ public final class MomirEmblem extends Emblem {
|
|||
|
||||
// {X}, Discard a card: Create a token that's a copy of a creature card with converted mana cost X chosen at random.
|
||||
// Activate this ability only any time you could cast a sorcery and only once each turn.
|
||||
LimitedTimesPerTurnActivatedAbility ability = new LimitedTimesPerTurnActivatedAbility(Zone.COMMAND, new MomirEffect(), new VariableManaCost());
|
||||
LimitedTimesPerTurnActivatedAbility ability = new LimitedTimesPerTurnActivatedAbility(Zone.COMMAND, new MomirEffect(), new VariableManaCost(VariableCostType.NORMAL));
|
||||
ability.addCost(new DiscardCardCost());
|
||||
ability.setTiming(TimingRule.SORCERY);
|
||||
this.getAbilities().add(ability);
|
||||
|
|
@ -65,7 +66,7 @@ class MomirEffect extends OneShotEffect {
|
|||
int value = source.getManaCostsToPay().getX();
|
||||
if (game.isSimulation()) {
|
||||
// Create dummy token to prevent multiple DB find cards what causes H2 java.lang.IllegalStateException if AI cancels calculation because of time out
|
||||
Token token = new CreatureToken(value, value +1);
|
||||
Token token = new CreatureToken(value, value + 1);
|
||||
token.putOntoBattlefield(1, game, source, source.getControllerId(), false, false);
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import mage.ManaSymbol;
|
|||
import mage.ObjectColor;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.costs.Cost;
|
||||
import mage.abilities.costs.VariableCostType;
|
||||
import mage.abilities.costs.common.TapSourceCost;
|
||||
import mage.abilities.costs.mana.*;
|
||||
import mage.abilities.dynamicvalue.DynamicValue;
|
||||
|
|
@ -648,7 +649,7 @@ public final class ManaUtil {
|
|||
*/
|
||||
public static ManaCost createManaCost(int genericManaCount, boolean payAsX) {
|
||||
if (payAsX) {
|
||||
VariableManaCost xCost = new VariableManaCost();
|
||||
VariableManaCost xCost = new VariableManaCost(VariableCostType.NORMAL);
|
||||
xCost.setAmount(genericManaCount, genericManaCount, false);
|
||||
return xCost;
|
||||
} else {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue