Improve GaleWaterdeepProdigy targeting, Genericize KamiOfCelebration and SunbirdsInvocation

This commit is contained in:
Steven Knipe 2025-06-17 23:46:06 -07:00
parent fbdff2e49c
commit a7258604c6
3 changed files with 43 additions and 134 deletions

View file

@ -1,22 +1,20 @@
package mage.cards.g;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.ChooseABackgroundAbility;
import mage.abilities.common.SpellCastControllerTriggeredAbility;
import mage.abilities.effects.common.MayCastTargetCardEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.SuperType;
import mage.constants.*;
import mage.filter.FilterCard;
import mage.filter.common.FilterInstantOrSorcerySpell;
import mage.filter.StaticFilters;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.stack.Spell;
import mage.target.common.TargetCardInYourGraveyard;
import mage.target.targetadjustment.DefineByTriggerTargetAdjuster;
import mage.watchers.common.CastFromHandWatcher;
import mage.target.targetadjustment.TargetAdjuster;
import mage.target.targetpointer.FirstTargetPointer;
import java.util.UUID;
@ -37,7 +35,14 @@ public final class GaleWaterdeepProdigy extends CardImpl {
// Whenever you cast an instant or sorcery spell from your hand,
// 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.
this.addAbility(new GaleWaterdeepProdigyTriggeredAbility());
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."),
StaticFilters.FILTER_SPELL_AN_INSTANT_OR_SORCERY,
false, SetTargetPointer.SPELL, Zone.HAND
);
ability.setTargetAdjuster(GaleWaterdeepProdigyAdjuster.instance);
this.addAbility(ability);
// Choose a Background
this.addAbility(ChooseABackgroundAbility.getInstance());
@ -53,7 +58,8 @@ public final class GaleWaterdeepProdigy extends CardImpl {
}
}
class GaleWaterdeepProdigyTriggeredAbility extends SpellCastControllerTriggeredAbility {
enum GaleWaterdeepProdigyAdjuster implements TargetAdjuster {
instance;
private static final FilterCard SORCERY_FILTER = new FilterCard("a sorcery card in your graveyard");
private static final FilterCard INSTANT_FILTER = new FilterCard("an instant card in your graveyard");
@ -62,52 +68,22 @@ class GaleWaterdeepProdigyTriggeredAbility extends SpellCastControllerTriggeredA
SORCERY_FILTER.add(CardType.SORCERY.getPredicate());
INSTANT_FILTER.add(CardType.INSTANT.getPredicate());
}
public GaleWaterdeepProdigyTriggeredAbility() {
super(
new MayCastTargetCardEffect(true)
.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."),
new FilterInstantOrSorcerySpell("an instant or sorcery spell from your hand"),
false
);
addWatcher(new CastFromHandWatcher());
setTargetAdjuster(DefineByTriggerTargetAdjuster.instance);
}
private GaleWaterdeepProdigyTriggeredAbility(final GaleWaterdeepProdigyTriggeredAbility ability) {
super(ability);
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
if (!super.checkTrigger(event, game)) {
return false;
}
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());
CastFromHandWatcher watcher = game.getState().getWatcher(CastFromHandWatcher.class);
if (watcher == null || !watcher.spellWasCastFromHand(event.getSourceId())) {
return false;
}
Spell spell = game.getState().getStack().getSpell(event.getSourceId());
Spell spell = game.getSpellOrLKIStack(spellId);
if (spell == null) {
return false;
return;
}
FilterCard filterCard;
FilterCard filter;
if (spell.isSorcery(game)) {
filterCard = INSTANT_FILTER;
filter = INSTANT_FILTER;
} else {
filterCard = SORCERY_FILTER;
filter = SORCERY_FILTER;
}
this.getTargets().clear();
this.getTargets().add(new TargetCardInYourGraveyard(filterCard));
return true;
ability.addTarget(new TargetCardInYourGraveyard(filter));
}
@Override
public GaleWaterdeepProdigyTriggeredAbility copy() {
return new GaleWaterdeepProdigyTriggeredAbility(this);
}
}
}

View file

