[J22] Implement Preston, the Vanisher (#9860)

This commit is contained in:
Alexander Novotny 2023-02-28 06:39:02 -08:00 committed by GitHub
parent eaa001aa14
commit 242490373e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 214 additions and 27 deletions

View file

@ -14,7 +14,7 @@ import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.events.ZoneChangeEvent;
import mage.players.Player;
import mage.watchers.common.CreatureWasCastWatcher;
import mage.watchers.common.PermanentWasCastWatcher;
import java.util.UUID;
@ -34,7 +34,8 @@ public final class ContainmentPriest extends CardImpl {
// Flash
this.addAbility(FlashAbility.getInstance());
// If a nontoken creature would enter the battlefield and it wasn't cast, exile it instead.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ContainmentPriestReplacementEffect()), new CreatureWasCastWatcher());
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ContainmentPriestReplacementEffect()),
new PermanentWasCastWatcher());
}
private ContainmentPriest(final ContainmentPriest card) {
@ -96,8 +97,8 @@ class ContainmentPriestReplacementEffect extends ReplacementEffectImpl {
card = card.getSecondCardFace();
}
if (card != null && card.isCreature(game)) { // TODO: Bestow Card cast as Enchantment probably not handled correctly
CreatureWasCastWatcher watcher = game.getState().getWatcher(CreatureWasCastWatcher.class);
return watcher != null && !watcher.wasCreatureCastThisTurn(event.getTargetId());
PermanentWasCastWatcher watcher = game.getState().getWatcher(PermanentWasCastWatcher.class);
return watcher != null && !watcher.wasPermanentCastThisTurn(event.getTargetId());
}
}
return false;

View file

