mirror of
https://github.com/correl/mage.git
synced 2024-12-25 03:00:15 +00:00
Rework Spell.counter using PutCards and reimplement Desertion. Fixes #9299
This commit is contained in:
parent
cbe610d339
commit
e40934921f
27 changed files with 124 additions and 251 deletions
|
@ -1,4 +1,3 @@
|
|||
|
||||
package mage.cards.a;
|
||||
|
||||
import java.util.UUID;
|
||||
|
@ -7,7 +6,7 @@ import mage.abilities.keyword.AffinityForArtifactsAbility;
|
|||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Zone;
|
||||
import mage.constants.PutCards;
|
||||
import mage.target.TargetSpell;
|
||||
|
||||
/**
|
||||
|
@ -23,7 +22,7 @@ public final class AssertAuthority extends CardImpl {
|
|||
// Affinity for artifacts
|
||||
this.addAbility(new AffinityForArtifactsAbility());
|
||||
// Counter target spell. If that spell is countered this way, exile it instead of putting it into its owner's graveyard.
|
||||
this.getSpellAbility().addEffect(new CounterTargetWithReplacementEffect(Zone.EXILED));
|
||||
this.getSpellAbility().addEffect(new CounterTargetWithReplacementEffect(PutCards.EXILED));
|
||||
this.getSpellAbility().addTarget(new TargetSpell());
|
||||
}
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ import mage.cards.CardImpl;
|
|||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.PutCards;
|
||||
import mage.constants.Zone;
|
||||
import mage.counters.CounterType;
|
||||
import mage.game.Game;
|
||||
|
@ -65,7 +66,7 @@ class DelayEffect extends OneShotEffect {
|
|||
Player controller = game.getPlayer(source.getControllerId());
|
||||
Spell spell = game.getStack().getSpell(getTargetPointer().getFirst(game, source));
|
||||
if (controller != null && spell != null) {
|
||||
Effect effect = new CounterTargetWithReplacementEffect(Zone.EXILED);
|
||||
Effect effect = new CounterTargetWithReplacementEffect(PutCards.EXILED);
|
||||
effect.setTargetPointer(targetPointer);
|
||||
Card card = game.getCard(spell.getSourceId());
|
||||
if (card != null && effect.apply(game, source) && game.getState().getZone(card.getId()) == Zone.EXILED) {
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
package mage.cards.d;
|
||||
|
||||
import java.util.UUID;
|
||||
|
@ -6,7 +5,7 @@ import mage.abilities.effects.common.CounterTargetWithReplacementEffect;
|
|||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Zone;
|
||||
import mage.constants.PutCards;
|
||||
import mage.filter.common.FilterCreatureSpell;
|
||||
import mage.target.TargetSpell;
|
||||
|
||||
|
@ -20,7 +19,7 @@ public final class DenyExistence extends CardImpl {
|
|||
super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{2}{U}");
|
||||
|
||||
// Counter target creature spell. If that spell is countered this way, exile it instead of putting it into its owner's graveyard.
|
||||
this.getSpellAbility().addEffect(new CounterTargetWithReplacementEffect(Zone.EXILED));
|
||||
this.getSpellAbility().addEffect(new CounterTargetWithReplacementEffect(PutCards.EXILED));
|
||||
this.getSpellAbility().addTarget(new TargetSpell(new FilterCreatureSpell()));
|
||||
}
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ import mage.abilities.effects.common.CounterTargetWithReplacementEffect;
|
|||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Zone;
|
||||
import mage.constants.PutCards;
|
||||
import mage.filter.FilterSpell;
|
||||
import mage.filter.predicate.Predicates;
|
||||
import mage.target.TargetSpell;
|
||||
|
@ -29,7 +29,7 @@ public final class DenyTheDivine extends CardImpl {
|
|||
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{U}");
|
||||
|
||||
// Counter target creature or enchantment spell. If that spell is countered this way, exile it instead of putting it into its owner's graveyard.
|
||||
this.getSpellAbility().addEffect(new CounterTargetWithReplacementEffect(Zone.EXILED));
|
||||
this.getSpellAbility().addEffect(new CounterTargetWithReplacementEffect(PutCards.EXILED));
|
||||
this.getSpellAbility().addTarget(new TargetSpell(filter));
|
||||
}
|
||||
|
||||
|
|
|
@ -1,38 +1,30 @@
|
|||
package mage.cards.d;
|
||||
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.effects.ReplacementEffectImpl;
|
||||
import mage.abilities.effects.common.CounterTargetEffect;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.Zone;
|
||||
import mage.constants.PutCards;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.events.ZoneChangeEvent;
|
||||
import mage.game.stack.StackObject;
|
||||
import mage.target.TargetSpell;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author Quercitron
|
||||
* @author awjackson
|
||||
*/
|
||||
public final class Desertion extends CardImpl {
|
||||
|
||||
public Desertion(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{3}{U}{U}");
|
||||
|
||||
// Counter target spell.
|
||||
this.getSpellAbility().addEffect(new CounterTargetEffect());
|
||||
// Counter target spell. If an artifact or creature spell is countered this way,
|
||||
// put that card onto the battlefield under your control instead of into its owner's graveyard.
|
||||
this.getSpellAbility().addEffect(new DesertionEffect());
|
||||
this.getSpellAbility().addTarget(new TargetSpell());
|
||||
|
||||
// If an artifact or creature spell is countered this way, put that card onto the battlefield under your control instead of into its owner's graveyard.
|
||||
this.addAbility(new SimpleStaticAbility(Zone.STACK, new DesertionReplacementEffect()));
|
||||
}
|
||||
|
||||
private Desertion(final Desertion card) {
|
||||
|
@ -45,48 +37,30 @@ public final class Desertion extends CardImpl {
|
|||
}
|
||||
}
|
||||
|
||||
class DesertionReplacementEffect extends ReplacementEffectImpl {
|
||||
class DesertionEffect extends OneShotEffect {
|
||||
|
||||
DesertionReplacementEffect() {
|
||||
super(Duration.WhileOnStack, Outcome.PutCardInPlay);
|
||||
staticText = "If an artifact or creature spell is countered this way, put that card onto the battlefield under your control instead of into its owner's graveyard";
|
||||
public DesertionEffect() {
|
||||
super(Outcome.Detriment);
|
||||
staticText = "counter target spell. If an artifact or creature spell is countered this way, put that card onto the battlefield under your control instead of into its owner's graveyard";
|
||||
}
|
||||
|
||||
private DesertionReplacementEffect(final DesertionReplacementEffect effect) {
|
||||
private DesertionEffect(final DesertionEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DesertionReplacementEffect copy() {
|
||||
return new DesertionReplacementEffect(this);
|
||||
public DesertionEffect copy() {
|
||||
return new DesertionEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
|
||||
ZoneChangeEvent zce = (ZoneChangeEvent) event;
|
||||
zce.setToZone(Zone.BATTLEFIELD);
|
||||
zce.setPlayerId(source.getControllerId());
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checksEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.ZONE_CHANGE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean applies(GameEvent event, Ability source, Game game) {
|
||||
if (!Objects.equals(event.getSourceId(), source.getSourceId())
|
||||
|| !(((ZoneChangeEvent) event).getToZone() == Zone.GRAVEYARD)) {
|
||||
StackObject spell = game.getStack().getStackObject(targetPointer.getFirst(game, source));
|
||||
if (spell == null) {
|
||||
return false;
|
||||
}
|
||||
MageObject mageObject = game.getObject(event.getTargetId());
|
||||
return mageObject != null
|
||||
&& (mageObject.isArtifact(game) || mageObject.isCreature(game));
|
||||
return game.getStack().counter(spell.getId(), source, game,
|
||||
spell.isArtifact() || spell.isCreature() ? PutCards.BATTLEFIELD : PutCards.GRAVEYARD
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ import mage.cards.Cards;
|
|||
import mage.cards.CardsImpl;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.Zone;
|
||||
import mage.constants.PutCards;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
import mage.target.TargetSpell;
|
||||
|
@ -29,7 +29,7 @@ public final class DeviousCoverUp extends CardImpl {
|
|||
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{U}{U}");
|
||||
|
||||
// Counter target spell. If that spell is countered this way, exile it instead of putting it into its owner's graveyard.
|
||||
this.getSpellAbility().addEffect(new CounterTargetWithReplacementEffect(Zone.EXILED));
|
||||
this.getSpellAbility().addEffect(new CounterTargetWithReplacementEffect(PutCards.EXILED));
|
||||
this.getSpellAbility().addTarget(new TargetSpell());
|
||||
|
||||
// You may shuffle up to four target cards from your graveyard into your library.
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
package mage.cards.d;
|
||||
|
||||
import java.util.UUID;
|
||||
|
@ -6,7 +5,7 @@ import mage.abilities.effects.common.CounterTargetWithReplacementEffect;
|
|||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Zone;
|
||||
import mage.constants.PutCards;
|
||||
import mage.target.TargetSpell;
|
||||
|
||||
/**
|
||||
|
@ -19,7 +18,7 @@ public final class Dissipate extends CardImpl {
|
|||
|
||||
|
||||
// Counter target spell. If that spell is countered this way, exile it instead of putting it into its owner's graveyard.
|
||||
this.getSpellAbility().addEffect(new CounterTargetWithReplacementEffect(Zone.EXILED));
|
||||
this.getSpellAbility().addEffect(new CounterTargetWithReplacementEffect(PutCards.EXILED));
|
||||
this.getSpellAbility().addTarget(new TargetSpell());
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
package mage.cards.f;
|
||||
|
||||
import java.util.UUID;
|
||||
|
@ -7,7 +6,7 @@ import mage.cards.CardImpl;
|
|||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.Zone;
|
||||
import mage.constants.PutCards;
|
||||
import mage.filter.FilterSpell;
|
||||
import mage.filter.predicate.Predicates;
|
||||
import mage.target.TargetSpell;
|
||||
|
@ -30,7 +29,7 @@ public final class FaerieTrickery extends CardImpl {
|
|||
|
||||
|
||||
// Counter target non-Faerie spell. If that spell is countered this way, exile it instead of putting it into its owner's graveyard.
|
||||
this.getSpellAbility().addEffect(new CounterTargetWithReplacementEffect(Zone.EXILED));
|
||||
this.getSpellAbility().addEffect(new CounterTargetWithReplacementEffect(PutCards.EXILED));
|
||||
this.getSpellAbility().addTarget(new TargetSpell(filter));
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ import mage.abilities.hint.common.NotMyTurnHint;
|
|||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Zone;
|
||||
import mage.constants.PutCards;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.filter.common.FilterOwnedCard;
|
||||
import mage.filter.predicate.mageobject.ColorPredicate;
|
||||
|
@ -40,7 +40,7 @@ public final class ForceOfNegation extends CardImpl {
|
|||
).addHint(NotMyTurnHint.instance));
|
||||
|
||||
// Counter target noncreature spell. If that spell is countered this way, exile it instead of putting it into its owner's graveyard.
|
||||
this.getSpellAbility().addEffect(new CounterTargetWithReplacementEffect(Zone.EXILED));
|
||||
this.getSpellAbility().addEffect(new CounterTargetWithReplacementEffect(PutCards.EXILED));
|
||||
this.getSpellAbility().addTarget(new TargetSpell(StaticFilters.FILTER_SPELL_NON_CREATURE));
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
package mage.cards.h;
|
||||
|
||||
import java.util.UUID;
|
||||
|
@ -6,8 +5,7 @@ import mage.abilities.effects.common.CounterTargetWithReplacementEffect;
|
|||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Zone;
|
||||
import mage.constants.ZoneDetail;
|
||||
import mage.constants.PutCards;
|
||||
import mage.target.TargetSpell;
|
||||
|
||||
/**
|
||||
|
@ -20,7 +18,7 @@ public final class Hinder extends CardImpl {
|
|||
super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{U}{U}");
|
||||
|
||||
// Counter target spell. If that spell is countered this way, put that card on the top or bottom of its owner's library instead of into that player's graveyard.
|
||||
this.getSpellAbility().addEffect(new CounterTargetWithReplacementEffect(Zone.LIBRARY, ZoneDetail.CHOOSE));
|
||||
this.getSpellAbility().addEffect(new CounterTargetWithReplacementEffect(PutCards.TOP_OR_BOTTOM));
|
||||
this.getSpellAbility().addTarget(new TargetSpell());
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ import mage.cards.CardImpl;
|
|||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.ComparisonType;
|
||||
import mage.constants.Zone;
|
||||
import mage.constants.PutCards;
|
||||
import mage.filter.common.FilterCreatureSpell;
|
||||
import mage.filter.predicate.mageobject.ManaValuePredicate;
|
||||
import mage.target.TargetSpell;
|
||||
|
@ -32,7 +32,7 @@ public final class HorriblyAwry extends CardImpl {
|
|||
this.addAbility(new DevoidAbility(this.color));
|
||||
|
||||
// Counter target creature spell with converted mana cost 4 or less. If that spell is countered this way, exile it instead of putting it into its owner's graveyard.
|
||||
this.getSpellAbility().addEffect(new CounterTargetWithReplacementEffect(Zone.EXILED));
|
||||
this.getSpellAbility().addEffect(new CounterTargetWithReplacementEffect(PutCards.EXILED));
|
||||
this.getSpellAbility().addTarget(new TargetSpell(filter));
|
||||
}
|
||||
|
||||
|
|
|
@ -73,7 +73,7 @@ class KheruSpellsnatcherEffect extends OneShotEffect {
|
|||
MageObject sourceObject = source.getSourceObject(game);
|
||||
StackObject stackObject = game.getStack().getStackObject(targetPointer.getFirst(game, source));
|
||||
if (stackObject != null && sourceObject != null
|
||||
&& game.getStack().counter(targetPointer.getFirst(game, source), source, game, Zone.EXILED, false, ZoneDetail.NONE)) {
|
||||
&& game.getStack().counter(targetPointer.getFirst(game, source), source, game, PutCards.EXILED)) {
|
||||
if (!stackObject.isCopy()) {
|
||||
MageObject card = game.getObject(stackObject.getSourceId());
|
||||
if (card instanceof Card) {
|
||||
|
|
|
@ -6,8 +6,7 @@ import mage.abilities.effects.common.CounterTargetWithReplacementEffect;
|
|||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Zone;
|
||||
import mage.constants.ZoneDetail;
|
||||
import mage.constants.PutCards;
|
||||
import mage.target.TargetSpell;
|
||||
|
||||
/**
|
||||
|
@ -20,7 +19,7 @@ public final class LapseOfCertainty extends CardImpl {
|
|||
super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{2}{W}");
|
||||
|
||||
// Counter target spell. If that spell is countered this way, put it on top of its owner's library instead of into that player's graveyard.
|
||||
this.getSpellAbility().addEffect(new CounterTargetWithReplacementEffect(Zone.LIBRARY, ZoneDetail.TOP));
|
||||
this.getSpellAbility().addEffect(new CounterTargetWithReplacementEffect(PutCards.TOP_ANY));
|
||||
this.getSpellAbility().addTarget(new TargetSpell());
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ import mage.cards.CardImpl;
|
|||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.ComparisonType;
|
||||
import mage.constants.Zone;
|
||||
import mage.constants.PutCards;
|
||||
import mage.filter.FilterSpell;
|
||||
import mage.filter.predicate.mageobject.ManaValuePredicate;
|
||||
import mage.target.TargetSpell;
|
||||
|
@ -28,7 +28,7 @@ public final class Liquify extends CardImpl {
|
|||
super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{2}{U}");
|
||||
|
||||
// Counter target spell with converted mana cost 3 or less. If that spell is countered this way, exile it instead of putting it into its owner's graveyard.
|
||||
this.getSpellAbility().addEffect(new CounterTargetWithReplacementEffect(Zone.EXILED));
|
||||
this.getSpellAbility().addEffect(new CounterTargetWithReplacementEffect(PutCards.EXILED));
|
||||
this.getSpellAbility().addTarget(new TargetSpell(filter));
|
||||
}
|
||||
|
||||
|
|
|
@ -6,8 +6,7 @@ import mage.abilities.effects.common.CounterTargetWithReplacementEffect;
|
|||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Zone;
|
||||
import mage.constants.ZoneDetail;
|
||||
import mage.constants.PutCards;
|
||||
import mage.target.TargetSpell;
|
||||
|
||||
/**
|
||||
|
@ -20,7 +19,7 @@ public final class MemoryLapse extends CardImpl {
|
|||
super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{U}");
|
||||
|
||||
// Counter target spell. If that spell is countered this way, put it on top of its owner's library instead of into that player's graveyard.
|
||||
this.getSpellAbility().addEffect(new CounterTargetWithReplacementEffect(Zone.LIBRARY, ZoneDetail.TOP));
|
||||
this.getSpellAbility().addEffect(new CounterTargetWithReplacementEffect(PutCards.TOP_ANY));
|
||||
this.getSpellAbility().addTarget(new TargetSpell());
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ import mage.abilities.effects.keyword.ScryEffect;
|
|||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Zone;
|
||||
import mage.constants.PutCards;
|
||||
import mage.filter.FilterSpell;
|
||||
import mage.filter.predicate.Predicates;
|
||||
import mage.target.TargetSpell;
|
||||
|
@ -30,7 +30,7 @@ public final class NoEscape extends CardImpl {
|
|||
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{U}");
|
||||
|
||||
// Counter target creature or planeswalker spell. If that spell is countered this way, exile it instead of putting it into its owner's graveyard.
|
||||
this.getSpellAbility().addEffect(new CounterTargetWithReplacementEffect(Zone.EXILED));
|
||||
this.getSpellAbility().addEffect(new CounterTargetWithReplacementEffect(PutCards.EXILED));
|
||||
this.getSpellAbility().addTarget(new TargetSpell(filter));
|
||||
|
||||
// Scry 1.
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
package mage.cards.r;
|
||||
|
||||
import java.util.UUID;
|
||||
|
@ -7,7 +6,7 @@ import mage.abilities.effects.common.DrawCardSourceControllerEffect;
|
|||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Zone;
|
||||
import mage.constants.PutCards;
|
||||
import mage.target.TargetSpell;
|
||||
|
||||
/**
|
||||
|
@ -19,12 +18,11 @@ public final class Remand extends CardImpl {
|
|||
public Remand(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{U}");
|
||||
|
||||
|
||||
// Counter target spell. If that spell is countered this way, put it into its owner's hand instead of into that player's graveyard.
|
||||
this.getSpellAbility().addEffect(new CounterTargetWithReplacementEffect(Zone.HAND));
|
||||
this.getSpellAbility().addEffect(new CounterTargetWithReplacementEffect(PutCards.HAND));
|
||||
this.getSpellAbility().addTarget(new TargetSpell());
|
||||
// Draw a card.
|
||||
this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1));
|
||||
this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1).concatBy("<br>"));
|
||||
}
|
||||
|
||||
private Remand(final Remand card) {
|
||||
|
|
|
@ -1,18 +1,12 @@
|
|||
|
||||
package mage.cards.s;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.common.CounterTargetWithReplacementEffect;
|
||||
import mage.abilities.effects.common.ReturnToLibrarySpellEffect;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.Zone;
|
||||
import mage.constants.ZoneDetail;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
import mage.constants.PutCards;
|
||||
import mage.target.TargetSpell;
|
||||
|
||||
/**
|
||||
|
@ -24,9 +18,11 @@ public final class SpellCrumple extends CardImpl {
|
|||
public SpellCrumple(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{U}{U}");
|
||||
|
||||
// Counter target spell. If that spell is countered this way, put it on the bottom of its owner's library instead of into that player's graveyard. Put Spell Crumple on the bottom of its owner's library.
|
||||
// Counter target spell. If that spell is countered this way, put it on the
|
||||
// bottom of its owner's library instead of into that player's graveyard.
|
||||
// Put Spell Crumple on the bottom of its owner's library.
|
||||
this.getSpellAbility().addTarget(new TargetSpell());
|
||||
this.getSpellAbility().addEffect(new SpellCrumpleCounterEffect());
|
||||
this.getSpellAbility().addEffect(new CounterTargetWithReplacementEffect(PutCards.BOTTOM_ANY));
|
||||
this.getSpellAbility().addEffect(new ReturnToLibrarySpellEffect(false));
|
||||
}
|
||||
|
||||
|
@ -39,29 +35,3 @@ public final class SpellCrumple extends CardImpl {
|
|||
return new SpellCrumple(this);
|
||||
}
|
||||
}
|
||||
|
||||
class SpellCrumpleCounterEffect extends OneShotEffect {
|
||||
|
||||
public SpellCrumpleCounterEffect() {
|
||||
super(Outcome.Benefit);
|
||||
this.staticText = "Counter target spell. If that spell is countered this way, put it on the bottom of its owner's library instead of into that player's graveyard";
|
||||
}
|
||||
|
||||
public SpellCrumpleCounterEffect(final SpellCrumpleCounterEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpellCrumpleCounterEffect copy() {
|
||||
return new SpellCrumpleCounterEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
if (controller != null) {
|
||||
return game.getStack().counter(targetPointer.getFirst(game, source), source, game, Zone.LIBRARY, false, ZoneDetail.BOTTOM);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,12 +9,7 @@ import mage.abilities.effects.common.asthought.PlayFromNotOwnHandZoneTargetEffec
|
|||
import mage.cards.Card;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.TargetController;
|
||||
import mage.constants.Zone;
|
||||
import mage.constants.ZoneDetail;
|
||||
import mage.constants.*;
|
||||
import mage.game.Game;
|
||||
import mage.game.stack.Spell;
|
||||
import mage.game.stack.StackObject;
|
||||
|
@ -68,7 +63,7 @@ class SpelljackEffect extends OneShotEffect {
|
|||
if (controller != null) {
|
||||
UUID targetId = targetPointer.getFirst(game, source);
|
||||
StackObject stackObject = game.getStack().getStackObject(targetId);
|
||||
if (stackObject != null && game.getStack().counter(targetId, source, game, Zone.EXILED, false, ZoneDetail.NONE)) {
|
||||
if (stackObject != null && game.getStack().counter(targetId, source, game, PutCards.EXILED)) {
|
||||
Card card = ((Spell) stackObject).getCard();
|
||||
if (card != null) {
|
||||
ContinuousEffect effect = new PlayFromNotOwnHandZoneTargetEffect(Zone.EXILED, TargetController.YOU, Duration.Custom, true);
|
||||
|
|
|
@ -7,7 +7,7 @@ import mage.abilities.keyword.DevoidAbility;
|
|||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Zone;
|
||||
import mage.constants.PutCards;
|
||||
import mage.target.TargetSpell;
|
||||
|
||||
/**
|
||||
|
@ -23,7 +23,7 @@ public final class VoidShatter extends CardImpl {
|
|||
this.addAbility(new DevoidAbility(this.color));
|
||||
|
||||
// Counter target spell. If that spell is countered this way, exile it instead of putting it into its owner's graveyard.
|
||||
this.getSpellAbility().addEffect(new CounterTargetWithReplacementEffect(Zone.EXILED));
|
||||
this.getSpellAbility().addEffect(new CounterTargetWithReplacementEffect(PutCards.EXILED));
|
||||
this.getSpellAbility().addTarget(new TargetSpell());
|
||||
}
|
||||
|
||||
|
|
|
@ -1,12 +1,10 @@
|
|||
|
||||
package mage.abilities.effects.common;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.Mode;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.Zone;
|
||||
import mage.constants.ZoneDetail;
|
||||
import mage.constants.PutCards;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
|
||||
|
@ -15,29 +13,20 @@ import mage.players.Player;
|
|||
*/
|
||||
public class CounterTargetWithReplacementEffect extends OneShotEffect {
|
||||
|
||||
private Zone targetZone;
|
||||
private ZoneDetail zoneDetail;
|
||||
|
||||
public CounterTargetWithReplacementEffect(Zone targetZone) {
|
||||
this(targetZone, ZoneDetail.NONE);
|
||||
}
|
||||
private final PutCards putIt;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param targetZone
|
||||
* @param zoneDetail use to specify when moving card to library <ul><li>true
|
||||
* = put on top</li><li>false = put on bottom</li></ul>
|
||||
* @param putIt
|
||||
*/
|
||||
public CounterTargetWithReplacementEffect(Zone targetZone, ZoneDetail zoneDetail) {
|
||||
public CounterTargetWithReplacementEffect(PutCards putIt) {
|
||||
super(Outcome.Detriment);
|
||||
this.targetZone = targetZone;
|
||||
this.zoneDetail = zoneDetail;
|
||||
this.putIt = putIt;
|
||||
}
|
||||
|
||||
public CounterTargetWithReplacementEffect(final CounterTargetWithReplacementEffect effect) {
|
||||
super(effect);
|
||||
this.targetZone = effect.targetZone;
|
||||
this.zoneDetail = effect.zoneDetail;
|
||||
this.putIt = effect.putIt;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -49,41 +38,26 @@ public class CounterTargetWithReplacementEffect extends OneShotEffect {
|
|||
public boolean apply(Game game, Ability source) {
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
if (controller != null) {
|
||||
return game.getStack().counter(targetPointer.getFirst(game, source), source, game, targetZone, false, zoneDetail);
|
||||
return game.getStack().counter(getTargetPointer().getFirst(game, source), source, game, putIt);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getText(Mode mode) {
|
||||
StringBuilder sb = new StringBuilder("Counter target ");
|
||||
sb.append(mode.getTargets().get(0).getTargetName());
|
||||
if (staticText != null && !staticText.isEmpty()) {
|
||||
return staticText;
|
||||
}
|
||||
StringBuilder sb = new StringBuilder("counter ");
|
||||
sb.append(getTargetPointer().describeTargets(mode.getTargets(), "it"));
|
||||
sb.append(". If that spell is countered this way, ");
|
||||
if (targetZone == Zone.EXILED) {
|
||||
if (putIt == PutCards.EXILED) {
|
||||
sb.append("exile it instead of putting it into its owner's graveyard");
|
||||
} else {
|
||||
sb.append(putIt == PutCards.TOP_OR_BOTTOM ? "put that card " : "put it ");
|
||||
sb.append(putIt.getMessage(true, false));
|
||||
sb.append(" instead of into that player's graveyard");
|
||||
}
|
||||
if (targetZone == Zone.HAND) {
|
||||
sb.append("put it into its owner's hand instead of into that player's graveyard");
|
||||
}
|
||||
if (targetZone == Zone.LIBRARY) {
|
||||
sb.append("put it on ");
|
||||
switch (zoneDetail) {
|
||||
case BOTTOM:
|
||||
sb.append("the bottom");
|
||||
break;
|
||||
case TOP:
|
||||
sb.append("top");
|
||||
break;
|
||||
case CHOOSE:
|
||||
sb.append("top or bottom");
|
||||
break;
|
||||
case NONE:
|
||||
sb.append("<not allowed value>");
|
||||
break;
|
||||
}
|
||||
sb.append(" of its owner's library instead of into that player's graveyard");
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,8 +7,7 @@ import mage.abilities.costs.mana.ManaCost;
|
|||
import mage.abilities.dynamicvalue.DynamicValue;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.Zone;
|
||||
import mage.constants.ZoneDetail;
|
||||
import mage.constants.PutCards;
|
||||
import mage.game.Game;
|
||||
import mage.game.stack.StackObject;
|
||||
import mage.players.Player;
|
||||
|
@ -88,11 +87,7 @@ public class CounterUnlessPaysEffect extends OneShotEffect {
|
|||
if (!(player.chooseUse(Outcome.Benefit, message, source, game)
|
||||
&& costToPay.pay(source, game, source, spell.getControllerId(), false, null))) {
|
||||
game.informPlayers(player.getLogName() + " chooses not to pay " + costValueMessage + " to prevent the counter effect");
|
||||
if (exile) {
|
||||
game.getStack().counter(spell.getId(), source, game, Zone.EXILED, false, ZoneDetail.NONE);
|
||||
} else {
|
||||
return game.getStack().counter(spell.getId(), source, game);
|
||||
}
|
||||
game.getStack().counter(spell.getId(), source, game, exile ? PutCards.EXILED : PutCards.GRAVEYARD);
|
||||
}
|
||||
game.informPlayers(player.getLogName() + " chooses to pay " + costValueMessage + " to prevent the counter effect");
|
||||
return true;
|
||||
|
@ -103,13 +98,8 @@ public class CounterUnlessPaysEffect extends OneShotEffect {
|
|||
if (staticText != null && !staticText.isEmpty()) {
|
||||
return staticText;
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (mode.getTargets().isEmpty()) {
|
||||
sb.append("counter it");
|
||||
} else {
|
||||
sb.append("counter target ").append(mode.getTargets().get(0).getTargetName());
|
||||
}
|
||||
StringBuilder sb = new StringBuilder("counter ");
|
||||
sb.append(getTargetPointer().describeTargets(mode.getTargets(), "it"));
|
||||
sb.append(" unless its controller pays ");
|
||||
if (cost != null) {
|
||||
sb.append(cost.getText());
|
||||
|
@ -121,9 +111,8 @@ public class CounterUnlessPaysEffect extends OneShotEffect {
|
|||
sb.append(genericMana.getMessage());
|
||||
}
|
||||
if (exile) {
|
||||
sb.append(". If that spell is countered this way, exile it instead of putting it into its owner's graveyard.");
|
||||
sb.append(". If that spell is countered this way, exile it instead of putting it into its owner's graveyard");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
|
||||
package mage.constants;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public enum ZoneDetail {
|
||||
|
||||
NONE,
|
||||
TOP,
|
||||
BOTTOM,
|
||||
CHOOSE
|
||||
}
|
|
@ -413,47 +413,46 @@ public class Spell extends StackObjectImpl implements Card {
|
|||
|
||||
@Override
|
||||
public void counter(Ability source, Game game) {
|
||||
this.counter(source, game, Zone.GRAVEYARD, false, ZoneDetail.NONE);
|
||||
this.counter(source, game, PutCards.GRAVEYARD);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void counter(Ability source, Game game, Zone zone, boolean owner, ZoneDetail zoneDetail) {
|
||||
public void counter(Ability source, Game game, PutCards zone) {
|
||||
// source can be null for fizzled spells, don't use that code in your ZONE_CHANGE watchers/triggers:
|
||||
// event.getSourceId().equals
|
||||
// TODO: so later it must be replaced to another technics with non null source
|
||||
UUID counteringSourceId = (source == null ? null : source.getSourceId());
|
||||
// TODO: fizzled spells are no longer considered "countered" as of current rules; may need refactor
|
||||
this.countered = true;
|
||||
if (!isCopy()) {
|
||||
Player player = game.getPlayer(game.getControllerId(counteringSourceId));
|
||||
if (player == null) {
|
||||
player = game.getPlayer(getControllerId());
|
||||
}
|
||||
if (player != null) {
|
||||
Ability counteringAbility = null;
|
||||
MageObject counteringObject = game.getObject(counteringSourceId);
|
||||
if (counteringObject instanceof StackObject) {
|
||||
counteringAbility = ((StackObject) counteringObject).getStackAbility();
|
||||
}
|
||||
if (zone == Zone.LIBRARY) {
|
||||
if (zoneDetail == ZoneDetail.CHOOSE) {
|
||||
if (player.chooseUse(Outcome.Detriment, "Move countered spell to the top of the library? (otherwise it goes to the bottom)", counteringAbility, game)) {
|
||||
zoneDetail = ZoneDetail.TOP;
|
||||
} else {
|
||||
zoneDetail = ZoneDetail.BOTTOM;
|
||||
}
|
||||
}
|
||||
if (zoneDetail == ZoneDetail.TOP) {
|
||||
player.putCardsOnTopOfLibrary(new CardsImpl(card), game, counteringAbility, false);
|
||||
} else {
|
||||
player.putCardsOnBottomOfLibrary(new CardsImpl(card), game, counteringAbility, false);
|
||||
}
|
||||
} else {
|
||||
player.moveCards(card, zone, counteringAbility, game, false, false, owner, null);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (isCopy()) {
|
||||
// copied spell, only remove from stack
|
||||
game.getStack().remove(this, game);
|
||||
return;
|
||||
}
|
||||
Player player = game.getPlayer(source == null ? getControllerId() : source.getControllerId());
|
||||
if (player != null) {
|
||||
switch (zone) {
|
||||
case TOP_OR_BOTTOM:
|
||||
if (player.chooseUse(Outcome.Detriment,
|
||||
"Put the countered spell on the top or bottom of its owner's library?",
|
||||
null, "Top", "Bottom", source, game
|
||||
)) {
|
||||
player.putCardsOnTopOfLibrary(new CardsImpl(card), game, source, false);
|
||||
} else {
|
||||
player.putCardsOnBottomOfLibrary(new CardsImpl(card), game, source, false);
|
||||
}
|
||||
break;
|
||||
case TOP_ANY:
|
||||
player.putCardsOnTopOfLibrary(new CardsImpl(card), game, source, false);
|
||||
break;
|
||||
case BOTTOM_ANY:
|
||||
case BOTTOM_RANDOM:
|
||||
player.putCardsOnBottomOfLibrary(new CardsImpl(card), game, source, false);
|
||||
break;
|
||||
case BATTLEFIELD_TAPPED:
|
||||
player.moveCards(card, Zone.BATTLEFIELD, source, game, true, false, false, null);
|
||||
break;
|
||||
default:
|
||||
player.moveCards(card, zone.getZone(), source, game);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,8 +6,7 @@ import java.util.Date;
|
|||
import java.util.UUID;
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.constants.Zone;
|
||||
import mage.constants.ZoneDetail;
|
||||
import mage.constants.PutCards;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import org.apache.log4j.Logger;
|
||||
|
@ -60,13 +59,10 @@ public class SpellStack extends ArrayDeque<StackObject> {
|
|||
}
|
||||
|
||||
public boolean counter(UUID objectId, Ability source, Game game) {
|
||||
return counter(objectId, source, game, Zone.GRAVEYARD, false, ZoneDetail.TOP);
|
||||
return counter(objectId, source, game, PutCards.GRAVEYARD);
|
||||
}
|
||||
|
||||
public boolean counter(UUID objectId, Ability source, Game game, Zone zone, boolean owner, ZoneDetail zoneDetail) {
|
||||
// the counter logic is copied by some spells to handle replacement effects of the countered spell
|
||||
// so if logic is changed here check those spells for needed changes too
|
||||
// Concerned cards to check: Hinder, Spell Crumple
|
||||
public boolean counter(UUID objectId, Ability source, Game game, PutCards zone) {
|
||||
StackObject stackObject = getStackObject(objectId);
|
||||
MageObject sourceObject = game.getObject(source);
|
||||
if (stackObject != null && sourceObject != null) {
|
||||
|
@ -86,7 +82,7 @@ public class SpellStack extends ArrayDeque<StackObject> {
|
|||
if (!(stackObject instanceof Spell)) { // spells are removed from stack by the card movement
|
||||
this.remove(stackObject, game);
|
||||
}
|
||||
stackObject.counter(source, game, zone, owner, zoneDetail);
|
||||
stackObject.counter(source, game, zone);
|
||||
if (!game.isSimulation()) {
|
||||
game.informPlayers(counteredObjectName + " is countered by " + sourceObject.getLogName());
|
||||
}
|
||||
|
|
|
@ -104,12 +104,12 @@ public class StackAbility extends StackObjectImpl implements Ability {
|
|||
|
||||
@Override
|
||||
public void counter(Ability source, Game game) {
|
||||
// zone, owner, top ignored
|
||||
this.counter(source, game, Zone.GRAVEYARD, true, ZoneDetail.TOP);
|
||||
// zone ignored
|
||||
this.counter(source, game, PutCards.GRAVEYARD);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void counter(Ability source, Game game, Zone zone, boolean owner, ZoneDetail zoneDetail) {
|
||||
public void counter(Ability source, Game game, PutCards zone) {
|
||||
//20100716 - 603.8
|
||||
if (ability instanceof StateTriggeredAbility) {
|
||||
((StateTriggeredAbility) ability).counter(game);
|
||||
|
|
|
@ -3,8 +3,7 @@ package mage.game.stack;
|
|||
import mage.MageItem;
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.constants.Zone;
|
||||
import mage.constants.ZoneDetail;
|
||||
import mage.constants.PutCards;
|
||||
import mage.filter.predicate.Predicate;
|
||||
import mage.filter.predicate.mageobject.MageObjectReferencePredicate;
|
||||
import mage.game.Controllable;
|
||||
|
@ -25,7 +24,7 @@ public interface StackObject extends MageObject, Controllable {
|
|||
*/
|
||||
void counter(Ability source, Game game);
|
||||
|
||||
void counter(Ability source, Game game, Zone zone, boolean owner, ZoneDetail zoneDetail);
|
||||
void counter(Ability source, Game game, PutCards zone);
|
||||
|
||||
Ability getStackAbility();
|
||||
|
||||
|
|
Loading…
Reference in a new issue