From 5e10c3a279968bd762b0ceb84d68ac0af2aaa575 Mon Sep 17 00:00:00 2001 From: "Alex W. Jackson" Date: Mon, 24 Oct 2022 07:48:32 -0400 Subject: [PATCH] Replace many custom CostImpl classes with common ones. Fix some wrong text. Fix #9679 --- Mage.Sets/src/mage/cards/a/Aboroth.java | 40 +------- Mage.Sets/src/mage/cards/a/AltarOfBhaal.java | 2 +- Mage.Sets/src/mage/cards/b/Blizzard.java | 7 +- Mage.Sets/src/mage/cards/c/CityOfShadows.java | 6 +- Mage.Sets/src/mage/cards/f/FoodChain.java | 2 +- .../src/mage/cards/g/GutwrencherOni.java | 26 ++--- .../src/mage/cards/h/HisokaMinamoSensei.java | 84 +++------------- Mage.Sets/src/mage/cards/l/Leashling.java | 64 +----------- .../mage/cards/n/NecromancersStockpile.java | 97 ++++--------------- Mage.Sets/src/mage/cards/n/NecroticFumes.java | 2 +- .../src/mage/cards/p/PainwrackerOni.java | 61 +++++------- .../src/mage/cards/s/ScourgeOfNumai.java | 62 +++++------- .../src/mage/cards/s/SilvergillAdept.java | 88 +++-------------- Mage.Sets/src/mage/cards/s/SoulExchange.java | 2 +- Mage.Sets/src/mage/cards/t/TectonicEdge.java | 58 ++--------- .../src/mage/cards/t/TidalInfluence.java | 89 ++++++----------- Mage.Sets/src/mage/cards/t/TombOfUrami.java | 69 +++++-------- .../src/mage/cards/v/VolrathsDungeon.java | 62 +----------- Mage.Sets/src/mage/cards/w/WormfangDrake.java | 74 +++----------- .../costs/common/ExileTargetCost.java | 5 +- 20 files changed, 211 insertions(+), 689 deletions(-) diff --git a/Mage.Sets/src/mage/cards/a/Aboroth.java b/Mage.Sets/src/mage/cards/a/Aboroth.java index df7955ce33..993ceb37eb 100644 --- a/Mage.Sets/src/mage/cards/a/Aboroth.java +++ b/Mage.Sets/src/mage/cards/a/Aboroth.java @@ -1,19 +1,15 @@ - package mage.cards.a; import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.costs.Cost; -import mage.abilities.costs.CostImpl; +import mage.abilities.costs.common.PutCountersSourceCost; import mage.abilities.keyword.CumulativeUpkeepAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.counters.CounterType; -import mage.game.Game; -import mage.game.permanent.Permanent; /** * @@ -29,7 +25,7 @@ public final class Aboroth extends CardImpl { this.toughness = new MageInt(9); // Cumulative upkeep-Put a -1/-1 counter on Aboroth. - this.addAbility(new CumulativeUpkeepAbility(new AborothCost())); + this.addAbility(new CumulativeUpkeepAbility(new PutCountersSourceCost(CounterType.M1M1.createInstance()))); } private Aboroth(final Aboroth card) { @@ -41,35 +37,3 @@ public final class Aboroth extends CardImpl { return new Aboroth(this); } } - -class AborothCost extends CostImpl { - - public AborothCost() { - this.text = "Put a -1/-1 counter on Aboroth"; - } - - private AborothCost(final AborothCost cost) { - super(cost); - } - - @Override - public boolean pay(Ability ability, Game game, Ability source, UUID controllerId, boolean noMana, Cost costToPay) { - Permanent permanent = game.getPermanent(source.getSourceId()); - if (permanent != null) { - permanent.addCounters(CounterType.M1M1.createInstance(), controllerId, ability, game); - this.paid = true; - return true; - } - return false; - } - - @Override - public boolean canPay(Ability ability, Ability source, UUID controllerId, Game game) { - return true; - } - - @Override - public AborothCost copy() { - return new AborothCost(this); - } -} diff --git a/Mage.Sets/src/mage/cards/a/AltarOfBhaal.java b/Mage.Sets/src/mage/cards/a/AltarOfBhaal.java index 383991853f..377da435a5 100644 --- a/Mage.Sets/src/mage/cards/a/AltarOfBhaal.java +++ b/Mage.Sets/src/mage/cards/a/AltarOfBhaal.java @@ -30,7 +30,7 @@ public final class AltarOfBhaal extends AdventureCard { new ReturnFromGraveyardToBattlefieldTargetEffect(), new ManaCostsImpl<>("{2}{B}") ); ability.addCost(new TapSourceCost()); - ability.addCost(new ExileTargetCost(new TargetControlledPermanent(1, 1, StaticFilters.FILTER_CONTROLLED_A_CREATURE, true))); + ability.addCost(new ExileTargetCost(new TargetControlledPermanent(StaticFilters.FILTER_CONTROLLED_A_CREATURE))); ability.addTarget(new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_CREATURE_YOUR_GRAVEYARD)); this.addAbility(ability); diff --git a/Mage.Sets/src/mage/cards/b/Blizzard.java b/Mage.Sets/src/mage/cards/b/Blizzard.java index 8d3a6d0ed1..b72ec8bf09 100644 --- a/Mage.Sets/src/mage/cards/b/Blizzard.java +++ b/Mage.Sets/src/mage/cards/b/Blizzard.java @@ -1,11 +1,10 @@ - package mage.cards.b; import java.util.UUID; import mage.abilities.common.CastOnlyIfConditionIsTrueAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; -import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.effects.common.DontUntapInControllersUntapStepAllEffect; import mage.abilities.keyword.CumulativeUpkeepAbility; import mage.abilities.keyword.FlyingAbility; @@ -27,7 +26,7 @@ import mage.filter.predicate.mageobject.AbilityPredicate; */ public final class Blizzard extends CardImpl { - private static final FilterControlledLandPermanent filter = new FilterControlledLandPermanent("a snow land"); + private static final FilterControlledLandPermanent filter = new FilterControlledLandPermanent("if you control a snow land"); private static final FilterCreaturePermanent filter2 = new FilterCreaturePermanent("creatures with flying"); static { @@ -44,7 +43,7 @@ public final class Blizzard extends CardImpl { )); // Cumulative upkeep {2} - this.addAbility(new CumulativeUpkeepAbility(new ManaCostsImpl<>("{2}"))); + this.addAbility(new CumulativeUpkeepAbility(new GenericManaCost(2))); // Creatures with flying don't untap during their controllers' untap steps. this.addAbility(new SimpleStaticAbility( diff --git a/Mage.Sets/src/mage/cards/c/CityOfShadows.java b/Mage.Sets/src/mage/cards/c/CityOfShadows.java index b4e11fdffd..1a260c13fb 100644 --- a/Mage.Sets/src/mage/cards/c/CityOfShadows.java +++ b/Mage.Sets/src/mage/cards/c/CityOfShadows.java @@ -28,12 +28,12 @@ public final class CityOfShadows extends CardImpl { // {T}, Exile a creature you control: Put a storage counter on City of Shadows. Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new AddCountersSourceEffect(CounterType.STORAGE.createInstance()), new TapSourceCost()); - ability.addCost(new ExileTargetCost(new TargetControlledCreaturePermanent(1, 1, StaticFilters.FILTER_CONTROLLED_A_CREATURE, true))); + ability.addCost(new ExileTargetCost(new TargetControlledCreaturePermanent(StaticFilters.FILTER_CONTROLLED_A_CREATURE))); this.addAbility(ability); - // {T}: Add X mana of {C}, where X is the number of storage counters on City of Shadows. + // {T}: Add {C} for each storage counter on City of Shadows. ability = new DynamicManaAbility(Mana.ColorlessMana(1), new CountersSourceCount(CounterType.STORAGE), - "Add X mana of {C}, where X is the number of storage counters on {this}"); + "Add {C} for each storage counter on {this}"); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/f/FoodChain.java b/Mage.Sets/src/mage/cards/f/FoodChain.java index 0baaf25c51..fd040e035c 100644 --- a/Mage.Sets/src/mage/cards/f/FoodChain.java +++ b/Mage.Sets/src/mage/cards/f/FoodChain.java @@ -36,7 +36,7 @@ public final class FoodChain extends CardImpl { // Exile a creature you control: Add X mana of any one color, where X is the exiled creature's converted mana cost plus one. Spend this mana only to cast creature spells. Ability ability = new SimpleManaAbility(Zone.BATTLEFIELD, new FoodChainManaEffect(), - new ExileTargetCost(new TargetControlledCreaturePermanent(1, 1, StaticFilters.FILTER_CONTROLLED_A_CREATURE, true))); + new ExileTargetCost(new TargetControlledCreaturePermanent(StaticFilters.FILTER_CONTROLLED_A_CREATURE))); this.addAbility(ability); } diff --git a/Mage.Sets/src/mage/cards/g/GutwrencherOni.java b/Mage.Sets/src/mage/cards/g/GutwrencherOni.java index 9972557554..d7074d61b9 100644 --- a/Mage.Sets/src/mage/cards/g/GutwrencherOni.java +++ b/Mage.Sets/src/mage/cards/g/GutwrencherOni.java @@ -1,9 +1,9 @@ - package mage.cards.g; import java.util.UUID; import mage.MageInt; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; import mage.abilities.decorator.ConditionalOneShotEffect; import mage.abilities.effects.common.discard.DiscardControllerEffect; @@ -14,8 +14,7 @@ import mage.constants.CardType; import mage.constants.ComparisonType; import mage.constants.SubType; import mage.constants.TargetController; -import mage.filter.FilterPermanent; -import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.common.FilterControlledPermanent; /** * @@ -23,16 +22,17 @@ import mage.filter.common.FilterControlledCreaturePermanent; */ public final class GutwrencherOni extends CardImpl { - private static final FilterPermanent filter = new FilterControlledCreaturePermanent("Ogre"); + private static final FilterControlledPermanent filter = new FilterControlledPermanent("Ogre"); static { filter.add(SubType.OGRE.getPredicate()); } + private static final Condition condition = new PermanentsOnTheBattlefieldCondition(filter, ComparisonType.EQUAL_TO, 0); + public GutwrencherOni(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{B}{B}"); - this.subtype.add(SubType.DEMON); - this.subtype.add(SubType.SPIRIT); + this.subtype.add(SubType.DEMON, SubType.SPIRIT); this.power = new MageInt(5); this.toughness = new MageInt(4); @@ -41,11 +41,14 @@ public final class GutwrencherOni extends CardImpl { this.addAbility(TrampleAbility.getInstance()); // At the beginning of your upkeep, discard a card if you don't control an Ogre. - this.addAbility(new BeginningOfUpkeepTriggeredAbility(new ConditionalOneShotEffect( - new DiscardControllerEffect(1), - new PermanentsOnTheBattlefieldCondition(filter, ComparisonType.EQUAL_TO, 0), - "discard a card if you don't control an Ogre"), TargetController.YOU, false)); - + this.addAbility(new BeginningOfUpkeepTriggeredAbility( + new ConditionalOneShotEffect( + new DiscardControllerEffect(1), + condition, + "discard a card if you don't control an Ogre" + ), + TargetController.YOU, false + )); } private GutwrencherOni(final GutwrencherOni card) { @@ -57,4 +60,3 @@ public final class GutwrencherOni extends CardImpl { return new GutwrencherOni(this); } } - diff --git a/Mage.Sets/src/mage/cards/h/HisokaMinamoSensei.java b/Mage.Sets/src/mage/cards/h/HisokaMinamoSensei.java index 59e31b19a4..1c3ebbc524 100644 --- a/Mage.Sets/src/mage/cards/h/HisokaMinamoSensei.java +++ b/Mage.Sets/src/mage/cards/h/HisokaMinamoSensei.java @@ -1,29 +1,24 @@ - - package mage.cards.h; +import java.util.Collection; 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.CostImpl; +import mage.abilities.costs.common.DiscardCardCost; +import mage.abilities.costs.common.DiscardTargetCost; 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.constants.CardType; import mage.constants.SubType; import mage.constants.Outcome; import mage.constants.SuperType; -import mage.constants.Zone; -import mage.filter.FilterCard; import mage.game.Game; import mage.game.stack.Spell; -import mage.players.Player; import mage.target.TargetSpell; -import mage.target.common.TargetCardInHand; +import mage.util.CardUtil; /** * @author LevelX @@ -33,17 +28,15 @@ public final class HisokaMinamoSensei extends CardImpl { public HisokaMinamoSensei(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{U}{U}"); this.addSuperType(SuperType.LEGENDARY); - this.subtype.add(SubType.HUMAN); - this.subtype.add(SubType.WIZARD); + this.subtype.add(SubType.HUMAN, SubType.WIZARD); this.power = new MageInt(1); this.toughness = new MageInt(3); // {2}{U}, Discard a card: Counter target spell if it has the same converted mana cost as the discarded card. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new HisokaMinamoSenseiCounterEffect(), new ManaCostsImpl<>("{2}{U}")); + Ability ability = new SimpleActivatedAbility(new HisokaMinamoSenseiCounterEffect(), new ManaCostsImpl<>("{2}{U}")); + ability.addCost(new DiscardCardCost()); ability.addTarget(new TargetSpell()); - TargetCardInHand targetCard = new TargetCardInHand(new FilterCard("a card")); - ability.addCost(new HisokaMinamoSenseiDiscardTargetCost(targetCard)); this.addAbility(ability); } @@ -55,54 +48,6 @@ public final class HisokaMinamoSensei extends CardImpl { public HisokaMinamoSensei copy() { return new HisokaMinamoSensei(this); } - -} - -class HisokaMinamoSenseiDiscardTargetCost extends CostImpl { - - protected Card card = null; - - public HisokaMinamoSenseiDiscardTargetCost(TargetCardInHand target) { - this.addTarget(target); - this.text = "Discard " + target.getTargetName(); - } - - public HisokaMinamoSenseiDiscardTargetCost(HisokaMinamoSenseiDiscardTargetCost cost) { - super(cost); - } - - @Override - public boolean pay(Ability ability, Game game, Ability source, UUID controllerId, boolean noMana, Cost costToPay) { - if (targets.choose(Outcome.Discard, controllerId, source.getSourceId(), source, game)) { - Player player = game.getPlayer(controllerId); - if(player != null) { - for (UUID targetId : targets.get(0).getTargets()) { - card = player.getHand().get(targetId, game); - if (card == null) { - return false; - } - paid |= player.discard(card, true, source, game); - - } - } - } - return paid; - } - - @Override - public boolean canPay(Ability ability, Ability source, UUID controllerId, Game game) { - return targets.canChoose(controllerId, source, game); - } - - @Override - public HisokaMinamoSenseiDiscardTargetCost copy() { - return new HisokaMinamoSenseiDiscardTargetCost(this); - } - - public Card getDiscardedCard() { - return card; - } - } class HisokaMinamoSenseiCounterEffect extends OneShotEffect { @@ -118,11 +63,14 @@ class HisokaMinamoSenseiCounterEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Spell spell = game.getStack().getSpell(targetPointer.getFirst(game, source)); - if (spell != null) { - HisokaMinamoSenseiDiscardTargetCost cost = (HisokaMinamoSenseiDiscardTargetCost) source.getCosts().get(0); - if (cost != null && cost.getDiscardedCard().getManaValue() == spell.getManaValue()) { - return game.getStack().counter(targetPointer.getFirst(game, source), source, game); - } + if (spell == null) { + return false; + } + if (CardUtil.castStream(source.getCosts().stream(), DiscardTargetCost.class) + .map(DiscardTargetCost::getCards) + .flatMap(Collection::stream) + .anyMatch(card -> card.getManaValue() == spell.getManaValue())) { + return game.getStack().counter(targetPointer.getFirst(game, source), source, game); } return false; } @@ -131,4 +79,4 @@ class HisokaMinamoSenseiCounterEffect extends OneShotEffect { public HisokaMinamoSenseiCounterEffect copy() { return new HisokaMinamoSenseiCounterEffect(this); } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/l/Leashling.java b/Mage.Sets/src/mage/cards/l/Leashling.java index 9a797fe70e..b35d626ab9 100644 --- a/Mage.Sets/src/mage/cards/l/Leashling.java +++ b/Mage.Sets/src/mage/cards/l/Leashling.java @@ -1,24 +1,15 @@ - package mage.cards.l; 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.CostImpl; +import mage.abilities.costs.common.PutCardFromHandOnTopOfLibraryCost; import mage.abilities.effects.common.ReturnToHandSourceEffect; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.cards.CardsImpl; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.Outcome; -import mage.constants.Zone; -import mage.game.Game; -import mage.players.Player; -import mage.target.common.TargetCardInHand; + /** * @@ -33,7 +24,7 @@ public final class Leashling extends CardImpl { this.toughness = new MageInt(3); // Put a card from your hand on top of your library: Return Leashling to its owner's hand. - this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new ReturnToHandSourceEffect(), new PutCardFromHandOnTopOfLibrary())); + this.addAbility(new SimpleActivatedAbility(new ReturnToHandSourceEffect(), new PutCardFromHandOnTopOfLibraryCost())); } private Leashling(final Leashling card) { @@ -45,52 +36,3 @@ public final class Leashling extends CardImpl { return new Leashling(this); } } - -class PutCardFromHandOnTopOfLibrary extends CostImpl { - - protected final int amount; - - public PutCardFromHandOnTopOfLibrary() { - this(1); - } - - public PutCardFromHandOnTopOfLibrary(final int amount) { - this.amount = amount; - this.text = "put " + (amount == 1 ? "a card" : (amount + " cards")) + " from your hand on top of your library"; - } - - public PutCardFromHandOnTopOfLibrary(final PutCardFromHandOnTopOfLibrary cost) { - super(cost); - this.amount = cost.amount; - } - - @Override - public Cost copy() { - return new PutCardFromHandOnTopOfLibrary(this); - } - - @Override - public boolean canPay(Ability ability, Ability source, UUID controllerId, Game game) { - Player controller = game.getPlayer(controllerId); - if (controller != null) { - return !controller.getHand().isEmpty(); - } - return false; - } - - @Override - public boolean pay(Ability ability, Game game, Ability source, UUID controllerId, boolean noMana, Cost costToPay) { - Player controller = game.getPlayer(controllerId); - if (controller != null) { - TargetCardInHand target = new TargetCardInHand(); - controller.chooseTarget(Outcome.ReturnToHand, target, ability, game); - Card card = controller.getHand().get(target.getFirstTarget(), game); - if (card != null) { - controller.putCardsOnTopOfLibrary(new CardsImpl(card), game, ability, false); - paid = true; - } - } - return paid; - } - -} diff --git a/Mage.Sets/src/mage/cards/n/NecromancersStockpile.java b/Mage.Sets/src/mage/cards/n/NecromancersStockpile.java index fe040b74a1..e9669ff7df 100644 --- a/Mage.Sets/src/mage/cards/n/NecromancersStockpile.java +++ b/Mage.Sets/src/mage/cards/n/NecromancersStockpile.java @@ -1,29 +1,27 @@ package mage.cards.n; +import java.util.Collection; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; -import mage.abilities.costs.Cost; -import mage.abilities.costs.CostImpl; +import mage.abilities.condition.Condition; +import mage.abilities.costs.common.DiscardTargetCost; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.decorator.ConditionalOneShotEffect; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.DrawCardSourceControllerEffect; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.SubType; -import mage.constants.Zone; import mage.filter.StaticFilters; import mage.game.Game; import mage.game.permanent.token.ZombieToken; -import mage.players.Player; import mage.target.common.TargetCardInHand; +import mage.util.CardUtil; /** - * @author noxx + * @author awjackson */ public final class NecromancersStockpile extends CardImpl { @@ -32,9 +30,12 @@ public final class NecromancersStockpile extends CardImpl { // {1}{B}, Discard a creature card: Draw a card. // If the discarded card was a Zombie card, create a tapped 2/2 black Zombie creature token. - Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(1), new ManaCostsImpl<>("{1}{B}")); - ability.addCost(new NecromancersStockpileDiscardTargetCost(new TargetCardInHand(StaticFilters.FILTER_CARD_CREATURE))); - ability.addEffect(new NecromancersStockpilePutTokenEffect()); + Ability ability = new SimpleActivatedAbility(new DrawCardSourceControllerEffect(1), new ManaCostsImpl<>("{1}{B}")); + ability.addCost(new DiscardTargetCost(new TargetCardInHand(StaticFilters.FILTER_CARD_CREATURE_A))); + ability.addEffect(new ConditionalOneShotEffect( + new CreateTokenEffect(new ZombieToken(), 1, true, false), + NecromancersStockpileCondition.instance + )); this.addAbility(ability); } @@ -46,79 +47,21 @@ public final class NecromancersStockpile extends CardImpl { public NecromancersStockpile copy() { return new NecromancersStockpile(this); } - } -class NecromancersStockpileDiscardTargetCost extends CostImpl { - - protected boolean isZombieCard; - - public NecromancersStockpileDiscardTargetCost(TargetCardInHand target) { - this.addTarget(target); - this.text = "Discard " + target.getTargetName(); - } - - public NecromancersStockpileDiscardTargetCost(NecromancersStockpileDiscardTargetCost cost) { - super(cost); - } - - @Override - public boolean pay(Ability ability, Game game, Ability source, UUID controllerId, boolean noMana, Cost costToPay) { - if (targets.choose(Outcome.Discard, controllerId, source.getSourceId(), source, game)) { - Player player = game.getPlayer(controllerId); - if (player != null) { - for (UUID targetId : targets.get(0).getTargets()) { - Card card = player.getHand().get(targetId, game); - if (card == null) { - return false; - } - isZombieCard = card.hasSubtype(SubType.ZOMBIE, game); - paid |= player.discard(card, true, source, game); - - } - } - } - return paid; - } - - @Override - public boolean canPay(Ability ability, Ability source, UUID controllerId, Game game) { - return targets.canChoose(controllerId, source, game); - } - - @Override - public NecromancersStockpileDiscardTargetCost copy() { - return new NecromancersStockpileDiscardTargetCost(this); - } - - public boolean isZombieCard() { - return isZombieCard; - } - -} - -class NecromancersStockpilePutTokenEffect extends OneShotEffect { - - NecromancersStockpilePutTokenEffect() { - super(Outcome.Neutral); - staticText = "If the discarded card was a Zombie card, create a tapped 2/2 black Zombie creature token"; - } - - NecromancersStockpilePutTokenEffect(final NecromancersStockpilePutTokenEffect effect) { - super(effect); - } +enum NecromancersStockpileCondition implements Condition { + instance; @Override public boolean apply(Game game, Ability source) { - NecromancersStockpileDiscardTargetCost cost = (NecromancersStockpileDiscardTargetCost) source.getCosts().get(0); - if (cost != null && cost.isZombieCard()) { - new CreateTokenEffect(new ZombieToken(), 1, true, false).apply(game, source); - } - return true; + return CardUtil.castStream(source.getCosts().stream(), DiscardTargetCost.class) + .map(DiscardTargetCost::getCards) + .flatMap(Collection::stream) + .anyMatch(card -> card.hasSubtype(SubType.ZOMBIE, game)); } @Override - public NecromancersStockpilePutTokenEffect copy() { - return new NecromancersStockpilePutTokenEffect(this); + public String toString() { + return "the discarded card was a Zombie card"; } } diff --git a/Mage.Sets/src/mage/cards/n/NecroticFumes.java b/Mage.Sets/src/mage/cards/n/NecroticFumes.java index 5f2d3e462c..eecd4e9c73 100644 --- a/Mage.Sets/src/mage/cards/n/NecroticFumes.java +++ b/Mage.Sets/src/mage/cards/n/NecroticFumes.java @@ -23,7 +23,7 @@ public final class NecroticFumes extends CardImpl { this.subtype.add(SubType.LESSON); // As an additional cost to cast this spell, exile a creature you control. - this.getSpellAbility().addCost(new ExileTargetCost(new TargetControlledPermanent(1, 1, StaticFilters.FILTER_CONTROLLED_A_CREATURE, true))); + this.getSpellAbility().addCost(new ExileTargetCost(new TargetControlledPermanent(StaticFilters.FILTER_CONTROLLED_A_CREATURE))); // Exile target creature or planeswalker. this.getSpellAbility().addEffect(new ExileTargetEffect()); diff --git a/Mage.Sets/src/mage/cards/p/PainwrackerOni.java b/Mage.Sets/src/mage/cards/p/PainwrackerOni.java index f71eafb06e..fbf4475c67 100644 --- a/Mage.Sets/src/mage/cards/p/PainwrackerOni.java +++ b/Mage.Sets/src/mage/cards/p/PainwrackerOni.java @@ -1,22 +1,21 @@ - - package mage.cards.p; import java.util.UUID; import mage.MageInt; -import mage.abilities.Ability; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; +import mage.abilities.decorator.ConditionalOneShotEffect; import mage.abilities.effects.common.SacrificeControllerEffect; import mage.abilities.keyword.FearAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.ComparisonType; import mage.constants.SubType; import mage.constants.TargetController; -import mage.filter.FilterPermanent; -import mage.filter.common.FilterControlledCreaturePermanent; -import mage.filter.common.FilterCreaturePermanent; -import mage.game.Game; +import mage.filter.StaticFilters; +import mage.filter.common.FilterControlledPermanent; /** * @@ -24,19 +23,33 @@ import mage.game.Game; */ public final class PainwrackerOni extends CardImpl { + private static final FilterControlledPermanent filter = new FilterControlledPermanent("Ogre"); + + static { + filter.add(SubType.OGRE.getPredicate()); + } + + private static final Condition condition = new PermanentsOnTheBattlefieldCondition(filter, ComparisonType.EQUAL_TO, 0); + public PainwrackerOni (UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{B}{B}"); - this.subtype.add(SubType.DEMON); - this.subtype.add(SubType.SPIRIT); + this.subtype.add(SubType.DEMON, SubType.SPIRIT); this.power = new MageInt(5); this.toughness = new MageInt(4); // Fear (This creature can't be blocked except by artifact creatures and/or black creatures.) this.addAbility(FearAbility.getInstance()); - + // At the beginning of your upkeep, sacrifice a creature if you don't control an Ogre. - this.addAbility(new BeginningOfUpkeepTriggeredAbility(new PainwrackerOniEffect(new FilterControlledCreaturePermanent(), 1, ""), TargetController.YOU, false)); + this.addAbility(new BeginningOfUpkeepTriggeredAbility( + new ConditionalOneShotEffect( + new SacrificeControllerEffect(StaticFilters.FILTER_PERMANENT_CREATURE, 1, null), + condition, + "sacrifice a creature if you don't control an Ogre" + ), + TargetController.YOU, false + )); } public PainwrackerOni (final PainwrackerOni card) { @@ -47,30 +60,4 @@ public final class PainwrackerOni extends CardImpl { public PainwrackerOni copy() { return new PainwrackerOni(this); } - -} - -class PainwrackerOniEffect extends SacrificeControllerEffect { - - public PainwrackerOniEffect(FilterPermanent filter, int count, String preText) { - super(filter, count, preText); - this.staticText = "sacrifice a creature if you don't control an Ogre"; - } - - public PainwrackerOniEffect(final PainwrackerOniEffect effect) { - super(effect); - } - - @Override - public PainwrackerOniEffect copy() { - return new PainwrackerOniEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - if (game.getBattlefield().countAll(new FilterCreaturePermanent(SubType.OGRE, "Ogre"), source.getControllerId(), game) < 1) { - return super.apply(game, source); - } - return true; - } } diff --git a/Mage.Sets/src/mage/cards/s/ScourgeOfNumai.java b/Mage.Sets/src/mage/cards/s/ScourgeOfNumai.java index 123b10c996..71910a6b88 100644 --- a/Mage.Sets/src/mage/cards/s/ScourgeOfNumai.java +++ b/Mage.Sets/src/mage/cards/s/ScourgeOfNumai.java @@ -1,20 +1,19 @@ - package mage.cards.s; import java.util.UUID; import mage.MageInt; -import mage.abilities.Ability; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; +import mage.abilities.decorator.ConditionalOneShotEffect; +import mage.abilities.effects.common.LoseLifeSourceControllerEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; +import mage.constants.ComparisonType; import mage.constants.SubType; import mage.constants.TargetController; -import mage.filter.common.FilterCreaturePermanent; -import mage.game.Game; -import mage.players.Player; +import mage.filter.common.FilterControlledPermanent; /** * @@ -22,16 +21,30 @@ import mage.players.Player; */ public final class ScourgeOfNumai extends CardImpl { + private static final FilterControlledPermanent filter = new FilterControlledPermanent("Ogre"); + + static { + filter.add(SubType.OGRE.getPredicate()); + } + + private static final Condition condition = new PermanentsOnTheBattlefieldCondition(filter, ComparisonType.EQUAL_TO, 0); + public ScourgeOfNumai(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{B}"); - this.subtype.add(SubType.DEMON); - this.subtype.add(SubType.SPIRIT); + this.subtype.add(SubType.DEMON, SubType.SPIRIT); this.power = new MageInt(4); this.toughness = new MageInt(4); // At the beginning of your upkeep, you lose 2 life if you don't control an Ogre. - this.addAbility(new BeginningOfUpkeepTriggeredAbility(new ScourgeOfNumaiEffect(), TargetController.YOU, false)); + this.addAbility(new BeginningOfUpkeepTriggeredAbility( + new ConditionalOneShotEffect( + new LoseLifeSourceControllerEffect(2), + condition, + "you lose 2 life if you don't control an Ogre" + ), + TargetController.YOU, false + )); } private ScourgeOfNumai(final ScourgeOfNumai card) { @@ -43,32 +56,3 @@ public final class ScourgeOfNumai extends CardImpl { return new ScourgeOfNumai(this); } } - -class ScourgeOfNumaiEffect extends OneShotEffect { - - public ScourgeOfNumaiEffect() { - super(Outcome.LoseLife); - this.staticText = "you lose 2 life if you don't control an Ogre."; - } - - public ScourgeOfNumaiEffect(final ScourgeOfNumaiEffect effect) { - super(effect); - } - - @Override - public ScourgeOfNumaiEffect copy() { - return new ScourgeOfNumaiEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - if (game.getBattlefield().countAll(new FilterCreaturePermanent(SubType.OGRE, "Ogre"), source.getControllerId(), game) < 1) { - controller.loseLife(2, game, source, false); - } - return true; - } - return false; - } -} diff --git a/Mage.Sets/src/mage/cards/s/SilvergillAdept.java b/Mage.Sets/src/mage/cards/s/SilvergillAdept.java index a28f9dd284..ab8c0db45b 100644 --- a/Mage.Sets/src/mage/cards/s/SilvergillAdept.java +++ b/Mage.Sets/src/mage/cards/s/SilvergillAdept.java @@ -1,23 +1,17 @@ package mage.cards.s; import mage.MageInt; -import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; -import mage.abilities.costs.Cost; -import mage.abilities.costs.CostImpl; +import mage.abilities.costs.OrCost; +import mage.abilities.costs.common.RevealTargetFromHandCost; +import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.effects.common.DrawCardSourceControllerEffect; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.cards.CardsImpl; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.SubType; import mage.filter.FilterCard; -import mage.game.Game; -import mage.players.Player; import mage.target.common.TargetCardInHand; -import mage.util.ManaUtil; import java.util.UUID; @@ -26,6 +20,12 @@ import java.util.UUID; */ public final class SilvergillAdept extends CardImpl { + private static final FilterCard filter = new FilterCard("a Merfolk card from your hand"); + + static { + filter.add(SubType.MERFOLK.getPredicate()); + } + public SilvergillAdept(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}"); this.subtype.add(SubType.MERFOLK); @@ -35,7 +35,11 @@ public final class SilvergillAdept extends CardImpl { this.toughness = new MageInt(1); // As an additional cost to cast Silvergill Adept, reveal a Merfolk card from your hand or pay {3}. - this.getSpellAbility().addCost(new SilvergillAdeptCost()); + this.getSpellAbility().addCost(new OrCost( + "reveal a Merfolk card from your hand or pay {3}", new RevealTargetFromHandCost(new TargetCardInHand(filter)), + new GenericManaCost(3) + )); + // When Silvergill Adept enters the battlefield, draw a card. this.addAbility(new EntersBattlefieldTriggeredAbility(new DrawCardSourceControllerEffect(1))); } @@ -49,67 +53,3 @@ public final class SilvergillAdept extends CardImpl { return new SilvergillAdept(this); } } - -class SilvergillAdeptCost extends CostImpl { - - private static final FilterCard filter = new FilterCard("Merfolk card"); - private Cost mana = ManaUtil.createManaCost(3, false); - - static { - filter.add(SubType.MERFOLK.getPredicate()); - } - - public SilvergillAdeptCost() { - this.text = "reveal a Merfolk card from your hand or pay {3}"; - } - - public SilvergillAdeptCost(SilvergillAdeptCost cost) { - super(cost); - this.mana = cost.mana.copy(); - } - - @Override - public boolean pay(Ability ability, Game game, Ability source, UUID controllerId, boolean noMana, Cost costToPay) { - - Player player = game.getPlayer(controllerId); - if (player == null) { - return false; - } - - paid = false; - if (player.getHand().count(filter, game) > 0 - && player.chooseUse(Outcome.Benefit, "Reveal a Merfolk card? Otherwise pay {3}.", ability, game)) { - TargetCardInHand target = new TargetCardInHand(filter); - if (player.choose(Outcome.Benefit, target, source, game)) { - Card card = player.getHand().get(target.getFirstTarget(), game); - if (card != null) { - paid = true; - player.revealCards("Revealed card", new CardsImpl(card), game); - } - } - } else { - mana.clearPaid(); - if (mana.pay(ability, game, source, player.getId(), false)) { - paid = true; - } - } - - return paid; - } - - @Override - public boolean canPay(Ability ability, Ability source, UUID controllerId, Game game) { - Player player = game.getPlayer(controllerId); - if (player != null && player.getHand().count(filter, game) > 0) { - return true; - } - - return mana.canPay(ability, source, controllerId, game); - - } - - @Override - public SilvergillAdeptCost copy() { - return new SilvergillAdeptCost(this); - } -} diff --git a/Mage.Sets/src/mage/cards/s/SoulExchange.java b/Mage.Sets/src/mage/cards/s/SoulExchange.java index cd158e4cba..74bea57c54 100644 --- a/Mage.Sets/src/mage/cards/s/SoulExchange.java +++ b/Mage.Sets/src/mage/cards/s/SoulExchange.java @@ -27,7 +27,7 @@ public final class SoulExchange extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{B}{B}"); // As an additional cost to cast Soul Exchange, exile a creature you control. - Cost cost = new ExileTargetCost(new TargetControlledCreaturePermanent(1, 1, StaticFilters.FILTER_CONTROLLED_A_CREATURE, true)); + Cost cost = new ExileTargetCost(new TargetControlledCreaturePermanent(StaticFilters.FILTER_CONTROLLED_A_CREATURE)); this.getSpellAbility().addCost(cost); // Return target creature card from your graveyard to the battlefield. Put a +2/+2 counter on that creature if the exiled creature was a Thrull. this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_CREATURE_YOUR_GRAVEYARD)); diff --git a/Mage.Sets/src/mage/cards/t/TectonicEdge.java b/Mage.Sets/src/mage/cards/t/TectonicEdge.java index 21b1a43df2..243bbc9cfb 100644 --- a/Mage.Sets/src/mage/cards/t/TectonicEdge.java +++ b/Mage.Sets/src/mage/cards/t/TectonicEdge.java @@ -1,16 +1,13 @@ - - package mage.cards.t; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.ActivateIfConditionActivatedAbility; +import mage.abilities.condition.Condition; import mage.abilities.condition.common.OpponentControlsPermanentCondition; -import mage.abilities.costs.Cost; -import mage.abilities.costs.CostImpl; import mage.abilities.costs.common.SacrificeSourceCost; import mage.abilities.costs.common.TapSourceCost; -import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.mana.ColorlessManaAbility; import mage.cards.CardImpl; @@ -18,9 +15,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.ComparisonType; import mage.constants.Zone; -import mage.filter.StaticFilters; import mage.filter.common.FilterLandPermanent; -import mage.game.Game; import mage.target.common.TargetNonBasicLandPermanent; /** @@ -29,20 +24,20 @@ import mage.target.common.TargetNonBasicLandPermanent; */ public final class TectonicEdge extends CardImpl { + private static final Condition condition = new OpponentControlsPermanentCondition( + new FilterLandPermanent("an opponent controls four or more lands"), ComparisonType.MORE_THAN, 3 + ); + public TectonicEdge(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.LAND},null); // Tap: Add 1. this.addAbility(new ColorlessManaAbility()); - // {1}, {T}, Sacrifice Tectonic Edge: Destroy target nonbasic land. Activate this ability only if an opponent controls four or more lands. + // {1}, {T}, Sacrifice Tectonic Edge: Destroy target nonbasic land. Activate only if an opponent controls four or more lands. Ability ability = new ActivateIfConditionActivatedAbility( - Zone.BATTLEFIELD, - new DestroyTargetEffect(), - new ManaCostsImpl<>("{1}"), - new OpponentControlsPermanentCondition( - new FilterLandPermanent("an opponent controls four or more lands"), - ComparisonType.MORE_THAN, 3)); + Zone.BATTLEFIELD, new DestroyTargetEffect(), new GenericManaCost(1), condition + ); ability.addCost(new TapSourceCost()); ability.addCost(new SacrificeSourceCost()); ability.addTarget(new TargetNonBasicLandPermanent()); @@ -57,39 +52,4 @@ public final class TectonicEdge extends CardImpl { public TectonicEdge copy() { return new TectonicEdge(this); } - -} - -class TectonicEdgeCost extends CostImpl { - - - public TectonicEdgeCost() { - this.text = "Activate only if an opponent controls four or more lands"; - } - - public TectonicEdgeCost(final TectonicEdgeCost cost) { - super(cost); - } - - @Override - public TectonicEdgeCost copy() { - return new TectonicEdgeCost(this); - } - - @Override - public boolean canPay(Ability ability, Ability source, UUID controllerId, Game game) { - for (UUID opponentId: game.getOpponents(controllerId)) { - if (game.getBattlefield().countAll(StaticFilters.FILTER_LANDS, opponentId, game) > 3) { - return true; - } - } - return false; - } - - @Override - public boolean pay(Ability ability, Game game, Ability source, UUID controllerId, boolean noMana, Cost costToPay) { - this.paid = true; - return paid; - } - } diff --git a/Mage.Sets/src/mage/cards/t/TidalInfluence.java b/Mage.Sets/src/mage/cards/t/TidalInfluence.java index 4611ab5ac5..6691e55aab 100644 --- a/Mage.Sets/src/mage/cards/t/TidalInfluence.java +++ b/Mage.Sets/src/mage/cards/t/TidalInfluence.java @@ -1,26 +1,22 @@ package mage.cards.t; import mage.ObjectColor; -import mage.abilities.Ability; import mage.abilities.StateTriggeredAbility; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.common.CastOnlyIfConditionIsTrueAbility; import mage.abilities.common.EntersBattlefieldAbility; import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; import mage.abilities.condition.common.SourceHasCounterCondition; -import mage.abilities.costs.Cost; -import mage.abilities.costs.CostImpl; import mage.abilities.decorator.ConditionalContinuousEffect; import mage.abilities.dynamicvalue.common.CountersSourceCount; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.RemoveAllCountersSourceEffect; import mage.abilities.effects.common.continuous.BoostAllEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.TargetController; -import mage.constants.Zone; +import mage.constants.*; import mage.counters.CounterType; import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; @@ -32,39 +28,51 @@ import mage.game.events.GameEvent; import java.util.UUID; /** - * @author LoneFox + * @author awjackson */ public final class TidalInfluence extends CardImpl { - private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("blue creatures"); + private static final FilterPermanent filterName = new FilterPermanent("if no permanents named Tidal Influence are on the battlefield"); + private static final FilterCreaturePermanent filterBlue = new FilterCreaturePermanent("all blue creatures"); static { - filter.add(new ColorPredicate(ObjectColor.BLUE)); + filterName.add(new NamePredicate("Tidal Influence")); + filterBlue.add(new ColorPredicate(ObjectColor.BLUE)); } + private static final Condition conditionCast = new PermanentsOnTheBattlefieldCondition( + filterName, ComparisonType.EQUAL_TO, 0, false); + private static final Condition condition1 = new SourceHasCounterCondition(CounterType.TIDE, 1, 1); + private static final Condition condition3 = new SourceHasCounterCondition(CounterType.TIDE, 3, 3); + public TidalInfluence(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{U}"); - // Cast Tidal Influence only if no permanents named Tidal Influence are on the battlefield. - this.getSpellAbility().addCost(new TidalInfluenceCost()); + // Cast this spell only if no permanents named Tidal Influence are on the battlefield. + this.addAbility(new CastOnlyIfConditionIsTrueAbility(conditionCast)); + // Tidal Influence enters the battlefield with a tide counter on it. this.addAbility(new EntersBattlefieldAbility(new AddCountersSourceEffect(CounterType.TIDE.createInstance()), "with a tide counter on it.")); + // At the beginning of your upkeep, put a tide counter on Tidal Influence. this.addAbility(new BeginningOfUpkeepTriggeredAbility(new AddCountersSourceEffect(CounterType.TIDE.createInstance()), TargetController.YOU, false)); + // As long as there is exactly one tide counter on Tidal Influence, all blue creatures get -2/-0. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect( - new BoostAllEffect(-2, -0, Duration.WhileOnBattlefield, filter, false), - new SourceHasCounterCondition(CounterType.TIDE, 1, 1), + this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect( + new BoostAllEffect(-2, -0, Duration.WhileOnBattlefield, filterBlue, false), + condition1, "As long as there is exactly one tide counter on {this}, all blue creatures get -2/-0."))); + // As long as there are exactly three tide counters on Tidal Influence, all blue creatures get +2/+0. - this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect( - new BoostAllEffect(2, -0, Duration.WhileOnBattlefield, filter, false), - new SourceHasCounterCondition(CounterType.TIDE, 3, 3), - "As long as there are exactly three tide counter on {this}, all blue creatures get +2/-0."))); + this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect( + new BoostAllEffect(2, 0, Duration.WhileOnBattlefield, filterBlue, false), + condition3, + "As long as there are exactly three tide counter on {this}, all blue creatures get +2/+0."))); + // Whenever there are four tide counters on Tidal Influence, remove all tide counters from it. - this.addAbility(new TidalInfluenceTriggeredAbility(new RemoveAllCountersSourceEffect(CounterType.TIDE))); + this.addAbility(new TidalInfluenceTriggeredAbility()); } private TidalInfluence(final TidalInfluence card) { @@ -77,45 +85,10 @@ public final class TidalInfluence extends CardImpl { } } - -class TidalInfluenceCost extends CostImpl { - - private static final FilterPermanent filter = new FilterPermanent(); - - static { - filter.add(new NamePredicate("Tidal Influence")); - } - - public TidalInfluenceCost() { - this.text = "Cast Tidal Influence only if no permanents named Tidal Influence are on the battlefield"; - } - - public TidalInfluenceCost(final TidalInfluenceCost cost) { - super(cost); - } - - @Override - public boolean canPay(Ability ability, Ability source, UUID controllerId, Game game) { - return !game.getBattlefield().contains(filter, source, game, 1); - } - - @Override - public boolean pay(Ability ability, Game game, Ability source, UUID controllerId, boolean noMana, Cost costToPay) { - this.paid = true; - return paid; - } - - @Override - public TidalInfluenceCost copy() { - return new TidalInfluenceCost(this); - } -} - - class TidalInfluenceTriggeredAbility extends StateTriggeredAbility { - public TidalInfluenceTriggeredAbility(Effect effect) { - super(Zone.BATTLEFIELD, effect); + public TidalInfluenceTriggeredAbility() { + super(Zone.BATTLEFIELD, new RemoveAllCountersSourceEffect(CounterType.TIDE)); setTriggerPhrase("Whenever there are four tide counters on {this}, "); } diff --git a/Mage.Sets/src/mage/cards/t/TombOfUrami.java b/Mage.Sets/src/mage/cards/t/TombOfUrami.java index 72b3414116..f42364bab4 100644 --- a/Mage.Sets/src/mage/cards/t/TombOfUrami.java +++ b/Mage.Sets/src/mage/cards/t/TombOfUrami.java @@ -1,24 +1,25 @@ - package mage.cards.t; import java.util.UUID; import mage.abilities.Ability; import mage.abilities.common.SimpleActivatedAbility; -import mage.abilities.costs.Cost; -import mage.abilities.costs.CostImpl; +import mage.abilities.condition.Condition; +import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition; +import mage.abilities.costs.common.SacrificeAllCost; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.decorator.ConditionalOneShotEffect; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.DamageControllerEffect; import mage.abilities.mana.BlackManaAbility; 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.filter.common.FilterControlledLandPermanent; -import mage.game.Game; -import mage.game.permanent.Permanent; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.StaticFilters; import mage.game.permanent.token.UramiToken; /** @@ -27,18 +28,31 @@ import mage.game.permanent.token.UramiToken; */ public final class TombOfUrami extends CardImpl { + private static final FilterControlledPermanent filter = new FilterControlledPermanent("Ogre"); + + static { + filter.add(SubType.OGRE.getPredicate()); + } + + private static final Condition condition = new PermanentsOnTheBattlefieldCondition(filter, ComparisonType.EQUAL_TO, 0); + public TombOfUrami(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); this.addSuperType(SuperType.LEGENDARY); // {tap}: Add {B}. Tomb of Urami deals 1 damage to you if you don't control an Ogre. Ability ability = new BlackManaAbility(); - ability.addEffect(new DamageControllerEffect(1)); + ability.addEffect(new ConditionalOneShotEffect( + new DamageControllerEffect(1), + condition, + "{this} deals 1 damage to you if you don't control an Ogre" + )); this.addAbility(ability); + // {2}{B}{B}, {tap}, Sacrifice all lands you control: Create a legendary 5/5 black Demon Spirit creature token with flying named Urami. - Ability ability2 = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CreateTokenEffect(new UramiToken()), new ManaCostsImpl<>("{2}{B}{B}")); + Ability ability2 = new SimpleActivatedAbility(new CreateTokenEffect(new UramiToken()), new ManaCostsImpl<>("{2}{B}{B}")); ability2.addCost(new TapSourceCost()); - ability2.addCost(new SacrificeAllLandCost()); + ability2.addCost(new SacrificeAllCost(StaticFilters.FILTER_CONTROLLED_PERMANENT_LANDS)); this.addAbility(ability2); } @@ -51,38 +65,3 @@ public final class TombOfUrami extends CardImpl { return new TombOfUrami(this); } } - -class SacrificeAllLandCost extends CostImpl { - - public SacrificeAllLandCost() { - this.text = "Sacrifice all lands you control"; - } - - public SacrificeAllLandCost(SacrificeAllLandCost cost) { - super(cost); - } - - @Override - public boolean pay(Ability ability, Game game, Ability source, UUID controllerId, boolean noMana, Cost costToPay) { - for (Permanent permanent : game.getBattlefield().getActivePermanents(new FilterControlledLandPermanent(), ability.getControllerId(), game)) { - paid |= permanent.sacrifice(source, game); - } - return paid; - } - - @Override - public boolean canPay(Ability ability, Ability source, UUID controllerId, Game game) { - for (Permanent permanent : game.getBattlefield().getActivePermanents(new FilterControlledLandPermanent(), ability.getControllerId(), game)) { - if (!game.getPlayer(controllerId).canPaySacrificeCost(permanent, source, controllerId, game)) { - return false; - } - } - return true; - } - - @Override - public SacrificeAllLandCost copy() { - return new SacrificeAllLandCost(this); - } - -} diff --git a/Mage.Sets/src/mage/cards/v/VolrathsDungeon.java b/Mage.Sets/src/mage/cards/v/VolrathsDungeon.java index bc2e745ea1..d32415b1c3 100644 --- a/Mage.Sets/src/mage/cards/v/VolrathsDungeon.java +++ b/Mage.Sets/src/mage/cards/v/VolrathsDungeon.java @@ -4,11 +4,8 @@ import mage.abilities.Ability; import mage.abilities.ActivatedAbility; import mage.abilities.common.ActivateAsSorceryActivatedAbility; import mage.abilities.common.SimpleActivatedAbility; -import mage.abilities.costs.Cost; -import mage.abilities.costs.CostImpl; import mage.abilities.costs.common.DiscardCardCost; -import mage.abilities.dynamicvalue.DynamicValue; -import mage.abilities.dynamicvalue.common.StaticValue; +import mage.abilities.costs.common.PayLifeCost; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DestroySourceEffect; import mage.cards.Card; @@ -17,7 +14,6 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.TargetController; -import mage.constants.Zone; import mage.game.Game; import mage.players.Player; import mage.target.TargetPlayer; @@ -36,16 +32,14 @@ public final class VolrathsDungeon extends CardImpl { // Pay 5 life: Destroy Volrath's Dungeon. Any player may activate this ability but only during their turn. ActivatedAbility ability = new SimpleActivatedAbility( - Zone.BATTLEFIELD, new DestroySourceEffect().setText("Destroy {this}. Any player may activate this ability but only during their turn."), - new PayLifeActivePlayerCost(5)); + new PayLifeCost(5)); ability.setMayActivate(TargetController.ACTIVE); this.addAbility(ability); - // Discard a card: Target player puts a card from their hand on top of their library. Activate this ability only any time you could cast a sorcery. - ability = new ActivateAsSorceryActivatedAbility( - Zone.BATTLEFIELD, new VolrathsDungeonEffect(), new DiscardCardCost() - ); + // Discard a card: Target player puts a card from their hand on top of their library. + // Activate this ability only any time you could cast a sorcery. + ability = new ActivateAsSorceryActivatedAbility(new VolrathsDungeonEffect(), new DiscardCardCost()); ability.addTarget(new TargetPlayer()); this.addAbility(ability); } @@ -60,52 +54,6 @@ public final class VolrathsDungeon extends CardImpl { } } -class PayLifeActivePlayerCost extends CostImpl { - - private final DynamicValue amount; - - public PayLifeActivePlayerCost(int amount) { - this.amount = StaticValue.get(amount); - this.text = "Pay " + amount + " life"; - } - - public PayLifeActivePlayerCost(DynamicValue amount, String text) { - this.amount = amount.copy(); - this.text = "Pay " + text; - } - - public PayLifeActivePlayerCost(PayLifeActivePlayerCost cost) { - super(cost); - this.amount = cost.amount.copy(); - } - - @Override - public boolean canPay(Ability ability, Ability source, UUID controllerId, Game game) { - int lifeToPayAmount = amount.calculate(game, ability, null); - return game.getPlayer(game.getActivePlayerId()).getLife() >= lifeToPayAmount; - } - - @Override - public boolean pay(Ability ability, Game game, Ability source, UUID controllerId, boolean noMana, Cost costToPay) { - Player activatingPlayer = game.getPlayer(game.getActivePlayerId()); - if (activatingPlayer == null) { - return false; - } - - int lifeToPayAmount = amount.calculate(game, ability, null); - if (activatingPlayer.chooseUse(Outcome.LoseLife, "Pay " + lifeToPayAmount + " life?", ability, game)) { - this.paid = CardUtil.tryPayLife(lifeToPayAmount, activatingPlayer, source, game); - return this.paid; - } - return false; - } - - @Override - public PayLifeActivePlayerCost copy() { - return new PayLifeActivePlayerCost(this); - } -} - class VolrathsDungeonEffect extends OneShotEffect { public VolrathsDungeonEffect() { diff --git a/Mage.Sets/src/mage/cards/w/WormfangDrake.java b/Mage.Sets/src/mage/cards/w/WormfangDrake.java index fed9e9ba01..c8130739fd 100644 --- a/Mage.Sets/src/mage/cards/w/WormfangDrake.java +++ b/Mage.Sets/src/mage/cards/w/WormfangDrake.java @@ -1,14 +1,10 @@ - package mage.cards.w; import java.util.UUID; import mage.MageInt; -import mage.MageObject; -import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.LeavesBattlefieldTriggeredAbility; -import mage.abilities.costs.Cost; -import mage.abilities.costs.CostImpl; +import mage.abilities.costs.common.ExileTargetCost; import mage.abilities.effects.common.ReturnFromExileForSourceEffect; import mage.abilities.effects.common.SacrificeSourceUnlessPaysEffect; import mage.abilities.keyword.FlyingAbility; @@ -16,15 +12,11 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.Outcome; import mage.constants.Zone; import mage.filter.common.FilterControlledCreaturePermanent; +import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.mageobject.AnotherPredicate; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; -import mage.target.common.TargetControlledCreaturePermanent; -import mage.util.CardUtil; +import mage.target.common.TargetControlledPermanent; /** * @@ -32,10 +24,15 @@ import mage.util.CardUtil; */ public final class WormfangDrake extends CardImpl { + private static final FilterControlledPermanent filter = new FilterControlledCreaturePermanent("a creature you control other than {this}"); + + static { + filter.add(AnotherPredicate.instance); + } + public WormfangDrake(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{U}"); - this.subtype.add(SubType.NIGHTMARE); - this.subtype.add(SubType.DRAKE); + this.subtype.add(SubType.NIGHTMARE, SubType.DRAKE); this.power = new MageInt(3); this.toughness = new MageInt(4); @@ -44,7 +41,9 @@ public final class WormfangDrake extends CardImpl { // When Wormfang Drake enters the battlefield, sacrifice it unless you exile a creature you control other than Wormfang Drake. this.addAbility(new EntersBattlefieldTriggeredAbility( - new SacrificeSourceUnlessPaysEffect(new WormfangDrakeExileCost()), false)); + new SacrificeSourceUnlessPaysEffect(new ExileTargetCost(new TargetControlledPermanent(filter))), + false + )); // When Wormfang Drake leaves the battlefield, return the exiled card to the battlefield under its owner's control. this.addAbility(new LeavesBattlefieldTriggeredAbility(new ReturnFromExileForSourceEffect(Zone.BATTLEFIELD), false)); @@ -59,50 +58,3 @@ public final class WormfangDrake extends CardImpl { return new WormfangDrake(this); } } - -class WormfangDrakeExileCost extends CostImpl { - - private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent(); - - static { - filter.add(AnotherPredicate.instance); - } - - public WormfangDrakeExileCost() { - this.addTarget(new TargetControlledCreaturePermanent(1, 1, filter, true)); - this.text = "Exile a creature you control other than {this}"; - } - - public WormfangDrakeExileCost(WormfangDrakeExileCost cost) { - super(cost); - } - - @Override - public boolean pay(Ability ability, Game game, Ability source, UUID controllerId, boolean noMana, Cost costToPay) { - Player controller = game.getPlayer(controllerId); - MageObject sourceObject = ability.getSourceObject(game); - if (controller != null && sourceObject != null) { - if (targets.choose(Outcome.Exile, controllerId, source.getSourceId(), source, game)) { - UUID exileId = CardUtil.getExileZoneId(game, ability.getSourceId(), ability.getSourceObjectZoneChangeCounter()); - for (UUID targetId : targets.get(0).getTargets()) { - Permanent permanent = game.getPermanent(targetId); - if (permanent == null) { - return false; - } - paid |= controller.moveCardToExileWithInfo(permanent, exileId, sourceObject.getIdName() + " exiled permanents", source, game, Zone.BATTLEFIELD, true); - } - } - } - return paid; - } - - @Override - public boolean canPay(Ability ability, Ability source, UUID controllerId, Game game) { - return targets.canChoose(controllerId, source, game); - } - - @Override - public WormfangDrakeExileCost copy() { - return new WormfangDrakeExileCost(this); - } -} diff --git a/Mage/src/main/java/mage/abilities/costs/common/ExileTargetCost.java b/Mage/src/main/java/mage/abilities/costs/common/ExileTargetCost.java index 541a63ebf8..8e7bb9b4e2 100644 --- a/Mage/src/main/java/mage/abilities/costs/common/ExileTargetCost.java +++ b/Mage/src/main/java/mage/abilities/costs/common/ExileTargetCost.java @@ -26,8 +26,9 @@ public class ExileTargetCost extends CostImpl { List permanents = new ArrayList<>(); public ExileTargetCost(TargetControlledPermanent target) { + target.setNotTarget(true); this.addTarget(target); - this.text = "Exile " + target.getTargetName(); + this.text = "exile " + target.getTargetName(); } public ExileTargetCost(TargetControlledPermanent target, boolean noText) { @@ -61,7 +62,7 @@ public class ExileTargetCost extends CostImpl { // so return state here is not important because the user indended to exile the target anyway } player.moveCardsToExile( - cards.getCards(game), source, game, false, + cards.getCards(game), source, game, true, CardUtil.getExileZoneId(game, source), CardUtil.getSourceName(game, source) );