mirror of
https://github.com/correl/mage.git
synced 2024-12-26 03:00:11 +00: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
|
@ -1,12 +1,12 @@
|
|||
package mage.cards.a;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.SimpleActivatedAbility;
|
||||
import mage.abilities.costs.Cost;
|
||||
import mage.abilities.costs.VariableCost;
|
||||
import mage.abilities.costs.VariableCostImpl;
|
||||
import mage.abilities.costs.VariableCostType;
|
||||
import mage.abilities.costs.common.TapSourceCost;
|
||||
import mage.abilities.costs.common.TapTargetCost;
|
||||
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||
|
@ -15,11 +15,7 @@ import mage.abilities.effects.common.DestroyTargetEffect;
|
|||
import mage.abilities.keyword.VigilanceAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.ComparisonType;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.SuperType;
|
||||
import mage.constants.Zone;
|
||||
import mage.constants.*;
|
||||
import mage.filter.common.FilterControlledCreaturePermanent;
|
||||
import mage.filter.common.FilterCreaturePermanent;
|
||||
import mage.filter.predicate.mageobject.PowerPredicate;
|
||||
|
@ -30,8 +26,9 @@ import mage.target.common.TargetControlledPermanent;
|
|||
import mage.target.common.TargetCreaturePermanent;
|
||||
import mage.target.targetadjustment.TargetAdjuster;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author jack-the-BOSS
|
||||
*/
|
||||
public final class AryelKnightOfWindgrace extends CardImpl {
|
||||
|
@ -83,7 +80,7 @@ class AryelTapXTargetCost extends VariableCostImpl {
|
|||
}
|
||||
|
||||
public AryelTapXTargetCost() {
|
||||
super("controlled untapped Knights you would like to tap");
|
||||
super(VariableCostType.NORMAL, "controlled untapped Knights you would like to tap");
|
||||
this.text = "Tap X untapped Knights you control";
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
|
||||
package mage.cards.b;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.ObjectColor;
|
||||
import mage.abilities.costs.AlternativeCostSourceAbility;
|
||||
import mage.abilities.costs.common.ExileFromHandCost;
|
||||
|
@ -20,14 +18,15 @@ import mage.filter.predicate.mageobject.ColorPredicate;
|
|||
import mage.target.common.TargetCardInHand;
|
||||
import mage.target.common.TargetCreaturePermanent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public final class BlazingShoal extends CardImpl {
|
||||
|
||||
public BlazingShoal(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{X}{R}{R}");
|
||||
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{X}{R}{R}");
|
||||
this.subtype.add(SubType.ARCANE);
|
||||
|
||||
|
||||
|
@ -35,7 +34,7 @@ public final class BlazingShoal extends CardImpl {
|
|||
FilterOwnedCard filter = new FilterOwnedCard("a red card with mana value X from your hand");
|
||||
filter.add(new ColorPredicate(ObjectColor.RED));
|
||||
filter.add(Predicates.not(new CardIdPredicate(this.getId()))); // the exile cost can never be paid with the card itself
|
||||
this.addAbility(new AlternativeCostSourceAbility(new ExileFromHandCost(new TargetCardInHand(filter),true)));
|
||||
this.addAbility(new AlternativeCostSourceAbility(new ExileFromHandCost(new TargetCardInHand(filter), true)));
|
||||
|
||||
// Target creature gets +X/+0 until end of turn.
|
||||
this.getSpellAbility().addEffect(new BoostTargetEffect(ExileFromHandCostCardConvertedMana.instance, StaticValue.get(0), Duration.EndOfTurn));
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
package mage.cards.c;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.EntersBattlefieldAbility;
|
||||
import mage.abilities.common.SimpleActivatedAbility;
|
||||
import mage.abilities.costs.Cost;
|
||||
import mage.abilities.costs.VariableCostImpl;
|
||||
import mage.abilities.costs.VariableCostType;
|
||||
import mage.abilities.costs.common.RemoveCountersSourceCost;
|
||||
import mage.abilities.costs.common.TapSourceCost;
|
||||
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||
|
@ -26,8 +26,9 @@ import mage.game.Game;
|
|||
import mage.game.permanent.Permanent;
|
||||
import mage.target.common.TargetAnyTarget;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author jmharmon
|
||||
*/
|
||||
public final class ChamberSentry extends CardImpl {
|
||||
|
@ -69,7 +70,7 @@ public final class ChamberSentry extends CardImpl {
|
|||
class ChamberSentryRemoveVariableCountersSourceCost extends VariableCostImpl {
|
||||
|
||||
protected int minimalCountersToPay = 0;
|
||||
private String counterName;
|
||||
private final String counterName;
|
||||
|
||||
public ChamberSentryRemoveVariableCountersSourceCost(Counter counter) {
|
||||
this(counter, 0);
|
||||
|
@ -84,7 +85,7 @@ class ChamberSentryRemoveVariableCountersSourceCost extends VariableCostImpl {
|
|||
}
|
||||
|
||||
public ChamberSentryRemoveVariableCountersSourceCost(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()) {
|
||||
|
|
|
@ -25,7 +25,7 @@ public final class ChanneledForce extends CardImpl {
|
|||
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{U}{R}");
|
||||
|
||||
// As an additional cost to cast this spell, discard X cards.
|
||||
this.getSpellAbility().addCost(new DiscardXTargetCost(StaticFilters.FILTER_CARD_CARDS, false));
|
||||
this.getSpellAbility().addCost(new DiscardXTargetCost(StaticFilters.FILTER_CARD_CARDS, true));
|
||||
|
||||
// Target player draws X cards. Channeled Force deals X damage to up to one target creature or planeswalker.
|
||||
this.getSpellAbility().addEffect(new ChanneledForceEffect());
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
|
||||
package mage.cards.c;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.SimpleActivatedAbility;
|
||||
|
@ -18,25 +16,26 @@ import mage.constants.Zone;
|
|||
import mage.counters.CounterType;
|
||||
import mage.filter.common.FilterControlledLandPermanent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Rene - bugisemail at gmail.com
|
||||
*/
|
||||
public final class CopperLeafAngel extends CardImpl {
|
||||
|
||||
public CopperLeafAngel(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{5}");
|
||||
|
||||
|
||||
this.subtype.add(SubType.ANGEL);
|
||||
this.power = new MageInt(2);
|
||||
this.toughness = new MageInt(2);
|
||||
|
||||
// Flying
|
||||
this.addAbility(FlyingAbility.getInstance());
|
||||
|
||||
|
||||
// {tap}, Sacrifice X lands: Put X +1/+1 counters on Copper-Leaf Angel.
|
||||
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new AddCountersSourceEffect(CounterType.P1P1.createInstance(),GetXValue.instance, false), new TapSourceCost());
|
||||
ability.addCost(new SacrificeXTargetCost(new FilterControlledLandPermanent("lands"), false));
|
||||
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new AddCountersSourceEffect(CounterType.P1P1.createInstance(), GetXValue.instance, false), new TapSourceCost());
|
||||
ability.addCost(new SacrificeXTargetCost(new FilterControlledLandPermanent("lands")));
|
||||
this.addAbility(ability);
|
||||
}
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ import mage.game.Game;
|
|||
import mage.game.permanent.Permanent;
|
||||
import mage.players.Player;
|
||||
import mage.target.common.TargetCreaturePermanent;
|
||||
import mage.util.ManaUtil;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
|
@ -79,7 +80,7 @@ class CrystalShardEffect extends OneShotEffect {
|
|||
if (player == null) {
|
||||
return true;
|
||||
}
|
||||
Cost cost = new GenericManaCost(1);
|
||||
Cost cost = ManaUtil.createManaCost(1, false);
|
||||
String message = "Pay {1}? (Otherwise " + targetCreature.getName() + " will be returned to its owner's hand)";
|
||||
if (player.chooseUse(Outcome.Benefit, message, source, game)) {
|
||||
cost.pay(source, game, source, targetCreature.getControllerId(), false, null);
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
|
||||
package mage.cards.d;
|
||||
|
||||
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.abilities.costs.common.DiscardTargetCost;
|
||||
import mage.abilities.dynamicvalue.common.GetXValue;
|
||||
import mage.abilities.effects.common.DamageAllEffect;
|
||||
|
@ -19,21 +18,22 @@ import mage.game.Game;
|
|||
import mage.players.Player;
|
||||
import mage.target.common.TargetCardInHand;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author emerald000
|
||||
*/
|
||||
public final class DevastatingDreams extends CardImpl {
|
||||
|
||||
public DevastatingDreams(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{R}{R}");
|
||||
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{R}{R}");
|
||||
|
||||
// As an additional cost to cast Devastating Dreams, discard X cards at random.
|
||||
this.getSpellAbility().addCost(new DevastatingDreamsAdditionalCost());
|
||||
|
||||
|
||||
// Each player sacrifices X lands.
|
||||
this.getSpellAbility().addEffect(new SacrificeAllEffect(GetXValue.instance, new FilterControlledLandPermanent("lands")));
|
||||
|
||||
|
||||
// Devastating Dreams deals X damage to each creature.
|
||||
this.getSpellAbility().addEffect(new DamageAllEffect(GetXValue.instance, new FilterCreaturePermanent()));
|
||||
}
|
||||
|
@ -51,7 +51,7 @@ public final class DevastatingDreams extends CardImpl {
|
|||
class DevastatingDreamsAdditionalCost extends VariableCostImpl {
|
||||
|
||||
DevastatingDreamsAdditionalCost() {
|
||||
super("cards to discard randomly");
|
||||
super(VariableCostType.ADDITIONAL, "cards to discard randomly");
|
||||
this.text = "as an additional cost to cast this spell, discard X cards at random";
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ import mage.abilities.Ability;
|
|||
import mage.abilities.common.ActivateIfConditionActivatedAbility;
|
||||
import mage.abilities.common.SimpleActivatedAbility;
|
||||
import mage.abilities.condition.common.MyTurnCondition;
|
||||
import mage.abilities.costs.VariableCostType;
|
||||
import mage.abilities.costs.common.TapSourceCost;
|
||||
import mage.abilities.costs.mana.GenericManaCost;
|
||||
import mage.abilities.costs.mana.VariableManaCost;
|
||||
|
@ -47,7 +48,7 @@ public final class EverythingamajigC extends CardImpl {
|
|||
|
||||
// Chimeric Staff
|
||||
// X: Everythingamajig becomes an X/X Construct artifact creature until end of turn.
|
||||
this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new ChimericStaffEffect(), new VariableManaCost()));
|
||||
this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new ChimericStaffEffect(), new VariableManaCost(VariableCostType.NORMAL)));
|
||||
}
|
||||
|
||||
private EverythingamajigC(final EverythingamajigC card) {
|
||||
|
|
|
@ -63,7 +63,7 @@ public final class ExtusOriqOverlord extends ModalDoubleFacesCard {
|
|||
// Awaken the Blood Avatar
|
||||
// Sorcery
|
||||
// As an additional cost to cast this spell, you may sacrifice any number of creatures. This spell costs {2} less to cast for each creature sacrificed this way.
|
||||
Cost cost = new SacrificeXTargetCost(StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT);
|
||||
Cost cost = new SacrificeXTargetCost(StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT, true);
|
||||
cost.setText("As an additional cost to cast this spell, you may sacrifice any number of creatures. " +
|
||||
"This spell costs {2} less to cast for each creature sacrificed this way");
|
||||
this.getRightHalfCard().getSpellAbility().addCost(cost);
|
||||
|
|
|
@ -27,7 +27,7 @@ public final class Firestorm extends CardImpl {
|
|||
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{R}");
|
||||
|
||||
// As an additional cost to cast Firestorm, discard X cards.
|
||||
this.getSpellAbility().addCost(new DiscardXTargetCost(new FilterCard("cards"), false));
|
||||
this.getSpellAbility().addCost(new DiscardXTargetCost(new FilterCard("cards"), true));
|
||||
|
||||
// Firestorm deals X damage to each of X target creatures and/or players.
|
||||
this.getSpellAbility().addEffect(new FirestormEffect());
|
||||
|
|
|
@ -5,6 +5,7 @@ import mage.abilities.Ability;
|
|||
import mage.abilities.common.SimpleActivatedAbility;
|
||||
import mage.abilities.costs.Cost;
|
||||
import mage.abilities.costs.VariableCostImpl;
|
||||
import mage.abilities.costs.VariableCostType;
|
||||
import mage.abilities.costs.common.TapSourceCost;
|
||||
import mage.abilities.costs.common.TapTargetCost;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
|
@ -114,7 +115,7 @@ class GlacianPowerstoneEngineerCost extends VariableCostImpl {
|
|||
}
|
||||
|
||||
GlacianPowerstoneEngineerCost() {
|
||||
super("controlled untapped artifacts you would like to tap");
|
||||
super(VariableCostType.NORMAL, "controlled untapped artifacts you would like to tap");
|
||||
this.text = "Tap X untapped artifacts you control";
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package mage.cards.h;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.costs.common.ExileXFromYourGraveCost;
|
||||
import mage.abilities.dynamicvalue.common.GetXValue;
|
||||
import mage.abilities.effects.common.DamageTargetEffect;
|
||||
|
@ -10,8 +9,9 @@ import mage.constants.CardType;
|
|||
import mage.filter.StaticFilters;
|
||||
import mage.target.common.TargetPlayerOrPlaneswalker;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author markedagain
|
||||
*/
|
||||
public final class HauntingMisery extends CardImpl {
|
||||
|
@ -21,6 +21,7 @@ public final class HauntingMisery extends CardImpl {
|
|||
|
||||
// As an additional cost to cast Haunting Misery, exile X creature cards from your graveyard.
|
||||
this.getSpellAbility().addCost(new ExileXFromYourGraveCost(StaticFilters.FILTER_CARD_CREATURE));
|
||||
|
||||
// Haunting Misery deals X damage to target player.
|
||||
this.getSpellAbility().addTarget(new TargetPlayerOrPlaneswalker());
|
||||
this.getSpellAbility().addEffect(new DamageTargetEffect(GetXValue.instance));
|
||||
|
|
|
@ -2,6 +2,7 @@ package mage.cards.h;
|
|||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.SimpleActivatedAbility;
|
||||
import mage.abilities.costs.VariableCostType;
|
||||
import mage.abilities.costs.common.TapSourceCost;
|
||||
import mage.abilities.costs.mana.VariableManaCost;
|
||||
import mage.abilities.dynamicvalue.common.ManacostVariableValue;
|
||||
|
@ -28,7 +29,7 @@ public final class HelmOfObedience extends CardImpl {
|
|||
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{4}");
|
||||
|
||||
// {X}, {T}: Target opponent puts cards from the top of their library into their graveyard until a creature card or X cards are put into that graveyard this way, whichever comes first. If a creature card is put into that graveyard this way, sacrifice Helm of Obedience and put that card onto the battlefield under your control. X can't be 0.
|
||||
VariableManaCost xCosts = new VariableManaCost();
|
||||
VariableManaCost xCosts = new VariableManaCost(VariableCostType.NORMAL);
|
||||
xCosts.setMinX(1);
|
||||
Ability ability = new SimpleActivatedAbility(new HelmOfObedienceEffect(), xCosts);
|
||||
ability.addCost(new TapSourceCost());
|
||||
|
|
|
@ -4,6 +4,7 @@ import mage.abilities.Ability;
|
|||
import mage.abilities.costs.Cost;
|
||||
import mage.abilities.costs.CostImpl;
|
||||
import mage.abilities.costs.VariableCostImpl;
|
||||
import mage.abilities.costs.VariableCostType;
|
||||
import mage.abilities.dynamicvalue.common.GetXValue;
|
||||
import mage.abilities.effects.common.DamageMultiEffect;
|
||||
import mage.cards.CardImpl;
|
||||
|
@ -33,7 +34,7 @@ public final class InfernalHarvest extends CardImpl {
|
|||
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{B}");
|
||||
|
||||
// As an additional cost to cast Infernal Harvest, return X Swamps you control to their owner's hand.
|
||||
this.getSpellAbility().addCost(new InfernalHarvestVariableCost());
|
||||
this.getSpellAbility().addCost(new InfernalHarvestAdditionalCost());
|
||||
|
||||
// Infernal Harvest deals X damage divided as you choose among any number of target creatures.
|
||||
this.getSpellAbility().addEffect(new DamageMultiEffect(GetXValue.instance));
|
||||
|
@ -50,22 +51,22 @@ public final class InfernalHarvest extends CardImpl {
|
|||
}
|
||||
}
|
||||
|
||||
class InfernalHarvestVariableCost extends VariableCostImpl {
|
||||
class InfernalHarvestAdditionalCost extends VariableCostImpl {
|
||||
|
||||
private static final FilterControlledPermanent filter = new FilterControlledPermanent(SubType.SWAMP);
|
||||
|
||||
InfernalHarvestVariableCost() {
|
||||
super("Swamps to return");
|
||||
InfernalHarvestAdditionalCost() {
|
||||
super(VariableCostType.ADDITIONAL, "Swamps to return");
|
||||
this.text = "return " + xText + " Swamps you control to their owner's hand";
|
||||
}
|
||||
|
||||
private InfernalHarvestVariableCost(final InfernalHarvestVariableCost cost) {
|
||||
private InfernalHarvestAdditionalCost(final InfernalHarvestAdditionalCost cost) {
|
||||
super(cost);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InfernalHarvestVariableCost copy() {
|
||||
return new InfernalHarvestVariableCost(this);
|
||||
public InfernalHarvestAdditionalCost copy() {
|
||||
return new InfernalHarvestAdditionalCost(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,19 +1,11 @@
|
|||
|
||||
package mage.cards.i;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.costs.Cost;
|
||||
import mage.abilities.costs.VariableCostImpl;
|
||||
import mage.abilities.costs.common.DiscardTargetCost;
|
||||
import mage.abilities.costs.common.DiscardXTargetCost;
|
||||
import mage.abilities.dynamicvalue.common.GetXValue;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.cards.Cards;
|
||||
import mage.cards.CardsImpl;
|
||||
import mage.cards.*;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.Zone;
|
||||
|
@ -21,20 +13,20 @@ import mage.filter.FilterCard;
|
|||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
import mage.target.TargetCard;
|
||||
import mage.target.common.TargetCardInHand;
|
||||
import mage.target.common.TargetCardInLibrary;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author spjspj
|
||||
*/
|
||||
public final class InsidiousDreams extends CardImpl {
|
||||
|
||||
public InsidiousDreams(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{3}{B}");
|
||||
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{3}{B}");
|
||||
|
||||
// As an additional cost to cast Insidious Dreams, discard X cards.
|
||||
this.getSpellAbility().addCost(new InsidiousDreamsAdditionalCost());
|
||||
this.getSpellAbility().addCost(new DiscardXTargetCost(new FilterCard("cards"), true));
|
||||
|
||||
// Search your library for X cards. Then shuffle your library and put those cards on top of it in any order.
|
||||
this.getSpellAbility().addEffect(new InsidiousDreamsEffect());
|
||||
|
@ -108,35 +100,3 @@ class InsidiousDreamsEffect extends OneShotEffect {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
class InsidiousDreamsAdditionalCost extends VariableCostImpl {
|
||||
|
||||
InsidiousDreamsAdditionalCost() {
|
||||
super("cards to discard");
|
||||
this.text = "discard X cards";
|
||||
}
|
||||
|
||||
InsidiousDreamsAdditionalCost(final InsidiousDreamsAdditionalCost cost) {
|
||||
super(cost);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InsidiousDreamsAdditionalCost copy() {
|
||||
return new InsidiousDreamsAdditionalCost(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxValue(Ability source, Game game) {
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
if (controller != null) {
|
||||
return controller.getHand().size();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cost getFixedCostsFromAnnouncedValue(int xValue) {
|
||||
TargetCardInHand target = new TargetCardInHand(xValue, new FilterCard());
|
||||
return new DiscardTargetCost(target);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package mage.cards.l;
|
|||
import mage.abilities.Ability;
|
||||
import mage.abilities.costs.Cost;
|
||||
import mage.abilities.costs.VariableCostImpl;
|
||||
import mage.abilities.costs.VariableCostType;
|
||||
import mage.abilities.dynamicvalue.DynamicValue;
|
||||
import mage.abilities.dynamicvalue.common.GetXValue;
|
||||
import mage.abilities.effects.Effect;
|
||||
|
@ -27,7 +28,7 @@ public final class LiquidFire extends CardImpl {
|
|||
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{R}{R}");
|
||||
|
||||
// As an additional cost to cast Liquid Fire, choose a number between 0 and 5.
|
||||
this.getSpellAbility().addCost(new LiquidFireCost());
|
||||
this.getSpellAbility().addCost(new LiquidFireAdditionalCost());
|
||||
// Liquid Fire deals X damage to target creature and 5 minus X damage to that creature's controller, where X is the chosen number.
|
||||
DynamicValue choiceValue = GetXValue.instance;
|
||||
this.getSpellAbility().addTarget(new TargetCreaturePermanent());
|
||||
|
@ -79,30 +80,31 @@ public final class LiquidFire extends CardImpl {
|
|||
return new LiquidFireEffect(this);
|
||||
}
|
||||
}
|
||||
|
||||
static class LiquidFireCost extends VariableCostImpl {
|
||||
public LiquidFireCost() {
|
||||
super("Choose a Number");
|
||||
this.text = "as an additional cost to cast this spell, choose a number between 0 and 5";
|
||||
}
|
||||
|
||||
public LiquidFireCost(final LiquidFireCost cost) {
|
||||
super(cost);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cost copy() {
|
||||
return new LiquidFireCost(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cost getFixedCostsFromAnnouncedValue(int xValue) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxValue(Ability source, Game game) {
|
||||
return 5;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class LiquidFireAdditionalCost extends VariableCostImpl {
|
||||
|
||||
public LiquidFireAdditionalCost() {
|
||||
super(VariableCostType.ADDITIONAL, "Choose a Number");
|
||||
this.text = "as an additional cost to cast this spell, choose a number between 0 and 5";
|
||||
}
|
||||
|
||||
public LiquidFireAdditionalCost(final LiquidFireAdditionalCost cost) {
|
||||
super(cost);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cost copy() {
|
||||
return new LiquidFireAdditionalCost(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cost getFixedCostsFromAnnouncedValue(int xValue) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxValue(Ability source, Game game) {
|
||||
return 5;
|
||||
}
|
||||
}
|
|
@ -7,6 +7,7 @@ import mage.abilities.common.SimpleActivatedAbility;
|
|||
import mage.abilities.costs.Cost;
|
||||
import mage.abilities.costs.VariableCost;
|
||||
import mage.abilities.costs.VariableCostImpl;
|
||||
import mage.abilities.costs.VariableCostType;
|
||||
import mage.abilities.costs.common.RevealTargetFromHandCost;
|
||||
import mage.abilities.costs.common.SacrificeSourceCost;
|
||||
import mage.abilities.costs.mana.GenericManaCost;
|
||||
|
@ -84,7 +85,7 @@ class RevealVariableBlackCardsFromHandCost extends VariableCostImpl {
|
|||
}
|
||||
|
||||
RevealVariableBlackCardsFromHandCost() {
|
||||
super("black cards to reveal");
|
||||
super(VariableCostType.NORMAL, "black cards to reveal");
|
||||
this.text = "Reveal " + xText + " black cards from {this}";
|
||||
}
|
||||
|
||||
|
|
|
@ -1,25 +1,20 @@
|
|||
|
||||
package mage.cards.m;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.MageInt;
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.SimpleActivatedAbility;
|
||||
import mage.abilities.costs.Cost;
|
||||
import mage.abilities.costs.VariableCostImpl;
|
||||
import mage.abilities.costs.VariableCostType;
|
||||
import mage.abilities.costs.common.TapSourceCost;
|
||||
import mage.abilities.costs.common.TapTargetCost;
|
||||
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.cards.Cards;
|
||||
import mage.cards.CardsImpl;
|
||||
import mage.cards.*;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.FilterCard;
|
||||
import mage.filter.common.FilterControlledArtifactPermanent;
|
||||
|
@ -29,8 +24,9 @@ import mage.players.Player;
|
|||
import mage.target.TargetCard;
|
||||
import mage.target.common.TargetControlledPermanent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public final class MerchantsDockhand extends CardImpl {
|
||||
|
@ -118,7 +114,7 @@ class TapXTargetCost extends VariableCostImpl {
|
|||
}
|
||||
|
||||
public TapXTargetCost() {
|
||||
super("controlled untapped artifacts you would like to tap");
|
||||
super(VariableCostType.NORMAL, "controlled untapped artifacts you would like to tap");
|
||||
this.text = "Tap X untapped artifacts you control";
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ import mage.MageInt;
|
|||
import mage.MageObjectReference;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.SimpleActivatedAbility;
|
||||
import mage.abilities.costs.VariableCostType;
|
||||
import mage.abilities.costs.mana.VariableManaCost;
|
||||
import mage.abilities.dynamicvalue.common.ManacostVariableValue;
|
||||
import mage.abilities.effects.ContinuousEffectImpl;
|
||||
|
@ -38,7 +39,7 @@ public final class MirrorEntity extends CardImpl {
|
|||
Ability ability = new SimpleActivatedAbility(new SetPowerToughnessAllEffect(
|
||||
ManacostVariableValue.REGULAR, ManacostVariableValue.REGULAR,
|
||||
Duration.EndOfTurn, StaticFilters.FILTER_CONTROLLED_CREATURES, true
|
||||
).setText("Until end of turn, creatures you control have base power and toughness X/X"), new VariableManaCost());
|
||||
).setText("Until end of turn, creatures you control have base power and toughness X/X"), new VariableManaCost(VariableCostType.NORMAL));
|
||||
ability.addEffect(new MirrorEntityEffect());
|
||||
this.addAbility(ability);
|
||||
}
|
||||
|
|
|
@ -1,11 +1,8 @@
|
|||
|
||||
package mage.cards.n;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.costs.Cost;
|
||||
import mage.abilities.costs.VariableCost;
|
||||
import mage.abilities.costs.VariableCostImpl;
|
||||
import mage.abilities.costs.common.DiscardTargetCost;
|
||||
import mage.abilities.costs.common.DiscardXTargetCost;
|
||||
import mage.abilities.dynamicvalue.common.DiscardCostCardManaValue;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.abilities.effects.common.DamageTargetEffect;
|
||||
|
@ -15,8 +12,6 @@ import mage.constants.CardType;
|
|||
import mage.filter.FilterCard;
|
||||
import mage.filter.common.FilterCreatureOrPlaneswalkerPermanent;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
import mage.target.common.TargetCardInHand;
|
||||
import mage.target.common.TargetCreatureOrPlaneswalker;
|
||||
import mage.target.targetadjustment.TargetAdjuster;
|
||||
|
||||
|
@ -31,7 +26,7 @@ public final class NahirisWrath extends CardImpl {
|
|||
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{R}");
|
||||
|
||||
// As an additional cost to cast Nahiri's Wrath, discard X cards.
|
||||
this.getSpellAbility().addCost(new NahirisWrathAdditionalCost());
|
||||
this.getSpellAbility().addCost(new DiscardXTargetCost(new FilterCard("cards"), true));
|
||||
|
||||
// Nahiri's Wrath deals damage equal to the total converted mana cost of the discarded cards to each of up to X target creatures and/or planeswalkers.
|
||||
Effect effect = new DamageTargetEffect(DiscardCostCardManaValue.instance);
|
||||
|
@ -58,7 +53,7 @@ enum NahirisWrathAdjuster implements TargetAdjuster {
|
|||
ability.getTargets().clear();
|
||||
int numTargets = 0;
|
||||
for (VariableCost cost : ability.getCosts().getVariableCosts()) {
|
||||
if (cost instanceof NahirisWrathAdditionalCost) {
|
||||
if (cost instanceof DiscardXTargetCost) {
|
||||
numTargets = cost.getAmount();
|
||||
break;
|
||||
}
|
||||
|
@ -67,36 +62,4 @@ enum NahirisWrathAdjuster implements TargetAdjuster {
|
|||
ability.addTarget(new TargetCreatureOrPlaneswalker(0, numTargets, new FilterCreatureOrPlaneswalkerPermanent(), false));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class NahirisWrathAdditionalCost extends VariableCostImpl {
|
||||
|
||||
NahirisWrathAdditionalCost() {
|
||||
super("cards to discard");
|
||||
this.text = "discard X cards";
|
||||
}
|
||||
|
||||
NahirisWrathAdditionalCost(final NahirisWrathAdditionalCost cost) {
|
||||
super(cost);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NahirisWrathAdditionalCost copy() {
|
||||
return new NahirisWrathAdditionalCost(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxValue(Ability source, Game game) {
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
if (controller != null) {
|
||||
return controller.getHand().size();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cost getFixedCostsFromAnnouncedValue(int xValue) {
|
||||
TargetCardInHand target = new TargetCardInHand(xValue, new FilterCard("cards to discard"));
|
||||
return new DiscardTargetCost(target);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -26,7 +26,7 @@ public final class NostalgicDreams extends CardImpl {
|
|||
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{G}{G}");
|
||||
|
||||
// As an additional cost to cast Nostalgic Dreams, discard X cards.
|
||||
this.getSpellAbility().addCost(new DiscardXTargetCost(new FilterCard("cards"), false));
|
||||
this.getSpellAbility().addCost(new DiscardXTargetCost(new FilterCard("cards"), true));
|
||||
|
||||
// Return X target cards from your graveyard to your hand.
|
||||
Effect effect = new ReturnFromGraveyardToHandTargetEffect();
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
|
||||
package mage.cards.p;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.SimpleActivatedAbility;
|
||||
|
@ -12,29 +10,30 @@ import mage.abilities.dynamicvalue.common.GetXValue;
|
|||
import mage.abilities.dynamicvalue.common.SignInversionDynamicValue;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.abilities.effects.common.continuous.BoostTargetEffect;
|
||||
import mage.constants.SubType;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.SubType;
|
||||
import mage.filter.FilterCard;
|
||||
import mage.target.common.TargetCreaturePermanent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author jeffwadsworth
|
||||
*/
|
||||
public final class Painbringer extends CardImpl {
|
||||
|
||||
public Painbringer(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}{B}");
|
||||
|
||||
|
||||
this.subtype.add(SubType.HUMAN);
|
||||
this.subtype.add(SubType.MINION);
|
||||
this.power = new MageInt(1);
|
||||
this.toughness = new MageInt(1);
|
||||
|
||||
// {tap}, Exile any number of cards from your graveyard: Target creature gets -X/-X until end of turn, where X is the number of cards exiled this way.
|
||||
// {T}, Exile any number of cards from your graveyard: Target creature gets -X/-X until end of turn, where X is the number of cards exiled this way.
|
||||
DynamicValue X = new SignInversionDynamicValue(GetXValue.instance);
|
||||
Effect effect = new BoostTargetEffect(X, X, Duration.EndOfTurn);
|
||||
effect.setText("Target creature gets -X/-X until end of turn, where X is the number of cards exiled this way");
|
||||
|
@ -42,7 +41,7 @@ public final class Painbringer extends CardImpl {
|
|||
ability.addCost(new ExileXFromYourGraveCost(new FilterCard("any number of cards from your graveyard")));
|
||||
ability.addTarget(new TargetCreaturePermanent());
|
||||
this.addAbility(ability);
|
||||
|
||||
|
||||
}
|
||||
|
||||
private Painbringer(final Painbringer card) {
|
||||
|
|
|
@ -4,6 +4,7 @@ import mage.ApprovingObject;
|
|||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.BeginningOfUpkeepTriggeredAbility;
|
||||
import mage.abilities.common.SimpleActivatedAbility;
|
||||
import mage.abilities.costs.VariableCostType;
|
||||
import mage.abilities.costs.common.TapSourceCost;
|
||||
import mage.abilities.costs.mana.VariableManaCost;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
|
@ -30,7 +31,7 @@ public final class PanopticMirror extends CardImpl {
|
|||
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{5}");
|
||||
|
||||
// Imprint - {X}, {tap}: You may exile an instant or sorcery card with converted mana cost X from your hand.
|
||||
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new PanopticMirrorExileEffect(), new VariableManaCost());
|
||||
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new PanopticMirrorExileEffect(), new VariableManaCost(VariableCostType.NORMAL));
|
||||
ability.addCost(new TapSourceCost());
|
||||
this.addAbility(ability.setAbilityWord(AbilityWord.IMPRINT));
|
||||
// At the beginning of your upkeep, you may copy a card exiled with Panoptic Mirror. If you do, you may cast the copy without paying its mana cost.
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
package mage.cards.p;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.SimpleActivatedAbility;
|
||||
import mage.abilities.costs.VariableCostType;
|
||||
import mage.abilities.costs.common.SacrificeSourceCost;
|
||||
import mage.abilities.costs.mana.VariableManaCost;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
|
@ -18,6 +18,8 @@ import mage.filter.predicate.Predicates;
|
|||
import mage.filter.predicate.mageobject.ManaValuePredicate;
|
||||
import mage.game.Game;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author Plopman
|
||||
*/
|
||||
|
@ -27,7 +29,7 @@ public final class PerniciousDeed extends CardImpl {
|
|||
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{B}{G}");
|
||||
|
||||
// {X}, Sacrifice Pernicious Deed: Destroy each artifact, creature, and enchantment with converted mana cost X or less.
|
||||
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new PerniciousDeedEffect(), new VariableManaCost());
|
||||
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new PerniciousDeedEffect(), new VariableManaCost(VariableCostType.NORMAL));
|
||||
ability.addCost(new SacrificeSourceCost());
|
||||
this.addAbility(ability);
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ public final class RestlessDreams extends CardImpl {
|
|||
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{B}");
|
||||
|
||||
// As an additional cost to cast Restless Dreams, discard X cards.
|
||||
this.getSpellAbility().addCost(new DiscardXTargetCost(StaticFilters.FILTER_CARD_CARDS, false));
|
||||
this.getSpellAbility().addCost(new DiscardXTargetCost(StaticFilters.FILTER_CARD_CARDS, true));
|
||||
|
||||
// Return X target creature cards from your graveyard to your hand.
|
||||
Effect effect = new ReturnFromGraveyardToHandTargetEffect();
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
|
||||
package mage.cards.r;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.SimpleActivatedAbility;
|
||||
import mage.abilities.costs.common.RemoveVariableCountersTargetCost;
|
||||
|
@ -19,8 +17,9 @@ import mage.counters.CounterType;
|
|||
import mage.filter.common.FilterControlledCreaturePermanent;
|
||||
import mage.target.common.TargetCreaturePermanent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public final class RetributionOfTheAncients extends CardImpl {
|
||||
|
@ -32,12 +31,11 @@ public final class RetributionOfTheAncients extends CardImpl {
|
|||
}
|
||||
|
||||
public RetributionOfTheAncients(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{B}");
|
||||
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{B}");
|
||||
|
||||
|
||||
DynamicValue xValue = new SignInversionDynamicValue(GetXValue.instance);
|
||||
// {B}, Remove X +1/+1 counters from among creatures you control: Target creature gets -X/-X until end of turn.
|
||||
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostTargetEffect(xValue,xValue,Duration.EndOfTurn, true), new ManaCostsImpl("{B}"));
|
||||
DynamicValue xValue = new SignInversionDynamicValue(GetXValue.instance);
|
||||
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostTargetEffect(xValue, xValue, Duration.EndOfTurn, true), new ManaCostsImpl("{B}"));
|
||||
ability.addCost(new RemoveVariableCountersTargetCost(filter, CounterType.P1P1, "X", 0));
|
||||
ability.addTarget(new TargetCreaturePermanent());
|
||||
this.addAbility(ability);
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
|
||||
|
||||
package mage.cards.s;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.SimpleActivatedAbility;
|
||||
import mage.abilities.costs.VariableCostType;
|
||||
import mage.abilities.costs.common.TapSourceCost;
|
||||
import mage.abilities.costs.mana.VariableManaCost;
|
||||
import mage.abilities.dynamicvalue.common.ManacostVariableValue;
|
||||
|
@ -15,17 +13,18 @@ import mage.constants.CardType;
|
|||
import mage.constants.Zone;
|
||||
import mage.target.TargetPlayer;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Loki
|
||||
*/
|
||||
public final class SandsOfDelirium extends CardImpl {
|
||||
|
||||
public SandsOfDelirium(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{3}");
|
||||
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}");
|
||||
|
||||
// {X}, {tap}: Target player puts the top X cards of their library into their graveyard.
|
||||
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new PutLibraryIntoGraveTargetEffect(ManacostVariableValue.REGULAR), new VariableManaCost());
|
||||
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new PutLibraryIntoGraveTargetEffect(ManacostVariableValue.REGULAR), new VariableManaCost(VariableCostType.NORMAL));
|
||||
ability.addCost(new TapSourceCost());
|
||||
ability.addTarget(new TargetPlayer());
|
||||
this.addAbility(ability);
|
||||
|
|
|
@ -1,11 +1,6 @@
|
|||
|
||||
package mage.cards.s;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.costs.Cost;
|
||||
import mage.abilities.costs.VariableCostImpl;
|
||||
import mage.abilities.costs.common.DiscardTargetCost;
|
||||
import mage.abilities.costs.common.DiscardXTargetCost;
|
||||
import mage.abilities.dynamicvalue.common.GetXValue;
|
||||
import mage.abilities.effects.common.DamageEverythingEffect;
|
||||
import mage.cards.CardImpl;
|
||||
|
@ -13,22 +8,20 @@ import mage.cards.CardSetInfo;
|
|||
import mage.constants.CardType;
|
||||
import mage.filter.FilterCard;
|
||||
import mage.filter.common.FilterCreaturePermanent;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
import mage.target.common.TargetCardInHand;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author fireshoes
|
||||
*/
|
||||
public final class SickeningDreams extends CardImpl {
|
||||
|
||||
public SickeningDreams(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{1}{B}");
|
||||
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{B}");
|
||||
|
||||
// As an additional cost to cast Sickening Dreams, discard X cards.
|
||||
this.getSpellAbility().addCost(new SickeningDreamsAdditionalCost());
|
||||
|
||||
this.getSpellAbility().addCost(new DiscardXTargetCost(new FilterCard("cards"), true));
|
||||
|
||||
// Sickening Dreams deals X damage to each creature and each player.
|
||||
this.getSpellAbility().addEffect(new DamageEverythingEffect(GetXValue.instance, new FilterCreaturePermanent()));
|
||||
}
|
||||
|
@ -41,36 +34,4 @@ public final class SickeningDreams extends CardImpl {
|
|||
public SickeningDreams copy() {
|
||||
return new SickeningDreams(this);
|
||||
}
|
||||
}
|
||||
|
||||
class SickeningDreamsAdditionalCost extends VariableCostImpl {
|
||||
|
||||
SickeningDreamsAdditionalCost() {
|
||||
super("cards to discard");
|
||||
this.text = "discard X cards";
|
||||
}
|
||||
|
||||
SickeningDreamsAdditionalCost(final SickeningDreamsAdditionalCost cost) {
|
||||
super(cost);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SickeningDreamsAdditionalCost copy() {
|
||||
return new SickeningDreamsAdditionalCost(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxValue(Ability source, Game game) {
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
if (controller != null) {
|
||||
return controller.getHand().size();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cost getFixedCostsFromAnnouncedValue(int xValue) {
|
||||
TargetCardInHand target = new TargetCardInHand(xValue, new FilterCard());
|
||||
return new DiscardTargetCost(target);
|
||||
}
|
||||
}
|
|
@ -1,7 +1,5 @@
|
|||
|
||||
package mage.cards.s;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.Mana;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.SimpleActivatedAbility;
|
||||
|
@ -21,10 +19,10 @@ import mage.constants.Zone;
|
|||
import mage.filter.common.FilterControlledCreaturePermanent;
|
||||
import mage.game.permanent.token.GoatToken;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author jeffwadsworth
|
||||
*
|
||||
*/
|
||||
public final class SpringjackPasture extends CardImpl {
|
||||
|
||||
|
@ -35,7 +33,7 @@ public final class SpringjackPasture extends CardImpl {
|
|||
}
|
||||
|
||||
public SpringjackPasture(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.LAND},"");
|
||||
super(ownerId, setInfo, new CardType[]{CardType.LAND}, "");
|
||||
|
||||
// {tap}: Add {C}.
|
||||
this.addAbility(new ColorlessManaAbility());
|
||||
|
@ -45,11 +43,11 @@ public final class SpringjackPasture extends CardImpl {
|
|||
ability.addCost(new TapSourceCost());
|
||||
this.addAbility(ability);
|
||||
|
||||
// {tap}, Sacrifice X Goats: Add X mana of any one color. You gain X life.
|
||||
// {T}, Sacrifice X Goats: Add X mana of any one color. You gain X life.
|
||||
ability = new DynamicManaAbility(
|
||||
new Mana(0, 0, 0, 0,0, 0,1,0),
|
||||
new Mana(0, 0, 0, 0, 0, 0, 1, 0),
|
||||
GetXValue.instance,
|
||||
new TapSourceCost(),
|
||||
new TapSourceCost(),
|
||||
"Add X mana of any one color",
|
||||
true);
|
||||
ability.addCost(new SacrificeXTargetCost(filter));
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
|
||||
package mage.cards.t;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.common.SimpleActivatedAbility;
|
||||
import mage.abilities.costs.Cost;
|
||||
import mage.abilities.costs.VariableCostImpl;
|
||||
import mage.abilities.costs.VariableCostType;
|
||||
import mage.abilities.costs.common.RemoveCountersSourceCost;
|
||||
import mage.abilities.costs.common.TapSourceCost;
|
||||
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||
|
@ -25,8 +24,9 @@ import mage.game.permanent.Permanent;
|
|||
import mage.players.Player;
|
||||
import mage.target.common.TargetAnyTarget;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author jerekwilson
|
||||
*/
|
||||
public final class TalonOfPain extends CardImpl {
|
||||
|
@ -89,19 +89,17 @@ public final class TalonOfPain extends CardImpl {
|
|||
if (controller.hasOpponent(event.getTargetId(), game)) {
|
||||
// a source you control other than Talon of Pain
|
||||
UUID sourceControllerId = game.getControllerId(event.getSourceId());
|
||||
if (sourceControllerId != null
|
||||
// return true so the effect will fire and a charge counter will be added
|
||||
return sourceControllerId != null
|
||||
&& sourceControllerId.equals(this.getControllerId())
|
||||
&& !this.getSourceId().equals(event.getSourceId())) {
|
||||
// return true so the effect will fire and a charge counter will be added
|
||||
return true;
|
||||
}
|
||||
&& !this.getSourceId().equals(event.getSourceId());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTriggerPhrase() {
|
||||
return "Whenever a source you control other than {this} deals damage to an opponent, " ;
|
||||
return "Whenever a source you control other than {this} deals damage to an opponent, ";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -109,7 +107,7 @@ public final class TalonOfPain extends CardImpl {
|
|||
class TalonOfPainRemoveVariableCountersSourceCost extends VariableCostImpl {
|
||||
|
||||
protected int minimalCountersToPay = 0;
|
||||
private String counterName;
|
||||
private final String counterName;
|
||||
|
||||
public TalonOfPainRemoveVariableCountersSourceCost(Counter counter) {
|
||||
this(counter, 0);
|
||||
|
@ -124,7 +122,7 @@ class TalonOfPainRemoveVariableCountersSourceCost extends VariableCostImpl {
|
|||
}
|
||||
|
||||
public TalonOfPainRemoveVariableCountersSourceCost(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()) {
|
||||
|
|
|
@ -2,6 +2,7 @@ package mage.cards.t;
|
|||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.SimpleActivatedAbility;
|
||||
import mage.abilities.costs.VariableCostType;
|
||||
import mage.abilities.costs.mana.VariableManaCost;
|
||||
import mage.abilities.dynamicvalue.common.ManacostVariableValue;
|
||||
import mage.abilities.effects.ContinuousEffectImpl;
|
||||
|
@ -27,7 +28,7 @@ public final class TestamentOfFaith extends CardImpl {
|
|||
// {X}: Testament of Faith becomes an X/X Wall creature with defender in addition to its other types until end of turn.
|
||||
Ability ability = new SimpleActivatedAbility(new SetPowerToughnessSourceEffect(
|
||||
ManacostVariableValue.REGULAR, Duration.EndOfTurn, SubLayer.SetPT_7b
|
||||
).setText("{this} becomes an X/X"), new VariableManaCost());
|
||||
).setText("{this} becomes an X/X"), new VariableManaCost(VariableCostType.NORMAL));
|
||||
ability.addEffect(new TestamentOfFaithEffect());
|
||||
ability.addEffect(new GainAbilitySourceEffect(
|
||||
DefenderAbility.getInstance(), Duration.EndOfTurn
|
||||
|
|
|
@ -38,7 +38,7 @@ public final class ThievingSkydiver extends CardImpl {
|
|||
|
||||
// Kicker {X}. X can't be 0.
|
||||
KickerAbility kickerAbility = new KickerAbility("{X}");
|
||||
kickerAbility.getKickerCosts().stream().forEach(cost -> {
|
||||
kickerAbility.getKickerCosts().forEach(cost -> {
|
||||
cost.setMinimumCost(1);
|
||||
cost.setReminderText(". X can't be 0.");
|
||||
});
|
||||
|
|
|
@ -2,6 +2,7 @@ package mage.cards.t;
|
|||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.SimpleActivatedAbility;
|
||||
import mage.abilities.costs.VariableCostType;
|
||||
import mage.abilities.costs.common.TapSourceCost;
|
||||
import mage.abilities.costs.mana.VariableManaCost;
|
||||
import mage.abilities.dynamicvalue.common.ManacostVariableValue;
|
||||
|
@ -21,17 +22,16 @@ import mage.target.common.TargetOpponent;
|
|||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author noahg
|
||||
*/
|
||||
public final class ThoughtDissector extends CardImpl {
|
||||
|
||||
public ThoughtDissector(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{4}");
|
||||
|
||||
|
||||
|
||||
// {X}, {tap}: Target opponent reveals cards from the top of their library until an artifact card or X cards are revealed, whichever comes first. If an artifact card is revealed this way, put it onto the battlefield under your control and sacrifice Thought Dissector. Put the rest of the revealed cards into that player's graveyard.
|
||||
SimpleActivatedAbility abilitiy = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ThoughtDissectorEffect(), new VariableManaCost());
|
||||
SimpleActivatedAbility abilitiy = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ThoughtDissectorEffect(), new VariableManaCost(VariableCostType.NORMAL));
|
||||
abilitiy.addCost(new TapSourceCost());
|
||||
abilitiy.addTarget(new TargetOpponent());
|
||||
this.addAbility(abilitiy);
|
||||
|
@ -82,7 +82,7 @@ class ThoughtDissectorEffect extends OneShotEffect {
|
|||
break;
|
||||
} else {
|
||||
numberOfCard++;
|
||||
if (numberOfCard > max){
|
||||
if (numberOfCard > max) {
|
||||
break;
|
||||
}
|
||||
nonArtifacts.add(card);
|
||||
|
|
|
@ -26,7 +26,7 @@ public final class TurbulentDreams extends CardImpl {
|
|||
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{U}{U}");
|
||||
|
||||
// As an additional cost to cast Turbulent Dreams, discard X cards.
|
||||
this.getSpellAbility().addCost(new DiscardXTargetCost(StaticFilters.FILTER_CARD_CARDS, false));
|
||||
this.getSpellAbility().addCost(new DiscardXTargetCost(StaticFilters.FILTER_CARD_CARDS, true));
|
||||
|
||||
// Return X target nonland permanents to their owners' hands.
|
||||
Effect effect = new ReturnToHandTargetEffect();
|
||||
|
|
|
@ -25,7 +25,7 @@ public final class VengefulDreams extends CardImpl {
|
|||
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{W}{W}");
|
||||
|
||||
// As an additional cost to cast Vengeful Dreams, discard X cards.
|
||||
this.getSpellAbility().addCost(new DiscardXTargetCost(new FilterCard("cards"), false));
|
||||
this.getSpellAbility().addCost(new DiscardXTargetCost(new FilterCard("cards"), true));
|
||||
|
||||
// Exile X target attacking creatures.
|
||||
Effect effect = new ExileTargetEffect();
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
package mage.cards.v;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
|
@ -27,7 +26,7 @@ public final class ViciousBetrayal extends CardImpl {
|
|||
|
||||
|
||||
// As an additional cost to cast Vicious Betrayal, sacrifice any number of creatures.
|
||||
this.getSpellAbility().addCost(new SacrificeXTargetCost(new FilterControlledCreaturePermanent()));
|
||||
this.getSpellAbility().addCost(new SacrificeXTargetCost(new FilterControlledCreaturePermanent(), true));
|
||||
// Target creature gets +2/+2 until end of turn for each creature sacrificed this way.
|
||||
this.getSpellAbility().addTarget(new TargetCreaturePermanent());
|
||||
this.getSpellAbility().addEffect(new BoostTargetEffect(GetXValue.instance, GetXValue.instance, Duration.EndOfTurn));
|
||||
|
|
|
@ -3,7 +3,6 @@ package mage.cards.w;
|
|||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.SpellCastOpponentTriggeredAbility;
|
||||
import mage.abilities.costs.Cost;
|
||||
import mage.abilities.costs.mana.GenericManaCost;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.cards.*;
|
||||
import mage.constants.*;
|
||||
|
@ -15,6 +14,7 @@ import mage.game.stack.Spell;
|
|||
import mage.players.Player;
|
||||
import mage.target.TargetCard;
|
||||
import mage.target.common.TargetCardInLibrary;
|
||||
import mage.util.ManaUtil;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
@ -85,7 +85,7 @@ class WanderingArchaicEffect extends OneShotEffect {
|
|||
if (controller == null || opponent == null || spell == null) {
|
||||
return false;
|
||||
}
|
||||
Cost cost = new GenericManaCost(2);
|
||||
Cost cost = ManaUtil.createManaCost(2, false);
|
||||
if (cost.canPay(source, source, opponent.getId(), game)
|
||||
&& opponent.chooseUse(outcome, "Pay {2}?", source, game)
|
||||
&& cost.pay(source, game, source, opponent.getId(), false)) {
|
||||
|
|
|
@ -2,7 +2,6 @@ package mage.cards.w;
|
|||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.costs.Cost;
|
||||
import mage.abilities.costs.mana.GenericManaCost;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
|
@ -10,6 +9,8 @@ import mage.constants.CardType;
|
|||
import mage.constants.Outcome;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
import mage.util.ManaUtil;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
|
@ -65,22 +66,22 @@ class WhirlwindDenialEffect extends OneShotEffect {
|
|||
if (player == null) {
|
||||
return;
|
||||
}
|
||||
Cost cost = new GenericManaCost(4);
|
||||
Cost cost = ManaUtil.createManaCost(4, false);
|
||||
if (cost.canPay(source, source, stackObject.getControllerId(), game)
|
||||
&& player.chooseUse(outcome, "Pay {4} to prevent "
|
||||
+ stackObject.getIdName() + " from being countered?", source, game)
|
||||
+ stackObject.getIdName() + " from being countered?", source, game)
|
||||
&& cost.pay(source, game, source, stackObject.getControllerId(), false)) {
|
||||
game.informPlayers("The cost was paid by "
|
||||
+ player.getLogName()
|
||||
+ " to prevent "
|
||||
+ stackObject.getIdName()
|
||||
game.informPlayers("The cost was paid by "
|
||||
+ player.getLogName()
|
||||
+ " to prevent "
|
||||
+ stackObject.getIdName()
|
||||
+ " from being countered.");
|
||||
return;
|
||||
}
|
||||
game.informPlayers("The cost was not paid by "
|
||||
+ player.getLogName()
|
||||
+ " to prevent "
|
||||
+ stackObject.getIdName()
|
||||
game.informPlayers("The cost was not paid by "
|
||||
+ player.getLogName()
|
||||
+ " to prevent "
|
||||
+ stackObject.getIdName()
|
||||
+ " from being countered.");
|
||||
game.getStack().counter(stackObject.getId(), source, game);
|
||||
});
|
||||
|
|
|
@ -651,4 +651,68 @@ public class KickerTest extends CardTestPlayerBase {
|
|||
assertGraveyardCount(playerA, "Marsh Casualties", 1);
|
||||
assertPowerToughness(playerB, "Centaur Courser", 1, 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_FreeCast_Normal() {
|
||||
skipInitShuffling();
|
||||
|
||||
// Kicker {2}
|
||||
// If Ardent Soldier was kicked, it enters the battlefield with a +1/+1 counter on it.
|
||||
addCard(Zone.LIBRARY, playerA, "Ardent Soldier", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2); // for kicker cost
|
||||
//
|
||||
// Whenever Etali, Primal Storm attacks, exile the top card of each player's library,
|
||||
// then you may cast any number of nonland cards exiled this way without paying their mana costs.
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Etali, Primal Storm", 1);
|
||||
|
||||
checkPlayableAbility("before", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Ardent Soldier", false);
|
||||
|
||||
// attack and prepare free cast, use kicker
|
||||
attack(1, playerA, "Etali, Primal Storm", playerB);
|
||||
setChoice(playerA, true); // cast for free
|
||||
setChoice(playerA, "Ardent Soldier"); // cast for free
|
||||
setChoice(playerA, true); // use kicker
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertCounterCount(playerA, "Ardent Soldier", CounterType.P1P1, 1); // from kicker
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_FreeCast_MinXValueMustWork() {
|
||||
// bug:
|
||||
// Can cast Thieving Skydiver with kicker's X = 0 on Etali, Primal Storm
|
||||
skipInitShuffling();
|
||||
|
||||
// Kicker {X}. X can't be 0.
|
||||
// When Thieving Skydiver enters the battlefield, if it was kicked, gain control of target artifact with
|
||||
// converted mana cost X or less. If that artifact is an Equipment, attach it to Thieving Skydiver.
|
||||
addCard(Zone.LIBRARY, playerA, "Thieving Skydiver", 1); // {1}{U}
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Brain in a Jar", 1); // {2}
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2); // for kicker cost
|
||||
//
|
||||
// Whenever Etali, Primal Storm attacks, exile the top card of each player's library,
|
||||
// then you may cast any number of nonland cards exiled this way without paying their mana costs.
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Etali, Primal Storm", 1);
|
||||
|
||||
checkPlayableAbility("before", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast Thieving Skydiver", false);
|
||||
|
||||
// attack and prepare free cast
|
||||
attack(1, playerA, "Etali, Primal Storm", playerB);
|
||||
setChoice(playerA, true); // cast for free
|
||||
setChoice(playerA, "Thieving Skydiver"); // cast for free
|
||||
setChoice(playerA, true); // use kicker
|
||||
setChoiceAmount(playerA, 2); // X=2 for Kicker X
|
||||
addTarget(playerA, "Brain in a Jar"); // kicker's target (take control of artifact)
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
assertAllCommandsUsed();
|
||||
|
||||
assertPermanentCount(playerA, "Brain in a Jar", 1);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package org.mage.test.cards.continuous;
|
||||
|
||||
import mage.abilities.costs.VariableCostType;
|
||||
import mage.abilities.costs.mana.VariableManaCost;
|
||||
import mage.constants.PhaseStep;
|
||||
import mage.constants.Zone;
|
||||
|
@ -182,7 +183,7 @@ public class UnboundFlourishingTest extends CardTestPlayerBase {
|
|||
int xInstancesCount = 2;
|
||||
int xAnnouncedValue = 3;
|
||||
int xMultiplier = 2;
|
||||
VariableManaCost cost = new VariableManaCost(xInstancesCount);
|
||||
VariableManaCost cost = new VariableManaCost(VariableCostType.NORMAL, xInstancesCount);
|
||||
cost.setAmount(xAnnouncedValue * xMultiplier, xAnnouncedValue * xInstancesCount, false);
|
||||
|
||||
Assert.assertEquals("instances count", xInstancesCount, cost.getXInstancesCount());
|
||||
|
|
|
@ -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…
Reference in a new issue