diff --git a/Mage.Sets/src/mage/cards/g/GaleWaterdeepProdigy.java b/Mage.Sets/src/mage/cards/g/GaleWaterdeepProdigy.java index 6d18e223b89..67471e9fd54 100644 --- a/Mage.Sets/src/mage/cards/g/GaleWaterdeepProdigy.java +++ b/Mage.Sets/src/mage/cards/g/GaleWaterdeepProdigy.java @@ -37,7 +37,7 @@ public final class GaleWaterdeepProdigy extends CardImpl { // If a spell cast from your graveyard this way would be put into your graveyard, exile it instead. Ability ability = new SpellCastControllerTriggeredAbility(Zone.BATTLEFIELD, new MayCastTargetCardEffect(true) - .setText("you may cast up to one of the other type from your graveyard. If a spell cast from your graveyard this way would be put into your graveyard, exile it instead."), + .setText("you may cast up to one target card of the other type from your graveyard. If a spell cast from your graveyard this way would be put into your graveyard, exile it instead."), StaticFilters.FILTER_SPELL_AN_INSTANT_OR_SORCERY, false, SetTargetPointer.SPELL, Zone.HAND ); @@ -84,6 +84,6 @@ enum GaleWaterdeepProdigyAdjuster implements TargetAdjuster { } else { filter = SORCERY_FILTER; } - ability.addTarget(new TargetCardInYourGraveyard(filter)); + ability.addTarget(new TargetCardInYourGraveyard(0, 1, filter)); } } diff --git a/Mage.Sets/src/mage/cards/h/HarnessTheStorm.java b/Mage.Sets/src/mage/cards/h/HarnessTheStorm.java index 2f5fe12510c..ca98ec48195 100644 --- a/Mage.Sets/src/mage/cards/h/HarnessTheStorm.java +++ b/Mage.Sets/src/mage/cards/h/HarnessTheStorm.java @@ -1,23 +1,21 @@ package mage.cards.h; -import mage.ApprovingObject; import mage.abilities.Ability; import mage.abilities.common.SpellCastControllerTriggeredAbility; -import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; +import mage.abilities.effects.common.MayCastTargetCardEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; +import mage.constants.SetTargetPointer; +import mage.constants.Zone; import mage.filter.FilterCard; -import mage.filter.common.FilterInstantOrSorcerySpell; +import mage.filter.StaticFilters; import mage.filter.predicate.mageobject.NamePredicate; import mage.game.Game; -import mage.game.events.GameEvent; import mage.game.stack.Spell; -import mage.players.Player; import mage.target.common.TargetCardInYourGraveyard; -import mage.watchers.common.CastFromHandWatcher; +import mage.target.targetadjustment.TargetAdjuster; +import mage.target.targetpointer.FirstTargetPointer; import java.util.UUID; @@ -26,13 +24,20 @@ import java.util.UUID; * @author LevelX2 */ public final class HarnessTheStorm extends CardImpl { + static FilterCard filter = new FilterCard("card with the same name as that spell from your graveyard"); public HarnessTheStorm(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{R}"); // Whenever you cast an instant or sorcery spell from your hand, you may cast // target card with the same name as that spell from your graveyard. - this.addAbility(new HarnessTheStormTriggeredAbility(), new CastFromHandWatcher()); + Ability ability = new SpellCastControllerTriggeredAbility( + Zone.BATTLEFIELD, new MayCastTargetCardEffect(false), StaticFilters.FILTER_SPELL_AN_INSTANT_OR_SORCERY, + false, SetTargetPointer.SPELL, Zone.HAND + ); + ability.addTarget(new TargetCardInYourGraveyard(filter)); // Only used for text generation + ability.setTargetAdjuster(HarnessTheStormAdjuster.instance); + this.addAbility(ability); } private HarnessTheStorm(final HarnessTheStorm card) { @@ -45,78 +50,20 @@ public final class HarnessTheStorm extends CardImpl { } } - -class HarnessTheStormTriggeredAbility extends SpellCastControllerTriggeredAbility { - - private static final FilterInstantOrSorcerySpell filterSpell = new FilterInstantOrSorcerySpell("an instant or sorcery spell from your hand"); - - HarnessTheStormTriggeredAbility() { - super(new HarnessTheStormEffect(), filterSpell, false); - } - - private HarnessTheStormTriggeredAbility(final HarnessTheStormTriggeredAbility ability) { - super(ability); - } - +enum HarnessTheStormAdjuster implements TargetAdjuster { + instance; @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (super.checkTrigger(event, game)) { - CastFromHandWatcher watcher = game.getState().getWatcher(CastFromHandWatcher.class); - if (watcher != null && watcher.spellWasCastFromHand(event.getSourceId())) { - Spell spell = game.getState().getStack().getSpell(event.getSourceId()); - if (spell != null) { - FilterCard filterCard = new FilterCard("a card named " + spell.getName() + " in your graveyard"); - filterCard.add(new NamePredicate(spell.getName())); - this.getTargets().clear(); - this.getTargets().add(new TargetCardInYourGraveyard(filterCard)); - return true; - } - } + public void adjustTargets(Ability ability, Game game) { + UUID spellId = ability.getEffects().get(0).getTargetPointer().getFirst(game, ability); + ability.getTargets().clear(); + ability.getAllEffects().setTargetPointer(new FirstTargetPointer()); + + Spell spell = game.getSpellOrLKIStack(spellId); + if (spell == null){ + return; } - return false; - } - - @Override - public HarnessTheStormTriggeredAbility copy() { - return new HarnessTheStormTriggeredAbility(this); - } - -} - -class HarnessTheStormEffect extends OneShotEffect { - - HarnessTheStormEffect() { - super(Outcome.Benefit); - this.staticText = "you may cast target card with the same name as that " - + "spell from your graveyard. (You still pay its costs.)"; - } - - private HarnessTheStormEffect(final HarnessTheStormEffect effect) { - super(effect); - } - - @Override - public HarnessTheStormEffect copy() { - return new HarnessTheStormEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller == null) { - return false; - } - Card card = controller.getGraveyard().get(getTargetPointer().getFirst(game, source), game); - if (card == null) { - return false; - } - if (controller.chooseUse(outcome, "Cast " + card.getIdName() + " from your graveyard?", source, game)) { - game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), Boolean.TRUE); - controller.cast(controller.chooseAbilityForCast(card, game, false), - game, false, new ApprovingObject(source, game)); - game.getState().setValue("PlayFromNotOwnHandZone" + card.getId(), null); - } - return true; - + FilterCard filter = new FilterCard("a card named " + spell.getName() + " in your graveyard"); + filter.add(new NamePredicate(spell.getName())); + ability.addTarget(new TargetCardInYourGraveyard(filter)); } } diff --git a/Mage.Sets/src/mage/cards/i/ImpetuousDevils.java b/Mage.Sets/src/mage/cards/i/ImpetuousDevils.java index 0d98b477525..c25814320f6 100644 --- a/Mage.Sets/src/mage/cards/i/ImpetuousDevils.java +++ b/Mage.Sets/src/mage/cards/i/ImpetuousDevils.java @@ -1,26 +1,21 @@ package mage.cards.i; -import java.util.UUID; import mage.MageInt; -import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; +import mage.abilities.TriggeredAbility; +import mage.abilities.common.AttacksTriggeredAbility; import mage.abilities.effects.common.SacrificeSourceEffect; import mage.abilities.effects.common.combat.MustBeBlockedByTargetSourceEffect; import mage.abilities.keyword.HasteAbility; import mage.abilities.keyword.TrampleAbility; +import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.CardType; -import mage.constants.SubType; -import mage.constants.Duration; -import mage.constants.TargetController; -import mage.constants.Zone; -import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.ControllerIdPredicate; -import mage.game.Game; -import mage.game.events.GameEvent; +import mage.constants.*; import mage.target.common.TargetCreaturePermanent; +import mage.target.targetadjustment.ThatPlayerControlsTargetAdjuster; + +import java.util.UUID; /** * @@ -39,7 +34,12 @@ public final class ImpetuousDevils extends CardImpl { // Haste this.addAbility(HasteAbility.getInstance()); // When Impetuous Devils attacks, up to one target creature defending player controls blocks it this combat if able. - this.addAbility(new ImpetuousDevilsAbility()); + TriggeredAbility ability = new AttacksTriggeredAbility(new MustBeBlockedByTargetSourceEffect(Duration.EndOfCombat) + .setText("up to one target creature defending player controls blocks it this combat if able"), false, null, SetTargetPointer.PLAYER); + ability.setTriggerPhrase("When {this} attacks, "); + ability.addTarget(new TargetCreaturePermanent(0, 1)); + ability.setTargetAdjuster(new ThatPlayerControlsTargetAdjuster()); + this.addAbility(ability); // At the beginning of the end step, sacrifice Impetuous Devils. this.addAbility(new BeginningOfEndStepTriggeredAbility(TargetController.NEXT, new SacrificeSourceEffect(), false)); @@ -54,44 +54,3 @@ public final class ImpetuousDevils extends CardImpl { return new ImpetuousDevils(this); } } - -class ImpetuousDevilsAbility extends TriggeredAbilityImpl { - - public ImpetuousDevilsAbility() { - super(Zone.BATTLEFIELD, new MustBeBlockedByTargetSourceEffect(Duration.EndOfCombat), false); - } - - private ImpetuousDevilsAbility(final ImpetuousDevilsAbility ability) { - super(ability); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.ATTACKER_DECLARED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (event.getSourceId().equals(this.getSourceId())) { - FilterCreaturePermanent filter = new FilterCreaturePermanent("creature defending player controls"); - UUID defenderId = game.getCombat().getDefendingPlayerId(sourceId, game); - filter.add(new ControllerIdPredicate(defenderId)); - - this.getTargets().clear(); - TargetCreaturePermanent target = new TargetCreaturePermanent(0, 1, filter, false); - this.addTarget(target); - return true; - } - return false; - } - - @Override - public String getRule() { - return "When {this} attacks, up to one target creature defending player controls blocks it this combat if able."; - } - - @Override - public ImpetuousDevilsAbility copy() { - return new ImpetuousDevilsAbility(this); - } -} diff --git a/Mage.Sets/src/mage/cards/k/KarmicJustice.java b/Mage.Sets/src/mage/cards/k/KarmicJustice.java index 68bb6406d27..ef9e7be0aa1 100644 --- a/Mage.Sets/src/mage/cards/k/KarmicJustice.java +++ b/Mage.Sets/src/mage/cards/k/KarmicJustice.java @@ -1,7 +1,6 @@ package mage.cards.k; -import java.util.UUID; import mage.MageObject; import mage.abilities.TriggeredAbilityImpl; import mage.abilities.effects.common.DestroyTargetEffect; @@ -15,6 +14,9 @@ import mage.game.Game; import mage.game.events.GameEvent; import mage.target.Target; import mage.target.TargetPermanent; +import mage.target.targetadjustment.DefineByTriggerTargetAdjuster; + +import java.util.UUID; /** * @@ -25,7 +27,6 @@ public final class KarmicJustice extends CardImpl { public KarmicJustice(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{W}"); - // Whenever a spell or ability an opponent controls destroys a noncreature permanent you control, you may destroy target permanent that opponent controls. this.addAbility(new KarmicJusticeTriggeredAbility()); } @@ -45,8 +46,9 @@ class KarmicJusticeTriggeredAbility extends TriggeredAbilityImpl { KarmicJusticeTriggeredAbility() { super(Zone.BATTLEFIELD, new DestroyTargetEffect(), true); this.setLeavesTheBattlefieldTrigger(true); + this.setTargetAdjuster(DefineByTriggerTargetAdjuster.instance); } - + private KarmicJusticeTriggeredAbility(final KarmicJusticeTriggeredAbility ability) { super(ability); } diff --git a/Mage.Sets/src/mage/cards/m/MageRingResponder.java b/Mage.Sets/src/mage/cards/m/MageRingResponder.java index 56ce5c14dca..069f0c4201f 100644 --- a/Mage.Sets/src/mage/cards/m/MageRingResponder.java +++ b/Mage.Sets/src/mage/cards/m/MageRingResponder.java @@ -1,9 +1,9 @@ package mage.cards.m; -import java.util.UUID; import mage.MageInt; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.Ability; +import mage.abilities.common.AttacksTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleStaticAbility; import mage.abilities.costs.mana.ManaCostsImpl; @@ -13,21 +13,21 @@ import mage.abilities.effects.common.UntapSourceEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.SetTargetPointer; import mage.constants.SubType; -import mage.constants.Zone; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.ControllerIdPredicate; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; import mage.target.common.TargetCreaturePermanent; +import mage.target.targetadjustment.ThatPlayerControlsTargetAdjuster; + +import java.util.UUID; /** * * @author fireshoes */ public final class MageRingResponder extends CardImpl { - + private static final FilterCreaturePermanent filter + = new FilterCreaturePermanent("creature defending player controls"); public MageRingResponder(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT,CardType.CREATURE},"{7}"); this.subtype.add(SubType.GOLEM); @@ -41,7 +41,10 @@ public final class MageRingResponder extends CardImpl { this.addAbility(new SimpleActivatedAbility(new UntapSourceEffect(), new ManaCostsImpl<>("{7}"))); // Whenever Mage-Ring Responder attacks, it deals 7 damage to target creature defending player controls. - this.addAbility(new MageRingResponderAbility()); + Ability ability = new AttacksTriggeredAbility(new DamageTargetEffect(7), false, null, SetTargetPointer.PLAYER); + ability.addTarget(new TargetCreaturePermanent(filter)); + ability.setTargetAdjuster(new ThatPlayerControlsTargetAdjuster()); + this.addAbility(ability); } private MageRingResponder(final MageRingResponder card) { @@ -53,44 +56,3 @@ public final class MageRingResponder extends CardImpl { return new MageRingResponder(this); } } - -class MageRingResponderAbility extends TriggeredAbilityImpl { - - public MageRingResponderAbility() { - super(Zone.BATTLEFIELD, new DamageTargetEffect(7)); - } - - private MageRingResponderAbility(final MageRingResponderAbility ability) { - super(ability); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.ATTACKER_DECLARED; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (event.getSourceId().equals(this.getSourceId())) { - FilterCreaturePermanent filter = new FilterCreaturePermanent("creature defending player controls"); - UUID defenderId = game.getCombat().getDefenderId(sourceId); - filter.add(new ControllerIdPredicate(defenderId)); - - this.getTargets().clear(); - TargetCreaturePermanent target = new TargetCreaturePermanent(filter); - this.addTarget(target); - return true; - } - return false; - } - - @Override - public String getRule() { - return "Whenever {this} attacks, it deals 7 damage to target creature defending player controls."; - } - - @Override - public MageRingResponderAbility copy() { - return new MageRingResponderAbility(this); - } -} diff --git a/Mage.Sets/src/mage/cards/m/MagusOfTheAbyss.java b/Mage.Sets/src/mage/cards/m/MagusOfTheAbyss.java index 2d293a138d2..b3a54890d0c 100644 --- a/Mage.Sets/src/mage/cards/m/MagusOfTheAbyss.java +++ b/Mage.Sets/src/mage/cards/m/MagusOfTheAbyss.java @@ -1,29 +1,32 @@ package mage.cards.m; -import java.util.UUID; import mage.MageInt; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.Ability; import mage.abilities.effects.common.DestroyTargetEffect; +import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.Zone; -import mage.filter.common.FilterCreaturePermanent; +import mage.constants.TargetController; +import mage.filter.FilterPermanent; import mage.filter.predicate.Predicates; import mage.filter.predicate.permanent.ControllerIdPredicate; import mage.game.Game; -import mage.game.events.GameEvent; -import mage.players.Player; import mage.target.Target; -import mage.target.common.TargetCreaturePermanent; +import mage.target.TargetPermanent; +import mage.target.targetadjustment.TargetAdjuster; +import mage.target.targetpointer.FirstTargetPointer; + +import java.util.UUID; /** * * @author escplan9 (Derek Monturo - dmontur1 at gmail dot com) */ public final class MagusOfTheAbyss extends CardImpl { + static FilterPermanent filter = new FilterPermanent("nonartifact creature that player controls of their choice"); public MagusOfTheAbyss(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{B}"); @@ -33,7 +36,11 @@ public final class MagusOfTheAbyss extends CardImpl { this.toughness = new MageInt(3); // At the beginning of each player's upkeep, destroy target nonartifact creature that player controls of their choice. It can't be regenerated. - this.addAbility(new MagusOfTheAbyssTriggeredAbility()); + Ability ability = new BeginningOfUpkeepTriggeredAbility(TargetController.EACH_PLAYER, + new DestroyTargetEffect(true), false).withTargetPointerSet(true); + ability.addTarget(new TargetPermanent(filter)); // Only used for text generation + ability.setTargetAdjuster(MagusOfTheAbyssTargetAdjuster.instance); + this.addAbility(ability); } private MagusOfTheAbyss(final MagusOfTheAbyss card) { @@ -46,45 +53,24 @@ public final class MagusOfTheAbyss extends CardImpl { } } -class MagusOfTheAbyssTriggeredAbility extends TriggeredAbilityImpl { - - MagusOfTheAbyssTriggeredAbility() { - super(Zone.BATTLEFIELD, new DestroyTargetEffect(true), false); +enum MagusOfTheAbyssTargetAdjuster implements TargetAdjuster { + instance; + private static final FilterPermanent filter + = new FilterPermanent("nonartifact creature that player controls of their choice"); + static { + filter.add(Predicates.not(CardType.ARTIFACT.getPredicate())); + filter.add(CardType.CREATURE.getPredicate()); } - - private MagusOfTheAbyssTriggeredAbility(final MagusOfTheAbyssTriggeredAbility ability) { - super(ability); - } - @Override - public MagusOfTheAbyssTriggeredAbility copy() { - return new MagusOfTheAbyssTriggeredAbility(this); - } + public void adjustTargets(Ability ability, Game game) { + UUID opponentId = ability.getEffects().get(0).getTargetPointer().getFirst(game, ability); + ability.getTargets().clear(); + ability.getAllEffects().setTargetPointer(new FirstTargetPointer()); - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.UPKEEP_STEP_PRE; + FilterPermanent adjustedFilter = filter.copy(); + adjustedFilter.add(new ControllerIdPredicate(opponentId)); + Target newTarget = new TargetPermanent(adjustedFilter); + newTarget.setTargetController(opponentId); + ability.addTarget(newTarget); } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - Player player = game.getPlayer(event.getPlayerId()); - if (player != null) { - FilterCreaturePermanent filter = new FilterCreaturePermanent("nonartifact creature you control"); - filter.add(Predicates.not(CardType.ARTIFACT.getPredicate())); - filter.add(new ControllerIdPredicate(player.getId())); - Target target = new TargetCreaturePermanent(filter); - target.setAbilityController(getControllerId()); - target.setTargetController(player.getId()); - this.getTargets().clear(); - this.getTargets().add(target); - return true; - } - return false; - } - - @Override - public String getRule() { - return "At the beginning of each player's upkeep, destroy target nonartifact creature that player controls of their choice. It can't be regenerated."; - } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/cards/m/ManaSkimmer.java b/Mage.Sets/src/mage/cards/m/ManaSkimmer.java index 17e473baede..2cff22f5320 100644 --- a/Mage.Sets/src/mage/cards/m/ManaSkimmer.java +++ b/Mage.Sets/src/mage/cards/m/ManaSkimmer.java @@ -1,9 +1,9 @@ package mage.cards.m; -import java.util.UUID; import mage.MageInt; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.Ability; +import mage.abilities.common.DealsDamageToAPlayerTriggeredAbility; import mage.abilities.effects.common.DontUntapInControllersNextUntapStepTargetEffect; import mage.abilities.effects.common.TapTargetEffect; import mage.abilities.keyword.FlyingAbility; @@ -11,13 +11,10 @@ import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.Zone; -import mage.filter.common.FilterLandPermanent; -import mage.filter.predicate.permanent.ControllerIdPredicate; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.permanent.Permanent; -import mage.target.TargetPermanent; +import mage.target.common.TargetLandPermanent; +import mage.target.targetadjustment.ThatPlayerControlsTargetAdjuster; + +import java.util.UUID; /** * @@ -34,7 +31,11 @@ public final class ManaSkimmer extends CardImpl { // Flying this.addAbility(FlyingAbility.getInstance()); // Whenever Mana Skimmer deals damage to a player, tap target land that player controls. That land doesn't untap during its controller's next untap step. - this.addAbility(new ManaSkimmerTriggeredAbility()); + Ability ability = new DealsDamageToAPlayerTriggeredAbility(new TapTargetEffect(), false, true); + ability.addEffect(new DontUntapInControllersNextUntapStepTargetEffect("That land")); + ability.addTarget(new TargetLandPermanent().withTargetName("target land that player controls")); + ability.setTargetAdjuster(new ThatPlayerControlsTargetAdjuster()); + this.addAbility(ability); } private ManaSkimmer(final ManaSkimmer card) { @@ -46,44 +47,3 @@ public final class ManaSkimmer extends CardImpl { return new ManaSkimmer(this); } } - -class ManaSkimmerTriggeredAbility extends TriggeredAbilityImpl { - - ManaSkimmerTriggeredAbility() { - super(Zone.BATTLEFIELD, new TapTargetEffect(), false); - addEffect(new DontUntapInControllersNextUntapStepTargetEffect()); - } - - private ManaSkimmerTriggeredAbility(final ManaSkimmerTriggeredAbility ability) { - super(ability); - } - - @Override - public ManaSkimmerTriggeredAbility copy() { - return new ManaSkimmerTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_PLAYER; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - Permanent source = game.getPermanent(event.getSourceId()); - if (source != null && source.getId().equals(this.getSourceId())) { - FilterLandPermanent filter = new FilterLandPermanent("land that player controls"); - filter.add(new ControllerIdPredicate(event.getPlayerId())); - filter.setMessage("land controlled by " + game.getPlayer(event.getTargetId()).getLogName()); - this.getTargets().clear(); - this.addTarget(new TargetPermanent(filter)); - return true; - } - return false; - } - - @Override - public String getRule() { - return "Whenever {this} deals damage to a player, tap target land that player controls. That land doesn't untap during its controller's next untap step."; - } -} diff --git a/Mage.Sets/src/mage/cards/m/MausoleumTurnkey.java b/Mage.Sets/src/mage/cards/m/MausoleumTurnkey.java index 5e95ea8d9c5..ea41e0956cf 100644 --- a/Mage.Sets/src/mage/cards/m/MausoleumTurnkey.java +++ b/Mage.Sets/src/mage/cards/m/MausoleumTurnkey.java @@ -1,31 +1,31 @@ package mage.cards.m; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; -import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; +import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.SubType; import mage.constants.Outcome; -import mage.constants.Zone; +import mage.constants.SubType; import mage.filter.common.FilterCreatureCard; -import mage.filter.predicate.card.OwnerIdPredicate; import mage.game.Game; import mage.players.Player; import mage.target.Target; -import mage.target.common.TargetCardInGraveyard; +import mage.target.common.TargetCardInYourGraveyard; import mage.target.common.TargetOpponent; +import mage.target.targetadjustment.TargetAdjuster; + +import java.util.UUID; /** * * @author LevelX2 */ public final class MausoleumTurnkey extends CardImpl { + static FilterCreatureCard filter = new FilterCreatureCard("creature card of an opponent's choice"); public MausoleumTurnkey(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}"); @@ -36,8 +36,10 @@ public final class MausoleumTurnkey extends CardImpl { this.toughness = new MageInt(2); // When Mausoleum Turnkey enters the battlefield, return target creature card of an opponent's choice from your graveyard to your hand. - this.addAbility(new EntersBattlefieldTriggeredAbility(new MausoleumTurnkeyEffect(), false)); - + Ability ability = new EntersBattlefieldTriggeredAbility(new ReturnFromGraveyardToHandTargetEffect()); + ability.addTarget(new TargetCardInYourGraveyard(filter)); + ability.setTargetAdjuster(MausoleumTurnkeyAdjuster.instance); + this.addAbility(ability); } private MausoleumTurnkey(final MausoleumTurnkey card) { @@ -50,50 +52,27 @@ public final class MausoleumTurnkey extends CardImpl { } } -class MausoleumTurnkeyEffect extends OneShotEffect { - - MausoleumTurnkeyEffect() { - super(Outcome.Benefit); - this.staticText = "return target creature card of an opponent's choice from your graveyard to your hand"; - } - - private MausoleumTurnkeyEffect(final MausoleumTurnkeyEffect effect) { - super(effect); - } +// Exact copy of KarplusanMinotaurAdjuster +enum MausoleumTurnkeyAdjuster implements TargetAdjuster { + instance; @Override - public MausoleumTurnkeyEffect copy() { - return new MausoleumTurnkeyEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - UUID opponentId = null; - if (game.getOpponents(controller.getId()).size() > 1) { - Target target = new TargetOpponent(true); - if (controller.chooseTarget(outcome, target, source, game)) { - opponentId = target.getFirstTarget(); - } - } else { - opponentId = game.getOpponents(controller.getId()).iterator().next(); - } - if (opponentId != null) { - Player opponent = game.getPlayer(opponentId); - if (opponent != null) { - FilterCreatureCard filter = new FilterCreatureCard("creature card from " + controller.getLogName() + " graveyard"); - filter.add(new OwnerIdPredicate(controller.getId())); - Target target = new TargetCardInGraveyard(filter); - opponent.chooseTarget(outcome, target, source, game); - Card card = game.getCard(target.getFirstTarget()); - if (card != null) { - controller.moveCards(card, Zone.HAND, source, game); - } - } - } - return true; + public void adjustTargets(Ability ability, Game game) { + Player controller = game.getPlayer(ability.getControllerId()); + if (controller == null) { + return; + } + UUID opponentId = null; + if (game.getOpponents(controller.getId()).size() > 1) { + Target target = new TargetOpponent(true); + if (controller.chooseTarget(Outcome.Neutral, target, ability, game)) { + opponentId = target.getFirstTarget(); + } + } else { + opponentId = game.getOpponents(controller.getId()).iterator().next(); + } + if (opponentId != null) { + ability.getTargets().get(0).setTargetController(opponentId); } - return false; } } diff --git a/Mage.Sets/src/mage/cards/n/Necrite.java b/Mage.Sets/src/mage/cards/n/Necrite.java index bc0b994b982..56fc010bedf 100644 --- a/Mage.Sets/src/mage/cards/n/Necrite.java +++ b/Mage.Sets/src/mage/cards/n/Necrite.java @@ -1,31 +1,30 @@ package mage.cards.n; -import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.AttacksAndIsNotBlockedTriggeredAbility; import mage.abilities.costs.common.SacrificeSourceCost; -import mage.abilities.effects.Effect; import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.effects.common.DoIfCostPaid; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; +import mage.constants.SetTargetPointer; import mage.constants.SubType; import mage.filter.FilterPermanent; import mage.filter.common.FilterCreaturePermanent; -import mage.filter.predicate.permanent.ControllerIdPredicate; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.target.Target; import mage.target.TargetPermanent; +import mage.target.targetadjustment.ThatPlayerControlsTargetAdjuster; + +import java.util.UUID; /** * * @author MarcoMarin */ public final class Necrite extends CardImpl { + static FilterPermanent filter = new FilterCreaturePermanent("creature defending player controls"); public Necrite(UUID ownerId, CardSetInfo setInfo) { super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{B}{B}"); @@ -35,10 +34,10 @@ public final class Necrite extends CardImpl { // Whenever Necrite attacks and isn't blocked, you may sacrifice it. If you do, destroy target creature defending player controls. It can't be regenerated. DoIfCostPaid effect = new DoIfCostPaid(new DestroyTargetEffect(true), new SacrificeSourceCost(), "Sacrifice {this} to destroy target creature defending player controls?"); - effect.setText("you may sacrifice it. If you do, destroy target creature defending player controls. It can't be regenerated."); - Ability ability = new NecriteTriggeredAbility(effect); + Ability ability = new AttacksAndIsNotBlockedTriggeredAbility(effect, false, SetTargetPointer.PLAYER); + ability.addTarget(new TargetPermanent(filter)); + ability.setTargetAdjuster(new ThatPlayerControlsTargetAdjuster()); this.addAbility(ability); - } private Necrite(final Necrite card) { @@ -50,33 +49,3 @@ public final class Necrite extends CardImpl { return new Necrite(this); } } - -class NecriteTriggeredAbility extends AttacksAndIsNotBlockedTriggeredAbility{ - - public NecriteTriggeredAbility(Effect effect) { - super(effect); - } - - private NecriteTriggeredAbility(final NecriteTriggeredAbility ability) { - super(ability); - } - - @Override - public NecriteTriggeredAbility copy() { - return new NecriteTriggeredAbility(this); - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (super.checkTrigger(event, game)){ - UUID defendingPlayerId = game.getCombat().getDefendingPlayerId(getSourceId(), game); - FilterPermanent filter = new FilterCreaturePermanent(); - filter.add(new ControllerIdPredicate(defendingPlayerId)); - Target target = new TargetPermanent(filter); - this.getTargets().clear(); - this.addTarget(target); - return true; - } - return false; - } -} \ No newline at end of file diff --git a/Mage.Sets/src/mage/cards/n/NestOfScarabs.java b/Mage.Sets/src/mage/cards/n/NestOfScarabs.java index 66c39c6f280..d4545e85df7 100644 --- a/Mage.Sets/src/mage/cards/n/NestOfScarabs.java +++ b/Mage.Sets/src/mage/cards/n/NestOfScarabs.java @@ -20,8 +20,10 @@ public final class NestOfScarabs extends CardImpl { super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{B}"); // Whenever you put one or more -1/-1 counters on a creature, create that many 1/1 black Insect creature tokens. - this.addAbility(new PutCounterOnCreatureTriggeredAbility(new CreateTokenEffect(new NestOfScarabsBlackInsectToken(), new EffectKeyValue("countersAdded")) - .setText("create that many 1/1 black Insect creature tokens"), CounterType.M1M1.createInstance())); + this.addAbility(new PutCounterOnCreatureTriggeredAbility( + new CreateTokenEffect(new NestOfScarabsBlackInsectToken(), + new EffectKeyValue("countersAdded", "that many")), + CounterType.M1M1.createInstance())); } private NestOfScarabs(final NestOfScarabs card) { diff --git a/Mage.Sets/src/mage/cards/o/OKagachiVengefulKami.java b/Mage.Sets/src/mage/cards/o/OKagachiVengefulKami.java index 403faf3ab60..9afa9db5bf5 100644 --- a/Mage.Sets/src/mage/cards/o/OKagachiVengefulKami.java +++ b/Mage.Sets/src/mage/cards/o/OKagachiVengefulKami.java @@ -1,22 +1,26 @@ package mage.cards.o; import mage.MageInt; -import mage.abilities.TriggeredAbilityImpl; -import mage.abilities.effects.Effect; +import mage.abilities.Ability; +import mage.abilities.TriggeredAbility; +import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility; +import mage.abilities.condition.Condition; import mage.abilities.effects.common.ExileTargetEffect; import mage.abilities.keyword.FlyingAbility; import mage.abilities.keyword.TrampleAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; +import mage.constants.WatcherScope; import mage.filter.FilterPermanent; import mage.filter.common.FilterNonlandPermanent; -import mage.filter.predicate.permanent.ControllerIdPredicate; import mage.game.Game; -import mage.game.events.DamagedPlayerEvent; import mage.game.events.GameEvent; -import mage.players.Player; import mage.target.TargetPermanent; +import mage.target.targetadjustment.ThatPlayerControlsTargetAdjuster; +import mage.util.CardUtil; import mage.watchers.Watcher; import java.util.*; @@ -25,6 +29,7 @@ import java.util.*; * @author TheElk801 */ public final class OKagachiVengefulKami extends CardImpl { + static FilterPermanent filter = new FilterNonlandPermanent("nonland permanent that player controls"); public OKagachiVengefulKami(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}{U}{B}{R}{G}"); @@ -42,7 +47,11 @@ public final class OKagachiVengefulKami extends CardImpl { this.addAbility(TrampleAbility.getInstance()); // Whenever O-Kagachi, Vengeful Kami deals combat damage to a player, if that player attacked you during their last turn, exile target nonland permanent that player controls - this.addAbility(new OKagachiVengefulKamiTriggeredAbility()); + TriggeredAbility ability = new DealsCombatDamageToAPlayerTriggeredAbility(new ExileTargetEffect(), false, true); + ability.addTarget(new TargetPermanent(filter)); + ability.setTargetAdjuster(new ThatPlayerControlsTargetAdjuster()); + ability.withInterveningIf(KagachiVengefulKamiCondition.instance); + this.addAbility(ability); } private OKagachiVengefulKami(final OKagachiVengefulKami card) { @@ -55,66 +64,25 @@ public final class OKagachiVengefulKami extends CardImpl { } } -class OKagachiVengefulKamiTriggeredAbility extends TriggeredAbilityImpl { - - OKagachiVengefulKamiTriggeredAbility() { - super(Zone.BATTLEFIELD, new ExileTargetEffect(), false); - this.addWatcher(new OKagachiVengefulKamiWatcher()); - } - - private OKagachiVengefulKamiTriggeredAbility(final OKagachiVengefulKamiTriggeredAbility ability) { - super(ability); - } +enum KagachiVengefulKamiCondition implements Condition { + instance; @Override - public OKagachiVengefulKamiTriggeredAbility copy() { - return new OKagachiVengefulKamiTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_PLAYER; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - Player player = game.getPlayer(event.getTargetId()); - if (player == null - || !((DamagedPlayerEvent) event).isCombatDamage() - || !event.getSourceId().equals(getSourceId())) { - return false; - } - this.getEffects().setValue("damagedPlayer", event.getTargetId()); - FilterPermanent filter = new FilterNonlandPermanent("nonland permanent controlled by " + player.getName()); - filter.add(new ControllerIdPredicate(event.getTargetId())); - this.getTargets().clear(); - this.addTarget(new TargetPermanent(filter)); - return true; - } - - @Override - public boolean checkInterveningIfClause(Game game) { + public boolean apply(Game game, Ability source) { OKagachiVengefulKamiWatcher watcher = game.getState().getWatcher(OKagachiVengefulKamiWatcher.class); if (watcher == null) { return false; } - UUID playerId = null; - for (Effect effect : this.getEffects()) { - Object obj = effect.getValue("damagedPlayer"); - if (obj instanceof UUID) { - playerId = (UUID) obj; - break; - } - } - return watcher.checkPlayer(getControllerId(), playerId); + return CardUtil.getEffectValueFromAbility(source, "damagedPlayer", UUID.class) + .filter(uuid -> watcher.checkPlayer(source.getControllerId(), uuid)) + .isPresent(); } @Override - public String getRule() { - return "Whenever {this} deals combat damage to a player, " + - "if that player attacked you during their last turn, " + - "exile target nonland permanent that player controls."; + public String toString() { + return "if that player attacked you during their last turn"; } + } class OKagachiVengefulKamiWatcher extends Watcher { diff --git a/Mage.Sets/src/mage/cards/o/OrahSkyclaveHierophant.java b/Mage.Sets/src/mage/cards/o/OrahSkyclaveHierophant.java index 96a82a5c3e8..0455c5b46d8 100644 --- a/Mage.Sets/src/mage/cards/o/OrahSkyclaveHierophant.java +++ b/Mage.Sets/src/mage/cards/o/OrahSkyclaveHierophant.java @@ -1,19 +1,25 @@ package mage.cards.o; import mage.MageInt; -import mage.MageObject; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.Ability; +import mage.abilities.common.DiesThisOrAnotherTriggeredAbility; import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect; import mage.abilities.keyword.LifelinkAbility; +import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.SubType; +import mage.constants.SuperType; import mage.filter.FilterCard; -import mage.filter.predicate.mageobject.ManaValuePredicate; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterControlledPermanent; +import mage.filter.predicate.ObjectSourcePlayer; +import mage.filter.predicate.ObjectSourcePlayerPredicate; import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.ZoneChangeEvent; +import mage.game.permanent.Permanent; import mage.target.common.TargetCardInYourGraveyard; +import mage.util.CardUtil; import java.util.UUID; @@ -21,6 +27,13 @@ import java.util.UUID; * @author TheElk801 */ public final class OrahSkyclaveHierophant extends CardImpl { + static FilterPermanent filterTrigger = new FilterControlledPermanent("Cleric you control"); + static FilterCard filterTarget = new FilterCard("Cleric card with lesser mana value"); + static { + filterTrigger.add(SubType.CLERIC.getPredicate()); + filterTarget.add(SubType.CLERIC.getPredicate()); + filterTarget.add(OrahSkyclaveHierophantPredicate.instance); + } public OrahSkyclaveHierophant(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}{B}"); @@ -35,7 +48,9 @@ public final class OrahSkyclaveHierophant extends CardImpl { this.addAbility(LifelinkAbility.getInstance()); // Whenever Orah, Skyclave Hierophant or another Cleric you control dies, return target Cleric card with lesser converted mana cost from your graveyard to the battlefield. - this.addAbility(new OrahSkyclaveHierophantTriggeredAbility()); + Ability ability = new DiesThisOrAnotherTriggeredAbility(new ReturnFromGraveyardToBattlefieldTargetEffect(), false, filterTrigger); + ability.addTarget(new TargetCardInYourGraveyard(filterTarget)); + this.addAbility(ability); } private OrahSkyclaveHierophant(final OrahSkyclaveHierophant card) { @@ -48,57 +63,15 @@ public final class OrahSkyclaveHierophant extends CardImpl { } } -class OrahSkyclaveHierophantTriggeredAbility extends TriggeredAbilityImpl { - - OrahSkyclaveHierophantTriggeredAbility() { - super(Zone.BATTLEFIELD, new ReturnFromGraveyardToBattlefieldTargetEffect()); - setLeavesTheBattlefieldTrigger(true); - } - - private OrahSkyclaveHierophantTriggeredAbility(final OrahSkyclaveHierophantTriggeredAbility ability) { - super(ability); - } +enum OrahSkyclaveHierophantPredicate implements ObjectSourcePlayerPredicate { + instance; @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.ZONE_CHANGE; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - ZoneChangeEvent zEvent = (ZoneChangeEvent) event; - if (!zEvent.isDiesEvent() - || !zEvent.getTarget().isControlledBy(getControllerId()) - || (!zEvent.getTarget().hasSubtype(SubType.CLERIC, game) - && !zEvent.getTarget().getId().equals(getSourceId())) - ) { - return false; - } - FilterCard filterCard = new FilterCard( - "Cleric card with mana value less than " + (zEvent.getTarget().getManaValue()) - ); - filterCard.add(SubType.CLERIC.getPredicate()); - filterCard.add(new ManaValuePredicate( - ComparisonType.FEWER_THAN, zEvent.getTarget().getManaValue() - )); - this.getTargets().clear(); - this.addTarget(new TargetCardInYourGraveyard(filterCard)); - return true; - } - - @Override - public OrahSkyclaveHierophantTriggeredAbility copy() { - return new OrahSkyclaveHierophantTriggeredAbility(this); - } - - @Override - public String getRule() { - return "Whenever {this} or another Cleric you control dies, return target Cleric card " + - "with lesser mana value from your graveyard to the battlefield."; - } - - @Override - public boolean isInUseableZone(Game game, MageObject sourceObject, GameEvent event) { - return TriggeredAbilityImpl.isInUseableZoneDiesTrigger(this, sourceObject, event, game); + public boolean apply(ObjectSourcePlayer input, Game game) { + return CardUtil.getEffectValueFromAbility( + input.getSource(), "creatureDied", Permanent.class + ) + .filter(permanent -> input.getObject().getManaValue() < permanent.getManaValue()) + .isPresent(); } } diff --git a/Mage.Sets/src/mage/cards/o/OrcishSettlers.java b/Mage.Sets/src/mage/cards/o/OrcishSettlers.java index 2d33e36f563..2570380a90e 100644 --- a/Mage.Sets/src/mage/cards/o/OrcishSettlers.java +++ b/Mage.Sets/src/mage/cards/o/OrcishSettlers.java @@ -7,20 +7,14 @@ import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.common.SacrificeSourceCost; import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.effects.common.DestroyTargetEffect; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.SubType; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.permanent.Permanent; -import mage.players.Player; import mage.target.common.TargetLandPermanent; -import mage.util.CardUtil; +import mage.target.targetadjustment.XTargetsCountAdjuster; -import java.util.List; import java.util.UUID; /** @@ -38,9 +32,12 @@ public final class OrcishSettlers extends CardImpl { this.toughness = new MageInt(1); // {X}{X}{R}, {tap}, Sacrifice Orcish Settlers: Destroy X target lands. - Ability ability = new SimpleActivatedAbility(new OrcishSettlersEffect(), new ManaCostsImpl<>("{X}{X}{R}")); + Ability ability = new SimpleActivatedAbility(new DestroyTargetEffect().setText("Destroy X target lands"), + new ManaCostsImpl<>("{X}{X}{R}")); ability.addCost(new TapSourceCost()); ability.addCost(new SacrificeSourceCost()); + ability.addTarget(new TargetLandPermanent()); + ability.setTargetAdjuster(new XTargetsCountAdjuster()); this.addAbility(ability); } @@ -53,43 +50,3 @@ public final class OrcishSettlers extends CardImpl { return new OrcishSettlers(this); } } - -class OrcishSettlersEffect extends OneShotEffect { - - OrcishSettlersEffect() { - super(Outcome.DestroyPermanent); - this.staticText = "Destroy X target lands"; - } - - private OrcishSettlersEffect(final OrcishSettlersEffect effect) { - super(effect); - } - - @Override - public OrcishSettlersEffect copy() { - return new OrcishSettlersEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - int amount = CardUtil.getSourceCostsTag(game, source, "X", 0); - if (amount == 0) { - return false; - } - TargetLandPermanent target = new TargetLandPermanent(amount); - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null - && target.canChoose(controller.getId(), source, game) - && controller.choose(Outcome.DestroyPermanent, target, source, game)) { - List targets = target.getTargets(); - targets.forEach((landId) -> { - Permanent land = game.getPermanent(landId); - if (land != null) { - land.destroy(source, game, false); - } - }); - return true; - } - return false; - } -} diff --git a/Mage.Sets/src/mage/cards/p/PawpatchRecruit.java b/Mage.Sets/src/mage/cards/p/PawpatchRecruit.java index 0c48850b2c3..b2ade10d334 100644 --- a/Mage.Sets/src/mage/cards/p/PawpatchRecruit.java +++ b/Mage.Sets/src/mage/cards/p/PawpatchRecruit.java @@ -22,6 +22,7 @@ import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.game.stack.StackObject; import mage.target.TargetPermanent; +import mage.target.targetadjustment.DefineByTriggerTargetAdjuster; import java.util.UUID; @@ -65,6 +66,7 @@ class PawpatchRecruitTriggeredAbility extends TriggeredAbilityImpl { public PawpatchRecruitTriggeredAbility() { super(Zone.BATTLEFIELD, new AddCountersTargetEffect(CounterType.P1P1.createInstance()), false); + this.setTargetAdjuster(DefineByTriggerTargetAdjuster.instance); } private PawpatchRecruitTriggeredAbility(final PawpatchRecruitTriggeredAbility ability) { diff --git a/Mage.Sets/src/mage/cards/r/RampagingRaptor.java b/Mage.Sets/src/mage/cards/r/RampagingRaptor.java index bf4ee11f65b..4f69d51409a 100644 --- a/Mage.Sets/src/mage/cards/r/RampagingRaptor.java +++ b/Mage.Sets/src/mage/cards/r/RampagingRaptor.java @@ -1,7 +1,8 @@ package mage.cards.r; import mage.MageInt; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.Ability; +import mage.abilities.common.DealsDamageToOpponentTriggeredAbility; import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.dynamicvalue.common.SavedDamageValue; @@ -14,16 +15,15 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Duration; import mage.constants.SubType; -import mage.constants.Zone; import mage.filter.FilterPermanent; import mage.filter.predicate.Predicates; import mage.filter.predicate.permanent.ControllerIdPredicate; import mage.filter.predicate.permanent.ProtectorIdPredicate; import mage.game.Game; -import mage.game.events.DamagedEvent; -import mage.game.events.GameEvent; import mage.players.Player; import mage.target.TargetPermanent; +import mage.target.targetadjustment.TargetAdjuster; +import mage.target.targetpointer.FirstTargetPointer; import java.util.UUID; @@ -51,7 +51,10 @@ public final class RampagingRaptor extends CardImpl { )); // Whenever Rampaging Raptor deals combat damage to an opponent, it deals that much damage to target planeswalker that player controls or battle that player protects. - this.addAbility(new RampagingRaptorTriggeredAbility()); + Ability ability = new DealsDamageToOpponentTriggeredAbility(new DamageTargetEffect(SavedDamageValue.MUCH) + .withTargetDescription("target planeswalker that player controls or battle that player protects"), false, true, true); + ability.setTargetAdjuster(RampagingRaptorTargetAdjuster.instance); + this.addAbility(ability); } private RampagingRaptor(final RampagingRaptor card) { @@ -64,33 +67,17 @@ public final class RampagingRaptor extends CardImpl { } } -class RampagingRaptorTriggeredAbility extends TriggeredAbilityImpl { - - RampagingRaptorTriggeredAbility() { - super(Zone.BATTLEFIELD, new DamageTargetEffect(SavedDamageValue.MUCH), false); - } - - private RampagingRaptorTriggeredAbility(final RampagingRaptorTriggeredAbility ability) { - super(ability); - } +enum RampagingRaptorTargetAdjuster implements TargetAdjuster { + instance; @Override - public RampagingRaptorTriggeredAbility copy() { - return new RampagingRaptorTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.DAMAGED_PLAYER; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - Player opponent = game.getPlayer(event.getPlayerId()); - if (opponent == null - || !event.getSourceId().equals(this.getSourceId()) - || !((DamagedEvent) event).isCombatDamage()) { - return false; + public void adjustTargets(Ability ability, Game game) { + UUID opponentId = ability.getEffects().get(0).getTargetPointer().getFirst(game, ability); + Player opponent = game.getPlayer(opponentId); + ability.getTargets().clear(); + ability.getAllEffects().setTargetPointer(new FirstTargetPointer()); + if (opponent == null) { + return; } FilterPermanent filter = new FilterPermanent( "planeswalker " + opponent.getLogName() + " controls " + @@ -106,15 +93,6 @@ class RampagingRaptorTriggeredAbility extends TriggeredAbilityImpl { new ProtectorIdPredicate(opponent.getId()) ) )); - this.getEffects().setValue("damage", event.getAmount()); - this.getTargets().clear(); - this.addTarget(new TargetPermanent(filter)); - return true; - } - - @Override - public String getRule() { - return "Whenever {this} deals combat damage to an opponent, it deals that much damage " + - "to target planeswalker that player controls or battle that player protects."; + ability.addTarget(new TargetPermanent(filter)); } } diff --git a/Mage.Sets/src/mage/cards/r/RampagingWarMammoth.java b/Mage.Sets/src/mage/cards/r/RampagingWarMammoth.java index 36302890eb2..f9a6a8324b6 100644 --- a/Mage.Sets/src/mage/cards/r/RampagingWarMammoth.java +++ b/Mage.Sets/src/mage/cards/r/RampagingWarMammoth.java @@ -1,22 +1,21 @@ package mage.cards.r; import mage.MageInt; -import mage.abilities.common.ZoneChangeTriggeredAbility; +import mage.abilities.Ability; +import mage.abilities.common.CycleTriggeredAbility; import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.dynamicvalue.common.EffectKeyValue; import mage.abilities.effects.common.DestroyTargetEffect; -import mage.abilities.hint.StaticHint; import mage.abilities.keyword.CyclingAbility; import mage.abilities.keyword.TrampleAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.stack.StackObject; -import mage.target.common.TargetArtifactPermanent; -import mage.util.CardUtil; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterArtifactPermanent; +import mage.target.TargetPermanent; +import mage.target.targetadjustment.TargetsCountAdjuster; import java.util.UUID; @@ -24,6 +23,7 @@ import java.util.UUID; * @author Susucr */ public final class RampagingWarMammoth extends CardImpl { + static FilterPermanent filter = new FilterArtifactPermanent("up to X target artifacts"); public RampagingWarMammoth(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{R}{R}"); @@ -39,7 +39,10 @@ public final class RampagingWarMammoth extends CardImpl { this.addAbility(new CyclingAbility(new ManaCostsImpl<>("{X}{2}{R}"))); // When you cycle Rampaging War Mammoth, destroy up to X target artifacts. - this.addAbility(new RampagingWarMammothTriggeredAbility()); + Ability ability = new CycleTriggeredAbility(new DestroyTargetEffect()); + ability.addTarget(new TargetPermanent(0,1, filter)); + ability.setTargetAdjuster(new TargetsCountAdjuster(new EffectKeyValue("cycleXValue"))); + this.addAbility(ability); } private RampagingWarMammoth(final RampagingWarMammoth card) { @@ -51,56 +54,3 @@ public final class RampagingWarMammoth extends CardImpl { return new RampagingWarMammoth(this); } } - -class RampagingWarMammothTriggeredAbility extends ZoneChangeTriggeredAbility { - - RampagingWarMammothTriggeredAbility() { - super(Zone.ALL, null, "", false); - } - - private RampagingWarMammothTriggeredAbility(RampagingWarMammothTriggeredAbility ability) { - super(ability); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.ACTIVATED_ABILITY; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (!event.getSourceId().equals(this.getSourceId())) { - return false; - } - StackObject object = game.getStack().getStackObject(event.getSourceId()); - if (object == null || !(object.getStackAbility() instanceof CyclingAbility)) { - return false; - } - - CyclingAbility cyclingAbility = (CyclingAbility) object.getStackAbility(); - // If X is 0, or cycling from another ability that does not have {X} in cost, - // this should trigger (but do nothing). - int xValue = CardUtil.getSourceCostsTag(game, cyclingAbility, "X", 0); - - this.getEffects().clear(); - this.getTargets().clear(); - - this.addEffect(new DestroyTargetEffect()); - // Target up to X artifacts - this.addTarget(new TargetArtifactPermanent(0, xValue)); - this.getHints().clear(); - this.addHint(new StaticHint("X = " + xValue)); - - return true; - } - - @Override - public RampagingWarMammothTriggeredAbility copy() { - return new RampagingWarMammothTriggeredAbility(this); - } - - @Override - public String getRule() { - return "When you cycle {this}, destroy up to X target artifacts."; - } -} diff --git a/Mage.Sets/src/mage/cards/r/RavenousGigantotherium.java b/Mage.Sets/src/mage/cards/r/RavenousGigantotherium.java index eeaacab7a2b..1e877367a4f 100644 --- a/Mage.Sets/src/mage/cards/r/RavenousGigantotherium.java +++ b/Mage.Sets/src/mage/cards/r/RavenousGigantotherium.java @@ -3,6 +3,8 @@ package mage.cards.r; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; +import mage.abilities.dynamicvalue.DynamicValue; +import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DamageMultiEffect; import mage.abilities.keyword.DevourAbility; @@ -12,10 +14,10 @@ import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SubType; import mage.game.Game; -import mage.game.events.GameEvent; import mage.game.permanent.Permanent; import mage.target.Target; import mage.target.common.TargetCreaturePermanentAmount; +import mage.util.CardUtil; import java.util.Collection; import java.util.List; @@ -39,7 +41,11 @@ public final class RavenousGigantotherium extends CardImpl { this.addAbility(new DevourAbility(3)); // When Ravenous Gigantotherium enters the battlefield, it deals X damage divided as you choose among up to X target creatures, where X is its power. Each of those creatures deals damage equal to its power to Ravenous Gigantotherium. - this.addAbility(new RavenousGigantotheriumAbility()); + Ability ability = new EntersBattlefieldTriggeredAbility(new DamageMultiEffect()); + ability.addEffect(new RavenousGigantotheriumEffect()); + ability.addTarget(new TargetCreaturePermanentAmount(RavenousGigantotheriumAmount.instance) + .withTargetName("up to X target creatures, where X is its power")); + this.addAbility(ability); } private RavenousGigantotherium(final RavenousGigantotherium card) { @@ -52,47 +58,28 @@ public final class RavenousGigantotherium extends CardImpl { } } -class RavenousGigantotheriumAbility extends EntersBattlefieldTriggeredAbility { +enum RavenousGigantotheriumAmount implements DynamicValue { + instance; - RavenousGigantotheriumAbility() { - super(null, false); - } - - private RavenousGigantotheriumAbility(final RavenousGigantotheriumAbility ability) { - super(ability); + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + return CardUtil.getEffectValueFromAbility(sourceAbility, "permanentEnteredBattlefield", Permanent.class) + .map(permanent -> permanent.getPower().getValue()).orElse(0); } @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (!super.checkTrigger(event, game)) { - return false; - } - Permanent permanent = game.getPermanentOrLKIBattlefield(event.getTargetId()); - if (permanent == null) { - return false; - } - int power = Math.max(permanent.getPower().getValue(), 0); - this.getEffects().clear(); - this.addEffect(new DamageMultiEffect()); - this.addEffect(new RavenousGigantotheriumEffect()); - this.getTargets().clear(); - if (power < 1) { - return true; - } - this.addTarget(new TargetCreaturePermanentAmount(power, 0, power)); - return true; + public DynamicValue copy() { + return instance; } @Override - public String getRule() { - return "When {this} enters, it deals X damage " + - "divided as you choose among up to X target creatures, where X is its power. " + - "Each of those creatures deals damage equal to its power to {this}."; + public String getMessage() { + return "its power"; } @Override - public RavenousGigantotheriumAbility copy() { - return new RavenousGigantotheriumAbility(this); + public String toString() { + return "X"; } } @@ -100,6 +87,7 @@ class RavenousGigantotheriumEffect extends OneShotEffect { RavenousGigantotheriumEffect() { super(Outcome.Benefit); + this.setText("Each of those creatures deals damage equal to its power to {this}."); } private RavenousGigantotheriumEffect(final RavenousGigantotheriumEffect effect) { diff --git a/Mage.Sets/src/mage/cards/r/RelentlessDead.java b/Mage.Sets/src/mage/cards/r/RelentlessDead.java index 3d5f3299013..cc3fe4406b5 100644 --- a/Mage.Sets/src/mage/cards/r/RelentlessDead.java +++ b/Mage.Sets/src/mage/cards/r/RelentlessDead.java @@ -3,22 +3,21 @@ package mage.cards.r; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.DiesSourceTriggeredAbility; +import mage.abilities.costs.common.DynamicValueGenericManaCost; import mage.abilities.costs.mana.ManaCostsImpl; -import mage.abilities.effects.OneShotEffect; +import mage.abilities.dynamicvalue.common.TargetManaValue; import mage.abilities.effects.common.DoIfCostPaid; +import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect; import mage.abilities.effects.common.ReturnToHandSourceEffect; import mage.abilities.keyword.MenaceAbility; -import mage.cards.Card; import mage.cards.CardImpl; import mage.cards.CardSetInfo; -import mage.constants.*; +import mage.constants.CardType; +import mage.constants.SubType; import mage.filter.FilterCard; +import mage.filter.common.FilterCreatureCard; import mage.filter.predicate.mageobject.AnotherPredicate; -import mage.filter.predicate.mageobject.ManaValuePredicate; -import mage.game.Game; -import mage.players.Player; import mage.target.common.TargetCardInYourGraveyard; -import mage.util.ManaUtil; import java.util.UUID; @@ -26,6 +25,11 @@ import java.util.UUID; * @author LevelX2 */ public final class RelentlessDead extends CardImpl { + static FilterCard filter = new FilterCreatureCard("another target Zombie creature card with mana value X from your graveyard"); // This target defines X + static{ + filter.add(SubType.ZOMBIE.getPredicate()); + filter.add(AnotherPredicate.instance); + } public RelentlessDead(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{B}{B}"); @@ -40,7 +44,11 @@ public final class RelentlessDead extends CardImpl { this.addAbility(new DiesSourceTriggeredAbility(new DoIfCostPaid(new ReturnToHandSourceEffect().setText("return it to its owner's hand"), new ManaCostsImpl<>("{B}")))); // When Relentless Dead dies, you may pay {X}. If you do, return another target Zombie creature card with converted mana cost X from your graveyard to the battlefield. - this.addAbility(new DiesSourceTriggeredAbility(new RelentlessDeadEffect())); + Ability ability = new DiesSourceTriggeredAbility(new DoIfCostPaid( + new ReturnFromGraveyardToBattlefieldTargetEffect(), + new DynamicValueGenericManaCost(TargetManaValue.instance, "{X}"))); + ability.addTarget(new TargetCardInYourGraveyard(filter)); + this.addAbility(ability); } private RelentlessDead(final RelentlessDead card) { @@ -52,46 +60,3 @@ public final class RelentlessDead extends CardImpl { return new RelentlessDead(this); } } - -class RelentlessDeadEffect extends OneShotEffect { - - RelentlessDeadEffect() { - super(Outcome.PutCardInPlay); - this.staticText = "you may pay {X}. If you do, return another target Zombie creature card with mana value X from your graveyard to the battlefield"; - } - - private RelentlessDeadEffect(final RelentlessDeadEffect effect) { - super(effect); - } - - @Override - public RelentlessDeadEffect copy() { - return new RelentlessDeadEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - if (controller.chooseUse(Outcome.Benefit, "Do you want to pay {X} to return zombie?", source, game)) { - int payCount = ManaUtil.playerPaysXGenericMana(true, "Relentless Dead", controller, source, game); - // can be 0 - FilterCard filter = new FilterCard("Another target Zombie card with mana value {" + payCount + "}"); - filter.add(SubType.ZOMBIE.getPredicate()); - filter.add(new ManaValuePredicate(ComparisonType.EQUAL_TO, payCount)); - filter.add(AnotherPredicate.instance); - TargetCardInYourGraveyard target = new TargetCardInYourGraveyard(filter); - if (controller.chooseTarget(outcome, target, source, game)) { - Card card = game.getCard(target.getFirstTarget()); - if (card != null) { - controller.moveCards(card, Zone.BATTLEFIELD, source, game); - } - } - - } - return true; - } - return false; - - } -} diff --git a/Mage.Sets/src/mage/cards/s/SharkTyphoon.java b/Mage.Sets/src/mage/cards/s/SharkTyphoon.java index 97e6883c30a..5e853ee68c9 100644 --- a/Mage.Sets/src/mage/cards/s/SharkTyphoon.java +++ b/Mage.Sets/src/mage/cards/s/SharkTyphoon.java @@ -1,25 +1,20 @@ package mage.cards.s; import mage.abilities.Ability; +import mage.abilities.common.CycleTriggeredAbility; import mage.abilities.common.SpellCastControllerTriggeredAbility; -import mage.abilities.common.ZoneChangeTriggeredAbility; import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.keyword.CyclingAbility; import mage.cards.CardImpl; import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.SetTargetPointer; -import mage.constants.Zone; import mage.filter.StaticFilters; import mage.game.Game; -import mage.game.events.GameEvent; import mage.game.permanent.token.SharkToken; import mage.game.stack.Spell; -import mage.game.stack.StackObject; -import mage.util.CardUtil; import java.util.UUID; @@ -42,7 +37,7 @@ public final class SharkTyphoon extends CardImpl { this.addAbility(new CyclingAbility(new ManaCostsImpl<>("{X}{1}{U}"))); // When you cycle Shark Typhoon, create an X/X blue Shark creature token with flying. - this.addAbility(new SharkTyphoonTriggeredAbility()); + this.addAbility(new CycleTriggeredAbility(new SharkTyphoonCycleEffect())); } private SharkTyphoon(final SharkTyphoon card) { @@ -82,42 +77,29 @@ class SharkTyphoonCastEffect extends OneShotEffect { } } -class SharkTyphoonTriggeredAbility extends ZoneChangeTriggeredAbility { +class SharkTyphoonCycleEffect extends OneShotEffect { - SharkTyphoonTriggeredAbility() { - super(Zone.ALL, null, "", false); + SharkTyphoonCycleEffect() { + super(Outcome.Benefit); + staticText = "create an X/X blue Shark creature token with flying"; } - private SharkTyphoonTriggeredAbility(SharkTyphoonTriggeredAbility ability) { - super(ability); + private SharkTyphoonCycleEffect(final SharkTyphoonCycleEffect effect) { + super(effect); } @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.ACTIVATED_ABILITY; + public SharkTyphoonCycleEffect copy() { + return new SharkTyphoonCycleEffect(this); } @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (!event.getSourceId().equals(this.getSourceId())) { - return false; + public boolean apply(Game game, Ability source) { + Object xValueObject = getValue("cycleXValue"); + int xValue = 0; + if (xValueObject instanceof Integer) { + xValue = (int) xValueObject; } - StackObject object = game.getStack().getStackObject(event.getSourceId()); - if (object == null || !(object.getStackAbility() instanceof CyclingAbility)) { - return false; - } - this.getEffects().clear(); - this.addEffect(new CreateTokenEffect(new SharkToken(CardUtil.getSourceCostsTag(game, object.getStackAbility(), "X", 0)))); - return true; - } - - @Override - public SharkTyphoonTriggeredAbility copy() { - return new SharkTyphoonTriggeredAbility(this); - } - - @Override - public String getRule() { - return "When you cycle this card, create an X/X blue Shark creature token with flying."; + return new SharkToken(xValue).putOntoBattlefield(1, game, source, source.getControllerId()); } } diff --git a/Mage.Sets/src/mage/cards/v/ValorsFlagship.java b/Mage.Sets/src/mage/cards/v/ValorsFlagship.java index 4bc90315f4f..d72e3615232 100644 --- a/Mage.Sets/src/mage/cards/v/ValorsFlagship.java +++ b/Mage.Sets/src/mage/cards/v/ValorsFlagship.java @@ -1,8 +1,9 @@ package mage.cards.v; import mage.MageInt; -import mage.abilities.TriggeredAbilityImpl; +import mage.abilities.common.CycleTriggeredAbility; import mage.abilities.costs.mana.ManaCostsImpl; +import mage.abilities.dynamicvalue.common.EffectKeyValue; import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.keyword.*; import mage.cards.CardImpl; @@ -10,12 +11,7 @@ import mage.cards.CardSetInfo; import mage.constants.CardType; import mage.constants.SubType; import mage.constants.SuperType; -import mage.constants.Zone; -import mage.game.Game; -import mage.game.events.GameEvent; import mage.game.permanent.token.PilotSaddleCrewToken; -import mage.game.stack.StackObject; -import mage.util.CardUtil; import java.util.UUID; @@ -48,7 +44,8 @@ public final class ValorsFlagship extends CardImpl { this.addAbility(new CyclingAbility(new ManaCostsImpl<>("{X}{2}{W}"))); // When you cycle this card, create X 1/1 colorless Pilot creature tokens with "This token saddles Mounts and crews Vehicles as though its power were 2 greater." - this.addAbility(new ValorsFlagshipTriggeredAbility()); + this.addAbility(new CycleTriggeredAbility( + new CreateTokenEffect(new PilotSaddleCrewToken(), new EffectKeyValue("cycleXValue", "X")))); } private ValorsFlagship(final ValorsFlagship card) { @@ -60,45 +57,3 @@ public final class ValorsFlagship extends CardImpl { return new ValorsFlagship(this); } } - -class ValorsFlagshipTriggeredAbility extends TriggeredAbilityImpl { - - ValorsFlagshipTriggeredAbility() { - super(Zone.ALL, null); - } - - private ValorsFlagshipTriggeredAbility(final ValorsFlagshipTriggeredAbility ability) { - super(ability); - } - - @Override - public ValorsFlagshipTriggeredAbility copy() { - return new ValorsFlagshipTriggeredAbility(this); - } - - @Override - public boolean checkEventType(GameEvent event, Game game) { - return event.getType() == GameEvent.EventType.ACTIVATED_ABILITY; - } - - @Override - public boolean checkTrigger(GameEvent event, Game game) { - if (!event.getSourceId().equals(this.getSourceId())) { - return false; - } - StackObject object = game.getStack().getStackObject(event.getSourceId()); - if (object == null || !(object.getStackAbility() instanceof CyclingAbility)) { - return false; - } - this.getEffects().clear(); - int amount = CardUtil.getSourceCostsTag(game, object.getStackAbility(), "X", 0); - this.addEffect(new CreateTokenEffect(new PilotSaddleCrewToken(), amount)); - return true; - } - - @Override - public String getRule() { - return "When you cycle this card, create X 1/1 colorless Pilot creature tokens with " + - "\"This token saddles Mounts and crews Vehicles as though its power were 2 greater.\""; - } -} diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/CyclingTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/CyclingTest.java index cbc4742c9a4..04696cceb3a 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/CyclingTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/CyclingTest.java @@ -61,6 +61,24 @@ public class CyclingTest extends CardTestPlayerBase { assertPowerToughness(playerB, "Pillarfield Ox", 0, 2); } + /** + * Tests that X value is correctly read by the resulting triggered ability + */ + @Test + public void cycleSharkTyphoon() { + addCard(Zone.HAND, playerA, "Shark Typhoon", 1); + addCard(Zone.BATTLEFIELD, playerA, "Island", 8); + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cycling"); + setChoice(playerA, "X=6"); + + setStopAt(1, PhaseStep.BEGIN_COMBAT); + execute(); + assertGraveyardCount(playerA, "Shark Typhoon", 1); + assertPowerToughness(playerA, "Shark Token", 6, 6); + assertTappedCount("Island", true, 8); + } + /** * Cycle from graveyard or battlefield should not work. */ diff --git a/Mage/src/main/java/mage/abilities/common/CycleTriggeredAbility.java b/Mage/src/main/java/mage/abilities/common/CycleTriggeredAbility.java index 0e08ea66c72..606b36db71c 100644 --- a/Mage/src/main/java/mage/abilities/common/CycleTriggeredAbility.java +++ b/Mage/src/main/java/mage/abilities/common/CycleTriggeredAbility.java @@ -6,6 +6,7 @@ import mage.constants.Zone; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.stack.StackObject; +import mage.util.CardUtil; /** * @author Plopman @@ -40,6 +41,7 @@ public class CycleTriggeredAbility extends ZoneChangeTriggeredAbility { return false; } this.getEffects().setValue("cycleCosts", object.getStackAbility().getCosts()); + this.getEffects().setValue("cycleXValue", CardUtil.getSourceCostsTag(game, object.getStackAbility(), "X", 0)); return true; } diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/common/EffectKeyValue.java b/Mage/src/main/java/mage/abilities/dynamicvalue/common/EffectKeyValue.java index b3cbe5cb55d..23001575e37 100644 --- a/Mage/src/main/java/mage/abilities/dynamicvalue/common/EffectKeyValue.java +++ b/Mage/src/main/java/mage/abilities/dynamicvalue/common/EffectKeyValue.java @@ -10,8 +10,8 @@ import mage.game.Game; * @author stravant */ public class EffectKeyValue implements DynamicValue { - private String key; - private String description; + private final String key; + private final String description; public EffectKeyValue(String key) { this.key = key; @@ -19,7 +19,7 @@ public class EffectKeyValue implements DynamicValue { } public EffectKeyValue(String key, String description) { - this(key); + this.key = key; this.description = description; } @@ -35,11 +35,11 @@ public class EffectKeyValue implements DynamicValue { @Override public String toString() { - return "equal to"; + return description; } @Override public String getMessage() { - return description; + return ""; } -} \ No newline at end of file +} diff --git a/Mage/src/main/java/mage/target/TargetImpl.java b/Mage/src/main/java/mage/target/TargetImpl.java index 1a15044b417..19dd98814b0 100644 --- a/Mage/src/main/java/mage/target/TargetImpl.java +++ b/Mage/src/main/java/mage/target/TargetImpl.java @@ -111,7 +111,7 @@ public abstract class TargetImpl implements Target { if (min > 0 && max == Integer.MAX_VALUE) { sb.append(CardUtil.numberToText(min)); sb.append(" or more "); - } else if (!targetName.startsWith("X ") && (min != 1 || max != 1)) { + } else if (!targetName.startsWith("X ") && !targetName.startsWith("up to ") && (min != 1 || max != 1)) { targetName = targetName.replace("another", "other"); //If non-singular, use "other" instead of "another" if (getUseAnyNumber()) {