Adding targets (Part 2/3) (#13765)

Adds target and/or target adjuster to cards whose abilities have the word "target", cards H-R.
fix EffectKeyValue text, NestOfScarabs
Add X value to effects for CycleTriggeredAbility, use with ValorsFlagship and SharkTyphoon, add test
This commit is contained in:
ssk97 2025-06-18 21:14:16 -07:00 committed by GitHub
parent 7be454fb92
commit 219cc53cf4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
24 changed files with 321 additions and 817 deletions

View file

@ -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. // If a spell cast from your graveyard this way would be put into your graveyard, exile it instead.
Ability ability = new SpellCastControllerTriggeredAbility(Zone.BATTLEFIELD, Ability ability = new SpellCastControllerTriggeredAbility(Zone.BATTLEFIELD,
new MayCastTargetCardEffect(true) 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, StaticFilters.FILTER_SPELL_AN_INSTANT_OR_SORCERY,
false, SetTargetPointer.SPELL, Zone.HAND false, SetTargetPointer.SPELL, Zone.HAND
); );
@ -84,6 +84,6 @@ enum GaleWaterdeepProdigyAdjuster implements TargetAdjuster {
} else { } else {
filter = SORCERY_FILTER; filter = SORCERY_FILTER;
} }
ability.addTarget(new TargetCardInYourGraveyard(filter)); ability.addTarget(new TargetCardInYourGraveyard(0, 1, filter));
} }
} }

View file

@ -1,23 +1,21 @@
package mage.cards.h; package mage.cards.h;
import mage.ApprovingObject;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.common.SpellCastControllerTriggeredAbility; import mage.abilities.common.SpellCastControllerTriggeredAbility;
import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.MayCastTargetCardEffect;
import mage.cards.Card;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.CardType; import mage.constants.CardType;
import mage.constants.Outcome; import mage.constants.SetTargetPointer;
import mage.constants.Zone;
import mage.filter.FilterCard; import mage.filter.FilterCard;
import mage.filter.common.FilterInstantOrSorcerySpell; import mage.filter.StaticFilters;
import mage.filter.predicate.mageobject.NamePredicate; import mage.filter.predicate.mageobject.NamePredicate;
import mage.game.Game; import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.stack.Spell; import mage.game.stack.Spell;
import mage.players.Player;
import mage.target.common.TargetCardInYourGraveyard; import mage.target.common.TargetCardInYourGraveyard;
import mage.watchers.common.CastFromHandWatcher; import mage.target.targetadjustment.TargetAdjuster;
import mage.target.targetpointer.FirstTargetPointer;
import java.util.UUID; import java.util.UUID;
@ -26,13 +24,20 @@ import java.util.UUID;
* @author LevelX2 * @author LevelX2
*/ */
public final class HarnessTheStorm extends CardImpl { 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) { public HarnessTheStorm(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{R}"); super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{R}");
// Whenever you cast an instant or sorcery spell from your hand, you may cast // 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. // 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) { private HarnessTheStorm(final HarnessTheStorm card) {
@ -45,78 +50,20 @@ public final class HarnessTheStorm extends CardImpl {
} }
} }
enum HarnessTheStormAdjuster implements TargetAdjuster {
class HarnessTheStormTriggeredAbility extends SpellCastControllerTriggeredAbility { instance;
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);
}
@Override @Override
public boolean checkTrigger(GameEvent event, Game game) { public void adjustTargets(Ability ability, Game game) {
if (super.checkTrigger(event, game)) { UUID spellId = ability.getEffects().get(0).getTargetPointer().getFirst(game, ability);
CastFromHandWatcher watcher = game.getState().getWatcher(CastFromHandWatcher.class); ability.getTargets().clear();
if (watcher != null && watcher.spellWasCastFromHand(event.getSourceId())) { ability.getAllEffects().setTargetPointer(new FirstTargetPointer());
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;
}
}
}
return false;
}
@Override Spell spell = game.getSpellOrLKIStack(spellId);
public HarnessTheStormTriggeredAbility copy() { if (spell == null){
return new HarnessTheStormTriggeredAbility(this); return;
} }
FilterCard filter = new FilterCard("a card named " + spell.getName() + " in your graveyard");
} filter.add(new NamePredicate(spell.getName()));
ability.addTarget(new TargetCardInYourGraveyard(filter));
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. <i>(You still pay its costs.)</i>";
}
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;
} }
} }

View file

@ -1,26 +1,21 @@
package mage.cards.i; package mage.cards.i;
import java.util.UUID;
import mage.MageInt; import mage.MageInt;
import mage.abilities.TriggeredAbilityImpl; import mage.abilities.TriggeredAbility;
import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility; import mage.abilities.common.AttacksTriggeredAbility;
import mage.abilities.effects.common.SacrificeSourceEffect; import mage.abilities.effects.common.SacrificeSourceEffect;
import mage.abilities.effects.common.combat.MustBeBlockedByTargetSourceEffect; import mage.abilities.effects.common.combat.MustBeBlockedByTargetSourceEffect;
import mage.abilities.keyword.HasteAbility; import mage.abilities.keyword.HasteAbility;
import mage.abilities.keyword.TrampleAbility; import mage.abilities.keyword.TrampleAbility;
import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.CardType; import mage.constants.*;
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.target.common.TargetCreaturePermanent; 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 // Haste
this.addAbility(HasteAbility.getInstance()); this.addAbility(HasteAbility.getInstance());
// When Impetuous Devils attacks, up to one target creature defending player controls blocks it this combat if able. // 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. // At the beginning of the end step, sacrifice Impetuous Devils.
this.addAbility(new BeginningOfEndStepTriggeredAbility(TargetController.NEXT, new SacrificeSourceEffect(), false)); this.addAbility(new BeginningOfEndStepTriggeredAbility(TargetController.NEXT, new SacrificeSourceEffect(), false));
@ -54,44 +54,3 @@ public final class ImpetuousDevils extends CardImpl {
return new ImpetuousDevils(this); 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);
}
}

