From bde65d62799f134ba7fde9a647a7aa318dd987e4 Mon Sep 17 00:00:00 2001 From: Evan Kranzler Date: Mon, 4 May 2020 20:51:38 -0400 Subject: [PATCH] Created class for reflexive triggered abilities (Ready for review) (#6500) * added class for reflexive triggered abilities * added DoWhenCostPaid * a few more refactors * some more refactoring * almost all refactors done * finished refactoring * updated text generation * Delete SendOptionUsedEventEffect.java * fixed Wildborn Preserver text --- Mage.Sets/src/mage/cards/b/BackForMore.java | 65 +++------- .../src/mage/cards/c/CabalTherapist.java | 91 +++----------- .../src/mage/cards/c/CavalierOfNight.java | 79 ++---------- Mage.Sets/src/mage/cards/d/DreamEater.java | 82 ++++--------- .../src/mage/cards/e/EscapeProtocol.java | 98 +++------------ .../src/mage/cards/f/FirebladeArtist.java | 84 ++----------- .../src/mage/cards/f/FlaxenIntruder.java | 76 ++---------- .../mage/cards/h/HeartPiercerManticore.java | 95 +++++---------- Mage.Sets/src/mage/cards/h/Hypothesizzle.java | 90 +++----------- .../src/mage/cards/i/IsarethTheAwakener.java | 100 +++++---------- .../src/mage/cards/l/LavabrinkFloodgates.java | 51 ++------ .../mage/cards/n/NarsetOfTheAncientWay.java | 52 ++------ .../src/mage/cards/s/SavaiThundermane.java | 80 ++---------- .../src/mage/cards/s/SkyriderPatrol.java | 114 ++++------------- .../mage/cards/s/SorinImperiousBloodlord.java | 78 +++--------- .../src/mage/cards/s/SparktongueDragon.java | 91 +++----------- .../src/mage/cards/t/TheRoyalScions.java | 44 ++----- .../src/mage/cards/v/ViviensInvocation.java | 115 ++++++------------ .../src/mage/cards/w/WildbornPreserver.java | 44 +------ .../cards/y/YannikScavengingSentinel.java | 48 ++------ .../delayed/ReflexiveTriggeredAbility.java | 50 ++++++++ .../effects/common/DoWhenCostPaid.java | 103 ++++++++++++++++ .../common/SendOptionUsedEventEffect.java | 42 ------- Mage/src/main/java/mage/game/Game.java | 3 + Mage/src/main/java/mage/game/GameImpl.java | 8 ++ 25 files changed, 505 insertions(+), 1278 deletions(-) create mode 100644 Mage/src/main/java/mage/abilities/common/delayed/ReflexiveTriggeredAbility.java create mode 100644 Mage/src/main/java/mage/abilities/effects/common/DoWhenCostPaid.java delete mode 100644 Mage/src/main/java/mage/abilities/effects/common/SendOptionUsedEventEffect.java diff --git a/Mage.Sets/src/mage/cards/b/BackForMore.java b/Mage.Sets/src/mage/cards/b/BackForMore.java index 3ef46f510ed..26ed102c20f 100644 --- a/Mage.Sets/src/mage/cards/b/BackForMore.java +++ b/Mage.Sets/src/mage/cards/b/BackForMore.java @@ -2,18 +2,19 @@ package mage.cards.b; import mage.MageObjectReference; import mage.abilities.Ability; -import mage.abilities.DelayedTriggeredAbility; +import mage.abilities.common.delayed.ReflexiveTriggeredAbility; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.SendOptionUsedEventEffect; import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.TargetController; +import mage.constants.Zone; import mage.filter.FilterPermanent; import mage.filter.StaticFilters; import mage.filter.common.FilterCreaturePermanent; import mage.game.Game; -import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.TargetPermanent; @@ -48,6 +49,12 @@ public final class BackForMore extends CardImpl { class BackForMoreEffect extends OneShotEffect { + private static final FilterPermanent filter = new FilterCreaturePermanent("creature you don't control"); + + static { + filter.add(TargetController.NOT_YOU.getControllerPredicate()); + } + BackForMoreEffect() { super(Outcome.Benefit); staticText = "Return target creature card from your graveyard to the battlefield. " + @@ -75,49 +82,13 @@ class BackForMoreEffect extends OneShotEffect { if (permanent == null) { return false; } - game.addDelayedTriggeredAbility(new BackForMoreReflexiveTriggeredAbility( - new MageObjectReference(permanent, game) - ), source); - return new SendOptionUsedEventEffect().apply(game, source); - } -} - -class BackForMoreReflexiveTriggeredAbility extends DelayedTriggeredAbility { - - private static final FilterPermanent filter = new FilterCreaturePermanent("creature you don't control"); - - static { - filter.add(TargetController.NOT_YOU.getControllerPredicate()); - } - - BackForMoreReflexiveTriggeredAbility(MageObjectReference mor) { - super(new BackForMoreDamageEffect(mor), Duration.OneUse, false); - this.addTarget(new TargetPermanent(0, 1, filter, false)); - } - - private BackForMoreReflexiveTriggeredAbility(final BackForMoreReflexiveTriggeredAbility ability) { - super(ability); - } - - @Override - public BackForMoreReflexiveTriggeredAbility copy() { - return new BackForMoreReflexiveTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.OPTION_USED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - return event.getPlayerId().equals(this.getControllerId()) - && event.getSourceId().equals(this.getSourceId()); - } - - @Override - public String getRule() { - return "When you do, it fights up to one target creature you don't control."; + ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility( + new BackForMoreDamageEffect(new MageObjectReference(permanent, game)), + false, "it fights up to one target creature you don't control" + ); + ability.addTarget(new TargetPermanent(0, 1, filter, false)); + game.fireReflexiveTriggeredAbility(ability, source); + return true; } } diff --git a/Mage.Sets/src/mage/cards/c/CabalTherapist.java b/Mage.Sets/src/mage/cards/c/CabalTherapist.java index 2a6a3ac881a..2ec2d13f0f0 100644 --- a/Mage.Sets/src/mage/cards/c/CabalTherapist.java +++ b/Mage.Sets/src/mage/cards/c/CabalTherapist.java @@ -3,19 +3,20 @@ package mage.cards.c; import mage.MageInt; import mage.MageObject; import mage.abilities.Ability; -import mage.abilities.DelayedTriggeredAbility; import mage.abilities.common.BeginningOfPreCombatMainTriggeredAbility; +import mage.abilities.common.delayed.ReflexiveTriggeredAbility; import mage.abilities.costs.common.SacrificeTargetCost; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.ChooseACardNameEffect; -import mage.abilities.effects.common.DoIfCostPaid; -import mage.abilities.effects.common.SendOptionUsedEventEffect; +import mage.abilities.effects.common.DoWhenCostPaid; import mage.abilities.keyword.MenaceAbility; import mage.cards.*; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.TargetController; import mage.filter.StaticFilters; import mage.game.Game; -import mage.game.events.GameEvent; import mage.players.Player; import mage.target.TargetPlayer; import mage.target.common.TargetControlledPermanent; @@ -39,16 +40,17 @@ public final class CabalTherapist extends CardImpl { this.addAbility(new MenaceAbility()); // At the beginning of your precombat main phase, you may sacrifice a creature. When you do, choose a nonland card name, then target player reveals their hand and discards all cards with that name. + ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility( + new ChooseACardNameEffect(ChooseACardNameEffect.TypeOfName.NON_LAND_NAME), + false, "choose a nonland card name, then target player " + + "reveals their hand and discards all cards with that name." + ); + ability.addEffect(new CabalTherapistDiscardEffect()); + ability.addTarget(new TargetPlayer()); this.addAbility(new BeginningOfPreCombatMainTriggeredAbility( - new DoIfCostPaid( - new CabalTherapistCreateReflexiveTriggerEffect(), - new SacrificeTargetCost(new TargetControlledPermanent( - StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT - )), "Sacrifice a creature?" - ).setText("you may sacrifice a creature. When you do, " + - "choose a nonland card name, then target player reveals their hand " + - "and discards all cards with that name."), - TargetController.YOU, false + new DoWhenCostPaid(ability, new SacrificeTargetCost( + new TargetControlledPermanent(StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT) + ), "Sacrifice a creature?"), TargetController.YOU, false )); } @@ -62,62 +64,6 @@ public final class CabalTherapist extends CardImpl { } } -class CabalTherapistCreateReflexiveTriggerEffect extends OneShotEffect { - - CabalTherapistCreateReflexiveTriggerEffect() { - super(Outcome.Benefit); - } - - private CabalTherapistCreateReflexiveTriggerEffect(final CabalTherapistCreateReflexiveTriggerEffect effect) { - super(effect); - } - - @Override - public CabalTherapistCreateReflexiveTriggerEffect copy() { - return new CabalTherapistCreateReflexiveTriggerEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - game.addDelayedTriggeredAbility(new CabalTherapistReflexiveTriggeredAbility(), source); - return new SendOptionUsedEventEffect().apply(game, source); - } -} - -class CabalTherapistReflexiveTriggeredAbility extends DelayedTriggeredAbility { - - CabalTherapistReflexiveTriggeredAbility() { - super(new ChooseACardNameEffect(ChooseACardNameEffect.TypeOfName.NON_LAND_NAME), Duration.OneUse, true); - this.addEffect(new CabalTherapistDiscardEffect()); - this.addTarget(new TargetPlayer()); - } - - private CabalTherapistReflexiveTriggeredAbility(final CabalTherapistReflexiveTriggeredAbility ability) { - super(ability); - } - - @Override - public CabalTherapistReflexiveTriggeredAbility copy() { - return new CabalTherapistReflexiveTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.OPTION_USED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - return event.getPlayerId().equals(this.getControllerId()) - && event.getSourceId().equals(this.getSourceId()); - } - - @Override - public String getRule() { - return "Choose a nonland card name, then target player reveals their hand and discards all cards with that name."; - } -} - class CabalTherapistDiscardEffect extends OneShotEffect { CabalTherapistDiscardEffect() { @@ -155,9 +101,8 @@ class CabalTherapistDiscardEffect extends OneShotEffect { if (CardUtil.haveSameNames(card.getName(), cardName)) { return false; } - return true; - }); - targetPlayer.discard(hand, source, game); + } + targetPlayer.revealCards(source, hand, game); return true; } diff --git a/Mage.Sets/src/mage/cards/c/CavalierOfNight.java b/Mage.Sets/src/mage/cards/c/CavalierOfNight.java index 47437215ba5..e8776a3ea4f 100644 --- a/Mage.Sets/src/mage/cards/c/CavalierOfNight.java +++ b/Mage.Sets/src/mage/cards/c/CavalierOfNight.java @@ -2,27 +2,25 @@ package mage.cards.c; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.DelayedTriggeredAbility; import mage.abilities.common.DiesTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.delayed.ReflexiveTriggeredAbility; import mage.abilities.costs.common.SacrificeTargetCost; -import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DestroyTargetEffect; -import mage.abilities.effects.common.DoIfCostPaid; +import mage.abilities.effects.common.DoWhenCostPaid; import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect; -import mage.abilities.effects.common.SendOptionUsedEventEffect; import mage.abilities.keyword.LifelinkAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.ComparisonType; +import mage.constants.SubType; import mage.filter.FilterCard; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.common.FilterControlledPermanent; import mage.filter.common.FilterCreatureCard; import mage.filter.predicate.mageobject.ConvertedManaCostPredicate; import mage.filter.predicate.permanent.AnotherPredicate; -import mage.game.Game; -import mage.game.events.GameEvent; import mage.target.common.TargetCardInYourGraveyard; import mage.target.common.TargetControlledPermanent; import mage.target.common.TargetOpponentsCreaturePermanent; @@ -56,10 +54,14 @@ public final class CavalierOfNight extends CardImpl { this.addAbility(LifelinkAbility.getInstance()); // When Cavalier of Night enters the battlefield, you may sacrifice another creature. When you do, destroy target creature an opponent controls. - this.addAbility(new EntersBattlefieldTriggeredAbility(new DoIfCostPaid( - new CavalierOfNightCreateReflexiveTriggerEffect(), - new SacrificeTargetCost(new TargetControlledPermanent(filter)) - ).setText("you may sacrifice another creature. When you do, destroy target creature an opponent controls."))); + ReflexiveTriggeredAbility triggeredAbility = new ReflexiveTriggeredAbility( + new DestroyTargetEffect(), false, "Sacrifice a creature?" + ); + triggeredAbility.addTarget(new TargetOpponentsCreaturePermanent()); + this.addAbility(new EntersBattlefieldTriggeredAbility(new DoWhenCostPaid( + triggeredAbility, new SacrificeTargetCost(new TargetControlledPermanent(filter)), + "destroy target creature an opponent controls" + ))); // When Cavalier of Night dies, return target creature card with converted mana cost 3 or less from your graveyard to the battlefield. Ability ability = new DiesTriggeredAbility(new ReturnFromGraveyardToBattlefieldTargetEffect()); @@ -76,58 +78,3 @@ public final class CavalierOfNight extends CardImpl { return new CavalierOfNight(this); } } - -class CavalierOfNightCreateReflexiveTriggerEffect extends OneShotEffect { - - CavalierOfNightCreateReflexiveTriggerEffect() { - super(Outcome.Benefit); - } - - private CavalierOfNightCreateReflexiveTriggerEffect(final CavalierOfNightCreateReflexiveTriggerEffect effect) { - super(effect); - } - - @Override - public CavalierOfNightCreateReflexiveTriggerEffect copy() { - return new CavalierOfNightCreateReflexiveTriggerEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - game.addDelayedTriggeredAbility(new CavalierOfNightReflexiveTriggeredAbility(), source); - return new SendOptionUsedEventEffect().apply(game, source); - } -} - -class CavalierOfNightReflexiveTriggeredAbility extends DelayedTriggeredAbility { - - CavalierOfNightReflexiveTriggeredAbility() { - super(new DestroyTargetEffect(), Duration.OneUse, true); - this.addTarget(new TargetOpponentsCreaturePermanent()); - } - - private CavalierOfNightReflexiveTriggeredAbility(final CavalierOfNightReflexiveTriggeredAbility ability) { - super(ability); - } - - @Override - public CavalierOfNightReflexiveTriggeredAbility copy() { - return new CavalierOfNightReflexiveTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.OPTION_USED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - return event.getPlayerId().equals(this.getControllerId()) - && event.getSourceId().equals(this.getSourceId()); - } - - @Override - public String getRule() { - return "Destroy target creature an opponent controls"; - } -} diff --git a/Mage.Sets/src/mage/cards/d/DreamEater.java b/Mage.Sets/src/mage/cards/d/DreamEater.java index de2fe374169..5c728566480 100644 --- a/Mage.Sets/src/mage/cards/d/DreamEater.java +++ b/Mage.Sets/src/mage/cards/d/DreamEater.java @@ -1,30 +1,28 @@ package mage.cards.d; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.DelayedTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.delayed.ReflexiveTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.ReturnToHandTargetEffect; -import mage.abilities.effects.common.SendOptionUsedEventEffect; import mage.abilities.effects.keyword.SurveilEffect; -import mage.constants.SubType; import mage.abilities.keyword.FlashAbility; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; +import mage.constants.SubType; import mage.constants.TargetController; import mage.filter.FilterPermanent; import mage.filter.common.FilterNonlandPermanent; import mage.game.Game; -import mage.game.events.GameEvent; import mage.target.TargetPermanent; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class DreamEater extends CardImpl { @@ -44,14 +42,12 @@ public final class DreamEater extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // When Dream Eater enters the battlefield, surveil 4. When you do, you may return target nonland permanent an opponent controls to its owner's hand. - Ability ability = new EntersBattlefieldTriggeredAbility( - new SurveilEffect(4) - ); - ability.addEffect(new DreamEaterCreateReflexiveTriggerEffect()); + Ability ability = new EntersBattlefieldTriggeredAbility(new SurveilEffect(4)); + ability.addEffect(new DreamEaterEffect()); this.addAbility(ability); } - public DreamEater(final DreamEater card) { + private DreamEater(final DreamEater card) { super(card); } @@ -61,31 +57,7 @@ public final class DreamEater extends CardImpl { } } -class DreamEaterCreateReflexiveTriggerEffect extends OneShotEffect { - - public DreamEaterCreateReflexiveTriggerEffect() { - super(Outcome.Benefit); - this.staticText = "When you do, you may return target " - + "nonland permanent an opponent controls to its owner's hand."; - } - - public DreamEaterCreateReflexiveTriggerEffect(final DreamEaterCreateReflexiveTriggerEffect effect) { - super(effect); - } - - @Override - public DreamEaterCreateReflexiveTriggerEffect copy() { - return new DreamEaterCreateReflexiveTriggerEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - game.addDelayedTriggeredAbility(new DreamEaterReflexiveTriggeredAbility(), source); - return new SendOptionUsedEventEffect().apply(game, source); - } -} - -class DreamEaterReflexiveTriggeredAbility extends DelayedTriggeredAbility { +class DreamEaterEffect extends OneShotEffect { private static final FilterPermanent filter = new FilterNonlandPermanent("nonland permanent an opponent controls"); @@ -94,35 +66,29 @@ class DreamEaterReflexiveTriggeredAbility extends DelayedTriggeredAbility { filter.add(TargetController.OPPONENT.getControllerPredicate()); } - public DreamEaterReflexiveTriggeredAbility() { - super(new ReturnToHandTargetEffect()); - this.addTarget(new TargetPermanent(filter)); - this.optional = true; + DreamEaterEffect() { + super(Outcome.Benefit); + this.staticText = "When you do, you may return target " + + "nonland permanent an opponent controls to its owner's hand."; } - public DreamEaterReflexiveTriggeredAbility(final DreamEaterReflexiveTriggeredAbility ability) { - super(ability); + private DreamEaterEffect(final DreamEaterEffect effect) { + super(effect); } @Override - public DreamEaterReflexiveTriggeredAbility copy() { - return new DreamEaterReflexiveTriggeredAbility(this); + public DreamEaterEffect copy() { + return new DreamEaterEffect(this); } @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.OPTION_USED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - return event.getPlayerId().equals(this.getControllerId()) - && event.getSourceId().equals(this.getSourceId()); - } - - @Override - public String getRule() { - return "When you surveil 4, you may return target nonland permanent " - + "an opponent controls to its owner's hand."; + public boolean apply(Game game, Ability source) { + ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility( + new ReturnToHandTargetEffect(), true, + "you may return target nonland permanent an opponent controls to its owner's hand" + ); + ability.addTarget(new TargetPermanent(filter)); + game.fireReflexiveTriggeredAbility(ability, source); + return true; } } diff --git a/Mage.Sets/src/mage/cards/e/EscapeProtocol.java b/Mage.Sets/src/mage/cards/e/EscapeProtocol.java index 9d4ddeaa649..e7335c60f1f 100644 --- a/Mage.Sets/src/mage/cards/e/EscapeProtocol.java +++ b/Mage.Sets/src/mage/cards/e/EscapeProtocol.java @@ -1,24 +1,17 @@ package mage.cards.e; -import mage.abilities.Ability; -import mage.abilities.DelayedTriggeredAbility; import mage.abilities.common.CycleControllerTriggeredAbility; +import mage.abilities.common.delayed.ReflexiveTriggeredAbility; import mage.abilities.costs.mana.GenericManaCost; -import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.DoIfCostPaid; +import mage.abilities.effects.common.DoWhenCostPaid; import mage.abilities.effects.common.ExileTargetForSourceEffect; import mage.abilities.effects.common.ReturnToBattlefieldUnderOwnerControlTargetEffect; -import mage.abilities.effects.common.SendOptionUsedEventEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.Outcome; import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledPermanent; import mage.filter.predicate.Predicates; -import mage.game.Game; -import mage.game.events.GameEvent; import mage.target.TargetPermanent; import java.util.UUID; @@ -28,50 +21,6 @@ import java.util.UUID; */ public final class EscapeProtocol extends CardImpl { - public EscapeProtocol(UUID ownerId, CardSetInfo setInfo) { - super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{U}"); - - // Whenever you cycle a card, you may pay {1}. When you do, exile target artifact or creature you control, then return it to the battlefield under its owner's control. - this.addAbility(new CycleControllerTriggeredAbility(new DoIfCostPaid( - new EscapeProtocolEffect(), new GenericManaCost(1) - ).setText("you may pay {1}. When you do, exile target artifact or creature you control, " + - "then return it to the battlefield under its owner's control."))); - } - - private EscapeProtocol(final EscapeProtocol card) { - super(card); - } - - @Override - public EscapeProtocol copy() { - return new EscapeProtocol(this); - } -} - -class EscapeProtocolEffect extends OneShotEffect { - - EscapeProtocolEffect() { - super(Outcome.Benefit); - } - - private EscapeProtocolEffect(final EscapeProtocolEffect effect) { - super(effect); - } - - @Override - public EscapeProtocolEffect copy() { - return new EscapeProtocolEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - game.addDelayedTriggeredAbility(new EscapeProtocolReflexiveTriggeredAbility(), source); - return new SendOptionUsedEventEffect().apply(game, source); - } -} - -class EscapeProtocolReflexiveTriggeredAbility extends DelayedTriggeredAbility { - private static final FilterPermanent filter = new FilterControlledPermanent("artifact or creature you control"); @@ -82,35 +31,28 @@ class EscapeProtocolReflexiveTriggeredAbility extends DelayedTriggeredAbility { )); } - EscapeProtocolReflexiveTriggeredAbility() { - super(new ExileTargetForSourceEffect(), Duration.OneUse, true); - this.addEffect(new ReturnToBattlefieldUnderOwnerControlTargetEffect()); - this.addTarget(new TargetPermanent(filter)); + public EscapeProtocol(UUID ownerId, CardSetInfo setInfo) { + super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{U}"); + + // Whenever you cycle a card, you may pay {1}. When you do, exile target artifact or creature you control, then return it to the battlefield under its owner's control. + ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility( + new ExileTargetForSourceEffect(), false, + "exile target artifact or creature you control, " + + "then return it to the battlefield under its owner's control." + ); + ability.addEffect(new ReturnToBattlefieldUnderOwnerControlTargetEffect()); + ability.addTarget(new TargetPermanent(filter)); + this.addAbility(new CycleControllerTriggeredAbility(new DoWhenCostPaid( + ability, new GenericManaCost(1), "Pay {1}?" + ))); } - private EscapeProtocolReflexiveTriggeredAbility(final EscapeProtocolReflexiveTriggeredAbility ability) { - super(ability); + private EscapeProtocol(final EscapeProtocol card) { + super(card); } @Override - public EscapeProtocolReflexiveTriggeredAbility copy() { - return new EscapeProtocolReflexiveTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.OPTION_USED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - return event.getPlayerId().equals(this.getControllerId()) - && event.getSourceId().equals(this.getSourceId()); - } - - @Override - public String getRule() { - return "Exile target artifact or creature you control, " + - "then return it to the battlefield under its owner's control."; + public EscapeProtocol copy() { + return new EscapeProtocol(this); } } diff --git a/Mage.Sets/src/mage/cards/f/FirebladeArtist.java b/Mage.Sets/src/mage/cards/f/FirebladeArtist.java index e11b412cc7b..5568d06ee3b 100644 --- a/Mage.Sets/src/mage/cards/f/FirebladeArtist.java +++ b/Mage.Sets/src/mage/cards/f/FirebladeArtist.java @@ -1,21 +1,18 @@ package mage.cards.f; import mage.MageInt; -import mage.abilities.Ability; -import mage.abilities.DelayedTriggeredAbility; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.common.delayed.ReflexiveTriggeredAbility; import mage.abilities.costs.common.SacrificeTargetCost; -import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DamageTargetEffect; -import mage.abilities.effects.common.DoIfCostPaid; -import mage.abilities.effects.common.SendOptionUsedEventEffect; +import mage.abilities.effects.common.DoWhenCostPaid; import mage.abilities.keyword.HasteAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.TargetController; import mage.filter.StaticFilters; -import mage.game.Game; -import mage.game.events.GameEvent; import mage.target.common.TargetControlledPermanent; import mage.target.common.TargetOpponentOrPlaneswalker; @@ -38,15 +35,15 @@ public final class FirebladeArtist extends CardImpl { this.addAbility(HasteAbility.getInstance()); // At the beginning of your upkeep, you may sacrifice a creature. When you do, Fireblade Artist deals 2 damage to target opponent or planeswalker. + ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility( + new DamageTargetEffect(2), false, + "{this} deals 2 damage to target opponent or planeswalker" + ); + ability.addTarget(new TargetOpponentOrPlaneswalker()); this.addAbility(new BeginningOfUpkeepTriggeredAbility( - new DoIfCostPaid( - new FirebladeArtistCreateReflexiveTriggerEffect(), - new SacrificeTargetCost(new TargetControlledPermanent( - StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT - )), "Sacrifice a creature to deal 2 damage to an opponent or planeswalker?" - ).setText("you may sacrifice a creature. When you do, " + - "{this} deals 2 damage to target opponent or planeswalker."), - TargetController.YOU, false + new DoWhenCostPaid(ability, new SacrificeTargetCost( + new TargetControlledPermanent(StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT) + ), "Sacrifice a creature?"), TargetController.YOU, false )); } @@ -59,58 +56,3 @@ public final class FirebladeArtist extends CardImpl { return new FirebladeArtist(this); } } - -class FirebladeArtistCreateReflexiveTriggerEffect extends OneShotEffect { - - FirebladeArtistCreateReflexiveTriggerEffect() { - super(Outcome.Benefit); - } - - private FirebladeArtistCreateReflexiveTriggerEffect(final FirebladeArtistCreateReflexiveTriggerEffect effect) { - super(effect); - } - - @Override - public FirebladeArtistCreateReflexiveTriggerEffect copy() { - return new FirebladeArtistCreateReflexiveTriggerEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - game.addDelayedTriggeredAbility(new FirebladeArtistReflexiveTriggeredAbility(), source); - return new SendOptionUsedEventEffect().apply(game, source); - } -} - -class FirebladeArtistReflexiveTriggeredAbility extends DelayedTriggeredAbility { - - FirebladeArtistReflexiveTriggeredAbility() { - super(new DamageTargetEffect(2), Duration.OneUse, true); - this.addTarget(new TargetOpponentOrPlaneswalker()); - } - - private FirebladeArtistReflexiveTriggeredAbility(final FirebladeArtistReflexiveTriggeredAbility ability) { - super(ability); - } - - @Override - public FirebladeArtistReflexiveTriggeredAbility copy() { - return new FirebladeArtistReflexiveTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.OPTION_USED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - return event.getPlayerId().equals(this.getControllerId()) - && event.getSourceId().equals(this.getSourceId()); - } - - @Override - public String getRule() { - return "{this} deals 2 damage to target opponent or planeswalker."; - } -} diff --git a/Mage.Sets/src/mage/cards/f/FlaxenIntruder.java b/Mage.Sets/src/mage/cards/f/FlaxenIntruder.java index 105e7d16c03..c64ba61968d 100644 --- a/Mage.Sets/src/mage/cards/f/FlaxenIntruder.java +++ b/Mage.Sets/src/mage/cards/f/FlaxenIntruder.java @@ -1,24 +1,17 @@ package mage.cards.f; import mage.MageInt; -import mage.abilities.Ability; -import mage.abilities.DelayedTriggeredAbility; import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; +import mage.abilities.common.delayed.ReflexiveTriggeredAbility; import mage.abilities.costs.common.SacrificeSourceCost; -import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.DestroyTargetEffect; -import mage.abilities.effects.common.DoIfCostPaid; -import mage.abilities.effects.common.SendOptionUsedEventEffect; +import mage.abilities.effects.common.DoWhenCostPaid; import mage.cards.AdventureCard; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.Outcome; import mage.constants.SubType; import mage.filter.StaticFilters; -import mage.game.Game; -import mage.game.events.GameEvent; import mage.game.permanent.token.BearToken; import mage.target.TargetPermanent; @@ -38,9 +31,13 @@ public final class FlaxenIntruder extends AdventureCard { this.toughness = new MageInt(2); // Whenever Flaxen Intruder deals combat damage to a player, you may sacrifice it. When you do, destroy target artifact or enchantment. - this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility(new DoIfCostPaid( - new FlaxenIntruderCreateReflexiveTriggerEffect(), new SacrificeSourceCost() - ).setText("you may sacrifice it. When you do, destroy target artifact or enchantment."), false)); + ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility( + new DestroyTargetEffect(), false, "destroy target artifact or enchantment" + ); + ability.addTarget(new TargetPermanent(StaticFilters.FILTER_PERMANENT_ARTIFACT_OR_ENCHANTMENT)); + this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility(new DoWhenCostPaid( + ability, new SacrificeSourceCost(), "Sacrifice {this}?" + ), false)); // Welcome Home // Create three 2/2 green Bear creature tokens. @@ -56,58 +53,3 @@ public final class FlaxenIntruder extends AdventureCard { return new FlaxenIntruder(this); } } - -class FlaxenIntruderCreateReflexiveTriggerEffect extends OneShotEffect { - - FlaxenIntruderCreateReflexiveTriggerEffect() { - super(Outcome.Benefit); - } - - private FlaxenIntruderCreateReflexiveTriggerEffect(final FlaxenIntruderCreateReflexiveTriggerEffect effect) { - super(effect); - } - - @Override - public FlaxenIntruderCreateReflexiveTriggerEffect copy() { - return new FlaxenIntruderCreateReflexiveTriggerEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - game.addDelayedTriggeredAbility(new FlaxenIntruderReflexiveTriggeredAbility(), source); - return new SendOptionUsedEventEffect().apply(game, source); - } -} - -class FlaxenIntruderReflexiveTriggeredAbility extends DelayedTriggeredAbility { - - FlaxenIntruderReflexiveTriggeredAbility() { - super(new DestroyTargetEffect(), Duration.OneUse, true); - this.addTarget(new TargetPermanent(StaticFilters.FILTER_PERMANENT_ARTIFACT_OR_ENCHANTMENT)); - } - - private FlaxenIntruderReflexiveTriggeredAbility(final FlaxenIntruderReflexiveTriggeredAbility ability) { - super(ability); - } - - @Override - public FlaxenIntruderReflexiveTriggeredAbility copy() { - return new FlaxenIntruderReflexiveTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.OPTION_USED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - return event.getPlayerId().equals(this.getControllerId()) - && event.getSourceId().equals(this.getSourceId()); - } - - @Override - public String getRule() { - return "When you do, destroy target artifact or enchantment."; - } -} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/h/HeartPiercerManticore.java b/Mage.Sets/src/mage/cards/h/HeartPiercerManticore.java index ecf355dea2d..371e4e83e30 100644 --- a/Mage.Sets/src/mage/cards/h/HeartPiercerManticore.java +++ b/Mage.Sets/src/mage/cards/h/HeartPiercerManticore.java @@ -1,32 +1,29 @@ package mage.cards.h; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.DelayedTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.delayed.ReflexiveTriggeredAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DamageTargetEffect; -import mage.abilities.effects.common.SendOptionUsedEventEffect; import mage.abilities.keyword.EmbalmAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.SubType; import mage.constants.Outcome; +import mage.constants.SubType; import mage.filter.StaticFilters; import mage.game.Game; -import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.Target; -import mage.target.common.TargetControlledCreaturePermanent; import mage.target.common.TargetAnyTarget; +import mage.target.common.TargetControlledCreaturePermanent; + +import java.util.UUID; /** - * * @author fireshoes */ public final class HeartPiercerManticore extends CardImpl { @@ -39,16 +36,13 @@ public final class HeartPiercerManticore extends CardImpl { this.toughness = new MageInt(3); // When Heart-Piercer Manticore enters the battlefield, you may sacrifice another creature. When you do, Heart-Piercer Manticore deals damage equal to that creature's power to any target. - this.addAbility(new EntersBattlefieldTriggeredAbility( - new HeartPiercerManticoreSacrificeEffect(), true - )); + this.addAbility(new EntersBattlefieldTriggeredAbility(new HeartPiercerManticoreSacrificeEffect(), true)); // Embalm {5}{R} this.addAbility(new EmbalmAbility(new ManaCostsImpl("{5}{R}"), this)); - } - public HeartPiercerManticore(final HeartPiercerManticore card) { + private HeartPiercerManticore(final HeartPiercerManticore card) { super(card); } @@ -60,13 +54,13 @@ public final class HeartPiercerManticore extends CardImpl { class HeartPiercerManticoreSacrificeEffect extends OneShotEffect { - public HeartPiercerManticoreSacrificeEffect() { + HeartPiercerManticoreSacrificeEffect() { super(Outcome.Damage); this.staticText = "sacrifice another creature. When you do, " + "{this} deals damage equal to that creature's power to any target"; } - public HeartPiercerManticoreSacrificeEffect(final HeartPiercerManticoreSacrificeEffect effect) { + private HeartPiercerManticoreSacrificeEffect(final HeartPiercerManticoreSacrificeEffect effect) { super(effect); } @@ -78,54 +72,29 @@ class HeartPiercerManticoreSacrificeEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - Target target = new TargetControlledCreaturePermanent(1, 1, StaticFilters.FILTER_CONTROLLED_ANOTHER_CREATURE, true); - if (controller.choose(outcome, target, source.getSourceId(), game)) { - Permanent toSacrifice = game.getPermanent(target.getFirstTarget()); - if (toSacrifice != null) { - DelayedTriggeredAbility trigger = new HeartPiercerManticoreReflexiveTriggeredAbility(toSacrifice.getPower().getValue()); - if (toSacrifice.sacrifice(source.getSourceId(), game)) { - game.addDelayedTriggeredAbility(trigger, source); - return new SendOptionUsedEventEffect().apply(game, source); - } - } - } - return true; + if (controller == null) { + return false; } - return false; - } -} - -class HeartPiercerManticoreReflexiveTriggeredAbility extends DelayedTriggeredAbility { - - public HeartPiercerManticoreReflexiveTriggeredAbility(int damage) { - super(new DamageTargetEffect(damage), Duration.OneUse, true); - this.addTarget(new TargetAnyTarget()); - } - - public HeartPiercerManticoreReflexiveTriggeredAbility(final HeartPiercerManticoreReflexiveTriggeredAbility ability) { - super(ability); - } - - @Override - public HeartPiercerManticoreReflexiveTriggeredAbility copy() { - return new HeartPiercerManticoreReflexiveTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.OPTION_USED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - return event.getPlayerId().equals(this.getControllerId()) - && event.getSourceId().equals(this.getSourceId()); - } - - @Override - public String getRule() { - return "When you sacrifice a creature to {this}'s ability, " - + "{this} deals damage equal to that creature's power to any target"; + Target target = new TargetControlledCreaturePermanent( + 1, 1, StaticFilters.FILTER_CONTROLLED_ANOTHER_CREATURE, true + ); + if (!controller.choose(outcome, target, source.getSourceId(), game)) { + return false; + } + Permanent toSacrifice = game.getPermanent(target.getFirstTarget()); + if (toSacrifice == null) { + return false; + } + int power = toSacrifice.getPower().getValue(); + if (!toSacrifice.sacrifice(source.getSourceId(), game)) { + return false; + } + ReflexiveTriggeredAbility trigger = new ReflexiveTriggeredAbility( + new DamageTargetEffect(power), false, + "{this} deals damage equal to that creature's power to any target." + ); + trigger.addTarget(new TargetAnyTarget()); + game.fireReflexiveTriggeredAbility(trigger, source); + return true; } } diff --git a/Mage.Sets/src/mage/cards/h/Hypothesizzle.java b/Mage.Sets/src/mage/cards/h/Hypothesizzle.java index f866ac8f025..82681c07c58 100644 --- a/Mage.Sets/src/mage/cards/h/Hypothesizzle.java +++ b/Mage.Sets/src/mage/cards/h/Hypothesizzle.java @@ -1,26 +1,19 @@ package mage.cards.h; -import java.util.UUID; -import mage.abilities.Ability; -import mage.abilities.DelayedTriggeredAbility; +import mage.abilities.common.delayed.ReflexiveTriggeredAbility; import mage.abilities.costs.common.DiscardCardCost; -import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DamageTargetEffect; -import mage.abilities.effects.common.DoIfCostPaid; +import mage.abilities.effects.common.DoWhenCostPaid; import mage.abilities.effects.common.DrawCardSourceControllerEffect; -import mage.abilities.effects.common.SendOptionUsedEventEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.Outcome; import mage.filter.StaticFilters; -import mage.game.Game; -import mage.game.events.GameEvent; import mage.target.common.TargetCreaturePermanent; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class Hypothesizzle extends CardImpl { @@ -30,15 +23,19 @@ public final class Hypothesizzle extends CardImpl { // Draw two cards. Then you may discard a nonland card. When you do, Hypothesizzle deals 4 damage to target creature. this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(2)); - this.getSpellAbility().addEffect(new DoIfCostPaid( - new HypothesizzleCreateReflexiveTriggerEffect(), - new DiscardCardCost(StaticFilters.FILTER_CARD_A_NON_LAND), - "Discard a nonland card to deal 4 damage?" - ).setText("Then you may discard a nonland card. " - + "When you do, {this} deals 4 damage to target creature.")); + + ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility( + new DamageTargetEffect(4), false, + "{this} deals 4 damage to target creature" + ); + ability.addTarget(new TargetCreaturePermanent()); + this.getSpellAbility().addEffect(new DoWhenCostPaid( + ability, new DiscardCardCost(StaticFilters.FILTER_CARD_A_NON_LAND), + "Discard a nonland card?" + ).concatBy("Then")); } - public Hypothesizzle(final Hypothesizzle card) { + private Hypothesizzle(final Hypothesizzle card) { super(card); } @@ -47,60 +44,3 @@ public final class Hypothesizzle extends CardImpl { return new Hypothesizzle(this); } } - -class HypothesizzleCreateReflexiveTriggerEffect extends OneShotEffect { - - public HypothesizzleCreateReflexiveTriggerEffect() { - super(Outcome.Benefit); - this.staticText = "When you do, it deals 4 damage to target creature"; - } - - public HypothesizzleCreateReflexiveTriggerEffect(final HypothesizzleCreateReflexiveTriggerEffect effect) { - super(effect); - } - - @Override - public HypothesizzleCreateReflexiveTriggerEffect copy() { - return new HypothesizzleCreateReflexiveTriggerEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - game.addDelayedTriggeredAbility(new HypothesizzleReflexiveTriggeredAbility(), source); - return new SendOptionUsedEventEffect().apply(game, source); - } -} - -class HypothesizzleReflexiveTriggeredAbility extends DelayedTriggeredAbility { - - public HypothesizzleReflexiveTriggeredAbility() { - super(new DamageTargetEffect(4), Duration.OneUse, true); - this.addTarget(new TargetCreaturePermanent()); - } - - public HypothesizzleReflexiveTriggeredAbility(final HypothesizzleReflexiveTriggeredAbility ability) { - super(ability); - } - - @Override - public HypothesizzleReflexiveTriggeredAbility copy() { - return new HypothesizzleReflexiveTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.OPTION_USED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - return event.getPlayerId().equals(this.getControllerId()) - && event.getSourceId().equals(this.getSourceId()); - } - - @Override - public String getRule() { - return "When you discard a nonland card, " - + "{this} deals 4 damage to target creature"; - } -} diff --git a/Mage.Sets/src/mage/cards/i/IsarethTheAwakener.java b/Mage.Sets/src/mage/cards/i/IsarethTheAwakener.java index 62cf6c7eff7..ced07404f12 100644 --- a/Mage.Sets/src/mage/cards/i/IsarethTheAwakener.java +++ b/Mage.Sets/src/mage/cards/i/IsarethTheAwakener.java @@ -1,27 +1,19 @@ package mage.cards.i; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.DelayedTriggeredAbility; import mage.abilities.common.AttacksTriggeredAbility; +import mage.abilities.common.delayed.ReflexiveTriggeredAbility; import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.costs.mana.ManaCosts; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.ReplacementEffectImpl; -import mage.abilities.effects.common.SendOptionUsedEventEffect; -import mage.constants.SubType; -import mage.constants.SuperType; import mage.abilities.keyword.DeathtouchAbility; import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.ComparisonType; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.constants.Zone; +import mage.constants.*; import mage.counters.CounterType; import mage.counters.Counters; import mage.filter.FilterCard; @@ -33,8 +25,9 @@ import mage.game.events.ZoneChangeEvent; import mage.players.Player; import mage.target.common.TargetCardInYourGraveyard; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class IsarethTheAwakener extends CardImpl { @@ -55,7 +48,7 @@ public final class IsarethTheAwakener extends CardImpl { this.addAbility(new AttacksTriggeredAbility(new IsarethTheAwakenerCreateReflexiveTriggerEffect(), false)); } - public IsarethTheAwakener(final IsarethTheAwakener card) { + private IsarethTheAwakener(final IsarethTheAwakener card) { super(card); } @@ -67,15 +60,17 @@ public final class IsarethTheAwakener extends CardImpl { class IsarethTheAwakenerCreateReflexiveTriggerEffect extends OneShotEffect { - public IsarethTheAwakenerCreateReflexiveTriggerEffect() { + private static final String rule = "return target creature card " + + "with converted mana cost X from your graveyard to the battlefield " + + "with a corpse counter on it. If that creature would leave the battlefield, " + + "exile it instead of putting it anywhere else."; + + IsarethTheAwakenerCreateReflexiveTriggerEffect() { super(Outcome.Benefit); - this.staticText = "you may pay {X}. When you do, return target creature card " - + "with converted mana cost X from your graveyard to the battlefield " - + "with a corpse counter on it. If that creature would leave the battlefield, " - + "exile it instead of putting it anywhere else."; + this.staticText = "you may pay {X}. When you do, " + rule; } - public IsarethTheAwakenerCreateReflexiveTriggerEffect(final IsarethTheAwakenerCreateReflexiveTriggerEffect effect) { + private IsarethTheAwakenerCreateReflexiveTriggerEffect(final IsarethTheAwakenerCreateReflexiveTriggerEffect effect) { super(effect); } @@ -97,67 +92,34 @@ class IsarethTheAwakenerCreateReflexiveTriggerEffect extends OneShotEffect { if (!cost.pay(source, game, source.getSourceId(), source.getControllerId(), false, null)) { return false; } - game.addDelayedTriggeredAbility(new IsarethTheAwakenerReflexiveTriggeredAbility(), source); - return new SendOptionUsedEventEffect(costX).apply(game, source); - } -} - -class IsarethTheAwakenerReflexiveTriggeredAbility extends DelayedTriggeredAbility { - - public IsarethTheAwakenerReflexiveTriggeredAbility() { - super(new IsarethTheAwakenerEffect(), Duration.OneUse, true); - this.addEffect(new IsarethTheAwakenerReplacementEffect()); - } - - public IsarethTheAwakenerReflexiveTriggeredAbility(final IsarethTheAwakenerReflexiveTriggeredAbility ability) { - super(ability); - } - - @Override - public IsarethTheAwakenerReflexiveTriggeredAbility copy() { - return new IsarethTheAwakenerReflexiveTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.OPTION_USED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (!event.getPlayerId().equals(this.getControllerId()) - || !event.getSourceId().equals(this.getSourceId())) { - return false; - } - FilterCard filter = new FilterCreatureCard( - "creature card with converted mana cost " - + event.getAmount() - + " or less from your graveyard" + ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility( + new IsarethTheAwakenerEffect(), false, rule ); - filter.add(new ConvertedManaCostPredicate( - ComparisonType.FEWER_THAN, event.getAmount() + 1 - )); - this.getTargets().clear(); - this.addTarget(new TargetCardInYourGraveyard(filter)); + ability.addEffect(new IsarethTheAwakenerReplacementEffect()); + ability.addTarget(new TargetCardInYourGraveyard(makeFilter(costX))); + game.fireReflexiveTriggeredAbility(ability, source); return true; } - @Override - public String getRule() { - return "When you pay {X}, return target creature card " - + "with converted mana cost X from your graveyard to the battlefield " - + "with a corpse counter on it. If that creature would leave the battlefield, " - + "exile it instead of putting it anywhere else."; + private static FilterCard makeFilter(int xValue) { + FilterCard filter = new FilterCreatureCard( + "creature card with converted mana cost " + + xValue + " or less from your graveyard" + ); + filter.add(new ConvertedManaCostPredicate( + ComparisonType.EQUAL_TO, xValue + )); + return filter; } } class IsarethTheAwakenerEffect extends OneShotEffect { - public IsarethTheAwakenerEffect() { + IsarethTheAwakenerEffect() { super(Outcome.PutCreatureInPlay); } - public IsarethTheAwakenerEffect(final IsarethTheAwakenerEffect effect) { + private IsarethTheAwakenerEffect(final IsarethTheAwakenerEffect effect) { super(effect); } @@ -182,11 +144,11 @@ class IsarethTheAwakenerEffect extends OneShotEffect { class IsarethTheAwakenerReplacementEffect extends ReplacementEffectImpl { - public IsarethTheAwakenerReplacementEffect() { + IsarethTheAwakenerReplacementEffect() { super(Duration.Custom, Outcome.Exile); } - public IsarethTheAwakenerReplacementEffect(final IsarethTheAwakenerReplacementEffect effect) { + private IsarethTheAwakenerReplacementEffect(final IsarethTheAwakenerReplacementEffect effect) { super(effect); } diff --git a/Mage.Sets/src/mage/cards/l/LavabrinkFloodgates.java b/Mage.Sets/src/mage/cards/l/LavabrinkFloodgates.java index a3f4e93fd3b..6dc1f35714e 100644 --- a/Mage.Sets/src/mage/cards/l/LavabrinkFloodgates.java +++ b/Mage.Sets/src/mage/cards/l/LavabrinkFloodgates.java @@ -2,22 +2,23 @@ package mage.cards.l; import mage.Mana; import mage.abilities.Ability; -import mage.abilities.DelayedTriggeredAbility; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; +import mage.abilities.common.delayed.ReflexiveTriggeredAbility; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DamageAllEffect; -import mage.abilities.effects.common.SendOptionUsedEventEffect; import mage.abilities.mana.SimpleManaAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.choices.Choice; import mage.choices.ChoiceImpl; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.TargetController; +import mage.constants.Zone; import mage.counters.CounterType; import mage.filter.StaticFilters; import mage.game.Game; -import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.players.Player; @@ -93,45 +94,17 @@ class LavabrinkFloodgatesEffect extends OneShotEffect { break; case "Do nothing": default: - return false; + break; } if (permanent.getCounters(game).getCount(CounterType.DOOM) < 3 || !permanent.sacrifice(source.getSourceId(), game)) { return true; } - game.addDelayedTriggeredAbility(new LavabrinkFloodgatesReflexiveTriggeredAbility(), source); - return new SendOptionUsedEventEffect().apply(game, source); - } -} - -class LavabrinkFloodgatesReflexiveTriggeredAbility extends DelayedTriggeredAbility { - - LavabrinkFloodgatesReflexiveTriggeredAbility() { - super(new DamageAllEffect(6, StaticFilters.FILTER_PERMANENT_CREATURE), Duration.OneUse, true); - } - - private LavabrinkFloodgatesReflexiveTriggeredAbility(final LavabrinkFloodgatesReflexiveTriggeredAbility ability) { - super(ability); - } - - @Override - public LavabrinkFloodgatesReflexiveTriggeredAbility copy() { - return new LavabrinkFloodgatesReflexiveTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.OPTION_USED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - return event.getPlayerId().equals(this.getControllerId()) - && event.getSourceId().equals(this.getSourceId()); - } - - @Override - public String getRule() { - return "It deals 6 damage to each creature."; + game.fireReflexiveTriggeredAbility(new ReflexiveTriggeredAbility( + new DamageAllEffect( + 6, StaticFilters.FILTER_PERMANENT_CREATURE + ), false, "it deals 6 damage to each creature." + ), source); + return true; } } diff --git a/Mage.Sets/src/mage/cards/n/NarsetOfTheAncientWay.java b/Mage.Sets/src/mage/cards/n/NarsetOfTheAncientWay.java index 336202caa96..1ef622e1c1e 100644 --- a/Mage.Sets/src/mage/cards/n/NarsetOfTheAncientWay.java +++ b/Mage.Sets/src/mage/cards/n/NarsetOfTheAncientWay.java @@ -3,25 +3,26 @@ package mage.cards.n; import mage.ConditionalMana; import mage.MageObject; import mage.abilities.Ability; -import mage.abilities.DelayedTriggeredAbility; import mage.abilities.LoyaltyAbility; import mage.abilities.SpellAbility; import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; +import mage.abilities.common.delayed.ReflexiveTriggeredAbility; import mage.abilities.condition.Condition; import mage.abilities.costs.Cost; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DamageTargetEffect; import mage.abilities.effects.common.GetEmblemEffect; -import mage.abilities.effects.common.SendOptionUsedEventEffect; import mage.abilities.mana.conditional.ManaCondition; import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.choices.ChoiceColor; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.SubType; +import mage.constants.SuperType; import mage.game.Game; import mage.game.command.emblems.NarsetOfTheAncientWayEmblem; -import mage.game.events.GameEvent; import mage.players.Player; import mage.target.common.TargetCreatureOrPlaneswalker; @@ -141,41 +142,12 @@ class NarsetOfTheAncientWayDrawEffect extends OneShotEffect { if (card == null || card.isLand()) { return false; } - game.addDelayedTriggeredAbility(new NarsetOfTheAncientWayReflexiveTriggeredAbility(card.getConvertedManaCost()), source); - return new SendOptionUsedEventEffect().apply(game, source); - } -} - -class NarsetOfTheAncientWayReflexiveTriggeredAbility extends DelayedTriggeredAbility { - - NarsetOfTheAncientWayReflexiveTriggeredAbility(int cmc) { - super(new DamageTargetEffect(cmc), Duration.OneUse, true); - this.addTarget(new TargetCreatureOrPlaneswalker()); - } - - private NarsetOfTheAncientWayReflexiveTriggeredAbility(final NarsetOfTheAncientWayReflexiveTriggeredAbility ability) { - super(ability); - } - - @Override - public NarsetOfTheAncientWayReflexiveTriggeredAbility copy() { - return new NarsetOfTheAncientWayReflexiveTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.OPTION_USED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - return event.getPlayerId().equals(this.getControllerId()) - && event.getSourceId().equals(this.getSourceId()); - } - - @Override - public String getRule() { - return "{this} deals damage to target creature or planeswalker " + - "equal to the discarded card's converted mana cost"; + ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility( + new DamageTargetEffect(card.getConvertedManaCost()), false, "{this} deals damage " + + "to target creature or planeswalker equal to the discarded card's converted mana cost" + ); + ability.addTarget(new TargetCreatureOrPlaneswalker()); + game.fireReflexiveTriggeredAbility(ability, source); + return true; } } diff --git a/Mage.Sets/src/mage/cards/s/SavaiThundermane.java b/Mage.Sets/src/mage/cards/s/SavaiThundermane.java index 921dfdb90ec..544f27681c0 100644 --- a/Mage.Sets/src/mage/cards/s/SavaiThundermane.java +++ b/Mage.Sets/src/mage/cards/s/SavaiThundermane.java @@ -1,23 +1,16 @@ package mage.cards.s; import mage.MageInt; -import mage.abilities.Ability; -import mage.abilities.DelayedTriggeredAbility; import mage.abilities.common.CycleControllerTriggeredAbility; +import mage.abilities.common.delayed.ReflexiveTriggeredAbility; import mage.abilities.costs.mana.GenericManaCost; -import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DamageTargetEffect; -import mage.abilities.effects.common.DoIfCostPaid; +import mage.abilities.effects.common.DoWhenCostPaid; import mage.abilities.effects.common.GainLifeEffect; -import mage.abilities.effects.common.SendOptionUsedEventEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.Outcome; import mage.constants.SubType; -import mage.game.Game; -import mage.game.events.GameEvent; import mage.target.common.TargetCreaturePermanent; import java.util.UUID; @@ -35,12 +28,15 @@ public final class SavaiThundermane extends CardImpl { this.power = new MageInt(3); this.toughness = new MageInt(2); - // Whenever you cycle a card, you may pay {2}. When you do Savai Thundermane deals 2 damage to target creature and you gain 2 life. + // Whenever you cycle a card, you may pay {2}. When you do, Savai Thundermane deals 2 damage to target creature and you gain 2 life. + ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility( + new DamageTargetEffect(2), false, + "{this} deals 2 damage to target creature and you gain 2 life" + ); + ability.addEffect(new GainLifeEffect(2)); + ability.addTarget(new TargetCreaturePermanent()); this.addAbility(new CycleControllerTriggeredAbility( - new DoIfCostPaid( - new SavaiThundermaneCreateReflexiveTriggerEffect(), new GenericManaCost(2), - "Pay {2} to deal 2 damage and gain 2 life?" - ).setText("you may pay {2}. When you do, {this} deals 2 damage to target creature and you gain 2 life") + new DoWhenCostPaid(ability, new GenericManaCost(2), "Pay {2}?") )); } @@ -53,59 +49,3 @@ public final class SavaiThundermane extends CardImpl { return new SavaiThundermane(this); } } - -class SavaiThundermaneCreateReflexiveTriggerEffect extends OneShotEffect { - - SavaiThundermaneCreateReflexiveTriggerEffect() { - super(Outcome.Benefit); - } - - private SavaiThundermaneCreateReflexiveTriggerEffect(final SavaiThundermaneCreateReflexiveTriggerEffect effect) { - super(effect); - } - - @Override - public SavaiThundermaneCreateReflexiveTriggerEffect copy() { - return new SavaiThundermaneCreateReflexiveTriggerEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - game.addDelayedTriggeredAbility(new SavaiThundermaneReflexiveTriggeredAbility(), source); - return new SendOptionUsedEventEffect().apply(game, source); - } -} - -class SavaiThundermaneReflexiveTriggeredAbility extends DelayedTriggeredAbility { - - SavaiThundermaneReflexiveTriggeredAbility() { - super(new DamageTargetEffect(2), Duration.OneUse, true); - this.addEffect(new GainLifeEffect(2)); - this.addTarget(new TargetCreaturePermanent()); - } - - private SavaiThundermaneReflexiveTriggeredAbility(final SavaiThundermaneReflexiveTriggeredAbility ability) { - super(ability); - } - - @Override - public SavaiThundermaneReflexiveTriggeredAbility copy() { - return new SavaiThundermaneReflexiveTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.OPTION_USED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - return event.getPlayerId().equals(this.getControllerId()) - && event.getSourceId().equals(this.getSourceId()); - } - - @Override - public String getRule() { - return "{this} deals 2 damage to target creature and you gain 2 life"; - } -} diff --git a/Mage.Sets/src/mage/cards/s/SkyriderPatrol.java b/Mage.Sets/src/mage/cards/s/SkyriderPatrol.java index c7119a81835..1ab4b0e8392 100644 --- a/Mage.Sets/src/mage/cards/s/SkyriderPatrol.java +++ b/Mage.Sets/src/mage/cards/s/SkyriderPatrol.java @@ -1,37 +1,38 @@ package mage.cards.s; -import java.util.UUID; import mage.MageInt; -import mage.abilities.Ability; -import mage.abilities.DelayedTriggeredAbility; import mage.abilities.common.BeginningOfCombatTriggeredAbility; +import mage.abilities.common.delayed.ReflexiveTriggeredAbility; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.DoIfCostPaid; -import mage.abilities.effects.common.SendOptionUsedEventEffect; +import mage.abilities.effects.common.DoWhenCostPaid; import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; import mage.abilities.effects.common.counter.AddCountersTargetEffect; -import mage.constants.SubType; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; -import mage.constants.Outcome; +import mage.constants.SubType; import mage.constants.TargetController; import mage.counters.CounterType; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.permanent.AnotherPredicate; -import mage.game.Game; -import mage.game.events.GameEvent; import mage.target.TargetPermanent; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class SkyriderPatrol extends CardImpl { + private static final FilterControlledCreaturePermanent filter + = new FilterControlledCreaturePermanent("another creature you control"); + + static { + filter.add(AnotherPredicate.instance); + } + public SkyriderPatrol(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}{U}"); @@ -44,20 +45,19 @@ public final class SkyriderPatrol extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // At the beginning of combat on your turn, you may pay {G}{U}. When you do, put a +1/+1 counter on another target creature you control, and that creature gains flying until end of turn. - this.addAbility(new BeginningOfCombatTriggeredAbility( - new DoIfCostPaid( - new SkyriderPatrolCreateReflexiveTriggerEffect(), - new ManaCostsImpl("{G}{U}"), - "Pay {G}{U} to put a +1/+1 counter on another" - + " creature you control and give it flying?" - ).setText("you may pay {G}{U}. When you do, " - + "put a +1/+1 counter on another target creature you control, " - + "and that creature gains flying until end of turn."), - TargetController.YOU, false - )); + ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility( + new AddCountersTargetEffect(CounterType.P1P1.createInstance()), false, + "put a +1/+1 counter on another target creature you control, " + + "and that creature gains flying until end of turn" + ); + ability.addEffect(new GainAbilityTargetEffect(FlyingAbility.getInstance(), Duration.EndOfTurn)); + ability.addTarget(new TargetPermanent(filter)); + this.addAbility(new BeginningOfCombatTriggeredAbility(new DoWhenCostPaid( + ability, new ManaCostsImpl("{G}{U}"), "Pay {G}{U}?" + ), TargetController.YOU, false)); } - public SkyriderPatrol(final SkyriderPatrol card) { + private SkyriderPatrol(final SkyriderPatrol card) { super(card); } @@ -66,71 +66,3 @@ public final class SkyriderPatrol extends CardImpl { return new SkyriderPatrol(this); } } - -class SkyriderPatrolCreateReflexiveTriggerEffect extends OneShotEffect { - - public SkyriderPatrolCreateReflexiveTriggerEffect() { - super(Outcome.Benefit); - } - - public SkyriderPatrolCreateReflexiveTriggerEffect(final SkyriderPatrolCreateReflexiveTriggerEffect effect) { - super(effect); - } - - @Override - public SkyriderPatrolCreateReflexiveTriggerEffect copy() { - return new SkyriderPatrolCreateReflexiveTriggerEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - game.addDelayedTriggeredAbility(new SkyriderPatrolReflexiveTriggeredAbility(), source); - return new SendOptionUsedEventEffect().apply(game, source); - } -} - -class SkyriderPatrolReflexiveTriggeredAbility extends DelayedTriggeredAbility { - - private static final FilterControlledCreaturePermanent filter - = new FilterControlledCreaturePermanent("another creature you control"); - - static { - filter.add(AnotherPredicate.instance); - } - - public SkyriderPatrolReflexiveTriggeredAbility() { - super(new AddCountersTargetEffect(CounterType.P1P1.createInstance()), Duration.OneUse, true); - this.addEffect(new GainAbilityTargetEffect( - FlyingAbility.getInstance(), - Duration.EndOfTurn - )); - this.addTarget(new TargetPermanent(filter)); - } - - public SkyriderPatrolReflexiveTriggeredAbility(final SkyriderPatrolReflexiveTriggeredAbility ability) { - super(ability); - } - - @Override - public SkyriderPatrolReflexiveTriggeredAbility copy() { - return new SkyriderPatrolReflexiveTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.OPTION_USED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - return event.getPlayerId().equals(this.getControllerId()) - && event.getSourceId().equals(this.getSourceId()); - } - - @Override - public String getRule() { - return "When you pay {G}{U}, put a +1/+1 counter " - + "on another target creature you control, " - + "and that creature gains flying until end of turn."; - } -} diff --git a/Mage.Sets/src/mage/cards/s/SorinImperiousBloodlord.java b/Mage.Sets/src/mage/cards/s/SorinImperiousBloodlord.java index 038ddf9ead3..8c720fcb43f 100644 --- a/Mage.Sets/src/mage/cards/s/SorinImperiousBloodlord.java +++ b/Mage.Sets/src/mage/cards/s/SorinImperiousBloodlord.java @@ -1,12 +1,15 @@ package mage.cards.s; import mage.abilities.Ability; -import mage.abilities.DelayedTriggeredAbility; import mage.abilities.LoyaltyAbility; import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; +import mage.abilities.common.delayed.ReflexiveTriggeredAbility; import mage.abilities.costs.common.SacrificeTargetCost; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.*; +import mage.abilities.effects.common.DamageTargetEffect; +import mage.abilities.effects.common.DoWhenCostPaid; +import mage.abilities.effects.common.GainLifeEffect; +import mage.abilities.effects.common.PutCardFromHandOntoBattlefieldEffect; import mage.abilities.effects.common.continuous.GainAbilityTargetEffect; import mage.abilities.keyword.DeathtouchAbility; import mage.abilities.keyword.LifelinkAbility; @@ -21,7 +24,6 @@ import mage.filter.FilterCard; import mage.filter.common.FilterControlledPermanent; import mage.filter.common.FilterCreatureCard; import mage.game.Game; -import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.target.common.TargetAnyTarget; import mage.target.common.TargetControlledCreaturePermanent; @@ -58,11 +60,16 @@ public final class SorinImperiousBloodlord extends CardImpl { this.addAbility(ability); // +1: You may sacrifice a Vampire. When you do, Sorin, Imperious Bloodlord deals 3 damage to any target and you gain 3 life. - this.addAbility(new LoyaltyAbility(new DoIfCostPaid( - new SorinImperiousBloodlordCreateReflexiveTriggerEffect(), - new SacrificeTargetCost(new TargetControlledPermanent(filter)) - ).setText("You may sacrifice a Vampire. " + - "When you do, {this} deals 3 damage to any target and you gain 3 life." + ReflexiveTriggeredAbility triggeredAbility = new ReflexiveTriggeredAbility( + new DamageTargetEffect(3), false, + "{this} deals 3 damage to any target and you gain 3 life" + ); + triggeredAbility.addEffect(new GainLifeEffect(3)); + triggeredAbility.addTarget(new TargetAnyTarget()); + this.addAbility(new LoyaltyAbility(new DoWhenCostPaid( + triggeredAbility, + new SacrificeTargetCost(new TargetControlledPermanent(filter)), + "Sacrifice a Vampire?" ), 1)); // −3: You may put a Vampire creature card from your hand onto the battlefield. @@ -110,58 +117,3 @@ class SorinImperiousBloodlordEffect extends OneShotEffect { return true; } } - -class SorinImperiousBloodlordCreateReflexiveTriggerEffect extends OneShotEffect { - - SorinImperiousBloodlordCreateReflexiveTriggerEffect() { - super(Benefit); - } - - private SorinImperiousBloodlordCreateReflexiveTriggerEffect(final SorinImperiousBloodlordCreateReflexiveTriggerEffect effect) { - super(effect); - } - - @Override - public SorinImperiousBloodlordCreateReflexiveTriggerEffect copy() { - return new SorinImperiousBloodlordCreateReflexiveTriggerEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - game.addDelayedTriggeredAbility(new SorinImperiousBloodlordReflexiveTriggeredAbility(), source); - return new SendOptionUsedEventEffect().apply(game, source); - } -} - -class SorinImperiousBloodlordReflexiveTriggeredAbility extends DelayedTriggeredAbility { - SorinImperiousBloodlordReflexiveTriggeredAbility() { - super(new DamageTargetEffect(3), Duration.OneUse, true); - this.addEffect(new GainLifeEffect(3)); - this.addTarget(new TargetAnyTarget()); - } - - private SorinImperiousBloodlordReflexiveTriggeredAbility(final SorinImperiousBloodlordReflexiveTriggeredAbility ability) { - super(ability); - } - - @Override - public SorinImperiousBloodlordReflexiveTriggeredAbility copy() { - return new SorinImperiousBloodlordReflexiveTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.OPTION_USED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - return event.getPlayerId().equals(this.getControllerId()) - && event.getSourceId().equals(this.getSourceId()); - } - - @Override - public String getRule() { - return "{this} deals 3 damage to any target and you gain 3 life"; - } -} diff --git a/Mage.Sets/src/mage/cards/s/SparktongueDragon.java b/Mage.Sets/src/mage/cards/s/SparktongueDragon.java index ae8dae4410c..5396b970d15 100644 --- a/Mage.Sets/src/mage/cards/s/SparktongueDragon.java +++ b/Mage.Sets/src/mage/cards/s/SparktongueDragon.java @@ -1,28 +1,21 @@ package mage.cards.s; -import java.util.UUID; import mage.MageInt; -import mage.abilities.Ability; -import mage.abilities.DelayedTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.common.delayed.ReflexiveTriggeredAbility; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DamageTargetEffect; -import mage.abilities.effects.common.DoIfCostPaid; -import mage.abilities.effects.common.SendOptionUsedEventEffect; -import mage.constants.SubType; +import mage.abilities.effects.common.DoWhenCostPaid; import mage.abilities.keyword.FlyingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Duration; -import mage.constants.Outcome; -import mage.game.Game; -import mage.game.events.GameEvent; +import mage.constants.SubType; import mage.target.common.TargetAnyTarget; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class SparktongueDragon extends CardImpl { @@ -38,16 +31,18 @@ public final class SparktongueDragon extends CardImpl { this.addAbility(FlyingAbility.getInstance()); // When Sparktongue Dragon enters the battlefield, you may pay {2}{R}. When you do, it deals 3 damage to any target. - this.addAbility(new EntersBattlefieldTriggeredAbility( - new DoIfCostPaid( - new SparktongueDragonCreateReflexiveTriggerEffect(), - new ManaCostsImpl("{2}{R}"), - "Pay {2}{R} to deal 3 damage?" - ).setText("you may pay {2}{R}. When you do, it deals 3 damage to any target") - )); + ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility( + new DamageTargetEffect(3), false, + "it deals 3 damage to any target" + ); + ability.addTarget(new TargetAnyTarget()); + this.addAbility(new EntersBattlefieldTriggeredAbility(new DoWhenCostPaid( + ability, new ManaCostsImpl("{2}{R}"), + "Pay {2}{R} to deal 3 damage?" + ))); } - public SparktongueDragon(final SparktongueDragon card) { + private SparktongueDragon(final SparktongueDragon card) { super(card); } @@ -56,59 +51,3 @@ public final class SparktongueDragon extends CardImpl { return new SparktongueDragon(this); } } - -class SparktongueDragonCreateReflexiveTriggerEffect extends OneShotEffect { - - public SparktongueDragonCreateReflexiveTriggerEffect() { - super(Outcome.Benefit); - this.staticText = "When you do, it deals 3 damage to any target"; - } - - public SparktongueDragonCreateReflexiveTriggerEffect(final SparktongueDragonCreateReflexiveTriggerEffect effect) { - super(effect); - } - - @Override - public SparktongueDragonCreateReflexiveTriggerEffect copy() { - return new SparktongueDragonCreateReflexiveTriggerEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - game.addDelayedTriggeredAbility(new SparktongueDragonReflexiveTriggeredAbility(), source); - return new SendOptionUsedEventEffect().apply(game, source); - } -} - -class SparktongueDragonReflexiveTriggeredAbility extends DelayedTriggeredAbility { - - public SparktongueDragonReflexiveTriggeredAbility() { - super(new DamageTargetEffect(3), Duration.OneUse, true); - this.addTarget(new TargetAnyTarget()); - } - - public SparktongueDragonReflexiveTriggeredAbility(final SparktongueDragonReflexiveTriggeredAbility ability) { - super(ability); - } - - @Override - public SparktongueDragonReflexiveTriggeredAbility copy() { - return new SparktongueDragonReflexiveTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.OPTION_USED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - return event.getPlayerId().equals(this.getControllerId()) - && event.getSourceId().equals(this.getSourceId()); - } - - @Override - public String getRule() { - return "When you pay {2}{R}, {this} deals 3 damage to any target"; - } -} diff --git a/Mage.Sets/src/mage/cards/t/TheRoyalScions.java b/Mage.Sets/src/mage/cards/t/TheRoyalScions.java index 83fda327c07..291bbe9b25f 100644 --- a/Mage.Sets/src/mage/cards/t/TheRoyalScions.java +++ b/Mage.Sets/src/mage/cards/t/TheRoyalScions.java @@ -1,9 +1,9 @@ package mage.cards.t; import mage.abilities.Ability; -import mage.abilities.DelayedTriggeredAbility; import mage.abilities.LoyaltyAbility; import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility; +import mage.abilities.common.delayed.ReflexiveTriggeredAbility; import mage.abilities.dynamicvalue.common.CardsInControllerHandCount; import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; @@ -18,7 +18,6 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.*; import mage.game.Game; -import mage.game.events.GameEvent; import mage.target.common.TargetAnyTarget; import mage.target.common.TargetCreaturePermanent; @@ -89,41 +88,12 @@ class TheRoyalScionsCreateReflexiveTriggerEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { effect.apply(game, source); - game.addDelayedTriggeredAbility(new TheRoyalScionsReflexiveTriggeredAbility(), source); - game.fireEvent(GameEvent.getEvent(GameEvent.EventType.OPTION_USED, source.getOriginalId(), source.getSourceId(), source.getControllerId(), 0)); + ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility( + new DamageTargetEffect(CardsInControllerHandCount.instance), false, + "{this} deals damage to any target equal to the number of cards in your hand" + ); + ability.addTarget(new TargetAnyTarget()); + game.fireReflexiveTriggeredAbility(ability, source); return true; } } - -class TheRoyalScionsReflexiveTriggeredAbility extends DelayedTriggeredAbility { - - TheRoyalScionsReflexiveTriggeredAbility() { - super(new DamageTargetEffect(CardsInControllerHandCount.instance), Duration.OneUse, true); - this.addTarget(new TargetAnyTarget()); - } - - private TheRoyalScionsReflexiveTriggeredAbility(final TheRoyalScionsReflexiveTriggeredAbility ability) { - super(ability); - } - - @Override - public TheRoyalScionsReflexiveTriggeredAbility copy() { - return new TheRoyalScionsReflexiveTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.OPTION_USED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - return event.getPlayerId().equals(this.getControllerId()) - && event.getSourceId().equals(this.getSourceId()); - } - - @Override - public String getRule() { - return "When you do, {this} deals damage to any target equal to the number of cards in your hand."; - } -} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/v/ViviensInvocation.java b/Mage.Sets/src/mage/cards/v/ViviensInvocation.java index 23d46f0a18d..b3ed065a12c 100644 --- a/Mage.Sets/src/mage/cards/v/ViviensInvocation.java +++ b/Mage.Sets/src/mage/cards/v/ViviensInvocation.java @@ -1,30 +1,24 @@ package mage.cards.v; -import java.util.UUID; import mage.MageObjectReference; import mage.abilities.Ability; -import mage.abilities.DelayedTriggeredAbility; +import mage.abilities.common.delayed.ReflexiveTriggeredAbility; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.SendOptionUsedEventEffect; -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.Duration; import mage.constants.Outcome; import mage.constants.Zone; +import mage.filter.FilterCard; import mage.filter.common.FilterCreatureCard; import mage.game.Game; -import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.TargetCard; import mage.target.common.TargetOpponentsCreaturePermanent; +import java.util.UUID; + /** - * * @author TheElk801 */ public final class ViviensInvocation extends CardImpl { @@ -36,7 +30,7 @@ public final class ViviensInvocation extends CardImpl { this.getSpellAbility().addEffect(new ViviensInvocationEffect()); } - public ViviensInvocation(final ViviensInvocation card) { + private ViviensInvocation(final ViviensInvocation card) { super(card); } @@ -48,11 +42,9 @@ public final class ViviensInvocation extends CardImpl { class ViviensInvocationEffect extends OneShotEffect { - public ViviensInvocationEffect(final ViviensInvocationEffect effect) { - super(effect); - } + private static final FilterCard filter = new FilterCreatureCard("creature card to put on the battlefield"); - public ViviensInvocationEffect() { + ViviensInvocationEffect() { super(Outcome.PutCreatureInPlay); this.staticText = "Look at the top seven cards of your library. " + "You may put a creature card from among them onto the battlefield. " @@ -61,6 +53,10 @@ class ViviensInvocationEffect extends OneShotEffect { + "it deals damage equal to its power to target creature an opponent controls"; } + private ViviensInvocationEffect(final ViviensInvocationEffect effect) { + super(effect); + } + @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); @@ -68,31 +64,32 @@ class ViviensInvocationEffect extends OneShotEffect { return false; } Cards cards = new CardsImpl(controller.getLibrary().getTopCards(game, 7)); - if (!cards.isEmpty()) { - TargetCard target = new TargetCard( - Zone.LIBRARY, - new FilterCreatureCard("creature card to put on the battlefield") - ); - target.setNotTarget(true); - if (controller.choose(Outcome.PutCreatureInPlay, cards, target, game)) { - Card card = cards.get(target.getFirstTarget(), game); - if (card != null) { - cards.remove(card); - controller.moveCards(card, Zone.BATTLEFIELD, source, game); - Permanent permanent = game.getPermanent(card.getId()); - if (permanent != null) { - game.addDelayedTriggeredAbility( - new ViviensInvocationReflexiveTriggeredAbility( - new MageObjectReference(permanent, game) - ), source); - new SendOptionUsedEventEffect().apply(game, source); - } - } - } - if (!cards.isEmpty()) { - controller.putCardsOnBottomOfLibrary(cards, game, source, false); - } + if (cards.isEmpty()) { + return true; } + TargetCard target = new TargetCard(Zone.LIBRARY, filter); + target.setNotTarget(true); + controller.choose(Outcome.PutCreatureInPlay, cards, target, game); + Card card = cards.get(target.getFirstTarget(), game); + if (card == null) { + controller.putCardsOnBottomOfLibrary(cards, game, source, false); + return true; + } + if (controller.moveCards(card, Zone.BATTLEFIELD, source, game)) { + cards.remove(card); + } + Permanent permanent = game.getPermanent(card.getId()); + if (permanent == null) { + controller.putCardsOnBottomOfLibrary(cards, game, source, false); + return true; + } + ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility( + new ViviensInvocationDamageEffect(new MageObjectReference(permanent, game)), false, + "it deals damage equals to its power to target creature an opponent controls" + ); + ability.addTarget(new TargetOpponentsCreaturePermanent()); + game.fireReflexiveTriggeredAbility(ability, source); + controller.putCardsOnBottomOfLibrary(cards, game, source, false); return true; } @@ -102,50 +99,16 @@ class ViviensInvocationEffect extends OneShotEffect { } } -class ViviensInvocationReflexiveTriggeredAbility extends DelayedTriggeredAbility { - - public ViviensInvocationReflexiveTriggeredAbility(MageObjectReference mor) { - super(new ViviensInvocationDamageEffect(mor), Duration.OneUse, false); - this.addTarget(new TargetOpponentsCreaturePermanent()); - } - - public ViviensInvocationReflexiveTriggeredAbility(final ViviensInvocationReflexiveTriggeredAbility ability) { - super(ability); - } - - @Override - public ViviensInvocationReflexiveTriggeredAbility copy() { - return new ViviensInvocationReflexiveTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.OPTION_USED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - return event.getPlayerId().equals(this.getControllerId()) - && event.getSourceId().equals(this.getSourceId()); - } - - @Override - public String getRule() { - return "When a creature is put onto the battlefield with {this}, " - + "it deals damage equal to its power to target creature an opponent controls"; - } -} - class ViviensInvocationDamageEffect extends OneShotEffect { private final MageObjectReference mor; - public ViviensInvocationDamageEffect(MageObjectReference mor) { + ViviensInvocationDamageEffect(MageObjectReference mor) { super(Outcome.Benefit); this.mor = mor; } - public ViviensInvocationDamageEffect(final ViviensInvocationDamageEffect effect) { + private ViviensInvocationDamageEffect(final ViviensInvocationDamageEffect effect) { super(effect); mor = effect.mor; } diff --git a/Mage.Sets/src/mage/cards/w/WildbornPreserver.java b/Mage.Sets/src/mage/cards/w/WildbornPreserver.java index 0fe9b9256a8..49ba457eaf4 100644 --- a/Mage.Sets/src/mage/cards/w/WildbornPreserver.java +++ b/Mage.Sets/src/mage/cards/w/WildbornPreserver.java @@ -2,8 +2,8 @@ package mage.cards.w; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.DelayedTriggeredAbility; import mage.abilities.common.EntersBattlefieldControlledTriggeredAbility; +import mage.abilities.common.delayed.ReflexiveTriggeredAbility; import mage.abilities.costs.mana.GenericManaCost; import mage.abilities.costs.mana.ManaCosts; import mage.abilities.costs.mana.ManaCostsImpl; @@ -14,7 +14,6 @@ import mage.abilities.keyword.ReachAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Duration; import mage.constants.Outcome; import mage.constants.SubType; import mage.counters.CounterType; @@ -23,7 +22,6 @@ import mage.filter.common.FilterCreaturePermanent; import mage.filter.predicate.Predicates; import mage.filter.predicate.permanent.AnotherPredicate; import mage.game.Game; -import mage.game.events.GameEvent; import mage.players.Player; import java.util.UUID; @@ -74,11 +72,11 @@ class WildbornPreserverCreateReflexiveTriggerEffect extends OneShotEffect { WildbornPreserverCreateReflexiveTriggerEffect() { super(Outcome.Benefit); + staticText = "you may pay {X}. When you do, put X +1/+1 counters on {this}"; } private WildbornPreserverCreateReflexiveTriggerEffect(final WildbornPreserverCreateReflexiveTriggerEffect effect) { super(effect); - staticText = "you may pay {X}. When you do, put X +1/+1 counters on {this}"; } @Override @@ -101,40 +99,10 @@ class WildbornPreserverCreateReflexiveTriggerEffect extends OneShotEffect { if (!cost.pay(source, game, source.getSourceId(), source.getControllerId(), false, null)) { return false; } - game.addDelayedTriggeredAbility(new WildbornPreserverReflexiveTriggeredAbility(costX), source); - game.fireEvent(GameEvent.getEvent(GameEvent.EventType.OPTION_USED, source.getOriginalId(), source.getSourceId(), source.getControllerId(), 0)); + game.fireReflexiveTriggeredAbility(new ReflexiveTriggeredAbility( + new AddCountersSourceEffect(CounterType.P1P1.createInstance(costX)), + false, "put X +1/+1 counters on {this}" + ), source); return true; } } - -class WildbornPreserverReflexiveTriggeredAbility extends DelayedTriggeredAbility { - - WildbornPreserverReflexiveTriggeredAbility(int counters) { - super(new AddCountersSourceEffect(CounterType.P1P1.createInstance(counters)), Duration.OneUse, true); - } - - private WildbornPreserverReflexiveTriggeredAbility(final WildbornPreserverReflexiveTriggeredAbility ability) { - super(ability); - } - - @Override - public WildbornPreserverReflexiveTriggeredAbility copy() { - return new WildbornPreserverReflexiveTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.OPTION_USED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - return event.getPlayerId().equals(this.getControllerId()) - && event.getSourceId().equals(this.getSourceId()); - } - - @Override - public String getRule() { - return "When you do, put X +1/+1 counters on {this}."; - } -} diff --git a/Mage.Sets/src/mage/cards/y/YannikScavengingSentinel.java b/Mage.Sets/src/mage/cards/y/YannikScavengingSentinel.java index 7efdb18ffc9..a0f527cdec4 100644 --- a/Mage.Sets/src/mage/cards/y/YannikScavengingSentinel.java +++ b/Mage.Sets/src/mage/cards/y/YannikScavengingSentinel.java @@ -2,12 +2,11 @@ package mage.cards.y; import mage.MageInt; import mage.abilities.Ability; -import mage.abilities.DelayedTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.delayed.OnLeaveReturnExiledToBattlefieldAbility; +import mage.abilities.common.delayed.ReflexiveTriggeredAbility; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.ExileTargetEffect; -import mage.abilities.effects.common.SendOptionUsedEventEffect; import mage.abilities.effects.common.counter.DistributeCountersEffect; import mage.abilities.keyword.PartnerWithAbility; import mage.abilities.keyword.VigilanceAbility; @@ -19,7 +18,6 @@ import mage.filter.FilterPermanent; import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.predicate.permanent.AnotherPredicate; import mage.game.Game; -import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.players.Player; import mage.target.TargetPermanent; @@ -109,43 +107,15 @@ class YannikScavengingSentinelEffect extends OneShotEffect { ), permanent.getIdName()).setTargetPointer(new FixedTarget(permanent, game)).apply(game, source); game.addDelayedTriggeredAbility(new OnLeaveReturnExiledToBattlefieldAbility(), source); if (game.getState().getZone(permanent.getId()) != Zone.BATTLEFIELD) { - game.addDelayedTriggeredAbility(new YannikScavengingSentinelReflexiveTriggeredAbility(power), source); - return new SendOptionUsedEventEffect().apply(game, source); + ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility( + new DistributeCountersEffect( + CounterType.P1P1, power, false, "" + ), false, "distribute X +1/+1 counters among any number of target creatures, " + + "where X is the exiled creature's power" + ); + ability.addTarget(new TargetCreaturePermanentAmount(power)); + game.fireReflexiveTriggeredAbility(ability, source); } return true; } } - -class YannikScavengingSentinelReflexiveTriggeredAbility extends DelayedTriggeredAbility { - - YannikScavengingSentinelReflexiveTriggeredAbility(int power) { - super(new DistributeCountersEffect(CounterType.P1P1, power, false, ""), Duration.OneUse, true); - this.addTarget(new TargetCreaturePermanentAmount(power)); - } - - private YannikScavengingSentinelReflexiveTriggeredAbility(final YannikScavengingSentinelReflexiveTriggeredAbility ability) { - super(ability); - } - - @Override - public YannikScavengingSentinelReflexiveTriggeredAbility copy() { - return new YannikScavengingSentinelReflexiveTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.OPTION_USED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - return event.getPlayerId().equals(this.getControllerId()) - && event.getSourceId().equals(this.getSourceId()); - } - - @Override - public String getRule() { - return "Distribute X +1/+1 counters among any number of target creatures, " + - "where X is the exiled creature's power."; - } -} diff --git a/Mage/src/main/java/mage/abilities/common/delayed/ReflexiveTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/delayed/ReflexiveTriggeredAbility.java new file mode 100644 index 00000000000..08fd88c78f7 --- /dev/null +++ b/Mage/src/main/java/mage/abilities/common/delayed/ReflexiveTriggeredAbility.java @@ -0,0 +1,50 @@ +package mage.abilities.common.delayed; + +import mage.abilities.DelayedTriggeredAbility; +import mage.abilities.effects.Effect; +import mage.constants.Duration; +import mage.game.Game; +import mage.game.events.GameEvent; + +/** + * @author TheElk801 + */ +public class ReflexiveTriggeredAbility extends DelayedTriggeredAbility { + + private final String text; + + public ReflexiveTriggeredAbility(Effect effect, boolean optional, String text) { + super(effect, Duration.EndOfTurn, true, optional); + this.text = text; + } + + protected ReflexiveTriggeredAbility(final ReflexiveTriggeredAbility ability) { + super(ability); + this.text = ability.text; + } + + @Override + public boolean checkEventType(GameEvent event, Game game) { + return event.getType() == GameEvent.EventType.OPTION_USED; + } + + @Override + public boolean checkTrigger(GameEvent event, Game game) { + return event.getPlayerId().equals(this.getControllerId()) + && event.getSourceId().equals(this.getSourceId()); + } + + @Override + public String getRule() { + return text.substring(0, 1).toUpperCase() + text.substring(1) + '.'; + } + + public String getText() { + return text; + } + + @Override + public ReflexiveTriggeredAbility copy() { + return new ReflexiveTriggeredAbility(this); + } +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/DoWhenCostPaid.java b/Mage/src/main/java/mage/abilities/effects/common/DoWhenCostPaid.java new file mode 100644 index 00000000000..7744212cf1b --- /dev/null +++ b/Mage/src/main/java/mage/abilities/effects/common/DoWhenCostPaid.java @@ -0,0 +1,103 @@ +package mage.abilities.effects.common; + +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.Mode; +import mage.abilities.common.delayed.ReflexiveTriggeredAbility; +import mage.abilities.costs.Cost; +import mage.abilities.effects.OneShotEffect; +import mage.constants.Outcome; +import mage.game.Game; +import mage.players.Player; +import mage.util.CardUtil; + +import java.util.Locale; + +public class DoWhenCostPaid extends OneShotEffect { + + private final ReflexiveTriggeredAbility ability; + private final Cost cost; + private final String chooseUseText; + private final boolean optional; + + public DoWhenCostPaid(ReflexiveTriggeredAbility ability, Cost cost, String chooseUseText) { + this(ability, cost, chooseUseText, true); + } + + public DoWhenCostPaid(ReflexiveTriggeredAbility ability, Cost cost, String chooseUseText, boolean optional) { + super(Outcome.Benefit); + this.ability = ability; + this.cost = cost; + this.chooseUseText = chooseUseText; + this.optional = optional; + } + + private DoWhenCostPaid(final DoWhenCostPaid effect) { + super(effect); + this.ability = effect.ability.copy(); + this.cost = effect.cost.copy(); + this.chooseUseText = effect.chooseUseText; + this.optional = effect.optional; + } + + @Override + public boolean apply(Game game, Ability source) { + Player player = game.getPlayer(source.getControllerId()); + MageObject mageObject = game.getObject(source.getSourceId()); + if (player == null || mageObject == null) { + return false; + } + String message = CardUtil.replaceSourceName(chooseUseText, mageObject.getLogName()); + Outcome payOutcome = ability.getEffects().getOutcome(source, this.outcome); + if (!cost.canPay(source, source.getSourceId(), player.getId(), game) + || (optional && !player.chooseUse(payOutcome, message, source, game))) { + return false; + } + cost.clearPaid(); + int bookmark = game.bookmarkState(); + if (cost.pay(source, game, source.getSourceId(), player.getId(), false)) { + game.fireReflexiveTriggeredAbility(ability, source); + player.resetStoredBookmark(game); + } + return true; + } + + public Cost getCost() { + return cost; + } + + @Override + public String getText(Mode mode) { + if (!staticText.isEmpty()) { + return staticText; + } + return (optional ? "you may " : "") + getCostText() + ". When you do, " + ability.getText(); + } + + private String getCostText() { + StringBuilder sb = new StringBuilder(); + String costText = cost.getText(); + if (costText != null + && !costText.toLowerCase(Locale.ENGLISH).startsWith("put") + && !costText.toLowerCase(Locale.ENGLISH).startsWith("return") + && !costText.toLowerCase(Locale.ENGLISH).startsWith("exile") + && !costText.toLowerCase(Locale.ENGLISH).startsWith("discard") + && !costText.toLowerCase(Locale.ENGLISH).startsWith("sacrifice") + && !costText.toLowerCase(Locale.ENGLISH).startsWith("remove") + && !costText.toLowerCase(Locale.ENGLISH).startsWith("pay")) { + sb.append("pay "); + } + return sb.append(costText).toString(); + } + + @Override + public void setValue(String key, Object value) { + super.setValue(key, value); + ability.getEffects().setValue(key, value); + } + + @Override + public DoWhenCostPaid copy() { + return new DoWhenCostPaid(this); + } +} diff --git a/Mage/src/main/java/mage/abilities/effects/common/SendOptionUsedEventEffect.java b/Mage/src/main/java/mage/abilities/effects/common/SendOptionUsedEventEffect.java deleted file mode 100644 index 462f650bae7..00000000000 --- a/Mage/src/main/java/mage/abilities/effects/common/SendOptionUsedEventEffect.java +++ /dev/null @@ -1,42 +0,0 @@ - -package mage.abilities.effects.common; - -import mage.abilities.Ability; -import mage.abilities.effects.OneShotEffect; -import mage.constants.Outcome; -import mage.game.Game; -import mage.game.events.GameEvent; - -/** - * - * @author LevelX2 - */ -public class SendOptionUsedEventEffect extends OneShotEffect { - - private final int value; - - public SendOptionUsedEventEffect() { - this(0); - } - - public SendOptionUsedEventEffect(int value) { - super(Outcome.Detriment); - this.value = value; - } - - public SendOptionUsedEventEffect(final SendOptionUsedEventEffect effect) { - super(effect); - this.value = effect.value; - } - - @Override - public SendOptionUsedEventEffect copy() { - return new SendOptionUsedEventEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - game.fireEvent(GameEvent.getEvent(GameEvent.EventType.OPTION_USED, source.getOriginalId(), source.getSourceId(), source.getControllerId(), value)); - return true; - } -} diff --git a/Mage/src/main/java/mage/game/Game.java b/Mage/src/main/java/mage/game/Game.java index b41080c668b..ebf4dad93b1 100644 --- a/Mage/src/main/java/mage/game/Game.java +++ b/Mage/src/main/java/mage/game/Game.java @@ -6,6 +6,7 @@ import mage.abilities.Ability; import mage.abilities.ActivatedAbility; import mage.abilities.DelayedTriggeredAbility; import mage.abilities.TriggeredAbility; +import mage.abilities.common.delayed.ReflexiveTriggeredAbility; import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.ContinuousEffects; import mage.abilities.effects.PreventionEffectData; @@ -398,6 +399,8 @@ public interface Game extends MageItem, Serializable { UUID addDelayedTriggeredAbility(DelayedTriggeredAbility delayedAbility, Ability source); + UUID fireReflexiveTriggeredAbility(ReflexiveTriggeredAbility reflexiveAbility, Ability source); + void applyEffects(); boolean checkStateAndTriggered(); diff --git a/Mage/src/main/java/mage/game/GameImpl.java b/Mage/src/main/java/mage/game/GameImpl.java index b5470ba96ff..018559d14f4 100644 --- a/Mage/src/main/java/mage/game/GameImpl.java +++ b/Mage/src/main/java/mage/game/GameImpl.java @@ -6,6 +6,7 @@ import mage.abilities.*; import mage.abilities.common.AttachableToRestrictedAbility; import mage.abilities.common.CantHaveMoreThanAmountCountersSourceAbility; import mage.abilities.common.SagaAbility; +import mage.abilities.common.delayed.ReflexiveTriggeredAbility; import mage.abilities.effects.ContinuousEffect; import mage.abilities.effects.ContinuousEffects; import mage.abilities.effects.Effect; @@ -1748,6 +1749,13 @@ public abstract class GameImpl implements Game, Serializable { return newAbility.getId(); } + @Override + public UUID fireReflexiveTriggeredAbility(ReflexiveTriggeredAbility reflexiveAbility, Ability source) { + UUID uuid = this.addDelayedTriggeredAbility(reflexiveAbility, source); + this.fireEvent(GameEvent.getEvent(GameEvent.EventType.OPTION_USED, source.getOriginalId(), source.getSourceId(), source.getControllerId())); + return uuid; + } + /** * 116.5. Each time a player would get priority, the game first performs all * applicable state-based actions as a single event (see rule 704,