mirror of
https://github.com/correl/mage.git
synced 2024-11-14 19:19:32 +00:00
* Some rework of card moving after spell countering.
This commit is contained in:
parent
1139495fd7
commit
3de7ff6808
13 changed files with 152 additions and 239 deletions
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
||||
}
|
|
@ -985,6 +985,12 @@ public class ContinuousEffects implements Serializable {
|
|||
}
|
||||
|
||||
layer = filterLayeredEffects(activeLayerEffects, Layer.PTChangingEffects_7);
|
||||
for (ContinuousEffect effect : layer) {
|
||||
HashSet<Ability> abilities = layeredEffects.getAbility(effect.getId());
|
||||
for (Ability ability : abilities) {
|
||||
effect.apply(Layer.PTChangingEffects_7, SubLayer.CharacteristicDefining_7a, ability, game);
|
||||
}
|
||||
}
|
||||
for (ContinuousEffect effect : layer) {
|
||||
HashSet<Ability> abilities = layeredEffects.getAbility(effect.getId());
|
||||
for (Ability ability : abilities) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -82,6 +82,10 @@ public class SpellStack extends ArrayDeque<StackObject> {
|
|||
}
|
||||
|
||||
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<StackObject> {
|
|||
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());
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
Loading…
Reference in a new issue