View file

@ -1,7 +1,6 @@
package mage.cards.k; package mage.cards.k;
import java.util.UUID;
import mage.MageObject; import mage.MageObject;
import mage.abilities.TriggeredAbilityImpl; import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.effects.common.DestroyTargetEffect;
@ -15,6 +14,9 @@ import mage.game.Game;
import mage.game.events.GameEvent; import mage.game.events.GameEvent;
import mage.target.Target; import mage.target.Target;
import mage.target.TargetPermanent; 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) { public KarmicJustice(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{W}"); 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. // 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()); this.addAbility(new KarmicJusticeTriggeredAbility());
} }
@ -45,6 +46,7 @@ class KarmicJusticeTriggeredAbility extends TriggeredAbilityImpl {
KarmicJusticeTriggeredAbility() { KarmicJusticeTriggeredAbility() {
super(Zone.BATTLEFIELD, new DestroyTargetEffect(), true); super(Zone.BATTLEFIELD, new DestroyTargetEffect(), true);
this.setLeavesTheBattlefieldTrigger(true); this.setLeavesTheBattlefieldTrigger(true);
this.setTargetAdjuster(DefineByTriggerTargetAdjuster.instance);
} }
private KarmicJusticeTriggeredAbility(final KarmicJusticeTriggeredAbility ability) { private KarmicJusticeTriggeredAbility(final KarmicJusticeTriggeredAbility ability) {

View file

@ -1,9 +1,9 @@
package mage.cards.m; package mage.cards.m;
import java.util.UUID;
import mage.MageInt; 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.SimpleActivatedAbility;
import mage.abilities.common.SimpleStaticAbility; import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.costs.mana.ManaCostsImpl;
@ -13,21 +13,21 @@ import mage.abilities.effects.common.UntapSourceEffect;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.CardType; import mage.constants.CardType;
import mage.constants.SetTargetPointer;
import mage.constants.SubType; import mage.constants.SubType;
import mage.constants.Zone;
import mage.filter.common.FilterCreaturePermanent; 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.common.TargetCreaturePermanent;
import mage.target.targetadjustment.ThatPlayerControlsTargetAdjuster;
import java.util.UUID;
/** /**
* *
* @author fireshoes * @author fireshoes
*/ */
public final class MageRingResponder extends CardImpl { public final class MageRingResponder extends CardImpl {
private static final FilterCreaturePermanent filter
= new FilterCreaturePermanent("creature defending player controls");
public MageRingResponder(UUID ownerId, CardSetInfo setInfo) { public MageRingResponder(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT,CardType.CREATURE},"{7}"); super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT,CardType.CREATURE},"{7}");
this.subtype.add(SubType.GOLEM); this.subtype.add(SubType.GOLEM);
@ -41,7 +41,10 @@ public final class MageRingResponder extends CardImpl {
this.addAbility(new SimpleActivatedAbility(new UntapSourceEffect(), new ManaCostsImpl<>("{7}"))); this.addAbility(new SimpleActivatedAbility(new UntapSourceEffect(), new ManaCostsImpl<>("{7}")));
// Whenever Mage-Ring Responder attacks, it deals 7 damage to target creature defending player controls. // 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) { private MageRingResponder(final MageRingResponder card) {
@ -53,44 +56,3 @@ public final class MageRingResponder extends CardImpl {
return new MageRingResponder(this); 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);
}
}

View file

@ -1,29 +1,32 @@
package mage.cards.m; package mage.cards.m;
import java.util.UUID;
import mage.MageInt; import mage.MageInt;
import mage.abilities.TriggeredAbilityImpl; import mage.abilities.Ability;
import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.effects.common.DestroyTargetEffect;
import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.CardType; import mage.constants.CardType;
import mage.constants.SubType; import mage.constants.SubType;
import mage.constants.Zone; import mage.constants.TargetController;
import mage.filter.common.FilterCreaturePermanent; import mage.filter.FilterPermanent;
import mage.filter.predicate.Predicates; import mage.filter.predicate.Predicates;
import mage.filter.predicate.permanent.ControllerIdPredicate; import mage.filter.predicate.permanent.ControllerIdPredicate;
import mage.game.Game; import mage.game.Game;
import mage.game.events.GameEvent;
import mage.players.Player;
import mage.target.Target; 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) * @author escplan9 (Derek Monturo - dmontur1 at gmail dot com)
*/ */
public final class MagusOfTheAbyss extends CardImpl { 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) { public MagusOfTheAbyss(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{B}"); super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{B}");
@ -33,7 +36,11 @@ public final class MagusOfTheAbyss extends CardImpl {
this.toughness = new MageInt(3); 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. // 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) { private MagusOfTheAbyss(final MagusOfTheAbyss card) {
@ -46,45 +53,24 @@ public final class MagusOfTheAbyss extends CardImpl {
} }
} }
class MagusOfTheAbyssTriggeredAbility extends TriggeredAbilityImpl { enum MagusOfTheAbyssTargetAdjuster implements TargetAdjuster {
instance;
MagusOfTheAbyssTriggeredAbility() { private static final FilterPermanent filter
super(Zone.BATTLEFIELD, new DestroyTargetEffect(true), false); = new FilterPermanent("nonartifact creature that player controls of their choice");
} static {
private MagusOfTheAbyssTriggeredAbility(final MagusOfTheAbyssTriggeredAbility ability) {
super(ability);
}
@Override
public MagusOfTheAbyssTriggeredAbility copy() {
return new MagusOfTheAbyssTriggeredAbility(this);
}
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.UPKEEP_STEP_PRE;
}
@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(Predicates.not(CardType.ARTIFACT.getPredicate()));
filter.add(new ControllerIdPredicate(player.getId())); filter.add(CardType.CREATURE.getPredicate());
Target target = new TargetCreaturePermanent(filter);
target.setAbilityController(getControllerId());
target.setTargetController(player.getId());
this.getTargets().clear();
this.getTargets().add(target);
return true;
} }
return false;
}
@Override @Override
public String getRule() { public void adjustTargets(Ability ability, Game game) {
return "At the beginning of each player's upkeep, destroy target nonartifact creature that player controls of their choice. It can't be regenerated."; UUID opponentId = ability.getEffects().get(0).getTargetPointer().getFirst(game, ability);
ability.getTargets().clear();
ability.getAllEffects().setTargetPointer(new FirstTargetPointer());
FilterPermanent adjustedFilter = filter.copy();
adjustedFilter.add(new ControllerIdPredicate(opponentId));
Target newTarget = new TargetPermanent(adjustedFilter);
newTarget.setTargetController(opponentId);
ability.addTarget(newTarget);
} }
} }