@ -16,7 +16,7 @@ import mage.game.Game;
import mage.game.events.EntersTheBattlefieldEvent;
import mage.game.events.GameEvent;
import mage.players.Player;
import mage.watchers.common.CreatureWasCastWatcher;
import mage.watchers.common.PermanentWasCastWatcher;
/**
*
@ -29,7 +29,7 @@ public final class HallowedMoonlight extends CardImpl {
// Until end of turn, if a creature would enter the battlefield and it wasn't cast, exile it instead.
this.getSpellAbility().addEffect(new HallowedMoonlightEffect());
this.getSpellAbility().addWatcher(new CreatureWasCastWatcher());
this.getSpellAbility().addWatcher(new PermanentWasCastWatcher());
// Draw a card.
this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1));
}
@ -83,8 +83,8 @@ class HallowedMoonlightEffect extends ReplacementEffectImpl {
public boolean applies(GameEvent event, Ability source, Game game) {
EntersTheBattlefieldEvent entersTheBattlefieldEvent = (EntersTheBattlefieldEvent) event;
if (entersTheBattlefieldEvent.getTarget().isCreature(game)) {
CreatureWasCastWatcher watcher = game.getState().getWatcher(CreatureWasCastWatcher.class);
if (watcher != null && !watcher.wasCreatureCastThisTurn(event.getTargetId())) {
PermanentWasCastWatcher watcher = game.getState().getWatcher(PermanentWasCastWatcher.class);
if (watcher != null && !watcher.wasPermanentCastThisTurn(event.getTargetId())) {
return true;
}
}

View file

@ -14,7 +14,7 @@ import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.events.ZoneChangeEvent;
import mage.players.Player;
import mage.watchers.common.CreatureWasCastWatcher;
import mage.watchers.common.PermanentWasCastWatcher;
import java.util.UUID;
@ -35,7 +35,7 @@ public final class Mistcaller extends CardImpl {
this.addAbility(new SimpleActivatedAbility(
new ContainmentPriestReplacementEffect(),
new SacrificeSourceCost()
), new CreatureWasCastWatcher());
), new PermanentWasCastWatcher());
}
private Mistcaller(final Mistcaller card) {
@ -98,8 +98,8 @@ class ContainmentPriestReplacementEffect extends ReplacementEffectImpl {
card = card.getSecondCardFace();
}
if (card != null && card.isCreature(game)) { // TODO: Bestow Card cast as Enchantment probably not handled correctly
CreatureWasCastWatcher watcher = game.getState().getWatcher(CreatureWasCastWatcher.class);
return watcher != null && !watcher.wasCreatureCastThisTurn(event.getTargetId());
PermanentWasCastWatcher watcher = game.getState().getWatcher(PermanentWasCastWatcher.class);
return watcher != null && !watcher.wasPermanentCastThisTurn(event.getTargetId());
}
}
}

View file

@ -0,0 +1,82 @@
package mage.cards.p;
import java.util.UUID;
import mage.MageInt;
import mage.ObjectColor;
import mage.abilities.Ability;
import mage.abilities.common.EntersBattlefieldCastTriggeredAbility;
import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.costs.common.SacrificeTargetCost;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.common.CreateTokenCopyTargetEffect;
import mage.abilities.effects.common.ExileTargetEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SetTargetPointer;
import mage.constants.SubType;
import mage.constants.SuperType;
import mage.constants.Zone;
import mage.filter.common.FilterControlledCreaturePermanent;
import mage.filter.common.FilterControlledPermanent;
import mage.filter.predicate.mageobject.AnotherPredicate;
import mage.filter.predicate.permanent.TokenPredicate;
import mage.target.common.TargetControlledPermanent;
import mage.target.common.TargetNonlandPermanent;
/**
*
* @author alexander-novo
*/
public final class PrestonTheVanisher extends CardImpl {
private static final FilterControlledCreaturePermanent triggerFilter = new FilterControlledCreaturePermanent(
"another nontoken creature");
private static final FilterControlledPermanent activeCostFilter = new FilterControlledCreaturePermanent(
SubType.ILLUSION, "Illusions");
static {
triggerFilter.add(TokenPredicate.FALSE);
triggerFilter.add(AnotherPredicate.instance);
}
public PrestonTheVanisher(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[] { CardType.CREATURE }, "{3}{W}");
addSuperType(SuperType.LEGENDARY);
this.subtype.add(SubType.RABBIT);
this.subtype.add(SubType.WIZARD);
this.power = new MageInt(2);
this.toughness = new MageInt(5);
// Whenever another nontoken creature enters the battlefield under your control,
// if it wasnt cast, create a token thats a copy of that creature, except its
// a 0/1 white Illusion.
CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect(
null, null, false, 1, false, false,
null, 0, 1, false);
effect.setOnlyColor(ObjectColor.WHITE);
effect.setOnlySubType(SubType.ILLUSION);
effect.setText("create a token that's a copy of that creature, except it's a 0/1 white Illusion");
this.addAbility(
new EntersBattlefieldCastTriggeredAbility(Zone.BATTLEFIELD, effect, triggerFilter, false, false,
SetTargetPointer.PERMANENT, null,
true));
// {1}{W}, Sacrifice five Illusions: Exile target nonland permanent.
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ExileTargetEffect(),
new ManaCostsImpl<>("{1}{W}"));
ability.addCost(new SacrificeTargetCost(new TargetControlledPermanent(5, activeCostFilter)));
ability.addTarget(new TargetNonlandPermanent());
this.addAbility(ability);
}
private PrestonTheVanisher(final PrestonTheVanisher card) {
super(card);
}
@Override
public PrestonTheVanisher copy() {
return new PrestonTheVanisher(this);
}
}

View file