@ -1,21 +1,17 @@
package mage.cards.k;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.AttacksCreatureYouControlTriggeredAbility;
import mage.abilities.common.SpellCastControllerTriggeredAbility;
import mage.abilities.effects.common.ExileTopXMayPlayUntilEffect;
import mage.abilities.effects.common.counter.AddCountersTargetEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.SubType;
import mage.constants.Zone;
import mage.constants.*;
import mage.counters.CounterType;
import mage.filter.common.FilterControlledCreaturePermanent;
import mage.filter.predicate.permanent.ModifiedPredicate;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.target.common.TargetControlledCreaturePermanent;
import java.util.UUID;
@ -45,7 +41,12 @@ public final class KamiOfCelebration extends CardImpl {
));
// Whenever you cast a spell from exile, put a +1/+1 counter on target creature you control.
this.addAbility(new KamiOfCelebrationAbility());
Ability ability = new SpellCastControllerTriggeredAbility(
Zone.BATTLEFIELD, new AddCountersTargetEffect(CounterType.P1P1.createInstance()), null,
false, SetTargetPointer.NONE, Zone.EXILED
);
ability.addTarget(new TargetControlledCreaturePermanent());
this.addAbility(ability);
}
private KamiOfCelebration(final KamiOfCelebration card) {
@ -57,30 +58,3 @@ public final class KamiOfCelebration extends CardImpl {
return new KamiOfCelebration(this);
}
}
class KamiOfCelebrationAbility extends SpellCastControllerTriggeredAbility {
KamiOfCelebrationAbility() {
super(new AddCountersTargetEffect(CounterType.P1P1.createInstance()), false);
this.addTarget(new TargetControlledCreaturePermanent());
}
private KamiOfCelebrationAbility(final KamiOfCelebrationAbility ability) {
super(ability);
}
@Override
public KamiOfCelebrationAbility copy() {
return new KamiOfCelebrationAbility(this);
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
return event.getZone() == Zone.EXILED && super.checkTrigger(event, game);
}
@Override
public String getRule() {
return "Whenever you cast a spell from exile, put a +1/+1 counter on target creature you control.";
}
}

View file

@ -2,23 +2,17 @@ package mage.cards.s;
import mage.abilities.Ability;
import mage.abilities.common.SpellCastControllerTriggeredAbility;
import mage.abilities.effects.Effect;
import mage.abilities.effects.OneShotEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.cards.Cards;
import mage.cards.CardsImpl;
import mage.constants.CardType;
import mage.constants.ComparisonType;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.constants.*;
import mage.filter.FilterCard;
import mage.filter.predicate.mageobject.ManaValuePredicate;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.stack.Spell;
import mage.players.Player;
import mage.target.targetpointer.FixedTarget;
import mage.util.CardUtil;
import java.util.UUID;
@ -35,7 +29,10 @@ public final class SunbirdsInvocation extends CardImpl {
// where X is that spell's converted mana cost. You may cast a card revealed this
// way with converted mana cost X or less without paying its mana cost. Put the
// rest on the bottom of your library in a random order.
this.addAbility(new SunbirdsInvocationTriggeredAbility());
this.addAbility(new SpellCastControllerTriggeredAbility(
Zone.BATTLEFIELD, new SunbirdsInvocationEffect(), null,
false, SetTargetPointer.SPELL, Zone.HAND
));
}
private SunbirdsInvocation(final SunbirdsInvocation card) {
@ -48,52 +45,14 @@ public final class SunbirdsInvocation extends CardImpl {
}
}
class SunbirdsInvocationTriggeredAbility extends SpellCastControllerTriggeredAbility {
SunbirdsInvocationTriggeredAbility() {
super(new SunbirdsInvocationEffect(), false);
}
private SunbirdsInvocationTriggeredAbility(SunbirdsInvocationTriggeredAbility ability) {
super(ability);
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
if (event.getPlayerId().equals(getControllerId())) {
Spell spell = game.getStack().getSpell(event.getTargetId());
if (spell != null
&& spell.getFromZone() == Zone.HAND
&& spell.isOwnedBy(getControllerId())) { // must be from the controller's hand
if (spell.getCard() != null) {
for (Effect effect : getEffects()) {
effect.setTargetPointer(new FixedTarget(spell.getId()));
}
return true;
}
}
}
return false;
}
@Override
public SunbirdsInvocationTriggeredAbility copy() {
return new SunbirdsInvocationTriggeredAbility(this);
}
@Override
public String getRule() {
return "Whenever you cast a spell from your hand, reveal the top X cards of your library, " +
"where X is that spell's mana value. You may cast a spell with mana value X or less " +
"from among cards revealed this way without paying its mana cost. " +
"Put the rest on the bottom of your library in a random order.";
}
}
class SunbirdsInvocationEffect extends OneShotEffect {
SunbirdsInvocationEffect() {
super(Outcome.PutCardInPlay);
setText("reveal the top X cards of your library, where X is that spell's mana value. " +
"You may cast a spell with mana value X or less " +
"from among cards revealed this way without paying its mana cost. " +
"Put the rest on the bottom of your library in a random order.");
}
private SunbirdsInvocationEffect(final SunbirdsInvocationEffect effect) {