View file

@ -1,9 +1,9 @@
package mage.cards.m; package mage.cards.m;
import java.util.UUID;
import mage.MageInt; 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.DontUntapInControllersNextUntapStepTargetEffect;
import mage.abilities.effects.common.TapTargetEffect; import mage.abilities.effects.common.TapTargetEffect;
import mage.abilities.keyword.FlyingAbility; import mage.abilities.keyword.FlyingAbility;
@ -11,13 +11,10 @@ import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.CardType; import mage.constants.CardType;
import mage.constants.SubType; import mage.constants.SubType;
import mage.constants.Zone; import mage.target.common.TargetLandPermanent;
import mage.filter.common.FilterLandPermanent; import mage.target.targetadjustment.ThatPlayerControlsTargetAdjuster;
import mage.filter.predicate.permanent.ControllerIdPredicate;
import mage.game.Game; import java.util.UUID;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.target.TargetPermanent;
/** /**
* *
@ -34,7 +31,11 @@ public final class ManaSkimmer extends CardImpl {
// Flying // Flying
this.addAbility(FlyingAbility.getInstance()); 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. // 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) { private ManaSkimmer(final ManaSkimmer card) {
@ -46,44 +47,3 @@ public final class ManaSkimmer extends CardImpl {
return new ManaSkimmer(this); 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.";
}
}

View file

@ -1,31 +1,31 @@
package mage.cards.m; package mage.cards.m;
import java.util.UUID;
import mage.MageInt; import mage.MageInt;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect;
import mage.cards.Card;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.CardType; import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.Outcome; import mage.constants.Outcome;
import mage.constants.Zone; import mage.constants.SubType;
import mage.filter.common.FilterCreatureCard; import mage.filter.common.FilterCreatureCard;
import mage.filter.predicate.card.OwnerIdPredicate;
import mage.game.Game; import mage.game.Game;
import mage.players.Player; import mage.players.Player;
import mage.target.Target; import mage.target.Target;
import mage.target.common.TargetCardInGraveyard; import mage.target.common.TargetCardInYourGraveyard;
import mage.target.common.TargetOpponent; import mage.target.common.TargetOpponent;
import mage.target.targetadjustment.TargetAdjuster;
import java.util.UUID;
/** /**
* *
* @author LevelX2 * @author LevelX2
*/ */
public final class MausoleumTurnkey extends CardImpl { public final class MausoleumTurnkey extends CardImpl {
static FilterCreatureCard filter = new FilterCreatureCard("creature card of an opponent's choice");
public MausoleumTurnkey(UUID ownerId, CardSetInfo setInfo) { public MausoleumTurnkey(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}"); super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}");
@ -36,8 +36,10 @@ public final class MausoleumTurnkey extends CardImpl {
this.toughness = new MageInt(2); 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. // 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) { private MausoleumTurnkey(final MausoleumTurnkey card) {
@ -50,50 +52,27 @@ public final class MausoleumTurnkey extends CardImpl {
} }
} }
class MausoleumTurnkeyEffect extends OneShotEffect { // Exact copy of KarplusanMinotaurAdjuster
enum MausoleumTurnkeyAdjuster implements TargetAdjuster {
MausoleumTurnkeyEffect() { instance;
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);
}
@Override @Override
public MausoleumTurnkeyEffect copy() { public void adjustTargets(Ability ability, Game game) {
return new MausoleumTurnkeyEffect(this); Player controller = game.getPlayer(ability.getControllerId());
if (controller == null) {
return;
} }
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
if (controller != null) {
UUID opponentId = null; UUID opponentId = null;
if (game.getOpponents(controller.getId()).size() > 1) { if (game.getOpponents(controller.getId()).size() > 1) {
Target target = new TargetOpponent(true); Target target = new TargetOpponent(true);
if (controller.chooseTarget(outcome, target, source, game)) { if (controller.chooseTarget(Outcome.Neutral, target, ability, game)) {
opponentId = target.getFirstTarget(); opponentId = target.getFirstTarget();
} }
} else { } else {
opponentId = game.getOpponents(controller.getId()).iterator().next(); opponentId = game.getOpponents(controller.getId()).iterator().next();
} }
if (opponentId != null) { if (opponentId != null) {
Player opponent = game.getPlayer(opponentId); ability.getTargets().get(0).setTargetController(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;
}
return false;
}
} }

View file

@ -1,31 +1,30 @@
package mage.cards.n; package mage.cards.n;
import java.util.UUID;
import mage.MageInt; import mage.MageInt;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.common.AttacksAndIsNotBlockedTriggeredAbility; import mage.abilities.common.AttacksAndIsNotBlockedTriggeredAbility;
import mage.abilities.costs.common.SacrificeSourceCost; import mage.abilities.costs.common.SacrificeSourceCost;
import mage.abilities.effects.Effect;
import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.effects.common.DestroyTargetEffect;
import mage.abilities.effects.common.DoIfCostPaid; import mage.abilities.effects.common.DoIfCostPaid;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.CardType; import mage.constants.CardType;
import mage.constants.SetTargetPointer;
import mage.constants.SubType; import mage.constants.SubType;
import mage.filter.FilterPermanent; import mage.filter.FilterPermanent;
import mage.filter.common.FilterCreaturePermanent; 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.TargetPermanent;
import mage.target.targetadjustment.ThatPlayerControlsTargetAdjuster;
import java.util.UUID;
/** /**
* *
* @author MarcoMarin * @author MarcoMarin
*/ */
public final class Necrite extends CardImpl { public final class Necrite extends CardImpl {
static FilterPermanent filter = new FilterCreaturePermanent("creature defending player controls");
public Necrite(UUID ownerId, CardSetInfo setInfo) { public Necrite(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{B}{B}"); 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. // 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?"); 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 AttacksAndIsNotBlockedTriggeredAbility(effect, false, SetTargetPointer.PLAYER);
Ability ability = new NecriteTriggeredAbility(effect); ability.addTarget(new TargetPermanent(filter));
ability.setTargetAdjuster(new ThatPlayerControlsTargetAdjuster());
this.addAbility(ability); this.addAbility(ability);
} }
private Necrite(final Necrite card) { private Necrite(final Necrite card) {
@ -50,33 +49,3 @@ public final class Necrite extends CardImpl {
return new Necrite(this); 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;
}
}

View file

@ -20,8 +20,10 @@ public final class NestOfScarabs extends CardImpl {
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{B}"); 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. // 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")) this.addAbility(new PutCounterOnCreatureTriggeredAbility(
.setText("create that many 1/1 black Insect creature tokens"), CounterType.M1M1.createInstance())); new CreateTokenEffect(new NestOfScarabsBlackInsectToken(),
new EffectKeyValue("countersAdded", "that many")),
CounterType.M1M1.createInstance()));
} }
private NestOfScarabs(final NestOfScarabs card) { private NestOfScarabs(final NestOfScarabs card) {

View file

@ -1,22 +1,26 @@
package mage.cards.o; package mage.cards.o;
import mage.MageInt; import mage.MageInt;
import mage.abilities.TriggeredAbilityImpl; import mage.abilities.Ability;
import mage.abilities.effects.Effect; import mage.abilities.TriggeredAbility;
import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility;
import mage.abilities.condition.Condition;
import mage.abilities.effects.common.ExileTargetEffect; import mage.abilities.effects.common.ExileTargetEffect;
import mage.abilities.keyword.FlyingAbility; import mage.abilities.keyword.FlyingAbility;
import mage.abilities.keyword.TrampleAbility; import mage.abilities.keyword.TrampleAbility;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; 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.FilterPermanent;
import mage.filter.common.FilterNonlandPermanent; import mage.filter.common.FilterNonlandPermanent;
import mage.filter.predicate.permanent.ControllerIdPredicate;
import mage.game.Game; import mage.game.Game;
import mage.game.events.DamagedPlayerEvent;
import mage.game.events.GameEvent; import mage.game.events.GameEvent;
import mage.players.Player;
import mage.target.TargetPermanent; import mage.target.TargetPermanent;
import mage.target.targetadjustment.ThatPlayerControlsTargetAdjuster;
import mage.util.CardUtil;
import mage.watchers.Watcher; import mage.watchers.Watcher;
import java.util.*; import java.util.*;
@ -25,6 +29,7 @@ import java.util.*;
* @author TheElk801 * @author TheElk801
*/ */
public final class OKagachiVengefulKami extends CardImpl { public final class OKagachiVengefulKami extends CardImpl {
static FilterPermanent filter = new FilterNonlandPermanent("nonland permanent that player controls");
public OKagachiVengefulKami(UUID ownerId, CardSetInfo setInfo) { public OKagachiVengefulKami(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}{U}{B}{R}{G}"); 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()); 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 // 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) { private OKagachiVengefulKami(final OKagachiVengefulKami card) {
@ -55,66 +64,25 @@ public final class OKagachiVengefulKami extends CardImpl {
} }
} }
class OKagachiVengefulKamiTriggeredAbility extends TriggeredAbilityImpl { enum KagachiVengefulKamiCondition implements Condition {
instance;
OKagachiVengefulKamiTriggeredAbility() {
super(Zone.BATTLEFIELD, new ExileTargetEffect(), false);
this.addWatcher(new OKagachiVengefulKamiWatcher());
}
private OKagachiVengefulKamiTriggeredAbility(final OKagachiVengefulKamiTriggeredAbility ability) {
super(ability);
}
@Override @Override
public OKagachiVengefulKamiTriggeredAbility copy() { public boolean apply(Game game, Ability source) {
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) {
OKagachiVengefulKamiWatcher watcher = game.getState().getWatcher(OKagachiVengefulKamiWatcher.class); OKagachiVengefulKamiWatcher watcher = game.getState().getWatcher(OKagachiVengefulKamiWatcher.class);
if (watcher == null) { if (watcher == null) {
return false; return false;
} }
UUID playerId = null; return CardUtil.getEffectValueFromAbility(source, "damagedPlayer", UUID.class)
for (Effect effect : this.getEffects()) { .filter(uuid -> watcher.checkPlayer(source.getControllerId(), uuid))
Object obj = effect.getValue("damagedPlayer"); .isPresent();
if (obj instanceof UUID) {
playerId = (UUID) obj;
break;
}
}
return watcher.checkPlayer(getControllerId(), playerId);
} }
@Override @Override
public String getRule() { public String toString() {
return "Whenever {this} deals combat damage to a player, " + return "if that player attacked you during their last turn";
"if that player attacked you during their last turn, " +
"exile target nonland permanent that player controls.";
} }
} }
class OKagachiVengefulKamiWatcher extends Watcher { class OKagachiVengefulKamiWatcher extends Watcher {

View file

@ -1,19 +1,25 @@
package mage.cards.o; package mage.cards.o;
import mage.MageInt; import mage.MageInt;
import mage.MageObject; import mage.abilities.Ability;
import mage.abilities.TriggeredAbilityImpl; import mage.abilities.common.DiesThisOrAnotherTriggeredAbility;
import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect; import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect;
import mage.abilities.keyword.LifelinkAbility; import mage.abilities.keyword.LifelinkAbility;
import mage.cards.Card;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; 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.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.Game;
import mage.game.events.GameEvent; import mage.game.permanent.Permanent;
import mage.game.events.ZoneChangeEvent;
import mage.target.common.TargetCardInYourGraveyard; import mage.target.common.TargetCardInYourGraveyard;
import mage.util.CardUtil;
import java.util.UUID; import java.util.UUID;
@ -21,6 +27,13 @@ import java.util.UUID;
* @author TheElk801 * @author TheElk801
*/ */
public final class OrahSkyclaveHierophant extends CardImpl { 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) { public OrahSkyclaveHierophant(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}{B}"); super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}{B}");
@ -35,7 +48,9 @@ public final class OrahSkyclaveHierophant extends CardImpl {
this.addAbility(LifelinkAbility.getInstance()); 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. // 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) { private OrahSkyclaveHierophant(final OrahSkyclaveHierophant card) {
@ -48,57 +63,15 @@ public final class OrahSkyclaveHierophant extends CardImpl {
} }
} }
class OrahSkyclaveHierophantTriggeredAbility extends TriggeredAbilityImpl { enum OrahSkyclaveHierophantPredicate implements ObjectSourcePlayerPredicate<Card> {
instance;
OrahSkyclaveHierophantTriggeredAbility() {
super(Zone.BATTLEFIELD, new ReturnFromGraveyardToBattlefieldTargetEffect());
setLeavesTheBattlefieldTrigger(true);
}
private OrahSkyclaveHierophantTriggeredAbility(final OrahSkyclaveHierophantTriggeredAbility ability) {
super(ability);
}
@Override @Override
public boolean checkEventType(GameEvent event, Game game) { public boolean apply(ObjectSourcePlayer<Card> input, Game game) {
return event.getType() == GameEvent.EventType.ZONE_CHANGE; return CardUtil.getEffectValueFromAbility(
} input.getSource(), "creatureDied", Permanent.class
)
@Override .filter(permanent -> input.getObject().getManaValue() < permanent.getManaValue())
public boolean checkTrigger(GameEvent event, Game game) { .isPresent();
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);
} }
} }

View file

@ -7,20 +7,14 @@ import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.costs.common.SacrificeSourceCost; import mage.abilities.costs.common.SacrificeSourceCost;
import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.common.TapSourceCost;
import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.common.DestroyTargetEffect;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.CardType; import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.SubType; 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.target.common.TargetLandPermanent;
import mage.util.CardUtil; import mage.target.targetadjustment.XTargetsCountAdjuster;
import java.util.List;
import java.util.UUID; import java.util.UUID;
/** /**
@ -38,9 +32,12 @@ public final class OrcishSettlers extends CardImpl {
this.toughness = new MageInt(1); this.toughness = new MageInt(1);
// {X}{X}{R}, {tap}, Sacrifice Orcish Settlers: Destroy X target lands. // {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 TapSourceCost());
ability.addCost(new SacrificeSourceCost()); ability.addCost(new SacrificeSourceCost());
ability.addTarget(new TargetLandPermanent());
ability.setTargetAdjuster(new XTargetsCountAdjuster());
this.addAbility(ability); this.addAbility(ability);
} }
@ -53,43 +50,3 @@ public final class OrcishSettlers extends CardImpl {
return new OrcishSettlers(this); 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<UUID> targets = target.getTargets();
targets.forEach((landId) -> {
Permanent land = game.getPermanent(landId);
if (land != null) {
land.destroy(source, game, false);
}
});
return true;
}
return false;
}
}

View file

@ -22,6 +22,7 @@ import mage.game.events.GameEvent;
import mage.game.permanent.Permanent; import mage.game.permanent.Permanent;
import mage.game.stack.StackObject; import mage.game.stack.StackObject;
import mage.target.TargetPermanent; import mage.target.TargetPermanent;
import mage.target.targetadjustment.DefineByTriggerTargetAdjuster;
import java.util.UUID; import java.util.UUID;
@ -65,6 +66,7 @@ class PawpatchRecruitTriggeredAbility extends TriggeredAbilityImpl {
public PawpatchRecruitTriggeredAbility() { public PawpatchRecruitTriggeredAbility() {
super(Zone.BATTLEFIELD, new AddCountersTargetEffect(CounterType.P1P1.createInstance()), false); super(Zone.BATTLEFIELD, new AddCountersTargetEffect(CounterType.P1P1.createInstance()), false);
this.setTargetAdjuster(DefineByTriggerTargetAdjuster.instance);
} }
private PawpatchRecruitTriggeredAbility(final PawpatchRecruitTriggeredAbility ability) { private PawpatchRecruitTriggeredAbility(final PawpatchRecruitTriggeredAbility ability) {

View file

@ -1,7 +1,8 @@
package mage.cards.r; package mage.cards.r;
import mage.MageInt; import mage.MageInt;
import mage.abilities.TriggeredAbilityImpl; import mage.abilities.Ability;
import mage.abilities.common.DealsDamageToOpponentTriggeredAbility;
import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.dynamicvalue.common.SavedDamageValue; import mage.abilities.dynamicvalue.common.SavedDamageValue;
@ -14,16 +15,15 @@ import mage.cards.CardSetInfo;
import mage.constants.CardType; import mage.constants.CardType;
import mage.constants.Duration; import mage.constants.Duration;
import mage.constants.SubType; import mage.constants.SubType;
import mage.constants.Zone;
import mage.filter.FilterPermanent; import mage.filter.FilterPermanent;
import mage.filter.predicate.Predicates; import mage.filter.predicate.Predicates;
import mage.filter.predicate.permanent.ControllerIdPredicate; import mage.filter.predicate.permanent.ControllerIdPredicate;
import mage.filter.predicate.permanent.ProtectorIdPredicate; import mage.filter.predicate.permanent.ProtectorIdPredicate;
import mage.game.Game; import mage.game.Game;
import mage.game.events.DamagedEvent;
import mage.game.events.GameEvent;
import mage.players.Player; import mage.players.Player;
import mage.target.TargetPermanent; import mage.target.TargetPermanent;
import mage.target.targetadjustment.TargetAdjuster;
import mage.target.targetpointer.FirstTargetPointer;
import java.util.UUID; 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. // 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) { private RampagingRaptor(final RampagingRaptor card) {
@ -64,33 +67,17 @@ public final class RampagingRaptor extends CardImpl {
} }
} }
class RampagingRaptorTriggeredAbility extends TriggeredAbilityImpl { enum RampagingRaptorTargetAdjuster implements TargetAdjuster {
instance;
RampagingRaptorTriggeredAbility() {
super(Zone.BATTLEFIELD, new DamageTargetEffect(SavedDamageValue.MUCH), false);
}
private RampagingRaptorTriggeredAbility(final RampagingRaptorTriggeredAbility ability) {
super(ability);
}
@Override @Override
public RampagingRaptorTriggeredAbility copy() { public void adjustTargets(Ability ability, Game game) {
return new RampagingRaptorTriggeredAbility(this); UUID opponentId = ability.getEffects().get(0).getTargetPointer().getFirst(game, ability);
} Player opponent = game.getPlayer(opponentId);
ability.getTargets().clear();
@Override ability.getAllEffects().setTargetPointer(new FirstTargetPointer());
public boolean checkEventType(GameEvent event, Game game) { if (opponent == null) {
return event.getType() == GameEvent.EventType.DAMAGED_PLAYER; return;
}
@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;
} }
FilterPermanent filter = new FilterPermanent( FilterPermanent filter = new FilterPermanent(
"planeswalker " + opponent.getLogName() + " controls " + "planeswalker " + opponent.getLogName() + " controls " +
@ -106,15 +93,6 @@ class RampagingRaptorTriggeredAbility extends TriggeredAbilityImpl {
new ProtectorIdPredicate(opponent.getId()) new ProtectorIdPredicate(opponent.getId())
) )
)); ));
this.getEffects().setValue("damage", event.getAmount()); ability.addTarget(new TargetPermanent(filter));
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.";
} }
} }

View file

@ -1,22 +1,21 @@
package mage.cards.r; package mage.cards.r;
import mage.MageInt; 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.costs.mana.ManaCostsImpl;
import mage.abilities.dynamicvalue.common.EffectKeyValue;
import mage.abilities.effects.common.DestroyTargetEffect; import mage.abilities.effects.common.DestroyTargetEffect;
import mage.abilities.hint.StaticHint;
import mage.abilities.keyword.CyclingAbility; import mage.abilities.keyword.CyclingAbility;
import mage.abilities.keyword.TrampleAbility; import mage.abilities.keyword.TrampleAbility;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.CardType; import mage.constants.CardType;
import mage.constants.SubType; import mage.constants.SubType;
import mage.constants.Zone; import mage.filter.FilterPermanent;
import mage.game.Game; import mage.filter.common.FilterArtifactPermanent;
import mage.game.events.GameEvent; import mage.target.TargetPermanent;
import mage.game.stack.StackObject; import mage.target.targetadjustment.TargetsCountAdjuster;
import mage.target.common.TargetArtifactPermanent;
import mage.util.CardUtil;
import java.util.UUID; import java.util.UUID;
@ -24,6 +23,7 @@ import java.util.UUID;
* @author Susucr * @author Susucr
*/ */
public final class RampagingWarMammoth extends CardImpl { public final class RampagingWarMammoth extends CardImpl {
static FilterPermanent filter = new FilterArtifactPermanent("up to X target artifacts");
public RampagingWarMammoth(UUID ownerId, CardSetInfo setInfo) { public RampagingWarMammoth(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{R}{R}"); 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}"))); this.addAbility(new CyclingAbility(new ManaCostsImpl<>("{X}{2}{R}")));
// When you cycle Rampaging War Mammoth, destroy up to X target artifacts. // 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) { private RampagingWarMammoth(final RampagingWarMammoth card) {
@ -51,56 +54,3 @@ public final class RampagingWarMammoth extends CardImpl {
return new RampagingWarMammoth(this); 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.";
}
}

View file

@ -3,6 +3,8 @@ package mage.cards.r;
import mage.MageInt; import mage.MageInt;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.effects.Effect;
import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.DamageMultiEffect; import mage.abilities.effects.common.DamageMultiEffect;
import mage.abilities.keyword.DevourAbility; import mage.abilities.keyword.DevourAbility;
@ -12,10 +14,10 @@ import mage.constants.CardType;
import mage.constants.Outcome; import mage.constants.Outcome;
import mage.constants.SubType; import mage.constants.SubType;
import mage.game.Game; import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent; import mage.game.permanent.Permanent;
import mage.target.Target; import mage.target.Target;
import mage.target.common.TargetCreaturePermanentAmount; import mage.target.common.TargetCreaturePermanentAmount;
import mage.util.CardUtil;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
@ -39,7 +41,11 @@ public final class RavenousGigantotherium extends CardImpl {
this.addAbility(new DevourAbility(3)); 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. // 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) { 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() { @Override
super(null, false); public int calculate(Game game, Ability sourceAbility, Effect effect) {
} return CardUtil.getEffectValueFromAbility(sourceAbility, "permanentEnteredBattlefield", Permanent.class)
.map(permanent -> permanent.getPower().getValue()).orElse(0);
private RavenousGigantotheriumAbility(final RavenousGigantotheriumAbility ability) {
super(ability);
} }
@Override @Override
public boolean checkTrigger(GameEvent event, Game game) { public DynamicValue copy() {
if (!super.checkTrigger(event, game)) { return instance;
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;
} }
@Override @Override
public String getRule() { public String getMessage() {
return "When {this} enters, it deals X damage " + return "its power";
"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}.";
} }
@Override @Override
public RavenousGigantotheriumAbility copy() { public String toString() {
return new RavenousGigantotheriumAbility(this); return "X";
} }
} }
@ -100,6 +87,7 @@ class RavenousGigantotheriumEffect extends OneShotEffect {
RavenousGigantotheriumEffect() { RavenousGigantotheriumEffect() {
super(Outcome.Benefit); super(Outcome.Benefit);
this.setText("Each of those creatures deals damage equal to its power to {this}.");
} }
private RavenousGigantotheriumEffect(final RavenousGigantotheriumEffect effect) { private RavenousGigantotheriumEffect(final RavenousGigantotheriumEffect effect) {

View file

@ -3,22 +3,21 @@ package mage.cards.r;
import mage.MageInt; import mage.MageInt;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.common.DiesSourceTriggeredAbility; import mage.abilities.common.DiesSourceTriggeredAbility;
import mage.abilities.costs.common.DynamicValueGenericManaCost;
import mage.abilities.costs.mana.ManaCostsImpl; 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.DoIfCostPaid;
import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect;
import mage.abilities.effects.common.ReturnToHandSourceEffect; import mage.abilities.effects.common.ReturnToHandSourceEffect;
import mage.abilities.keyword.MenaceAbility; import mage.abilities.keyword.MenaceAbility;
import mage.cards.Card;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.*; import mage.constants.CardType;
import mage.constants.SubType;
import mage.filter.FilterCard; import mage.filter.FilterCard;
import mage.filter.common.FilterCreatureCard;
import mage.filter.predicate.mageobject.AnotherPredicate; 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.target.common.TargetCardInYourGraveyard;
import mage.util.ManaUtil;
import java.util.UUID; import java.util.UUID;
@ -26,6 +25,11 @@ import java.util.UUID;
* @author LevelX2 * @author LevelX2
*/ */
public final class RelentlessDead extends CardImpl { 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) { public RelentlessDead(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{B}{B}"); 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}")))); 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. // 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) { private RelentlessDead(final RelentlessDead card) {
@ -52,46 +60,3 @@ public final class RelentlessDead extends CardImpl {
return new RelentlessDead(this); 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;
}
}

