mirror of
https://github.com/magefree/mage.git
synced 2025-12-20 10:40:06 -08:00
implement [EOE] Lightstall Inquisitor
This commit is contained in:
parent
59ff808af2
commit
5c08d310a7
7 changed files with 322 additions and 8 deletions
185
Mage.Sets/src/mage/cards/l/LightstallInquisitor.java
Normal file
185
Mage.Sets/src/mage/cards/l/LightstallInquisitor.java
Normal file
|
|
@ -0,0 +1,185 @@
|
||||||
|
package mage.cards.l;
|
||||||
|
|
||||||
|
import mage.MageIdentifier;
|
||||||
|
import mage.MageInt;
|
||||||
|
import mage.MageObjectReference;
|
||||||
|
import mage.abilities.Ability;
|
||||||
|
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
|
||||||
|
import mage.abilities.costs.mana.GenericManaCost;
|
||||||
|
import mage.abilities.costs.mana.ManaCost;
|
||||||
|
import mage.abilities.costs.mana.ManaCosts;
|
||||||
|
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||||
|
import mage.abilities.effects.AsThoughEffectImpl;
|
||||||
|
import mage.abilities.effects.OneShotEffect;
|
||||||
|
import mage.abilities.effects.common.replacement.CardMorEnteringTappedEffect;
|
||||||
|
import mage.abilities.keyword.VigilanceAbility;
|
||||||
|
import mage.cards.*;
|
||||||
|
import mage.constants.*;
|
||||||
|
import mage.game.Game;
|
||||||
|
import mage.game.events.GameEvent;
|
||||||
|
import mage.players.Player;
|
||||||
|
import mage.target.TargetCard;
|
||||||
|
import mage.target.common.TargetCardInHand;
|
||||||
|
import mage.watchers.Watcher;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Susucr
|
||||||
|
*/
|
||||||
|
public final class LightstallInquisitor extends CardImpl {
|
||||||
|
|
||||||
|
public LightstallInquisitor(UUID ownerId, CardSetInfo setInfo) {
|
||||||
|
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{W}");
|
||||||
|
|
||||||
|
this.subtype.add(SubType.ANGEL);
|
||||||
|
this.subtype.add(SubType.WIZARD);
|
||||||
|
this.power = new MageInt(2);
|
||||||
|
this.toughness = new MageInt(1);
|
||||||
|
|
||||||
|
// Vigilance
|
||||||
|
this.addAbility(VigilanceAbility.getInstance());
|
||||||
|
|
||||||
|
// When this creature enters, each opponent exiles a card from their hand and may play that card for as long as it remains exiled. Each spell cast this way costs {1} more to cast. Each land played this way enters tapped.
|
||||||
|
this.addAbility(
|
||||||
|
new EntersBattlefieldTriggeredAbility(new LightstallInquisitorEffect())
|
||||||
|
.setIdentifier(MageIdentifier.LightstallInquisitorAlternateCast),
|
||||||
|
new LightstallInquisitorWatcher());
|
||||||
|
}
|
||||||
|
|
||||||
|
private LightstallInquisitor(final LightstallInquisitor card) {
|
||||||
|
super(card);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LightstallInquisitor copy() {
|
||||||
|
return new LightstallInquisitor(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class LightstallInquisitorEffect extends OneShotEffect {
|
||||||
|
|
||||||
|
public LightstallInquisitorEffect() {
|
||||||
|
super(Outcome.Benefit);
|
||||||
|
staticText = "each opponent exiles a card from their hand and may play that card for as long as it remains exiled. "
|
||||||
|
+ "Each spell cast this way costs {1} more to cast. Each land played this way enters tapped.";
|
||||||
|
}
|
||||||
|
|
||||||
|
private LightstallInquisitorEffect(final LightstallInquisitorEffect effect) {
|
||||||
|
super(effect);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LightstallInquisitorEffect copy() {
|
||||||
|
return new LightstallInquisitorEffect(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean apply(Game game, Ability source) {
|
||||||
|
for (UUID playerId : game.getOpponents(source.getControllerId())) {
|
||||||
|
Player opponent = game.getPlayer(playerId);
|
||||||
|
if (opponent == null || opponent.getHand().isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
TargetCard target = new TargetCardInHand();
|
||||||
|
opponent.choose(Outcome.Exile, opponent.getHand(), target, source, game);
|
||||||
|
Cards cards = new CardsImpl(target.getTargets());
|
||||||
|
if (cards.isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
opponent.moveCardsToExile(cards.getCards(game), source, game, true, null, "");
|
||||||
|
cards.retainZone(Zone.EXILED, game);
|
||||||
|
for (Card card : cards.getCards(game)) {
|
||||||
|
game.addEffect(new LightstallInquisitorAsThoughEffect(playerId, new MageObjectReference(card, game)), source);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class LightstallInquisitorAsThoughEffect extends AsThoughEffectImpl {
|
||||||
|
|
||||||
|
private final UUID playerId;
|
||||||
|
private final MageObjectReference cardMOR;
|
||||||
|
|
||||||
|
LightstallInquisitorAsThoughEffect(UUID playerId, MageObjectReference cardMOR) {
|
||||||
|
super(AsThoughEffectType.PLAY_FROM_NOT_OWN_HAND_ZONE, Duration.Custom, Outcome.Benefit);
|
||||||
|
this.playerId = playerId;
|
||||||
|
this.cardMOR = cardMOR;
|
||||||
|
}
|
||||||
|
|
||||||
|
private LightstallInquisitorAsThoughEffect(final LightstallInquisitorAsThoughEffect effect) {
|
||||||
|
super(effect);
|
||||||
|
this.playerId = effect.playerId;
|
||||||
|
this.cardMOR = effect.cardMOR;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean apply(Game game, Ability source) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LightstallInquisitorAsThoughEffect copy() {
|
||||||
|
return new LightstallInquisitorAsThoughEffect(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
|
||||||
|
Card card = this.cardMOR.getCard(game);
|
||||||
|
if (card == null) {
|
||||||
|
// cleanup, the card moved from exile.
|
||||||
|
discard();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!this.cardMOR.refersTo(objectId, game)
|
||||||
|
|| !playerId.equals(affectedControllerId)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Player player = game.getPlayer(affectedControllerId);
|
||||||
|
if (player == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (card.getSpellAbility() != null) {
|
||||||
|
ManaCosts<ManaCost> newManaCosts = new ManaCostsImpl<>();
|
||||||
|
newManaCosts.addAll(card.getManaCost());
|
||||||
|
newManaCosts.add(new GenericManaCost(1));
|
||||||
|
player.setCastSourceIdWithAlternateMana(
|
||||||
|
card.getId(), newManaCosts, card.getSpellAbility().getCosts(),
|
||||||
|
MageIdentifier.LightstallInquisitorAlternateCast
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Similar to CastFromGraveyardOnceWatcher, this Watcher add a EnteringTapped effects for lands played with the identifier.
|
||||||
|
class LightstallInquisitorWatcher extends Watcher {
|
||||||
|
|
||||||
|
LightstallInquisitorWatcher() {
|
||||||
|
super(WatcherScope.GAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void watch(GameEvent event, Game game) {
|
||||||
|
if (!GameEvent.EventType.PLAY_LAND.equals(event.getType())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!event.hasApprovingIdentifier(MageIdentifier.LightstallInquisitorAlternateCast)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// The land enters the battlefield tapped.
|
||||||
|
Card landCard = game.getCard(event.getTargetId());
|
||||||
|
if (landCard == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
MageObjectReference mor = new MageObjectReference(landCard, game);
|
||||||
|
game.getState().addEffect(
|
||||||
|
new CardMorEnteringTappedEffect(mor),
|
||||||
|
event.getApprovingObject().getApprovingAbility() // ability that approved the cast is the source of the tapping.
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -197,6 +197,8 @@ public final class EdgeOfEternities extends ExpansionSet {
|
||||||
cards.add(new SetCardInfo("Larval Scoutlander", 194, Rarity.UNCOMMON, mage.cards.l.LarvalScoutlander.class));
|
cards.add(new SetCardInfo("Larval Scoutlander", 194, Rarity.UNCOMMON, mage.cards.l.LarvalScoutlander.class));
|
||||||
cards.add(new SetCardInfo("Lashwhip Predator", 195, Rarity.UNCOMMON, mage.cards.l.LashwhipPredator.class));
|
cards.add(new SetCardInfo("Lashwhip Predator", 195, Rarity.UNCOMMON, mage.cards.l.LashwhipPredator.class));
|
||||||
cards.add(new SetCardInfo("Lightless Evangel", 109, Rarity.UNCOMMON, mage.cards.l.LightlessEvangel.class));
|
cards.add(new SetCardInfo("Lightless Evangel", 109, Rarity.UNCOMMON, mage.cards.l.LightlessEvangel.class));
|
||||||
|
cards.add(new SetCardInfo("Lightstall Inquisitor", 24, Rarity.RARE, mage.cards.l.LightstallInquisitor.class, NON_FULL_USE_VARIOUS));
|
||||||
|
cards.add(new SetCardInfo("Lightstall Inquisitor", 320, Rarity.RARE, mage.cards.l.LightstallInquisitor.class, NON_FULL_USE_VARIOUS));
|
||||||
cards.add(new SetCardInfo("Lithobraking", 142, Rarity.UNCOMMON, mage.cards.l.Lithobraking.class));
|
cards.add(new SetCardInfo("Lithobraking", 142, Rarity.UNCOMMON, mage.cards.l.Lithobraking.class));
|
||||||
cards.add(new SetCardInfo("Loading Zone", 196, Rarity.RARE, mage.cards.l.LoadingZone.class, NON_FULL_USE_VARIOUS));
|
cards.add(new SetCardInfo("Loading Zone", 196, Rarity.RARE, mage.cards.l.LoadingZone.class, NON_FULL_USE_VARIOUS));
|
||||||
cards.add(new SetCardInfo("Loading Zone", 344, Rarity.RARE, mage.cards.l.LoadingZone.class, NON_FULL_USE_VARIOUS));
|
cards.add(new SetCardInfo("Loading Zone", 344, Rarity.RARE, mage.cards.l.LoadingZone.class, NON_FULL_USE_VARIOUS));
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,60 @@
|
||||||
|
package org.mage.test.cards.single.eoe;
|
||||||
|
|
||||||
|
import mage.constants.PhaseStep;
|
||||||
|
import mage.constants.Zone;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Susucr
|
||||||
|
*/
|
||||||
|
public class LightstallInquisitorTest extends CardTestPlayerBase {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link mage.cards.l.LightstallInquisitor Lightstall Inquisitor} {W}
|
||||||
|
* Creature — Angel Wizard
|
||||||
|
* Vigilance
|
||||||
|
* When this creature enters, each opponent exiles a card from their hand and may play that card for as long as it remains exiled. Each spell cast this way costs {1} more to cast. Each land played this way enters tapped.
|
||||||
|
* 2/1
|
||||||
|
*/
|
||||||
|
private static final String inquisitor = "Lightstall Inquisitor";
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_Land() {
|
||||||
|
addCard(Zone.HAND, playerA, inquisitor);
|
||||||
|
addCard(Zone.HAND, playerB, "Savannah", 1);
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Plains", 1);
|
||||||
|
|
||||||
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, inquisitor);
|
||||||
|
setChoice(playerB, "Savannah");
|
||||||
|
|
||||||
|
playLand(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Savannah");
|
||||||
|
|
||||||
|
setStopAt(2, PhaseStep.BEGIN_COMBAT);
|
||||||
|
setStrictChooseMode(true);
|
||||||
|
execute();
|
||||||
|
|
||||||
|
assertPermanentCount(playerB, "Savannah", 1);
|
||||||
|
assertTappedCount("Savannah", true, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_NonLand() {
|
||||||
|
addCard(Zone.HAND, playerA, inquisitor);
|
||||||
|
addCard(Zone.HAND, playerB, "Centaur Courser", 1);
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Plains", 1);
|
||||||
|
addCard(Zone.BATTLEFIELD, playerB, "Forest", 4);
|
||||||
|
|
||||||
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, inquisitor);
|
||||||
|
setChoice(playerB, "Centaur Courser");
|
||||||
|
|
||||||
|
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Centaur Courser");
|
||||||
|
|
||||||
|
setStopAt(2, PhaseStep.BEGIN_COMBAT);
|
||||||
|
setStrictChooseMode(true);
|
||||||
|
execute();
|
||||||
|
|
||||||
|
assertPermanentCount(playerB, "Centaur Courser", 1);
|
||||||
|
assertTappedCount("Forest", true, 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -84,7 +84,8 @@ public enum MageIdentifier {
|
||||||
QuilledGreatwurmAlternateCast,
|
QuilledGreatwurmAlternateCast,
|
||||||
WickerfolkIndomitableAlternateCast,
|
WickerfolkIndomitableAlternateCast,
|
||||||
UriangerAugureltAlternateCast,
|
UriangerAugureltAlternateCast,
|
||||||
ValgavothTerrorEaterAlternateCast;
|
ValgavothTerrorEaterAlternateCast,
|
||||||
|
LightstallInquisitorAlternateCast;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Additional text if there is need to differentiate two very similar effects
|
* Additional text if there is need to differentiate two very similar effects
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ import mage.abilities.costs.Cost;
|
||||||
import mage.abilities.costs.Costs;
|
import mage.abilities.costs.Costs;
|
||||||
import mage.abilities.costs.CostsImpl;
|
import mage.abilities.costs.CostsImpl;
|
||||||
import mage.abilities.effects.AsThoughEffectImpl;
|
import mage.abilities.effects.AsThoughEffectImpl;
|
||||||
import mage.abilities.effects.common.replacement.MorEnteringTappedEffect;
|
import mage.abilities.effects.common.replacement.SpellMorEnteringTappedEffect;
|
||||||
import mage.cards.Card;
|
import mage.cards.Card;
|
||||||
import mage.constants.*;
|
import mage.constants.*;
|
||||||
import mage.filter.FilterCard;
|
import mage.filter.FilterCard;
|
||||||
|
|
@ -170,7 +170,7 @@ class CastFromGraveyardOnceWatcher extends Watcher {
|
||||||
if (target != null) {
|
if (target != null) {
|
||||||
MageObjectReference mor = new MageObjectReference(target, game);
|
MageObjectReference mor = new MageObjectReference(target, game);
|
||||||
game.getState().addEffect(
|
game.getState().addEffect(
|
||||||
new MorEnteringTappedEffect(mor),
|
new SpellMorEnteringTappedEffect(mor),
|
||||||
event.getApprovingObject().getApprovingAbility() // ability that approved the cast is the source of the tapping.
|
event.getApprovingObject().getApprovingAbility() // ability that approved the cast is the source of the tapping.
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,66 @@
|
||||||
|
package mage.abilities.effects.common.replacement;
|
||||||
|
|
||||||
|
import mage.MageObjectReference;
|
||||||
|
import mage.abilities.Ability;
|
||||||
|
import mage.abilities.effects.ReplacementEffectImpl;
|
||||||
|
import mage.cards.Card;
|
||||||
|
import mage.constants.Duration;
|
||||||
|
import mage.constants.Outcome;
|
||||||
|
import mage.game.Game;
|
||||||
|
import mage.game.events.EntersTheBattlefieldEvent;
|
||||||
|
import mage.game.events.GameEvent;
|
||||||
|
import mage.game.permanent.Permanent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to affect a card that will enter the battlefield (land played, or card put into play by effect).
|
||||||
|
* The permanent it becomes enters tapped.
|
||||||
|
* <p>
|
||||||
|
* Short-lived replacement effect, auto-cleanup if the mor is no longer current.
|
||||||
|
*
|
||||||
|
* @author Susucr
|
||||||
|
*/
|
||||||
|
public class CardMorEnteringTappedEffect extends ReplacementEffectImpl {
|
||||||
|
|
||||||
|
private final MageObjectReference mor;
|
||||||
|
|
||||||
|
public CardMorEnteringTappedEffect(MageObjectReference mor) {
|
||||||
|
super(Duration.OneUse, Outcome.Tap);
|
||||||
|
this.staticText = "That permanent enters the battlefield tapped";
|
||||||
|
this.mor = mor;
|
||||||
|
}
|
||||||
|
|
||||||
|
private CardMorEnteringTappedEffect(final CardMorEnteringTappedEffect effect) {
|
||||||
|
super(effect);
|
||||||
|
this.mor = effect.mor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CardMorEnteringTappedEffect copy() {
|
||||||
|
return new CardMorEnteringTappedEffect(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean checksEventType(GameEvent event, Game game) {
|
||||||
|
return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean applies(GameEvent event, Ability source, Game game) {
|
||||||
|
Card morCard = mor.getCard(game);
|
||||||
|
if (morCard == null) {
|
||||||
|
// cleanup if something other than resolving happens to the spell.
|
||||||
|
discard();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return event.getTargetId().equals(morCard.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
|
||||||
|
Permanent permanent = ((EntersTheBattlefieldEvent) event).getTarget();
|
||||||
|
if (permanent != null) {
|
||||||
|
permanent.setTapped(true);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -19,24 +19,24 @@ import mage.game.stack.Spell;
|
||||||
*
|
*
|
||||||
* @author Susucr
|
* @author Susucr
|
||||||
*/
|
*/
|
||||||
public class MorEnteringTappedEffect extends ReplacementEffectImpl {
|
public class SpellMorEnteringTappedEffect extends ReplacementEffectImpl {
|
||||||
|
|
||||||
private final MageObjectReference mor;
|
private final MageObjectReference mor;
|
||||||
|
|
||||||
public MorEnteringTappedEffect(MageObjectReference mor) {
|
public SpellMorEnteringTappedEffect(MageObjectReference mor) {
|
||||||
super(Duration.OneUse, Outcome.Tap);
|
super(Duration.OneUse, Outcome.Tap);
|
||||||
this.staticText = "That permanent enters the battlefield tapped";
|
this.staticText = "That permanent enters the battlefield tapped";
|
||||||
this.mor = mor;
|
this.mor = mor;
|
||||||
}
|
}
|
||||||
|
|
||||||
private MorEnteringTappedEffect(final MorEnteringTappedEffect effect) {
|
private SpellMorEnteringTappedEffect(final SpellMorEnteringTappedEffect effect) {
|
||||||
super(effect);
|
super(effect);
|
||||||
this.mor = effect.mor;
|
this.mor = effect.mor;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MorEnteringTappedEffect copy() {
|
public SpellMorEnteringTappedEffect copy() {
|
||||||
return new MorEnteringTappedEffect(this);
|
return new SpellMorEnteringTappedEffect(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
Loading…
Add table
Add a link
Reference in a new issue