diff --git a/Mage.Sets/src/mage/sets/championsofkamigawa/Hinder.java b/Mage.Sets/src/mage/sets/championsofkamigawa/Hinder.java index 378e07cfa4..0325ba73da 100644 --- a/Mage.Sets/src/mage/sets/championsofkamigawa/Hinder.java +++ b/Mage.Sets/src/mage/sets/championsofkamigawa/Hinder.java @@ -33,6 +33,7 @@ import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Rarity; import mage.constants.Zone; +import mage.constants.ZoneDetail; import mage.target.TargetSpell; /** @@ -46,7 +47,7 @@ public class Hinder extends CardImpl { this.expansionSetCode = "CHK"; // 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, true)); + this.getSpellAbility().addEffect(new CounterTargetWithReplacementEffect(Zone.LIBRARY, ZoneDetail.CHOOSE)); this.getSpellAbility().addTarget(new TargetSpell()); } diff --git a/Mage.Sets/src/mage/sets/commander/SpellCrumple.java b/Mage.Sets/src/mage/sets/commander/SpellCrumple.java index 1f1c8bd5f3..3a6f99f096 100644 --- a/Mage.Sets/src/mage/sets/commander/SpellCrumple.java +++ b/Mage.Sets/src/mage/sets/commander/SpellCrumple.java @@ -36,6 +36,7 @@ import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.Rarity; import mage.constants.Zone; +import mage.constants.ZoneDetail; import mage.game.Game; import mage.players.Player; import mage.target.TargetSpell; @@ -86,7 +87,7 @@ class SpellCrumpleCounterEffect 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.getSourceId(), game, Zone.LIBRARY, false, false); + return game.getStack().counter(targetPointer.getFirst(game, source), source.getSourceId(), game, Zone.LIBRARY, false, ZoneDetail.BOTTOM); } return false; } diff --git a/Mage.Sets/src/mage/sets/conflux/LapseOfCertainty.java b/Mage.Sets/src/mage/sets/conflux/LapseOfCertainty.java index 3e14a1dffb..20e40d4e14 100644 --- a/Mage.Sets/src/mage/sets/conflux/LapseOfCertainty.java +++ b/Mage.Sets/src/mage/sets/conflux/LapseOfCertainty.java @@ -28,11 +28,12 @@ package mage.sets.conflux; import java.util.UUID; +import mage.abilities.effects.common.CounterTargetWithReplacementEffect; +import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Rarity; import mage.constants.Zone; -import mage.abilities.effects.common.CounterTargetWithReplacementEffect; -import mage.cards.CardImpl; +import mage.constants.ZoneDetail; import mage.target.TargetSpell; /** @@ -45,9 +46,8 @@ public class LapseOfCertainty extends CardImpl { super(ownerId, 9, "Lapse of Certainty", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{2}{W}"); this.expansionSetCode = "CON"; - // 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, true)); + this.getSpellAbility().addEffect(new CounterTargetWithReplacementEffect(Zone.LIBRARY, ZoneDetail.TOP)); this.getSpellAbility().addTarget(new TargetSpell()); } diff --git a/Mage.Sets/src/mage/sets/judgment/Spelljack.java b/Mage.Sets/src/mage/sets/judgment/Spelljack.java index f6961e9683..53eb77a358 100644 --- a/Mage.Sets/src/mage/sets/judgment/Spelljack.java +++ b/Mage.Sets/src/mage/sets/judgment/Spelljack.java @@ -40,6 +40,7 @@ import mage.constants.Duration; import mage.constants.Outcome; import mage.constants.Rarity; import mage.constants.Zone; +import mage.constants.ZoneDetail; import mage.game.Game; import mage.game.stack.Spell; import mage.game.stack.StackObject; @@ -94,7 +95,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.getSourceId(), game, Zone.EXILED, false, false)) { + if (stackObject != null && game.getStack().counter(targetId, source.getSourceId(), game, Zone.EXILED, false, ZoneDetail.NONE)) { Card card = ((Spell) stackObject).getCard(); if (card != null) { ContinuousEffect effect = new SpelljackCastFromExileEffect(); diff --git a/Mage.Sets/src/mage/sets/khansoftarkir/KheruSpellsnatcher.java b/Mage.Sets/src/mage/sets/khansoftarkir/KheruSpellsnatcher.java index 2ce29a65aa..76e0486b3a 100644 --- a/Mage.Sets/src/mage/sets/khansoftarkir/KheruSpellsnatcher.java +++ b/Mage.Sets/src/mage/sets/khansoftarkir/KheruSpellsnatcher.java @@ -45,6 +45,7 @@ import mage.constants.Duration; import mage.constants.Outcome; import mage.constants.Rarity; import mage.constants.Zone; +import mage.constants.ZoneDetail; import mage.game.Game; import mage.game.stack.Spell; import mage.game.stack.StackObject; @@ -109,7 +110,7 @@ class KheruSpellsnatcherEffect extends OneShotEffect { StackObject stackObject = game.getStack().getStackObject(objectId); if (stackObject != null - && game.getStack().counter(targetPointer.getFirst(game, source), source.getSourceId(), game, Zone.EXILED, false, false)) { + && game.getStack().counter(targetPointer.getFirst(game, source), source.getSourceId(), game, Zone.EXILED, false, ZoneDetail.NONE)) { if (!((Spell) stackObject).isCopiedSpell()) { MageObject card = game.getObject(stackObject.getSourceId()); if (card instanceof Card) { diff --git a/Mage.Sets/src/mage/sets/seventhedition/MemoryLapse.java b/Mage.Sets/src/mage/sets/seventhedition/MemoryLapse.java index ccf4f0c46f..a1a0d6e709 100644 --- a/Mage.Sets/src/mage/sets/seventhedition/MemoryLapse.java +++ b/Mage.Sets/src/mage/sets/seventhedition/MemoryLapse.java @@ -27,15 +27,15 @@ */ package mage.sets.seventhedition; -import mage.constants.CardType; -import mage.constants.Rarity; +import java.util.UUID; import mage.abilities.effects.common.CounterTargetWithReplacementEffect; import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Rarity; import mage.constants.Zone; +import mage.constants.ZoneDetail; import mage.target.TargetSpell; -import java.util.UUID; - /** * * @author magenoxx_at_gmail.com @@ -46,9 +46,8 @@ public class MemoryLapse extends CardImpl { super(ownerId, 88, "Memory Lapse", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{1}{U}"); this.expansionSetCode = "7ED"; - // 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, true)); + this.getSpellAbility().addEffect(new CounterTargetWithReplacementEffect(Zone.LIBRARY, ZoneDetail.TOP)); this.getSpellAbility().addTarget(new TargetSpell()); } diff --git a/Mage.Sets/src/mage/sets/visions/Desertion.java b/Mage.Sets/src/mage/sets/visions/Desertion.java index d963b62b70..42d35efc92 100644 --- a/Mage.Sets/src/mage/sets/visions/Desertion.java +++ b/Mage.Sets/src/mage/sets/visions/Desertion.java @@ -35,6 +35,7 @@ import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.Rarity; import mage.constants.Zone; +import mage.constants.ZoneDetail; import mage.filter.FilterSpell; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.CardTypePredicate; @@ -95,7 +96,7 @@ class DesertionEffect 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.getSourceId(), game, Zone.BATTLEFIELD, false, false); + return game.getStack().counter(targetPointer.getFirst(game, source), source.getSourceId(), game, Zone.BATTLEFIELD, false, ZoneDetail.NONE); } return false; } diff --git a/Mage/src/main/java/mage/abilities/effects/common/CounterTargetWithReplacementEffect.java b/Mage/src/main/java/mage/abilities/effects/common/CounterTargetWithReplacementEffect.java index 5a9a0485f5..7658e5554b 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/CounterTargetWithReplacementEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/CounterTargetWithReplacementEffect.java @@ -32,6 +32,7 @@ import mage.abilities.Mode; import mage.abilities.effects.OneShotEffect; import mage.constants.Outcome; import mage.constants.Zone; +import mage.constants.ZoneDetail; import mage.game.Game; import mage.players.Player; @@ -41,28 +42,28 @@ import mage.players.Player; public class CounterTargetWithReplacementEffect extends OneShotEffect { private Zone targetZone; - private boolean flag; + private ZoneDetail zoneDetail; public CounterTargetWithReplacementEffect(Zone targetZone) { - this(targetZone, false); + this(targetZone, ZoneDetail.NONE); } /** * * @param targetZone - * @param flag use to specify when moving card to library + * @param zoneDetail use to specify when moving card to library */ - public CounterTargetWithReplacementEffect(Zone targetZone, boolean flag) { + public CounterTargetWithReplacementEffect(Zone targetZone, ZoneDetail zoneDetail) { super(Outcome.Detriment); this.targetZone = targetZone; - this.flag = flag; + this.zoneDetail = zoneDetail; } public CounterTargetWithReplacementEffect(final CounterTargetWithReplacementEffect effect) { super(effect); this.targetZone = effect.targetZone; - this.flag = effect.flag; + this.zoneDetail = effect.zoneDetail; } @Override @@ -74,7 +75,7 @@ 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.getSourceId(), game, targetZone, false, flag); + return game.getStack().counter(targetPointer.getFirst(game, source), source.getSourceId(), game, targetZone, false, zoneDetail); } return false; } @@ -92,7 +93,16 @@ public class CounterTargetWithReplacementEffect extends OneShotEffect { } if (targetZone.equals(Zone.LIBRARY)) { sb.append("put it on "); - sb.append(flag ? "top" : "the bottom"); + switch (zoneDetail) { + case BOTTOM: + sb.append("the bottom"); + case TOP: + sb.append("top"); + case CHOOSE: + sb.append("top or bottom"); + case NONE: + sb.append(""); + } sb.append(" of its owner's library instead of into that player's graveyard"); } diff --git a/Mage/src/main/java/mage/constants/Zone.java b/Mage/src/main/java/mage/constants/Zone.java index 8114afa9e7..048aa80ad3 100644 --- a/Mage/src/main/java/mage/constants/Zone.java +++ b/Mage/src/main/java/mage/constants/Zone.java @@ -1,3 +1,30 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ package mage.constants; /** diff --git a/Mage/src/main/java/mage/constants/ZoneDetail.java b/Mage/src/main/java/mage/constants/ZoneDetail.java new file mode 100644 index 0000000000..3d76d9d0b6 --- /dev/null +++ b/Mage/src/main/java/mage/constants/ZoneDetail.java @@ -0,0 +1,40 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.constants; + +/** + * + * @author LevelX2 + */ +public enum ZoneDetail { + + NONE, + TOP, + BOTTOM, + CHOOSE +} diff --git a/Mage/src/main/java/mage/game/stack/Spell.java b/Mage/src/main/java/mage/game/stack/Spell.java index 5e23cf1f1d..0c14adfa3d 100644 --- a/Mage/src/main/java/mage/game/stack/Spell.java +++ b/Mage/src/main/java/mage/game/stack/Spell.java @@ -48,9 +48,11 @@ import mage.cards.Card; import mage.cards.CardsImpl; import mage.cards.SplitCard; import mage.constants.CardType; +import mage.constants.Outcome; import mage.constants.Rarity; import mage.constants.SpellAbilityType; import mage.constants.Zone; +import mage.constants.ZoneDetail; import mage.counters.Counter; import mage.counters.Counters; import mage.game.Game; @@ -328,11 +330,11 @@ public class Spell extends StackObjImpl implements Card { @Override public void counter(UUID sourceId, Game game) { - this.counter(sourceId, game, Zone.GRAVEYARD, false, true); + this.counter(sourceId, game, Zone.GRAVEYARD, false, ZoneDetail.NONE); } @Override - public void counter(UUID sourceId, Game game, Zone zone, boolean owner, boolean top) { + public void counter(UUID sourceId, Game game, Zone zone, boolean owner, ZoneDetail zoneDetail) { this.countered = true; if (!isCopiedSpell()) { Player player = game.getPlayer(game.getControllerId(sourceId)); @@ -346,7 +348,14 @@ public class Spell extends StackObjImpl implements Card { counteringAbility = ((StackObject) counteringObject).getStackAbility(); } if (zone.equals(Zone.LIBRARY)) { - if (top) { + if (zoneDetail.equals(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.equals(ZoneDetail.TOP)) { player.putCardsOnTopOfLibrary(new CardsImpl(card), game, counteringAbility, false); } else { player.putCardsOnBottomOfLibrary(new CardsImpl(card), game, counteringAbility, false); diff --git a/Mage/src/main/java/mage/game/stack/SpellStack.java b/Mage/src/main/java/mage/game/stack/SpellStack.java index cf261770e8..ed0bf56387 100644 --- a/Mage/src/main/java/mage/game/stack/SpellStack.java +++ b/Mage/src/main/java/mage/game/stack/SpellStack.java @@ -32,6 +32,7 @@ import java.util.Date; import java.util.UUID; import mage.MageObject; import mage.constants.Zone; +import mage.constants.ZoneDetail; import mage.game.Game; import mage.game.events.GameEvent; import org.apache.log4j.Logger; @@ -82,10 +83,10 @@ public class SpellStack extends ArrayDeque { } public boolean counter(UUID objectId, UUID sourceId, Game game) { - return counter(objectId, sourceId, game, Zone.GRAVEYARD, false, true); + return counter(objectId, sourceId, game, Zone.GRAVEYARD, false, ZoneDetail.TOP); } - public boolean counter(UUID objectId, UUID sourceId, Game game, Zone zone, boolean owner, boolean onTop) { + public boolean counter(UUID objectId, UUID sourceId, 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 @@ -108,7 +109,7 @@ public class SpellStack extends ArrayDeque { if (!(stackObject instanceof Spell)) { // spells are removed from stack by the card movement this.remove(stackObject); } - stackObject.counter(sourceId, game, zone, owner, onTop); + stackObject.counter(sourceId, game, zone, owner, zoneDetail); if (!game.isSimulation()) { game.informPlayers(counteredObjectName + " is countered by " + sourceObject.getLogName()); } diff --git a/Mage/src/main/java/mage/game/stack/StackAbility.java b/Mage/src/main/java/mage/game/stack/StackAbility.java index 7215000ee4..91c735bf9c 100644 --- a/Mage/src/main/java/mage/game/stack/StackAbility.java +++ b/Mage/src/main/java/mage/game/stack/StackAbility.java @@ -57,6 +57,7 @@ import mage.constants.AbilityWord; import mage.constants.CardType; import mage.constants.EffectType; import mage.constants.Zone; +import mage.constants.ZoneDetail; import mage.game.Game; import mage.game.events.GameEvent; import mage.players.Player; @@ -123,11 +124,11 @@ public class StackAbility extends StackObjImpl implements Ability { @Override public void counter(UUID sourceId, Game game) { // zone, owner, top ignored - this.counter(sourceId, game, Zone.GRAVEYARD, true, true); + this.counter(sourceId, game, Zone.GRAVEYARD, true, ZoneDetail.TOP); } @Override - public void counter(UUID sourceId, Game game, Zone zone, boolean owner, boolean top) { + public void counter(UUID sourceId, Game game, Zone zone, boolean owner, ZoneDetail zoneDetail) { //20100716 - 603.8 if (ability instanceof StateTriggeredAbility) { ((StateTriggeredAbility) ability).counter(game); diff --git a/Mage/src/main/java/mage/game/stack/StackObject.java b/Mage/src/main/java/mage/game/stack/StackObject.java index c1540d9138..0e973e879e 100644 --- a/Mage/src/main/java/mage/game/stack/StackObject.java +++ b/Mage/src/main/java/mage/game/stack/StackObject.java @@ -31,6 +31,7 @@ import java.util.UUID; import mage.MageObject; import mage.abilities.Ability; import mage.constants.Zone; +import mage.constants.ZoneDetail; import mage.filter.FilterPermanent; import mage.game.Controllable; import mage.game.Game; @@ -43,7 +44,7 @@ public interface StackObject extends MageObject, Controllable { void counter(UUID sourceId, Game game); - void counter(UUID sourceId, Game game, Zone zone, boolean owner, boolean top); + void counter(UUID sourceId, Game game, Zone zone, boolean owner, ZoneDetail zoneDetail); Ability getStackAbility();