View file

@ -1,25 +1,20 @@
package mage.cards.s; package mage.cards.s;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.common.CycleTriggeredAbility;
import mage.abilities.common.SpellCastControllerTriggeredAbility; import mage.abilities.common.SpellCastControllerTriggeredAbility;
import mage.abilities.common.ZoneChangeTriggeredAbility;
import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.CreateTokenEffect;
import mage.abilities.keyword.CyclingAbility; import mage.abilities.keyword.CyclingAbility;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.CardType; import mage.constants.CardType;
import mage.constants.Outcome; import mage.constants.Outcome;
import mage.constants.SetTargetPointer; import mage.constants.SetTargetPointer;
import mage.constants.Zone;
import mage.filter.StaticFilters; import mage.filter.StaticFilters;
import mage.game.Game; import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.permanent.token.SharkToken; import mage.game.permanent.token.SharkToken;
import mage.game.stack.Spell; import mage.game.stack.Spell;
import mage.game.stack.StackObject;
import mage.util.CardUtil;
import java.util.UUID; import java.util.UUID;
@ -42,7 +37,7 @@ public final class SharkTyphoon extends CardImpl {
this.addAbility(new CyclingAbility(new ManaCostsImpl<>("{X}{1}{U}"))); this.addAbility(new CyclingAbility(new ManaCostsImpl<>("{X}{1}{U}")));
// When you cycle Shark Typhoon, create an X/X blue Shark creature token with flying. // 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) { private SharkTyphoon(final SharkTyphoon card) {
@ -82,42 +77,29 @@ class SharkTyphoonCastEffect extends OneShotEffect {
} }
} }
class SharkTyphoonTriggeredAbility extends ZoneChangeTriggeredAbility { class SharkTyphoonCycleEffect extends OneShotEffect {
SharkTyphoonTriggeredAbility() { SharkTyphoonCycleEffect() {
super(Zone.ALL, null, "", false); super(Outcome.Benefit);
staticText = "create an X/X blue Shark creature token with flying";
} }
private SharkTyphoonTriggeredAbility(SharkTyphoonTriggeredAbility ability) { private SharkTyphoonCycleEffect(final SharkTyphoonCycleEffect effect) {
super(ability); super(effect);
} }
@Override @Override
public boolean checkEventType(GameEvent event, Game game) { public SharkTyphoonCycleEffect copy() {
return event.getType() == GameEvent.EventType.ACTIVATED_ABILITY; return new SharkTyphoonCycleEffect(this);
} }
@Override @Override
public boolean checkTrigger(GameEvent event, Game game) { public boolean apply(Game game, Ability source) {
if (!event.getSourceId().equals(this.getSourceId())) { Object xValueObject = getValue("cycleXValue");
return false; int xValue = 0;
if (xValueObject instanceof Integer) {
xValue = (int) xValueObject;
} }
StackObject object = game.getStack().getStackObject(event.getSourceId()); return new SharkToken(xValue).putOntoBattlefield(1, game, source, source.getControllerId());
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.";
} }
} }

