diff --git a/Mage.Sets/src/mage/cards/a/AryelKnightOfWindgrace.java b/Mage.Sets/src/mage/cards/a/AryelKnightOfWindgrace.java index 2b6be379b1..5c493e3317 100644 --- a/Mage.Sets/src/mage/cards/a/AryelKnightOfWindgrace.java +++ b/Mage.Sets/src/mage/cards/a/AryelKnightOfWindgrace.java @@ -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"; } diff --git a/Mage.Sets/src/mage/cards/b/BlazingShoal.java b/Mage.Sets/src/mage/cards/b/BlazingShoal.java index 38cacbfced..c460a3d722 100644 --- a/Mage.Sets/src/mage/cards/b/BlazingShoal.java +++ b/Mage.Sets/src/mage/cards/b/BlazingShoal.java @@ -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)); diff --git a/Mage.Sets/src/mage/cards/c/ChamberSentry.java b/Mage.Sets/src/mage/cards/c/ChamberSentry.java index da52d4c0c8..02efeee2f8 100644 --- a/Mage.Sets/src/mage/cards/c/ChamberSentry.java +++ b/Mage.Sets/src/mage/cards/c/ChamberSentry.java @@ -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()) { diff --git a/Mage.Sets/src/mage/cards/c/ChanneledForce.java b/Mage.Sets/src/mage/cards/c/ChanneledForce.java index 508bf069f3..7e933d2919 100644 --- a/Mage.Sets/src/mage/cards/c/ChanneledForce.java +++ b/Mage.Sets/src/mage/cards/c/ChanneledForce.java @@ -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()); diff --git a/Mage.Sets/src/mage/cards/c/CopperLeafAngel.java b/Mage.Sets/src/mage/cards/c/CopperLeafAngel.java index f1edd7c73a..adb0608707 100644 --- a/Mage.Sets/src/mage/cards/c/CopperLeafAngel.java +++ b/Mage.Sets/src/mage/cards/c/CopperLeafAngel.java @@ -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); } diff --git a/Mage.Sets/src/mage/cards/c/CrystalShard.java b/Mage.Sets/src/mage/cards/c/CrystalShard.java index d8d1427df3..a0a09fec63 100644 --- a/Mage.Sets/src/mage/cards/c/CrystalShard.java +++ b/Mage.Sets/src/mage/cards/c/CrystalShard.java @@ -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); diff --git a/Mage.Sets/src/mage/cards/d/DevastatingDreams.java b/Mage.Sets/src/mage/cards/d/DevastatingDreams.java index 14096c1d6d..6d2ef8887c 100644 --- a/Mage.Sets/src/mage/cards/d/DevastatingDreams.java +++ b/Mage.Sets/src/mage/cards/d/DevastatingDreams.java @@ -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"; } diff --git a/Mage.Sets/src/mage/cards/e/EverythingamajigC.java b/Mage.Sets/src/mage/cards/e/EverythingamajigC.java index 47998b887a..1e739b37e7 100644 --- a/Mage.Sets/src/mage/cards/e/EverythingamajigC.java +++ b/Mage.Sets/src/mage/cards/e/EverythingamajigC.java @@ -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) { diff --git a/Mage.Sets/src/mage/cards/e/ExtusOriqOverlord.java b/Mage.Sets/src/mage/cards/e/ExtusOriqOverlord.java index 72c64a97f2..e762a53d39 100644 --- a/Mage.Sets/src/mage/cards/e/ExtusOriqOverlord.java +++ b/Mage.Sets/src/mage/cards/e/ExtusOriqOverlord.java @@ -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); diff --git a/Mage.Sets/src/mage/cards/f/Firestorm.java b/Mage.Sets/src/mage/cards/f/Firestorm.java index c00ee422f8..47d626817f 100644 --- a/Mage.Sets/src/mage/cards/f/Firestorm.java +++ b/Mage.Sets/src/mage/cards/f/Firestorm.java @@ -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()); diff --git a/Mage.Sets/src/mage/cards/g/GlacianPowerstoneEngineer.java b/Mage.Sets/src/mage/cards/g/GlacianPowerstoneEngineer.java index 77d8237389..fd5cdfc454 100644 --- a/Mage.Sets/src/mage/cards/g/GlacianPowerstoneEngineer.java +++ b/Mage.Sets/src/mage/cards/g/GlacianPowerstoneEngineer.java @@ -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"; } diff --git a/Mage.Sets/src/mage/cards/h/HauntingMisery.java b/Mage.Sets/src/mage/cards/h/HauntingMisery.java index 8362c9de65..aac9d2fef1 100644 --- a/Mage.Sets/src/mage/cards/h/HauntingMisery.java +++ b/Mage.Sets/src/mage/cards/h/HauntingMisery.java @@ -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)); diff --git a/Mage.Sets/src/mage/cards/h/HelmOfObedience.java b/Mage.Sets/src/mage/cards/h/HelmOfObedience.java index a925bb5a9f..b9ea7eea0d 100644 --- a/Mage.Sets/src/mage/cards/h/HelmOfObedience.java +++ b/Mage.Sets/src/mage/cards/h/HelmOfObedience.java @@ -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()); diff --git a/Mage.Sets/src/mage/cards/i/InfernalHarvest.java b/Mage.Sets/src/mage/cards/i/InfernalHarvest.java index 2e46cd9521..28a3e0ce38 100644 --- a/Mage.Sets/src/mage/cards/i/InfernalHarvest.java +++ b/Mage.Sets/src/mage/cards/i/InfernalHarvest.java @@ -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 diff --git a/Mage.Sets/src/mage/cards/i/InsidiousDreams.java b/Mage.Sets/src/mage/cards/i/InsidiousDreams.java index d1e15ea53b..af18bb4ef8 100644 --- a/Mage.Sets/src/mage/cards/i/InsidiousDreams.java +++ b/Mage.Sets/src/mage/cards/i/InsidiousDreams.java @@ -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); - } -} diff --git a/Mage.Sets/src/mage/cards/l/LiquidFire.java b/Mage.Sets/src/mage/cards/l/LiquidFire.java index fa1db49fe0..f6746b2468 100644 --- a/Mage.Sets/src/mage/cards/l/LiquidFire.java +++ b/Mage.Sets/src/mage/cards/l/LiquidFire.java @@ -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; + } +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/m/MartyrOfBones.java b/Mage.Sets/src/mage/cards/m/MartyrOfBones.java index fd03d562ce..c469930c10 100644 --- a/Mage.Sets/src/mage/cards/m/MartyrOfBones.java +++ b/Mage.Sets/src/mage/cards/m/MartyrOfBones.java @@ -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}"; } diff --git a/Mage.Sets/src/mage/cards/m/MerchantsDockhand.java b/Mage.Sets/src/mage/cards/m/MerchantsDockhand.java index 0cc38eac76..b61b910d7f 100644 --- a/Mage.Sets/src/mage/cards/m/MerchantsDockhand.java +++ b/Mage.Sets/src/mage/cards/m/MerchantsDockhand.java @@ -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"; } diff --git a/Mage.Sets/src/mage/cards/m/MirrorEntity.java b/Mage.Sets/src/mage/cards/m/MirrorEntity.java index b4a1f25e68..2e616b5b63 100644 --- a/Mage.Sets/src/mage/cards/m/MirrorEntity.java +++ b/Mage.Sets/src/mage/cards/m/MirrorEntity.java @@ -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); } diff --git a/Mage.Sets/src/mage/cards/n/NahirisWrath.java b/Mage.Sets/src/mage/cards/n/NahirisWrath.java index eb5f70a0cb..eae4f92b51 100644 --- a/Mage.Sets/src/mage/cards/n/NahirisWrath.java +++ b/Mage.Sets/src/mage/cards/n/NahirisWrath.java @@ -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); - } -} +} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/n/NostalgicDreams.java b/Mage.Sets/src/mage/cards/n/NostalgicDreams.java index 6d9aeed918..695230231f 100644 --- a/Mage.Sets/src/mage/cards/n/NostalgicDreams.java +++ b/Mage.Sets/src/mage/cards/n/NostalgicDreams.java @@ -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(); diff --git a/Mage.Sets/src/mage/cards/p/Painbringer.java b/Mage.Sets/src/mage/cards/p/Painbringer.java index 7a21225f43..1ad9beda52 100644 --- a/Mage.Sets/src/mage/cards/p/Painbringer.java +++ b/Mage.Sets/src/mage/cards/p/Painbringer.java @@ -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) { diff --git a/Mage.Sets/src/mage/cards/p/PanopticMirror.java b/Mage.Sets/src/mage/cards/p/PanopticMirror.java index ee1aabaccf..34e7bc91e8 100644 --- a/Mage.Sets/src/mage/cards/p/PanopticMirror.java +++ b/Mage.Sets/src/mage/cards/p/PanopticMirror.java @@ -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. diff --git a/Mage.Sets/src/mage/cards/p/PerniciousDeed.java b/Mage.Sets/src/mage/cards/p/PerniciousDeed.java index a84867f98a..e389e9b1c8 100644 --- a/Mage.Sets/src/mage/cards/p/PerniciousDeed.java +++ b/Mage.Sets/src/mage/cards/p/PerniciousDeed.java @@ -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); } diff --git a/Mage.Sets/src/mage/cards/r/RestlessDreams.java b/Mage.Sets/src/mage/cards/r/RestlessDreams.java index c7246da6d5..c24e7fb174 100644 --- a/Mage.Sets/src/mage/cards/r/RestlessDreams.java +++ b/Mage.Sets/src/mage/cards/r/RestlessDreams.java @@ -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(); diff --git a/Mage.Sets/src/mage/cards/r/RetributionOfTheAncients.java b/Mage.Sets/src/mage/cards/r/RetributionOfTheAncients.java index a58fc99f7f..3aa922b835 100644 --- a/Mage.Sets/src/mage/cards/r/RetributionOfTheAncients.java +++ b/Mage.Sets/src/mage/cards/r/RetributionOfTheAncients.java @@ -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); diff --git a/Mage.Sets/src/mage/cards/s/SandsOfDelirium.java b/Mage.Sets/src/mage/cards/s/SandsOfDelirium.java index 5cf023e7cb..e94d9945ba 100644 --- a/Mage.Sets/src/mage/cards/s/SandsOfDelirium.java +++ b/Mage.Sets/src/mage/cards/s/SandsOfDelirium.java @@ -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); diff --git a/Mage.Sets/src/mage/cards/s/SickeningDreams.java b/Mage.Sets/src/mage/cards/s/SickeningDreams.java index 0fe85106d4..19079354d5 100644 --- a/Mage.Sets/src/mage/cards/s/SickeningDreams.java +++ b/Mage.Sets/src/mage/cards/s/SickeningDreams.java @@ -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); - } } \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/s/SpringjackPasture.java b/Mage.Sets/src/mage/cards/s/SpringjackPasture.java index a91e56ea19..ed7b0f7f89 100644 --- a/Mage.Sets/src/mage/cards/s/SpringjackPasture.java +++ b/Mage.Sets/src/mage/cards/s/SpringjackPasture.java @@ -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)); diff --git a/Mage.Sets/src/mage/cards/t/TalonOfPain.java b/Mage.Sets/src/mage/cards/t/TalonOfPain.java index d3f1583a2a..47d58afe26 100644 --- a/Mage.Sets/src/mage/cards/t/TalonOfPain.java +++ b/Mage.Sets/src/mage/cards/t/TalonOfPain.java @@ -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()) { diff --git a/Mage.Sets/src/mage/cards/t/TestamentOfFaith.java b/Mage.Sets/src/mage/cards/t/TestamentOfFaith.java index f7d9846f4b..4c451ad512 100644 --- a/Mage.Sets/src/mage/cards/t/TestamentOfFaith.java +++ b/Mage.Sets/src/mage/cards/t/TestamentOfFaith.java @@ -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 diff --git a/Mage.Sets/src/mage/cards/t/ThievingSkydiver.java b/Mage.Sets/src/mage/cards/t/ThievingSkydiver.java index 36a1bbf83f..8fa7e69d36 100644 --- a/Mage.Sets/src/mage/cards/t/ThievingSkydiver.java +++ b/Mage.Sets/src/mage/cards/t/ThievingSkydiver.java @@ -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."); }); diff --git a/Mage.Sets/src/mage/cards/t/ThoughtDissector.java b/Mage.Sets/src/mage/cards/t/ThoughtDissector.java index 45155f63e6..3c37894aaf 100644 --- a/Mage.Sets/src/mage/cards/t/ThoughtDissector.java +++ b/Mage.Sets/src/mage/cards/t/ThoughtDissector.java @@ -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); diff --git a/Mage.Sets/src/mage/cards/t/TurbulentDreams.java b/Mage.Sets/src/mage/cards/t/TurbulentDreams.java index e2a91ec4d3..01716915bc 100644 --- a/Mage.Sets/src/mage/cards/t/TurbulentDreams.java +++ b/Mage.Sets/src/mage/cards/t/TurbulentDreams.java @@ -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(); diff --git a/Mage.Sets/src/mage/cards/v/VengefulDreams.java b/Mage.Sets/src/mage/cards/v/VengefulDreams.java index efeaad1acc..f02a5a1da6 100644 --- a/Mage.Sets/src/mage/cards/v/VengefulDreams.java +++ b/Mage.Sets/src/mage/cards/v/VengefulDreams.java @@ -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(); diff --git a/Mage.Sets/src/mage/cards/v/ViciousBetrayal.java b/Mage.Sets/src/mage/cards/v/ViciousBetrayal.java index 596d8dfadb..1c5a03303d 100644 --- a/Mage.Sets/src/mage/cards/v/ViciousBetrayal.java +++ b/Mage.Sets/src/mage/cards/v/ViciousBetrayal.java @@ -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)); diff --git a/Mage.Sets/src/mage/cards/w/WanderingArchaic.java b/Mage.Sets/src/mage/cards/w/WanderingArchaic.java index 95775a39e8..caec8d1b10 100644 --- a/Mage.Sets/src/mage/cards/w/WanderingArchaic.java +++ b/Mage.Sets/src/mage/cards/w/WanderingArchaic.java @@ -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)) { diff --git a/Mage.Sets/src/mage/cards/w/WhirlwindDenial.java b/Mage.Sets/src/mage/cards/w/WhirlwindDenial.java index d13c6bdd90..1df06fd581 100644 --- a/Mage.Sets/src/mage/cards/w/WhirlwindDenial.java +++ b/Mage.Sets/src/mage/cards/w/WhirlwindDenial.java @@ -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); }); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/KickerTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/KickerTest.java index 8afe385617..2b4e91d129 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/KickerTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/KickerTest.java @@ -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); + } } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/UnboundFlourishingTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/UnboundFlourishingTest.java index 71811dea30..5017e677c3 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/continuous/UnboundFlourishingTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/continuous/UnboundFlourishingTest.java @@ -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()); diff --git a/Mage/src/main/java/mage/abilities/AbilityImpl.java b/Mage/src/main/java/mage/abilities/AbilityImpl.java index f5ad920bd6..6816176332 100644 --- a/Mage/src/main/java/mage/abilities/AbilityImpl.java +++ b/Mage/src/main/java/mage/abilities/AbilityImpl.java @@ -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(); diff --git a/Mage/src/main/java/mage/abilities/costs/OptionalAdditionalCost.java b/Mage/src/main/java/mage/abilities/costs/OptionalAdditionalCost.java index a97e768731..f8b4796a30 100644 --- a/Mage/src/main/java/mage/abilities/costs/OptionalAdditionalCost.java +++ b/Mage/src/main/java/mage/abilities/costs/OptionalAdditionalCost.java @@ -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 + *

