Rework Spell.counter using PutCards and reimplement Desertion. Fixes #9299

This commit is contained in:
Alex W. Jackson 2022-10-10 21:21:31 -04:00
parent cbe610d339
commit e40934921f
27 changed files with 124 additions and 251 deletions

View file

@ -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());
}

View file

@ -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) {

View file

@ -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()));
}

View file

@ -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));
}

View file

@ -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
);
}
}

View file

@ -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.

View file

@ -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());
}

View file

@ -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));
}

View file

@ -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));
}

View file

@ -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());
}

View file

@ -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));
}

View file

@ -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) {

View file

@ -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());
}

View file

@ -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));
}

View file

@ -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());
}

View file

@ -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.

View file

@ -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) {

View file

@ -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;
}
}

View file

@ -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);

View file

@ -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());
}

View file

@ -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();
}
}

View file

@ -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();
}
}

View file

@ -1,14 +0,0 @@
package mage.constants;
/**
*
* @author LevelX2
*/
public enum ZoneDetail {
NONE,
TOP,
BOTTOM,
CHOOSE
}

View file

@ -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);
}
}
}

View file

@ -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());
}

View file

@ -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);

View file

@ -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();