View file

@ -1,8 +1,9 @@
package mage.cards.v; package mage.cards.v;
import mage.MageInt; import mage.MageInt;
import mage.abilities.TriggeredAbilityImpl; import mage.abilities.common.CycleTriggeredAbility;
import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.dynamicvalue.common.EffectKeyValue;
import mage.abilities.effects.common.CreateTokenEffect; import mage.abilities.effects.common.CreateTokenEffect;
import mage.abilities.keyword.*; import mage.abilities.keyword.*;
import mage.cards.CardImpl; import mage.cards.CardImpl;
@ -10,12 +11,7 @@ import mage.cards.CardSetInfo;
import mage.constants.CardType; import mage.constants.CardType;
import mage.constants.SubType; import mage.constants.SubType;
import mage.constants.SuperType; 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.permanent.token.PilotSaddleCrewToken;
import mage.game.stack.StackObject;
import mage.util.CardUtil;
import java.util.UUID; import java.util.UUID;
@ -48,7 +44,8 @@ public final class ValorsFlagship extends CardImpl {
this.addAbility(new CyclingAbility(new ManaCostsImpl<>("{X}{2}{W}"))); 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." // 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) { private ValorsFlagship(final ValorsFlagship card) {
@ -60,45 +57,3 @@ public final class ValorsFlagship extends CardImpl {
return new ValorsFlagship(this); 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.\"";
}
}

