diff --git a/Mage.Sets/src/mage/sets/championsofkamigawa/Hinder.java b/Mage.Sets/src/mage/sets/championsofkamigawa/Hinder.java index dac84fa0c2d..378e07cfa4a 100644 --- a/Mage.Sets/src/mage/sets/championsofkamigawa/Hinder.java +++ b/Mage.Sets/src/mage/sets/championsofkamigawa/Hinder.java @@ -28,27 +28,12 @@ package mage.sets.championsofkamigawa; import java.util.UUID; +import mage.abilities.effects.common.CounterTargetWithReplacementEffect; +import mage.cards.CardImpl; import mage.constants.CardType; -import mage.constants.Outcome; import mage.constants.Rarity; import mage.constants.Zone; -import mage.MageObject; -import mage.abilities.Ability; -import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.ReplacementEffectImpl; -import mage.cards.Card; -import mage.cards.CardImpl; -import mage.constants.Duration; -import mage.constants.PhaseStep; -import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.events.GameEvent.EventType; -import mage.game.events.ZoneChangeEvent; -import mage.game.stack.Spell; -import mage.game.stack.StackObject; -import mage.players.Player; import mage.target.TargetSpell; -import mage.target.targetpointer.FixedTarget; /** * @@ -61,7 +46,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 HinderEffect()); + this.getSpellAbility().addEffect(new CounterTargetWithReplacementEffect(Zone.LIBRARY, true)); this.getSpellAbility().addTarget(new TargetSpell()); } @@ -74,119 +59,3 @@ public class Hinder extends CardImpl { return new Hinder(this); } } - -class HinderEffect extends OneShotEffect { - - public HinderEffect() { - super(Outcome.Detriment); - this.staticText = "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"; - } - - public HinderEffect(final HinderEffect effect) { - super(effect); - } - - @Override - public HinderEffect copy() { - return new HinderEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - UUID objectId = source.getFirstTarget(); - UUID sourceId = source.getSourceId(); - // counter code from Spellstack - StackObject stackObject = game.getStack().getStackObject(objectId); - MageObject sourceObject = game.getObject(sourceId); - if (stackObject != null && sourceObject != null) { - if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.COUNTER, objectId, sourceId, stackObject.getControllerId()))) { - if ( stackObject instanceof Spell ) { - game.rememberLKI(objectId, Zone.STACK, (Spell)stackObject); - } - // Hinder specific code - ReplacementEffectImpl effect = new HinderReplacementEffect(); - effect.setTargetPointer(new FixedTarget(stackObject.getId())); - game.addEffect(effect, source); - // Hinder specific code end - game.informPlayers(new StringBuilder(stackObject.getName()).append(" is countered by ").append(sourceObject.getLogName()).toString()); - game.getStack().remove(stackObject); - stackObject.counter(sourceId, game); // tries to move to graveyard - game.fireEvent(GameEvent.getEvent(GameEvent.EventType.COUNTERED, objectId, sourceId, stackObject.getControllerId())); - } else { - game.informPlayers(new StringBuilder(stackObject.getName()).append(" could not be countered by ").append(sourceObject.getLogName()).toString()); - } - return true; - } - return false; - // counter code from Spellstack end - } -} - -class HinderReplacementEffect extends ReplacementEffectImpl { - - private PhaseStep phaseStep; - - public HinderReplacementEffect() { - super(Duration.OneUse, Outcome.Benefit); - staticText = "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"; - phaseStep = null; - } - - public HinderReplacementEffect(final HinderReplacementEffect effect) { - super(effect); - phaseStep = effect.phaseStep; - } - - @Override - public HinderReplacementEffect copy() { - return new HinderReplacementEffect(this); - } - - @Override - public boolean isInactive(Ability source, Game game) { - if (!game.getPhase().getStep().getType().equals(phaseStep)) { - return true; - } - return super.isInactive(source, game); - } - - @Override - public void init(Ability source, Game game) { - phaseStep = game.getPhase().getStep().getType(); - super.init(source, game); - } - - @Override - public boolean replaceEvent(GameEvent event, Ability source, Game game) { - MageObject targetObject = game.getObject(event.getTargetId()); - if (targetObject instanceof Card) { - Card card = (Card) targetObject; - Player player = game.getPlayer(source.getControllerId()); - if (player != null) { - boolean top = player.chooseUse(Outcome.Neutral, "Put " + card.getName() + " on top of the library? Otherwise it will be put on the bottom.", source, game); - if (card.moveToZone(Zone.LIBRARY, source.getSourceId(), game, top, event.getAppliedEffects())) { - game.informPlayers(player.getLogName() + " has put " + card.getName() + " on " + (top ? "top" : "the bottom") + " of the library."); - } - return true; - } - } - return false; - } - - @Override - public boolean checksEventType(GameEvent event, Game game) { - return event.getType() == EventType.ZONE_CHANGE; - } - - @Override - public boolean applies(GameEvent event, Ability source, Game game) { - if (((ZoneChangeEvent)event).getToZone().equals(Zone.GRAVEYARD)) { - MageObject mageObject = game.getLastKnownInformation(getTargetPointer().getFirst(game, source), Zone.STACK); - if (mageObject instanceof Spell) { - return ((Spell)mageObject).getSourceId().equals(event.getTargetId()); - } - } - return false; - } - -} diff --git a/Mage.Sets/src/mage/sets/fifthdawn/FoldIntoAEther.java b/Mage.Sets/src/mage/sets/fifthdawn/FoldIntoAEther.java index 7ddcccce951..c9022050cad 100644 --- a/Mage.Sets/src/mage/sets/fifthdawn/FoldIntoAEther.java +++ b/Mage.Sets/src/mage/sets/fifthdawn/FoldIntoAEther.java @@ -28,14 +28,14 @@ package mage.sets.fifthdawn; import java.util.UUID; -import mage.constants.CardType; -import mage.constants.Outcome; -import mage.constants.Rarity; -import mage.constants.Zone; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.cards.Card; import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.Zone; import mage.filter.common.FilterCreatureCard; import mage.game.Game; import mage.game.stack.StackObject; @@ -53,7 +53,6 @@ public class FoldIntoAEther extends CardImpl { super(ownerId, 31, "Fold into AEther", Rarity.UNCOMMON, new CardType[]{CardType.INSTANT}, "{2}{U}{U}"); this.expansionSetCode = "5DN"; - // Counter target spell. If that spell is countered this way, its controller may put a creature card from his or her hand onto the battlefield. this.getSpellAbility().addEffect(new FoldIntoAEtherEffect()); this.getSpellAbility().addTarget(new TargetSpell()); @@ -87,19 +86,21 @@ class FoldIntoAEtherEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - StackObject stackObject = game.getStack().getStackObject(source.getFirstTarget()); - Player player = null; + UUID targetId = getTargetPointer().getFirst(game, source); + StackObject stackObject = game.getStack().getStackObject(targetId); + Player spellController = null; if (stackObject != null) { - player = game.getPlayer(stackObject.getControllerId()); + spellController = game.getPlayer(stackObject.getControllerId()); } - if (game.getStack().counter(source.getFirstTarget(), source.getSourceId(), game)) { + if (game.getStack().counter(targetId, source.getSourceId(), game)) { TargetCardInHand target = new TargetCardInHand(new FilterCreatureCard()); - if (player != null - && player.chooseUse(Outcome.Neutral, "Put a creature card from your hand in play?", source, game) - && player.choose(Outcome.PutCreatureInPlay, target, source.getSourceId(), game)) { + if (spellController != null + && target.canChoose(source.getSourceId(), source.getSourceId(), game) + && spellController.chooseUse(Outcome.Neutral, "Put a creature card from your hand in play?", source, game) + && spellController.choose(Outcome.PutCreatureInPlay, target, source.getSourceId(), game)) { Card card = game.getCard(target.getFirstTarget()); if (card != null) { - card.putOntoBattlefield(game, Zone.HAND, source.getSourceId(), player.getId()); + spellController.moveCards(card, Zone.BATTLEFIELD, source, game); } } return true; diff --git a/Mage.Sets/src/mage/sets/judgment/Spelljack.java b/Mage.Sets/src/mage/sets/judgment/Spelljack.java index 4c5f862cbe5..13152d69392 100644 --- a/Mage.Sets/src/mage/sets/judgment/Spelljack.java +++ b/Mage.Sets/src/mage/sets/judgment/Spelljack.java @@ -28,7 +28,6 @@ package mage.sets.judgment; import java.util.UUID; -import mage.MageObject; import mage.abilities.Ability; import mage.abilities.effects.AsThoughEffectImpl; import mage.abilities.effects.ContinuousEffect; @@ -42,7 +41,6 @@ import mage.constants.Outcome; import mage.constants.Rarity; import mage.constants.Zone; import mage.game.Game; -import mage.game.events.GameEvent; import mage.game.stack.Spell; import mage.game.stack.StackObject; import mage.players.Player; @@ -75,40 +73,35 @@ public class Spelljack extends CardImpl { } class SpelljackEffect extends OneShotEffect { - + SpelljackEffect() { super(Outcome.PlayForFree); this.staticText = "Counter target spell. If that spell is countered this way, exile it instead of putting it into its owner's graveyard. You may play it without paying its mana cost for as long as it remains exiled"; } - + SpelljackEffect(final SpelljackEffect effect) { super(effect); } - + @Override public SpelljackEffect copy() { return new SpelljackEffect(this); } - + @Override public boolean apply(Game game, Ability source) { - UUID objectId = targetPointer.getFirst(game, source); - UUID sourceId = source.getSourceId(); - - StackObject stackObject = game.getStack().getStackObject(objectId); - if (stackObject != null && !game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.COUNTER, objectId, sourceId, stackObject.getControllerId()))) { - game.rememberLKI(objectId, Zone.STACK, stackObject); - game.getStack().remove(stackObject); - if (!((Spell) stackObject).isCopiedSpell()) { - MageObject card = game.getObject(stackObject.getSourceId()); - if (card instanceof Card) { - ((Card) card).moveToZone(Zone.EXILED, sourceId, game, true); + Player controller = game.getPlayer(source.getControllerId()); + 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)) { + Card card = ((Spell) stackObject).getCard(); + if (card != null) { ContinuousEffect effect = new SpelljackCastFromExileEffect(); - effect.setTargetPointer(new FixedTarget(card.getId())); + effect.setTargetPointer(new FixedTarget(card.getId(), game.getState().getZoneChangeCounter(card.getId()))); game.addEffect(effect, source); } } - game.fireEvent(GameEvent.getEvent(GameEvent.EventType.COUNTERED, objectId, sourceId, stackObject.getControllerId())); return true; } return false; @@ -150,8 +143,7 @@ class SpelljackCastFromExileEffect extends AsThoughEffectImpl { Player player = game.getPlayer(affectedControllerId); player.setCastSourceIdWithAlternateMana(sourceId, null); return true; - } - else { + } else { this.discard(); } } @@ -159,4 +151,4 @@ class SpelljackCastFromExileEffect extends AsThoughEffectImpl { } return false; } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/sets/khansoftarkir/KheruSpellsnatcher.java b/Mage.Sets/src/mage/sets/khansoftarkir/KheruSpellsnatcher.java index 19cced0b3a5..df16a3a0fbe 100644 --- a/Mage.Sets/src/mage/sets/khansoftarkir/KheruSpellsnatcher.java +++ b/Mage.Sets/src/mage/sets/khansoftarkir/KheruSpellsnatcher.java @@ -46,7 +46,6 @@ import mage.constants.Outcome; import mage.constants.Rarity; import mage.constants.Zone; import mage.game.Game; -import mage.game.events.GameEvent; import mage.game.stack.Spell; import mage.game.stack.StackObject; import mage.players.Player; @@ -70,7 +69,7 @@ public class KheruSpellsnatcher extends CardImpl { // Morph {4}{U}{U} this.addAbility(new MorphAbility(this, new ManaCostsImpl<>("{4}{U}{U}"))); - + // When Kheru Spellthief is turned face up, counter target spell. If that spell is countered this way, exile it instead of putting it into its owner's graveyard. You may cast that card without paying its mana cost as long as it remains exiled. Ability ability = new TurnedFaceUpSourceTriggeredAbility(new KheruSpellsnatcherEffect()); ability.addTarget(new TargetSpell()); @@ -88,30 +87,29 @@ public class KheruSpellsnatcher extends CardImpl { } class KheruSpellsnatcherEffect extends OneShotEffect { - + KheruSpellsnatcherEffect() { super(Outcome.Benefit); this.staticText = "counter target spell. If that spell is countered this way, exile it instead of putting it into its owner's graveyard. You may cast that card without paying its mana cost as long as it remains exiled"; } - + KheruSpellsnatcherEffect(final KheruSpellsnatcherEffect effect) { super(effect); } - + @Override public KheruSpellsnatcherEffect copy() { return new KheruSpellsnatcherEffect(this); } - + @Override public boolean apply(Game game, Ability source) { UUID objectId = targetPointer.getFirst(game, source); UUID sourceId = source.getSourceId(); StackObject stackObject = game.getStack().getStackObject(objectId); - if (stackObject != null && !game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.COUNTER, objectId, sourceId, stackObject.getControllerId()))) { - game.rememberLKI(objectId, Zone.STACK, stackObject); - game.getStack().remove(stackObject); + if (stackObject != null + && game.getStack().counter(targetPointer.getFirst(game, source), source.getSourceId(), game, Zone.EXILED, false, false)) { if (!((Spell) stackObject).isCopiedSpell()) { MageObject card = game.getObject(stackObject.getSourceId()); if (card instanceof Card) { @@ -121,7 +119,6 @@ class KheruSpellsnatcherEffect extends OneShotEffect { game.addEffect(effect, source); } } - game.fireEvent(GameEvent.getEvent(GameEvent.EventType.COUNTERED, objectId, sourceId, stackObject.getControllerId())); return true; } return false; @@ -163,8 +160,7 @@ class KheruSpellsnatcherCastFromExileEffect extends AsThoughEffectImpl { Player player = game.getPlayer(affectedControllerId); player.setCastSourceIdWithAlternateMana(sourceId, null); return true; - } - else { + } else { this.discard(); } } @@ -172,4 +168,4 @@ class KheruSpellsnatcherCastFromExileEffect extends AsThoughEffectImpl { } return false; } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/sets/visions/Desertion.java b/Mage.Sets/src/mage/sets/visions/Desertion.java index c375729596b..d963b62b70c 100644 --- a/Mage.Sets/src/mage/sets/visions/Desertion.java +++ b/Mage.Sets/src/mage/sets/visions/Desertion.java @@ -28,10 +28,8 @@ package mage.sets.visions; import java.util.UUID; -import mage.MageObject; import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Outcome; @@ -41,9 +39,6 @@ import mage.filter.FilterSpell; import mage.filter.predicate.Predicates; import mage.filter.predicate.mageobject.CardTypePredicate; import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.stack.Spell; -import mage.game.stack.StackObject; import mage.players.Player; import mage.target.TargetSpell; @@ -98,29 +93,10 @@ class DesertionEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - UUID objectId = source.getFirstTarget(); - UUID sourceId = source.getSourceId(); Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { - StackObject stackObject = game.getStack().getStackObject(objectId); - if (stackObject != null && !game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.COUNTER, objectId, sourceId, stackObject.getControllerId()))) { - if (stackObject instanceof Spell) { - game.rememberLKI(objectId, Zone.STACK, (Spell) stackObject); - game.getStack().remove(stackObject); - if (!((Spell) stackObject).isCopiedSpell() && filter.match(stackObject, source.getSourceId(), source.getControllerId(), game)) { - MageObject card = game.getObject(stackObject.getSourceId()); - if (card instanceof Card) { - controller.moveCards((Card) card, Zone.BATTLEFIELD, source, game); - } - } else { - stackObject.counter(sourceId, game); - } - game.fireEvent(GameEvent.getEvent(GameEvent.EventType.COUNTERED, objectId, sourceId, stackObject.getControllerId())); - return true; - } - } + return game.getStack().counter(targetPointer.getFirst(game, source), source.getSourceId(), game, Zone.BATTLEFIELD, false, false); } - return false; } diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/counterspell/DesertionTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/counterspell/DesertionTest.java new file mode 100644 index 00000000000..ce8872c313d --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/oneshot/counterspell/DesertionTest.java @@ -0,0 +1,76 @@ +/* + * 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 org.mage.test.cards.abilities.oneshot.counterspell; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * + * @author LevelX2 + */ +public class DesertionTest extends CardTestPlayerBase { + + /** + * I cast Kozilek, Butcher of Truth from my hand and my opponent cast + * Desertion targeting Kozilek, Butcher of Truth. Desertion resolved but + * Kozilek, Butcher of Truth has disappeared (not in play for my opponent as + * expected and not in my command zone or hand or graveyard or library) + * + */ + @Test + public void testCounterKozilek() { + // When you cast Kozilek, Butcher of Truth, draw four cards. + // Annihilator 4 (Whenever this creature attacks, defending player sacrifices four permanents.) + // When Kozilek is put into a graveyard from anywhere, its owner shuffles his or her graveyard into his or her library. + addCard(Zone.HAND, playerA, "Kozilek, Butcher of Truth"); // {10} + addCard(Zone.BATTLEFIELD, playerA, "Island", 10); + addCard(Zone.GRAVEYARD, playerA, "Silvercoat Lion"); // {10} + + // 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. + addCard(Zone.HAND, playerB, "Desertion"); // {3}{U}{U} + addCard(Zone.BATTLEFIELD, playerB, "Island", 5); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Kozilek, Butcher of Truth"); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Desertion", "Kozilek, Butcher of Truth"); + + setStopAt(1, PhaseStep.CLEANUP); + execute(); + + assertGraveyardCount(playerB, "Desertion", 1); + + assertGraveyardCount(playerA, "Silvercoat Lion", 1); + assertGraveyardCount(playerA, "Kozilek, Butcher of Truth", 0); + assertHandCount(playerA, "Kozilek, Butcher of Truth", 0); + assertPermanentCount(playerB, "Kozilek, Butcher of Truth", 1); + + } +} diff --git a/Mage/src/mage/abilities/effects/ContinuousEffects.java b/Mage/src/mage/abilities/effects/ContinuousEffects.java index 2f35dbd4901..86b9389a115 100644 --- a/Mage/src/mage/abilities/effects/ContinuousEffects.java +++ b/Mage/src/mage/abilities/effects/ContinuousEffects.java @@ -985,6 +985,12 @@ public class ContinuousEffects implements Serializable { } layer = filterLayeredEffects(activeLayerEffects, Layer.PTChangingEffects_7); + for (ContinuousEffect effect : layer) { + HashSet abilities = layeredEffects.getAbility(effect.getId()); + for (Ability ability : abilities) { + effect.apply(Layer.PTChangingEffects_7, SubLayer.CharacteristicDefining_7a, ability, game); + } + } for (ContinuousEffect effect : layer) { HashSet abilities = layeredEffects.getAbility(effect.getId()); for (Ability ability : abilities) { diff --git a/Mage/src/mage/abilities/effects/common/CounterTargetWithReplacementEffect.java b/Mage/src/mage/abilities/effects/common/CounterTargetWithReplacementEffect.java index 01e36d989b4..5a9a0485f59 100644 --- a/Mage/src/mage/abilities/effects/common/CounterTargetWithReplacementEffect.java +++ b/Mage/src/mage/abilities/effects/common/CounterTargetWithReplacementEffect.java @@ -27,17 +27,12 @@ */ package mage.abilities.effects.common; -import java.util.UUID; import mage.abilities.Ability; import mage.abilities.Mode; import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; import mage.constants.Outcome; import mage.constants.Zone; import mage.game.Game; -import mage.game.events.GameEvent; -import mage.game.stack.Spell; -import mage.game.stack.StackObject; import mage.players.Player; /** @@ -77,20 +72,9 @@ public class CounterTargetWithReplacementEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - UUID objectId = source.getFirstTarget(); - UUID sourceId = source.getSourceId(); Player controller = game.getPlayer(source.getControllerId()); if (controller != null) { - StackObject stackObject = game.getStack().getStackObject(objectId); - if (stackObject != null && !game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.COUNTER, objectId, sourceId, stackObject.getControllerId()))) { - if (stackObject instanceof Spell) { - controller.moveCards((Card) stackObject, null, targetZone, source, game); - } else { - game.getStack().remove(stackObject); - } - game.fireEvent(GameEvent.getEvent(GameEvent.EventType.COUNTERED, objectId, sourceId, stackObject.getControllerId())); - return true; - } + return game.getStack().counter(targetPointer.getFirst(game, source), source.getSourceId(), game, targetZone, false, flag); } return false; } diff --git a/Mage/src/mage/cards/CardImpl.java b/Mage/src/mage/cards/CardImpl.java index a24fc9323df..c57a99b9a5d 100644 --- a/Mage/src/mage/cards/CardImpl.java +++ b/Mage/src/mage/cards/CardImpl.java @@ -558,7 +558,7 @@ public abstract class CardImpl extends MageObjectImpl implements Card { break; } if (removed) { - game.rememberLKI(objectId, fromZone, lkiObject != null ? lkiObject : this); + game.rememberLKI(lkiObject != null ? lkiObject.getId() : objectId, fromZone, lkiObject != null ? lkiObject : this); } else { logger.warn("Couldn't find card in fromZone, card=" + getIdName() + ", fromZone=" + fromZone); } diff --git a/Mage/src/mage/game/stack/Spell.java b/Mage/src/mage/game/stack/Spell.java index 9e09c8d7ebb..79092da68aa 100644 --- a/Mage/src/mage/game/stack/Spell.java +++ b/Mage/src/mage/game/stack/Spell.java @@ -44,6 +44,7 @@ import mage.abilities.costs.mana.ManaCosts; import mage.abilities.keyword.BestowAbility; import mage.abilities.keyword.MorphAbility; import mage.cards.Card; +import mage.cards.CardsImpl; import mage.cards.SplitCard; import mage.constants.CardType; import mage.constants.Rarity; @@ -328,21 +329,32 @@ public class Spell extends StackObjImpl implements Card { @Override public void counter(UUID sourceId, Game game) { - this.counter(sourceId, game, true); + this.counter(sourceId, game, Zone.GRAVEYARD, false, true); } @Override - public void counter(UUID sourceId, Game game, boolean moveToGraveyard) { + public void counter(UUID sourceId, Game game, Zone zone, boolean owner, boolean top) { this.countered = true; - if (!isCopiedSpell() && moveToGraveyard) { - Player player = game.getPlayer(getControllerId()); + if (!isCopiedSpell()) { + Player player = game.getPlayer(game.getControllerId(sourceId)); + if (player == null) { + player = game.getPlayer(getControllerId()); + } if (player != null) { Ability counteringAbility = null; MageObject counteringObject = game.getObject(sourceId); if (counteringObject instanceof StackObject) { counteringAbility = ((StackObject) counteringObject).getStackAbility(); } - player.moveCards(card, Zone.GRAVEYARD, counteringAbility, game); + if (zone.equals(Zone.LIBRARY)) { + if (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); + } } } } diff --git a/Mage/src/mage/game/stack/SpellStack.java b/Mage/src/mage/game/stack/SpellStack.java index c22c449d2b6..f8b9e80b751 100644 --- a/Mage/src/mage/game/stack/SpellStack.java +++ b/Mage/src/mage/game/stack/SpellStack.java @@ -82,6 +82,10 @@ public class SpellStack extends ArrayDeque { } public boolean counter(UUID objectId, UUID sourceId, Game game) { + return counter(objectId, sourceId, game, Zone.GRAVEYARD, false, true); + } + + public boolean counter(UUID objectId, UUID sourceId, Game game, Zone zone, boolean owner, boolean onTop) { // 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 @@ -101,12 +105,7 @@ public class SpellStack extends ArrayDeque { counteredObjectName = "Ability (" + stackObject.getStackAbility().getRule(targetSourceName) + ") of " + targetSourceName; } if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.COUNTER, objectId, sourceId, stackObject.getControllerId()))) { - if (stackObject instanceof Spell) { - game.rememberLKI(objectId, Zone.STACK, (Spell) stackObject); - } else { - this.remove(stackObject); - } - stackObject.counter(sourceId, game); + stackObject.counter(sourceId, game, zone, owner, onTop); if (!game.isSimulation()) { game.informPlayers(counteredObjectName + " is countered by " + sourceObject.getLogName()); } diff --git a/Mage/src/mage/game/stack/StackAbility.java b/Mage/src/mage/game/stack/StackAbility.java index c437795c271..6b689529d96 100644 --- a/Mage/src/mage/game/stack/StackAbility.java +++ b/Mage/src/mage/game/stack/StackAbility.java @@ -122,11 +122,12 @@ public class StackAbility extends StackObjImpl implements Ability { @Override public void counter(UUID sourceId, Game game) { - this.counter(sourceId, game, true); + // zone, owner, top ignored + this.counter(sourceId, game, Zone.GRAVEYARD, true, true); } @Override - public void counter(UUID sourceId, Game game, boolean moveToGraveyard) { + public void counter(UUID sourceId, Game game, Zone zone, boolean owner, boolean top) { //20100716 - 603.8 if (ability instanceof StateTriggeredAbility) { ((StateTriggeredAbility) ability).counter(game); diff --git a/Mage/src/mage/game/stack/StackObject.java b/Mage/src/mage/game/stack/StackObject.java index db3753f1474..c1540d9138f 100644 --- a/Mage/src/mage/game/stack/StackObject.java +++ b/Mage/src/mage/game/stack/StackObject.java @@ -30,6 +30,7 @@ package mage.game.stack; import java.util.UUID; import mage.MageObject; import mage.abilities.Ability; +import mage.constants.Zone; import mage.filter.FilterPermanent; import mage.game.Controllable; import mage.game.Game; @@ -42,7 +43,7 @@ public interface StackObject extends MageObject, Controllable { void counter(UUID sourceId, Game game); - void counter(UUID sourceId, Game game, boolean moveToGraveyard); + void counter(UUID sourceId, Game game, Zone zone, boolean owner, boolean top); Ability getStackAbility();