@ -16,7 +16,7 @@ import mage.game.events.EntersTheBattlefieldEvent;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.watchers.Watcher;
import mage.watchers.common.CreatureWasCastWatcher;
import mage.watchers.common.PermanentWasCastWatcher;
/**
*
@ -29,7 +29,7 @@ public final class UphillBattle extends CardImpl {
// Creatures played by your opponents enter the battlefield tapped.
Ability tapAbility = new SimpleStaticAbility(Zone.BATTLEFIELD, new UphillBattleTapEffect());
tapAbility.addWatcher(new CreatureWasCastWatcher());
tapAbility.addWatcher(new PermanentWasCastWatcher());
tapAbility.addWatcher(new PlayCreatureLandWatcher());
addAbility(tapAbility);
}
@ -97,11 +97,11 @@ class UphillBattleTapEffect extends ReplacementEffectImpl {
@Override
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
Permanent target = ((EntersTheBattlefieldEvent) event).getTarget();
CreatureWasCastWatcher creatureSpellWatcher = game.getState().getWatcher(CreatureWasCastWatcher.class);
PermanentWasCastWatcher creatureSpellWatcher = game.getState().getWatcher(PermanentWasCastWatcher.class);
PlayCreatureLandWatcher landWatcher = game.getState().getWatcher(PlayCreatureLandWatcher.class);
if (target != null
&& ((creatureSpellWatcher != null && creatureSpellWatcher.wasCreatureCastThisTurn(target.getId()))
&& ((creatureSpellWatcher != null && creatureSpellWatcher.wasPermanentCastThisTurn(target.getId()))
|| (landWatcher != null && landWatcher.wasLandPlayed(target.getId())))) {
target.setTapped(true);
}

View file

@ -557,6 +557,7 @@ public final class Jumpstart2022 extends ExpansionSet {
cards.add(new SetCardInfo("Preordain", 63, Rarity.COMMON, mage.cards.p.Preordain.class));
cards.add(new SetCardInfo("Presence of Gond", 709, Rarity.COMMON, mage.cards.p.PresenceOfGond.class));
cards.add(new SetCardInfo("Press for Answers", 337, Rarity.COMMON, mage.cards.p.PressForAnswers.class));
cards.add(new SetCardInfo("Preston, the Vanisher", 8, Rarity.RARE, mage.cards.p.PrestonTheVanisher.class));
cards.add(new SetCardInfo("Prey Upon", 710, Rarity.COMMON, mage.cards.p.PreyUpon.class));
cards.add(new SetCardInfo("Prickly Marmoset", 580, Rarity.COMMON, mage.cards.p.PricklyMarmoset.class));
cards.add(new SetCardInfo("Pridemalkin", 711, Rarity.COMMON, mage.cards.p.Pridemalkin.class));

View file

@ -109,7 +109,7 @@ public class EntersBattlefieldAllTriggeredAbility extends TriggeredAbilityImpl {
return super.getRule();
}
private String generateTriggerPhrase() {
protected String generateTriggerPhrase() {
StringBuilder sb = new StringBuilder("Whenever ");
if (thisOrAnother) {
sb.append("{this} or another ");

View file

@ -0,0 +1,103 @@
package mage.abilities.common;
import java.util.UUID;
import mage.abilities.effects.Effect;
import mage.constants.SetTargetPointer;
import mage.constants.Zone;
import mage.filter.FilterPermanent;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.watchers.common.PermanentWasCastWatcher;
/**
* An extension of triggered abilities that trigger when permanents enter the
* battlefield, but this time they either must be cast or must not be cast.
*
* @author alexander-novo
*/
public class EntersBattlefieldCastTriggeredAbility extends EntersBattlefieldAllTriggeredAbility {
private final boolean mustCast;
/**
* zone = BATTLEFIELD optional = false
*
* @param effect
* @param filter
*/
public EntersBattlefieldCastTriggeredAbility(Effect effect, FilterPermanent filter, boolean mustCast) {
this(Zone.BATTLEFIELD, effect, filter, mustCast, false);
}
public EntersBattlefieldCastTriggeredAbility(Effect effect, FilterPermanent filter, boolean mustCast, String rule) {
this(Zone.BATTLEFIELD, effect, filter, mustCast, false, rule);
}
public EntersBattlefieldCastTriggeredAbility(Zone zone, Effect effect, FilterPermanent filter, boolean mustCast,
boolean optional) {
this(zone, effect, filter, mustCast, optional, SetTargetPointer.NONE, null, false);
}
public EntersBattlefieldCastTriggeredAbility(Zone zone, Effect effect, FilterPermanent filter, boolean mustCast,
boolean optional,
String rule) {
this(zone, effect, filter, mustCast, optional, rule, false);
}
public EntersBattlefieldCastTriggeredAbility(Zone zone, Effect effect, FilterPermanent filter, boolean mustCast,
boolean optional,
String rule, boolean controlledText) {
this(zone, effect, filter, mustCast, optional, SetTargetPointer.NONE, rule, controlledText);
}
public EntersBattlefieldCastTriggeredAbility(Zone zone, Effect effect, FilterPermanent filter, boolean mustCast,
boolean optional,
SetTargetPointer setTargetPointer, String rule) {
this(zone, effect, filter, mustCast, optional, setTargetPointer, rule, false);
}
public EntersBattlefieldCastTriggeredAbility(Zone zone, Effect effect, FilterPermanent filter, boolean mustCast,
boolean optional,
SetTargetPointer setTargetPointer, String rule, boolean controlledText) {
this(zone, effect, filter, mustCast, optional, setTargetPointer, rule, controlledText, false);
}
protected EntersBattlefieldCastTriggeredAbility(Zone zone, Effect effect, FilterPermanent filter, boolean mustCast,
boolean optional,
SetTargetPointer setTargetPointer, String rule, boolean controlledText, boolean thisOrAnother) {
super(zone, effect, filter, optional, setTargetPointer, rule, controlledText, thisOrAnother);
this.mustCast = mustCast;
this.addWatcher(new PermanentWasCastWatcher());
StringBuilder triggerPhrase = new StringBuilder(this.generateTriggerPhrase());
if (mustCast) {
triggerPhrase.append("if it was cast, ");
} else {
triggerPhrase.append("if it wasn't cast, ");
}
this.setTriggerPhrase(triggerPhrase.toString());
}
public EntersBattlefieldCastTriggeredAbility(final EntersBattlefieldCastTriggeredAbility ability) {
super(ability);
this.mustCast = ability.mustCast;
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
if (!super.checkTrigger(event, game))
return false;
PermanentWasCastWatcher watcher = game.getState().getWatcher(PermanentWasCastWatcher.class);
UUID targetId = event.getTargetId();
return watcher.wasPermanentCastThisTurn(targetId) == this.mustCast;
}
@Override
public EntersBattlefieldCastTriggeredAbility copy() {
return new EntersBattlefieldCastTriggeredAbility(this);
}
}