View file

@ -61,6 +61,24 @@ public class CyclingTest extends CardTestPlayerBase {
assertPowerToughness(playerB, "Pillarfield Ox", 0, 2); 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. * Cycle from graveyard or battlefield should not work.
*/ */

View file

@ -6,6 +6,7 @@ import mage.constants.Zone;
import mage.game.Game; import mage.game.Game;
import mage.game.events.GameEvent; import mage.game.events.GameEvent;
import mage.game.stack.StackObject; import mage.game.stack.StackObject;
import mage.util.CardUtil;
/** /**
* @author Plopman * @author Plopman
@ -40,6 +41,7 @@ public class CycleTriggeredAbility extends ZoneChangeTriggeredAbility {
return false; return false;
} }
this.getEffects().setValue("cycleCosts", object.getStackAbility().getCosts()); this.getEffects().setValue("cycleCosts", object.getStackAbility().getCosts());
this.getEffects().setValue("cycleXValue", CardUtil.getSourceCostsTag(game, object.getStackAbility(), "X", 0));
return true; return true;
} }

View file

@ -10,8 +10,8 @@ import mage.game.Game;
* @author stravant * @author stravant
*/ */
public class EffectKeyValue implements DynamicValue { public class EffectKeyValue implements DynamicValue {
private String key; private final String key;
private String description; private final String description;
public EffectKeyValue(String key) { public EffectKeyValue(String key) {
this.key = key; this.key = key;
@ -19,7 +19,7 @@ public class EffectKeyValue implements DynamicValue {
} }
public EffectKeyValue(String key, String description) { public EffectKeyValue(String key, String description) {
this(key); this.key = key;
this.description = description; this.description = description;
} }
@ -35,11 +35,11 @@ public class EffectKeyValue implements DynamicValue {
@Override @Override
public String toString() { public String toString() {
return "equal to"; return description;
} }
@Override @Override
public String getMessage() { public String getMessage() {
return description; return "";
} }
} }

View file

@ -111,7 +111,7 @@ public abstract class TargetImpl implements Target {
if (min > 0 && max == Integer.MAX_VALUE) { if (min > 0 && max == Integer.MAX_VALUE) {
sb.append(CardUtil.numberToText(min)); sb.append(CardUtil.numberToText(min));
sb.append(" or more "); 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" targetName = targetName.replace("another", "other"); //If non-singular, use "other" instead of "another"
if (getUseAnyNumber()) { if (getUseAnyNumber()) {