+ * 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(); } diff --git a/Mage/src/main/java/mage/abilities/costs/OptionalAdditionalCostImpl.java b/Mage/src/main/java/mage/abilities/costs/OptionalAdditionalCostImpl.java index 30b1db820f..10440512d0 100644 --- a/Mage/src/main/java/mage/abilities/costs/OptionalAdditionalCostImpl.java +++ b/Mage/src/main/java/mage/abilities/costs/OptionalAdditionalCostImpl.java @@ -166,6 +166,12 @@ public class OptionalAdditionalCostImpl extends CostsImpl implements Optio return activatedCounter; } + @Override + public void setCostType(VariableCostType costType) { + this.getVariableCosts().forEach(cost -> { + cost.setCostType(costType); + }); + } @Override public OptionalAdditionalCostImpl copy() { diff --git a/Mage/src/main/java/mage/abilities/costs/OptionalAdditionalSourceCosts.java b/Mage/src/main/java/mage/abilities/costs/OptionalAdditionalSourceCosts.java index 7893c3505e..8f8dd7b43a 100644 --- a/Mage/src/main/java/mage/abilities/costs/OptionalAdditionalSourceCosts.java +++ b/Mage/src/main/java/mage/abilities/costs/OptionalAdditionalSourceCosts.java @@ -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); diff --git a/Mage/src/main/java/mage/abilities/costs/VariableCost.java b/Mage/src/main/java/mage/abilities/costs/VariableCost.java index eb2bda2250..743b782944 100644 --- a/Mage/src/main/java/mage/abilities/costs/VariableCost.java +++ b/Mage/src/main/java/mage/abilities/costs/VariableCost.java @@ -64,4 +64,8 @@ public interface VariableCost { * @return */ Cost getFixedCostsFromAnnouncedValue(int xValue); + + VariableCostType getCostType(); + + void setCostType(VariableCostType costType); } diff --git a/Mage/src/main/java/mage/abilities/costs/VariableCostImpl.java b/Mage/src/main/java/mage/abilities/costs/VariableCostImpl.java index 4f89a46d3e..bc5d7047c3 100644 --- a/Mage/src/main/java/mage/abilities/costs/VariableCostImpl.java +++ b/Mage/src/main/java/mage/abilities/costs/VariableCostImpl.java @@ -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; + } } diff --git a/Mage/src/main/java/mage/abilities/costs/VariableCostType.java b/Mage/src/main/java/mage/abilities/costs/VariableCostType.java new file mode 100644 index 0000000000..68f71e63df --- /dev/null +++ b/Mage/src/main/java/mage/abilities/costs/VariableCostType.java @@ -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; + } +} diff --git a/Mage/src/main/java/mage/abilities/costs/common/DiscardXTargetCost.java b/Mage/src/main/java/mage/abilities/costs/common/DiscardXTargetCost.java index 8f65b0782f..2a21808b72 100644 --- a/Mage/src/main/java/mage/abilities/costs/common/DiscardXTargetCost.java +++ b/Mage/src/main/java/mage/abilities/costs/common/DiscardXTargetCost.java @@ -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; } diff --git a/Mage/src/main/java/mage/abilities/costs/common/ExileFromHandCost.java b/Mage/src/main/java/mage/abilities/costs/common/ExileFromHandCost.java index b937c39c19..8d3f1a3228 100644 --- a/Mage/src/main/java/mage/abilities/costs/common/ExileFromHandCost.java +++ b/Mage/src/main/java/mage/abilities/costs/common/ExileFromHandCost.java @@ -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 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); diff --git a/Mage/src/main/java/mage/abilities/costs/common/ExileXFromYourGraveCost.java b/Mage/src/main/java/mage/abilities/costs/common/ExileXFromYourGraveCost.java index ee33a18933..cb55ff180f 100644 --- a/Mage/src/main/java/mage/abilities/costs/common/ExileXFromYourGraveCost.java +++ b/Mage/src/main/java/mage/abilities/costs/common/ExileXFromYourGraveCost.java @@ -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) { diff --git a/Mage/src/main/java/mage/abilities/costs/common/PayVariableLifeCost.java b/Mage/src/main/java/mage/abilities/costs/common/PayVariableLifeCost.java index 6e69eead36..4224907d13 100644 --- a/Mage/src/main/java/mage/abilities/costs/common/PayVariableLifeCost.java +++ b/Mage/src/main/java/mage/abilities/costs/common/PayVariableLifeCost.java @@ -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(); } diff --git a/Mage/src/main/java/mage/abilities/costs/common/PayVariableLoyaltyCost.java b/Mage/src/main/java/mage/abilities/costs/common/PayVariableLoyaltyCost.java index bcf6a872ad..b1cacfecfa 100644 --- a/Mage/src/main/java/mage/abilities/costs/common/PayVariableLoyaltyCost.java +++ b/Mage/src/main/java/mage/abilities/costs/common/PayVariableLoyaltyCost.java @@ -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"; } diff --git a/Mage/src/main/java/mage/abilities/costs/common/RemoveVariableCountersSourceCost.java b/Mage/src/main/java/mage/abilities/costs/common/RemoveVariableCountersSourceCost.java index 3aeec0594e..b2b122a756 100644 --- a/Mage/src/main/java/mage/abilities/costs/common/RemoveVariableCountersSourceCost.java +++ b/Mage/src/main/java/mage/abilities/costs/common/RemoveVariableCountersSourceCost.java @@ -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()) { diff --git a/Mage/src/main/java/mage/abilities/costs/common/RemoveVariableCountersTargetCost.java b/Mage/src/main/java/mage/abilities/costs/common/RemoveVariableCountersTargetCost.java index bdae6a38c5..0b017a680e 100644 --- a/Mage/src/main/java/mage/abilities/costs/common/RemoveVariableCountersTargetCost.java +++ b/Mage/src/main/java/mage/abilities/costs/common/RemoveVariableCountersTargetCost.java @@ -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); } } diff --git a/Mage/src/main/java/mage/abilities/costs/common/SacrificeXTargetCost.java b/Mage/src/main/java/mage/abilities/costs/common/SacrificeXTargetCost.java index 99f7bd1614..a2b043a336 100644 --- a/Mage/src/main/java/mage/abilities/costs/common/SacrificeXTargetCost.java +++ b/Mage/src/main/java/mage/abilities/costs/common/SacrificeXTargetCost.java @@ -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; } diff --git a/Mage/src/main/java/mage/abilities/costs/common/TapVariableTargetCost.java b/Mage/src/main/java/mage/abilities/costs/common/TapVariableTargetCost.java index 92db2c8540..7bb1d31184 100644 --- a/Mage/src/main/java/mage/abilities/costs/common/TapVariableTargetCost.java +++ b/Mage/src/main/java/mage/abilities/costs/common/TapVariableTargetCost.java @@ -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(); } diff --git a/Mage/src/main/java/mage/abilities/costs/mana/ManaCostsImpl.java b/Mage/src/main/java/mage/abilities/costs/mana/ManaCostsImpl.java index 165fe43e8a..7de0101755 100644 --- a/Mage/src/main/java/mage/abilities/costs/mana/ManaCostsImpl.java +++ b/Mage/src/main/java/mage/abilities/costs/mana/ManaCostsImpl.java @@ -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 extends ArrayList implements M protected final UUID id; protected String text = null; - private static Map costsCache = new ConcurrentHashMap<>(); // must be thread safe, can't use nulls + private static final Map costsCache = new ConcurrentHashMap<>(); // must be thread safe, can't use nulls public ManaCostsImpl() { this.id = UUID.randomUUID(); @@ -471,7 +468,7 @@ public class ManaCostsImpl extends ArrayList 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; diff --git a/Mage/src/main/java/mage/abilities/costs/mana/VariableManaCost.java b/Mage/src/main/java/mage/abilities/costs/mana/VariableManaCost.java index bd77f3c076..491f0dd911 100644 --- a/Mage/src/main/java/mage/abilities/costs/mana/VariableManaCost.java +++ b/Mage/src/main/java/mage/abilities/costs/mana/VariableManaCost.java @@ -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; + } } diff --git a/Mage/src/main/java/mage/abilities/keyword/BuybackAbility.java b/Mage/src/main/java/mage/abilities/keyword/BuybackAbility.java index 44bab1cb43..9e20f64e2a 100644 --- a/Mage/src/main/java/mage/abilities/keyword/BuybackAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/BuybackAbility.java @@ -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 diff --git a/Mage/src/main/java/mage/abilities/keyword/ConspireAbility.java b/Mage/src/main/java/mage/abilities/keyword/ConspireAbility.java index 87a45f5b43..43332eb8b5 100644 --- a/Mage/src/main/java/mage/abilities/keyword/ConspireAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/ConspireAbility.java @@ -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()); diff --git a/Mage/src/main/java/mage/abilities/keyword/EntwineAbility.java b/Mage/src/main/java/mage/abilities/keyword/EntwineAbility.java index 5c258241ce..0b299abd14 100644 --- a/Mage/src/main/java/mage/abilities/keyword/EntwineAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/EntwineAbility.java @@ -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 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); } diff --git a/Mage/src/main/java/mage/abilities/keyword/KickerAbility.java b/Mage/src/main/java/mage/abilities/keyword/KickerAbility.java index ae06330536..5ad122945e 100644 --- a/Mage/src/main/java/mage/abilities/keyword/KickerAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/KickerAbility.java @@ -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 { diff --git a/Mage/src/main/java/mage/abilities/keyword/SuspendAbility.java b/Mage/src/main/java/mage/abilities/keyword/SuspendAbility.java index e29b7ce273..12c6d39f26 100644 --- a/Mage/src/main/java/mage/abilities/keyword/SuspendAbility.java +++ b/Mage/src/main/java/mage/abilities/keyword/SuspendAbility.java @@ -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(" (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); diff --git a/Mage/src/main/java/mage/game/command/emblems/MomirEmblem.java b/Mage/src/main/java/mage/game/command/emblems/MomirEmblem.java index 62db3b1113..c6417013ee 100644 --- a/Mage/src/main/java/mage/game/command/emblems/MomirEmblem.java +++ b/Mage/src/main/java/mage/game/command/emblems/MomirEmblem.java @@ -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; } diff --git a/Mage/src/main/java/mage/util/ManaUtil.java b/Mage/src/main/java/mage/util/ManaUtil.java index 14998a5eb0..5276cafc9d 100644 --- a/Mage/src/main/java/mage/util/ManaUtil.java +++ b/Mage/src/main/java/mage/util/ManaUtil.java @@ -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 {