View file

@ -17,11 +17,11 @@ import mage.watchers.Watcher;
*
* @author LevelX2
*/
public class CreatureWasCastWatcher extends Watcher {
public class PermanentWasCastWatcher extends Watcher {
private final Set<UUID> creaturesCasted = new HashSet<>();
private final Set<UUID> permanentsCasted = new HashSet<>();
public CreatureWasCastWatcher() {
public PermanentWasCastWatcher() {
super(WatcherScope.GAME);
}
@ -31,27 +31,27 @@ public class CreatureWasCastWatcher extends Watcher {
Spell spell = (Spell) game.getObject(event.getTargetId());
if (spell != null) {
Card card = game.getCard(spell.getSourceId());
if (card != null && card.isCreature(game)) {
creaturesCasted.add(card.getId());
if (card != null && card.isPermanent(game)) {
permanentsCasted.add(card.getId());
}
}
}
if (event.getType() == GameEvent.EventType.ZONE_CHANGE
&& ((ZoneChangeEvent) event).getFromZone() == Zone.BATTLEFIELD) {
Card card = game.getCard(event.getTargetId());
if (card != null && card.isCreature(game)) {
creaturesCasted.remove(card.getId());
if (card != null && card.isPermanent(game)) {
permanentsCasted.remove(card.getId());
}
}
}
public boolean wasCreatureCastThisTurn(UUID creatureSourceId) {
return creaturesCasted.contains(creatureSourceId);
public boolean wasPermanentCastThisTurn(UUID permanentSourceId) {
return permanentsCasted.contains(permanentSourceId);
}
@Override
public void reset() {
super.reset();
creaturesCasted.clear();
permanentsCasted.clear();
}
}