Some changes related to #4893.

This commit is contained in:
LevelX2 2018-05-03 01:33:21 +02:00
parent 9919a3403d
commit cddd81123b
37 changed files with 245 additions and 137 deletions

View file

@ -45,8 +45,11 @@ public final class CardImageUtils {
pathCache.put(card, filePath); pathCache.put(card, filePath);
return filePath; return filePath;
} }
log.warn("Token image file not found. Set: " + card.getSet() + " Token Set Code: " + card.getTokenSetCode() + " Name: " + card.getName() + " File path: " + filePath);
} else {
log.warn("Trying to get token path for non token card. Set: " + card.getSet() + " Set Code: " + card.getTokenSetCode() + " Name: " + card.getName());
} }
log.warn("Token image file not found: " + card.getSet() + " - " + card.getTokenSetCode() + " - " + card.getName());
return null; return null;
} }

View file

@ -430,7 +430,7 @@ public class ComputerPlayer6 extends ComputerPlayer /*implements Player*/ {
} }
stackObject.resolve(game); stackObject.resolve(game);
if (stackObject instanceof StackAbility) { if (stackObject instanceof StackAbility) {
game.getStack().remove(stackObject); game.getStack().remove(stackObject, game);
} }
game.applyEffects(); game.applyEffects();
game.getPlayers().resetPassed(); game.getPlayers().resetPassed();
@ -968,7 +968,7 @@ public class ComputerPlayer6 extends ComputerPlayer /*implements Player*/ {
// swallow // swallow
e.printStackTrace(); e.printStackTrace();
} finally { } finally {
if(scanner != null) { if (scanner != null) {
scanner.close(); scanner.close();
} }
} }

View file

@ -48,7 +48,7 @@ import mage.game.events.GameEvent.EventType;
public class NorinTheWary extends CardImpl { public class NorinTheWary extends CardImpl {
public NorinTheWary(UUID ownerId, CardSetInfo setInfo) { public NorinTheWary(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{R}"); super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{R}");
addSuperType(SuperType.LEGENDARY); addSuperType(SuperType.LEGENDARY);
this.subtype.add(SubType.HUMAN); this.subtype.add(SubType.HUMAN);
this.subtype.add(SubType.WARRIOR); this.subtype.add(SubType.WARRIOR);
@ -94,7 +94,7 @@ class NorinTheWaryTriggeredAbility extends TriggeredAbilityImpl {
@Override @Override
public String getRule() { public String getRule() {
return new StringBuilder("When a player casts a spell or a creature attacks, ").append(super.getRule()).toString(); return "When a player casts a spell or a creature attacks, " + super.getRule();
} }
@Override @Override

View file

@ -25,7 +25,6 @@
* authors and should not be interpreted as representing official policies, either expressed * authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com. * or implied, of BetaSteward_at_googlemail.com.
*/ */
package mage.cards.u; package mage.cards.u;
import java.util.UUID; import java.util.UUID;
@ -37,8 +36,8 @@ import mage.abilities.keyword.UnearthAbility;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.CardType; import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.Duration; import mage.constants.Duration;
import mage.constants.SubType;
import mage.constants.Zone; import mage.constants.Zone;
/** /**
@ -47,21 +46,22 @@ import mage.constants.Zone;
*/ */
public class UndeadLeotau extends CardImpl { public class UndeadLeotau extends CardImpl {
public UndeadLeotau (UUID ownerId, CardSetInfo setInfo) { public UndeadLeotau(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{5}{B}"); super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{B}");
this.subtype.add(SubType.ZOMBIE); this.subtype.add(SubType.ZOMBIE);
this.subtype.add(SubType.CAT); this.subtype.add(SubType.CAT);
this.power = new MageInt(3); this.power = new MageInt(3);
this.toughness = new MageInt(4); this.toughness = new MageInt(4);
// {R}: Undead Leotau gets +1/-1 until end of turn. // {R}: Undead Leotau gets +1/-1 until end of turn.
// Unearth {2}{B}
this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostSourceEffect(+1, -1, Duration.EndOfTurn), new ManaCostsImpl("{R}"))); this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new BoostSourceEffect(+1, -1, Duration.EndOfTurn), new ManaCostsImpl("{R}")));
// Unearth {2}{B}
this.addAbility(new UnearthAbility(new ManaCostsImpl("{2}{B}"))); this.addAbility(new UnearthAbility(new ManaCostsImpl("{2}{B}")));
} }
public UndeadLeotau (final UndeadLeotau card) { public UndeadLeotau(final UndeadLeotau card) {
super(card); super(card);
} }

View file

@ -37,8 +37,8 @@ import mage.abilities.keyword.FlyingAbility;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.CardType; import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.Outcome; import mage.constants.Outcome;
import mage.constants.SubType;
import mage.constants.Zone; import mage.constants.Zone;
/** /**
@ -48,17 +48,18 @@ import mage.constants.Zone;
public class VoiceOfAll extends CardImpl { public class VoiceOfAll extends CardImpl {
public VoiceOfAll(UUID ownerId, CardSetInfo setInfo) { public VoiceOfAll(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{W}{W}"); super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}{W}");
this.subtype.add(SubType.ANGEL); this.subtype.add(SubType.ANGEL);
this.power = new MageInt(2); this.power = new MageInt(2);
this.toughness = new MageInt(2); this.toughness = new MageInt(2);
// Flying // Flying
this.addAbility(FlyingAbility.getInstance()); this.addAbility(FlyingAbility.getInstance());
// As Voice of All enters the battlefield, choose a color. // As Voice of All enters the battlefield, choose a color.
this.addAbility(new AsEntersBattlefieldAbility(new ChooseColorEffect(Outcome.Benefit))); this.addAbility(new AsEntersBattlefieldAbility(new ChooseColorEffect(Outcome.Benefit)));
// Voice of All has protection from the chosen color.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ProtectionChosenColorSourceEffect())); this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ProtectionChosenColorSourceEffect()));
} }
@ -70,4 +71,4 @@ public class VoiceOfAll extends CardImpl {
public VoiceOfAll copy() { public VoiceOfAll copy() {
return new VoiceOfAll(this); return new VoiceOfAll(this);
} }
} }

View file

@ -6,14 +6,11 @@ import mage.constants.PhaseStep;
import mage.constants.Zone; import mage.constants.Zone;
import mage.game.permanent.Permanent; import mage.game.permanent.Permanent;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import org.junit.Ignore; import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/** /**
* @author noxx * @author noxx
@ -179,6 +176,9 @@ public class PhantasmalImageTest extends CardTestPlayerBase {
@Test @Test
public void testCopyEntersTapped() { public void testCopyEntersTapped() {
addCard(Zone.BATTLEFIELD, playerA, "Island", 2); addCard(Zone.BATTLEFIELD, playerA, "Island", 2);
// You may have Phantasmal Image enter the battlefield as a copy of any creature
// on the battlefield, except it's an Illusion in addition to its other types and
// it gains "When this creature becomes the target of a spell or ability, sacrifice it."
addCard(Zone.HAND, playerA, "Phantasmal Image"); addCard(Zone.HAND, playerA, "Phantasmal Image");
addCard(Zone.BATTLEFIELD, playerB, "Geralf's Messenger"); addCard(Zone.BATTLEFIELD, playerB, "Geralf's Messenger");

View file

@ -7,8 +7,7 @@ import org.mage.test.serverside.base.CardTestPlayerBase;
/** /**
* Harm's Way: The next 2 damage that a source of your choice would deal to you * Harm's Way: The next 2 damage that a source of your choice would deal to you
* and/or permanents you control this turn is dealt to any target * and/or permanents you control this turn is dealt to any target instead.
* instead.
* *
* @author noxx * @author noxx
*/ */
@ -23,7 +22,7 @@ public class HarmsWayRedirectDamageTest extends CardTestPlayerBase {
addCard(Zone.HAND, playerA, "Lightning Bolt"); addCard(Zone.HAND, playerA, "Lightning Bolt");
addCard(Zone.BATTLEFIELD, playerA, "Mountain"); addCard(Zone.BATTLEFIELD, playerA, "Mountain");
addCard(Zone.HAND, playerB, "Harm's Way"); addCard(Zone.HAND, playerB, "Harm's Way"); // Instant {W}
addCard(Zone.BATTLEFIELD, playerB, "Plains"); addCard(Zone.BATTLEFIELD, playerB, "Plains");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerB); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", playerB);
@ -33,6 +32,9 @@ public class HarmsWayRedirectDamageTest extends CardTestPlayerBase {
setStopAt(1, PhaseStep.END_TURN); setStopAt(1, PhaseStep.END_TURN);
execute(); execute();
assertGraveyardCount(playerA, "Lightning Bolt", 1);
assertGraveyardCount(playerB, "Harm's Way", 1);
// 2 damage was redirected back // 2 damage was redirected back
assertLife(playerA, 18); assertLife(playerA, 18);

View file

@ -29,7 +29,6 @@ package org.mage.test.cards.single.bfz;
import mage.constants.PhaseStep; import mage.constants.PhaseStep;
import mage.constants.Zone; import mage.constants.Zone;
import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase; import org.mage.test.serverside.base.CardTestPlayerBase;

View file

@ -157,6 +157,8 @@ public abstract class AbilityImpl implements Ability {
public void newId() { public void newId() {
if (!(this instanceof MageSingleton)) { if (!(this instanceof MageSingleton)) {
this.id = UUID.randomUUID(); this.id = UUID.randomUUID();
// this.sourceObject = null;
// this.sourceObjectZoneChangeCounter = -1;
} }
getEffects().newId(); getEffects().newId();
} }
@ -1211,7 +1213,7 @@ public abstract class AbilityImpl implements Ability {
@Override @Override
public Permanent getSourcePermanentIfItStillExists(Game game) { public Permanent getSourcePermanentIfItStillExists(Game game) {
if (sourceObject == null) { if (sourceObject == null || !sourceObject.getId().equals(getSourceId())) {
setSourceObject(game.getObject(getSourceId()), game); setSourceObject(game.getObject(getSourceId()), game);
} }
if (sourceObject instanceof Permanent) { if (sourceObject instanceof Permanent) {

View file

@ -63,7 +63,7 @@ public class ExileFromStackCost extends CostImpl {
} }
String spellName = spellToExile.getName(); String spellName = spellToExile.getName();
if (spellToExile.isCopy()) { if (spellToExile.isCopy()) {
game.getStack().remove(spellToExile); game.getStack().remove(spellToExile, game);
} else { } else {
spellToExile.moveToExile(null, "", ability.getSourceId(), game); spellToExile.moveToExile(null, "", ability.getSourceId(), game);
} }

View file

@ -27,6 +27,7 @@
*/ */
package mage.abilities.effects.common; package mage.abilities.effects.common;
import java.util.Objects;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.Mode; import mage.abilities.Mode;
import mage.abilities.effects.ContinuousRuleModifyingEffectImpl; import mage.abilities.effects.ContinuousRuleModifyingEffectImpl;
@ -36,8 +37,6 @@ import mage.game.Game;
import mage.game.events.GameEvent; import mage.game.events.GameEvent;
import mage.game.events.GameEvent.EventType; import mage.game.events.GameEvent.EventType;
import java.util.Objects;
/** /**
* *
* @author jeffwadsworth * @author jeffwadsworth
@ -67,6 +66,17 @@ public class CantBeRegeneratedSourceEffect extends ContinuousRuleModifyingEffect
return event.getType() == EventType.REGENERATE; return event.getType() == EventType.REGENERATE;
} }
@Override
public void init(Ability source, Game game) {
super.init(source, game); //To change body of generated methods, choose Tools | Templates.
if (duration.isOnlyValidIfNoZoneChange()) {
// If source permanent is no longer onto battlefield discard the effect
if (source.getSourcePermanentIfItStillExists(game) == null) {
discard();
}
}
}
@Override @Override
public boolean applies(GameEvent event, Ability source, Game game) { public boolean applies(GameEvent event, Ability source, Game game) {
return Objects.equals(source.getSourceId(), event.getTargetId()); return Objects.equals(source.getSourceId(), event.getTargetId());

View file

@ -63,7 +63,7 @@ public class DestroySourceEffect extends OneShotEffect {
@Override @Override
public boolean apply(Game game, Ability source) { public boolean apply(Game game, Ability source) {
Permanent permanent = game.getPermanent(source.getSourceId()); Permanent permanent = source.getSourcePermanentIfItStillExists(game);
if (permanent != null) { if (permanent != null) {
permanent.destroy(source.getSourceId(), game, noRegen); permanent.destroy(source.getSourceId(), game, noRegen);
return true; return true;

View file

@ -27,6 +27,7 @@
*/ */
package mage.abilities.effects.common; package mage.abilities.effects.common;
import mage.MageObject;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.Mode; import mage.abilities.Mode;
import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.OneShotEffect;
@ -50,21 +51,19 @@ public class FightTargetSourceEffect extends OneShotEffect {
@Override @Override
public boolean apply(Game game, Ability source) { public boolean apply(Game game, Ability source) {
Permanent originalPermanent = game.getPermanentOrLKIBattlefield(source.getSourceId()); MageObject sourceObject = source.getSourceObject(game);
if (originalPermanent != null) { if (sourceObject != null) {
Permanent sourcePermanent = game.getPermanent(source.getSourceId()); Permanent sourcePermanent = source.getSourcePermanentIfItStillExists(game);
// only if target is legal the effect will be applied Permanent creature1 = game.getPermanent(getTargetPointer().getFirst(game, source));
if (source.getTargets().get(0).isLegal(source, game)) { // 20110930 - 701.10
Permanent creature1 = game.getPermanent(source.getTargets().get(0).getFirstTarget()); if (creature1 != null && sourcePermanent != null) {
// 20110930 - 701.10 if (creature1.isCreature() && sourcePermanent.isCreature()) {
if (creature1 != null && sourcePermanent != null) { return sourcePermanent.fight(creature1, source, game);
if (creature1.isCreature() && sourcePermanent.isCreature()) {
return sourcePermanent.fight(creature1, source, game);
}
} }
} }
if (!game.isSimulation()) if (!game.isSimulation()) {
game.informPlayers(originalPermanent.getLogName() + ": Fighting effect has been fizzled."); game.informPlayers(sourceObject.getLogName() + ": Fighting effect has been fizzled.");
}
} }
return false; return false;
} }
@ -83,4 +82,3 @@ public class FightTargetSourceEffect extends OneShotEffect {
} }
} }

View file

@ -8,11 +8,9 @@ import mage.abilities.effects.OneShotEffect;
import mage.constants.Outcome; import mage.constants.Outcome;
import mage.game.Game; import mage.game.Game;
import mage.game.permanent.Permanent; import mage.game.permanent.Permanent;
import mage.game.permanent.token.TokenImpl;
import mage.game.permanent.token.Token; import mage.game.permanent.token.Token;
import mage.players.Player; import mage.players.Player;
/** /**
* @author Loki * @author Loki
*/ */
@ -33,14 +31,15 @@ public class FlipSourceEffect extends OneShotEffect {
@Override @Override
public boolean apply(Game game, Ability source) { public boolean apply(Game game, Ability source) {
Permanent permanent = game.getPermanent(source.getSourceId()); Permanent permanent = source.getSourcePermanentIfItStillExists(game);
Player controller = game.getPlayer(source.getControllerId()); Player controller = game.getPlayer(source.getControllerId());
if (permanent != null && controller != null) { if (permanent != null && controller != null) {
if (permanent.flip(game)) { if (permanent.flip(game)) {
ContinuousEffect effect = new ConditionalContinuousEffect(new CopyTokenEffect(flipToken), FlippedCondition.instance, ""); ContinuousEffect effect = new ConditionalContinuousEffect(new CopyTokenEffect(flipToken), FlippedCondition.instance, "");
game.addEffect(effect, source); game.addEffect(effect, source);
if (!game.isSimulation()) if (!game.isSimulation()) {
game.informPlayers(new StringBuilder(controller.getLogName()).append(" flips ").append(permanent.getName()).toString()); game.informPlayers(new StringBuilder(controller.getLogName()).append(" flips ").append(permanent.getName()).toString());
}
return true; return true;
} }
} }

View file

@ -5,7 +5,6 @@
*/ */
package mage.abilities.effects.common; package mage.abilities.effects.common;
import mage.MageObject;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.OneShotEffect;
import mage.constants.Outcome; import mage.constants.Outcome;
@ -34,9 +33,8 @@ public class PhaseOutSourceEffect extends OneShotEffect {
@Override @Override
public boolean apply(Game game, Ability source) { public boolean apply(Game game, Ability source) {
MageObject sourceObject = source.getSourceObjectIfItStillExists(game); Permanent permanent = source.getSourcePermanentIfItStillExists(game);
if (sourceObject instanceof Permanent) { if (permanent != null) {
Permanent permanent = (Permanent) sourceObject;
return permanent.phaseOut(game); return permanent.phaseOut(game);
} }
return false; return false;

View file

@ -54,6 +54,17 @@ public class PreventDamageToSourceEffect extends PreventionEffectImpl {
return new PreventDamageToSourceEffect(this); return new PreventDamageToSourceEffect(this);
} }
@Override
public void init(Ability source, Game game) {
super.init(source, game); //To change body of generated methods, choose Tools | Templates.
if (duration.isOnlyValidIfNoZoneChange()) {
// If source permanent is no longer onto battlefield discard the effect
if (source.getSourcePermanentIfItStillExists(game) == null) {
discard();
}
}
}
@Override @Override
public boolean apply(Game game, Ability source) { public boolean apply(Game game, Ability source) {
return true; return true;

View file

@ -28,12 +28,18 @@
package mage.abilities.effects.common; package mage.abilities.effects.common;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.effects.ContinuousEffect;
import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.continuous.GainAbilityTargetEffect;
import mage.abilities.keyword.HasteAbility;
import mage.cards.Card; import mage.cards.Card;
import mage.constants.Duration;
import mage.constants.Outcome; import mage.constants.Outcome;
import mage.constants.Zone; import mage.constants.Zone;
import mage.game.Game; import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player; import mage.players.Player;
import mage.target.targetpointer.FixedTarget;
/** /**
* *
@ -43,21 +49,25 @@ public class ReturnSourceFromGraveyardToBattlefieldEffect extends OneShotEffect
private boolean tapped; private boolean tapped;
private boolean ownerControl; private boolean ownerControl;
private boolean haste;
public ReturnSourceFromGraveyardToBattlefieldEffect() { public ReturnSourceFromGraveyardToBattlefieldEffect() {
this(false); this(false);
} }
public ReturnSourceFromGraveyardToBattlefieldEffect(boolean tapped) { public ReturnSourceFromGraveyardToBattlefieldEffect(boolean tapped) {
super(Outcome.PutCreatureInPlay); this(tapped, true);
this.tapped = tapped;
setText();
} }
public ReturnSourceFromGraveyardToBattlefieldEffect(boolean tapped, boolean ownerControl) { public ReturnSourceFromGraveyardToBattlefieldEffect(boolean tapped, boolean ownerControl) {
this(tapped, ownerControl, false);
}
public ReturnSourceFromGraveyardToBattlefieldEffect(boolean tapped, boolean ownerControl, boolean haste) {
super(Outcome.PutCreatureInPlay); super(Outcome.PutCreatureInPlay);
this.tapped = tapped; this.tapped = tapped;
this.ownerControl = ownerControl; this.ownerControl = ownerControl;
this.haste = haste;
setText(); setText();
} }
@ -65,6 +75,7 @@ public class ReturnSourceFromGraveyardToBattlefieldEffect extends OneShotEffect
super(effect); super(effect);
this.tapped = effect.tapped; this.tapped = effect.tapped;
this.ownerControl = effect.ownerControl; this.ownerControl = effect.ownerControl;
this.haste = effect.haste;
} }
@Override @Override
@ -89,6 +100,14 @@ public class ReturnSourceFromGraveyardToBattlefieldEffect extends OneShotEffect
} }
if (game.getState().getZone(source.getSourceId()) == Zone.GRAVEYARD) { if (game.getState().getZone(source.getSourceId()) == Zone.GRAVEYARD) {
player.moveCards(card, Zone.BATTLEFIELD, source, game, tapped, false, true, null); player.moveCards(card, Zone.BATTLEFIELD, source, game, tapped, false, true, null);
if (haste) {
Permanent permanent = game.getPermanent(card.getId());
if (permanent != null) {
ContinuousEffect effect = new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.Custom);
effect.setTargetPointer(new FixedTarget(permanent, game));
game.addEffect(effect, source);
}
}
} }
return true; return true;
} }

View file

@ -33,6 +33,7 @@ import mage.cards.Card;
import mage.constants.Outcome; import mage.constants.Outcome;
import mage.constants.Zone; import mage.constants.Zone;
import mage.game.Game; import mage.game.Game;
import mage.players.Player;
/** /**
* *
@ -81,16 +82,17 @@ public class ReturnToBattlefieldUnderOwnerControlSourceEffect extends OneShotEff
@Override @Override
public boolean apply(Game game, Ability source) { public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
Card card = game.getCard(source.getSourceId()); Card card = game.getCard(source.getSourceId());
if (card != null) { if (controller != null && card != null) {
// return only from public zones // return only from public zones
switch (game.getState().getZone(card.getId())) { switch (game.getState().getZone(card.getId())) {
case EXILED: case EXILED:
case COMMAND: case COMMAND:
case GRAVEYARD: case GRAVEYARD:
if (zoneChangeCounter < 0 || game.getState().getZoneChangeCounter(card.getId()) == zoneChangeCounter) { if (zoneChangeCounter < 0 || game.getState().getZoneChangeCounter(card.getId()) == zoneChangeCounter) {
Zone currentZone = game.getState().getZone(card.getId());
if (card.putOntoBattlefield(game, currentZone, source.getSourceId(), card.getOwnerId(), tapped)) { if (controller.moveCards(card, Zone.BATTLEFIELD, source, game, tapped, false, true, null)) {
if (attacking) { if (attacking) {
game.getCombat().addAttackingCreature(card.getId(), game); game.getCombat().addAttackingCreature(card.getId(), game);
} }

View file

@ -103,7 +103,7 @@ public class ReturnToHandTargetEffect extends OneShotEffect {
} }
} }
for (UUID copyId : copyIds) { for (UUID copyId : copyIds) {
game.getStack().remove(game.getSpell(copyId)); game.getStack().remove(game.getSpell(copyId), game);
} }
return controller.moveCards(cards, Zone.HAND, source, game); return controller.moveCards(cards, Zone.HAND, source, game);
} }

View file

@ -63,10 +63,7 @@ public class TapSourceEffect extends OneShotEffect {
@Override @Override
public boolean apply(Game game, Ability source) { public boolean apply(Game game, Ability source) {
Permanent permanent = game.getPermanent(source.getSourceId()); Permanent permanent = source.getSourcePermanentIfItStillExists(game);
if (permanent == null) {
permanent = game.getPermanentEntering(source.getSourceId());
}
if (permanent != null) { if (permanent != null) {
if (withoutTrigger) { if (withoutTrigger) {
permanent.setTapped(true); permanent.setTapped(true);

View file

@ -92,6 +92,13 @@ public class GainAbilitySourceEffect extends ContinuousEffectImpl implements Sou
@Override @Override
public void init(Ability source, Game game) { public void init(Ability source, Game game) {
super.init(source, game); super.init(source, game);
if (!onCard && Duration.WhileOnBattlefield != duration) {
// If source permanent is no longer onto battlefield discard the effect
if (source.getSourcePermanentIfItStillExists(game) == null) {
discard();
return;
}
}
if (affectedObjectsSet) { if (affectedObjectsSet) {
Permanent permanent = game.getPermanentEntering(source.getSourceId()); Permanent permanent = game.getPermanentEntering(source.getSourceId());
if (permanent != null) { if (permanent != null) {

View file

@ -12,38 +12,50 @@ import mage.constants.Outcome;
import mage.constants.SubLayer; import mage.constants.SubLayer;
import mage.game.Game; import mage.game.Game;
import mage.game.permanent.Permanent; import mage.game.permanent.Permanent;
/** /**
* *
* @author Noahsark * @author Noahsark
*/ */
public class LoseAbilitySourceEffect extends ContinuousEffectImpl{ public class LoseAbilitySourceEffect extends ContinuousEffectImpl {
protected Ability ability; protected Ability ability;
public LoseAbilitySourceEffect(Ability ability){ public LoseAbilitySourceEffect(Ability ability) {
this(ability, Duration.WhileOnBattlefield); this(ability, Duration.WhileOnBattlefield);
} }
public LoseAbilitySourceEffect(Ability ability, Duration duration){ public LoseAbilitySourceEffect(Ability ability, Duration duration) {
super(duration, Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, Outcome.LoseAbility); super(duration, Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, Outcome.LoseAbility);
this.ability = ability; this.ability = ability;
staticText = "{this} loses " + ability.getRule() + ' ' + duration.toString(); staticText = "{this} loses " + ability.getRule() + ' ' + duration.toString();
} }
public LoseAbilitySourceEffect(final LoseAbilitySourceEffect effect){ public LoseAbilitySourceEffect(final LoseAbilitySourceEffect effect) {
super(effect); super(effect);
this.ability = effect.ability.copy(); this.ability = effect.ability.copy();
} }
@Override @Override
public LoseAbilitySourceEffect copy(){ public LoseAbilitySourceEffect copy() {
return new LoseAbilitySourceEffect(this); return new LoseAbilitySourceEffect(this);
} }
@Override @Override
public boolean apply(Game game, Ability source){ public void init(Ability source, Game game) {
super.init(source, game); //To change body of generated methods, choose Tools | Templates.
if (duration.isOnlyValidIfNoZoneChange()) {
// If source permanent is no longer onto battlefield discard the effect
if (source.getSourcePermanentIfItStillExists(game) == null) {
discard();
}
}
}
@Override
public boolean apply(Game game, Ability source) {
Permanent permanent = game.getPermanent(source.getSourceId()); Permanent permanent = game.getPermanent(source.getSourceId());
if (permanent != null){ if (permanent != null) {
// 112.10 // 112.10
while (permanent.getAbilities().remove(ability)) { while (permanent.getAbilities().remove(ability)) {

View file

@ -69,6 +69,17 @@ public class LoseCreatureTypeSourceEffect extends ContinuousEffectImpl implement
return new LoseCreatureTypeSourceEffect(this); return new LoseCreatureTypeSourceEffect(this);
} }
@Override
public void init(Ability source, Game game) {
super.init(source, game); //To change body of generated methods, choose Tools | Templates.
if (duration.isOnlyValidIfNoZoneChange()) {
// If source permanent is no longer onto battlefield discard the effect
if (source.getSourcePermanentIfItStillExists(game) == null) {
discard();
}
}
}
@Override @Override
public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) { public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) {
if (dynamicValue.calculate(game, source, this) >= lessThan) { if (dynamicValue.calculate(game, source, this) >= lessThan) {

View file

@ -1,16 +1,16 @@
/* /*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without modification, are * Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met: * permitted provided that the following conditions are met:
* *
* 1. Redistributions of source code must retain the above copyright notice, this list of * 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer. * conditions and the following disclaimer.
* *
* 2. Redistributions in binary form must reproduce the above copyright notice, this list * 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 * of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution. * provided with the distribution.
* *
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED * 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 * 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 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
@ -20,19 +20,19 @@
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * 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 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* *
* The views and conclusions contained in the software and documentation are those of the * 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 * authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com. * or implied, of BetaSteward_at_googlemail.com.
*/ */
package mage.abilities.effects.common.continuous; package mage.abilities.effects.common.continuous;
import mage.abilities.Ability;
import mage.abilities.effects.ContinuousEffectImpl;
import mage.constants.Duration; import mage.constants.Duration;
import mage.constants.Layer; import mage.constants.Layer;
import mage.constants.Outcome; import mage.constants.Outcome;
import mage.constants.SubLayer; import mage.constants.SubLayer;
import mage.abilities.Ability;
import mage.abilities.effects.ContinuousEffectImpl;
import mage.game.Game; import mage.game.Game;
import mage.game.permanent.Permanent; import mage.game.permanent.Permanent;
@ -55,14 +55,29 @@ public class SwitchPowerToughnessSourceEffect extends ContinuousEffectImpl {
return new SwitchPowerToughnessSourceEffect(this); return new SwitchPowerToughnessSourceEffect(this);
} }
@Override
public void init(Ability source, Game game) {
super.init(source, game); //To change body of generated methods, choose Tools | Templates.
if (duration.isOnlyValidIfNoZoneChange()) {
// If source permanent is no longer onto battlefield discard the effect
if (source.getSourcePermanentIfItStillExists(game) == null) {
discard();
}
}
}
@Override @Override
public boolean apply(Game game, Ability source) { public boolean apply(Game game, Ability source) {
Permanent target = game.getPermanent(source.getSourceId()); Permanent sourcePermanent = source.getSourcePermanentIfItStillExists(game);
if (target != null) { if (sourcePermanent != null) {
int power = target.getPower().getValue(); int power = sourcePermanent.getPower().getValue();
target.getPower().setValue(target.getToughness().getValue()); sourcePermanent.getPower().setValue(sourcePermanent.getToughness().getValue());
target.getToughness().setValue(power); sourcePermanent.getToughness().setValue(power);
return true; return true;
} else {
if (duration.isOnlyValidIfNoZoneChange()) {
discard();
}
} }
return false; return false;
} }

View file

@ -39,7 +39,7 @@ import mage.game.permanent.Permanent;
* @author TheElk801 * @author TheElk801
*/ */
public class RemoveAllCountersSourceEffect extends OneShotEffect { public class RemoveAllCountersSourceEffect extends OneShotEffect {
private final CounterType counterType; private final CounterType counterType;
public RemoveAllCountersSourceEffect(CounterType counterType) { public RemoveAllCountersSourceEffect(CounterType counterType) {
@ -55,13 +55,13 @@ public class RemoveAllCountersSourceEffect extends OneShotEffect {
@Override @Override
public boolean apply(Game game, Ability source) { public boolean apply(Game game, Ability source) {
Permanent permanent = game.getPermanent(source.getSourceId()); Permanent permanent = source.getSourcePermanentIfItStillExists(game);
if(permanent != null) { if (permanent != null) {
int count = permanent.getCounters(game).getCount(counterType); int count = permanent.getCounters(game).getCount(counterType);
permanent.removeCounters(counterType.getName(), count, game); permanent.removeCounters(counterType.getName(), count, game);
return true; return true;
} }
return false; return false;
} }
@Override @Override

View file

@ -56,7 +56,7 @@ public class RemoveCounterSourceEffect extends OneShotEffect {
@Override @Override
public boolean apply(Game game, Ability source) { public boolean apply(Game game, Ability source) {
Permanent permanent = game.getPermanent(source.getSourceId()); Permanent permanent = source.getSourcePermanentIfItStillExists(game);
if (permanent != null) { if (permanent != null) {
int toRemove = Math.min(counter.getCount(), permanent.getCounters(game).getCount(counter.getName())); int toRemove = Math.min(counter.getCount(), permanent.getCounters(game).getCount(counter.getName()));
if (toRemove > 0) { if (toRemove > 0) {
@ -67,18 +67,20 @@ public class RemoveCounterSourceEffect extends OneShotEffect {
} }
return true; return true;
} }
Card card = game.getCard(source.getSourceId()); if (!(source.getSourceObject(game) instanceof Permanent)) {
if (card != null) { Card card = game.getCard(source.getSourceId());
int toRemove = Math.min(counter.getCount(), card.getCounters(game).getCount(counter.getName())); if (card != null) {
if (toRemove > 0) { int toRemove = Math.min(counter.getCount(), card.getCounters(game).getCount(counter.getName()));
card.removeCounters(counter.getName(), toRemove, game); if (toRemove > 0) {
if (!game.isSimulation()) { card.removeCounters(counter.getName(), toRemove, game);
game.informPlayers("Removed " + toRemove + ' ' + counter.getName() if (!game.isSimulation()) {
+ " counter from " + card.getLogName() game.informPlayers("Removed " + toRemove + ' ' + counter.getName()
+ " (" + card.getCounters(game).getCount(counter.getName()) + " left)"); + " counter from " + card.getLogName()
+ " (" + card.getCounters(game).getCount(counter.getName()) + " left)");
}
} }
return true;
} }
return true;
} }
return false; return false;
} }

View file

@ -35,7 +35,6 @@ import mage.abilities.effects.ReplacementEffectImpl;
import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect;
import mage.abilities.effects.common.ExileSourceEffect; import mage.abilities.effects.common.ExileSourceEffect;
import mage.abilities.effects.common.ReturnSourceFromGraveyardToBattlefieldEffect; import mage.abilities.effects.common.ReturnSourceFromGraveyardToBattlefieldEffect;
import mage.abilities.effects.common.continuous.GainAbilitySourceEffect;
import mage.constants.Duration; import mage.constants.Duration;
import mage.constants.Outcome; import mage.constants.Outcome;
import mage.constants.TimingRule; import mage.constants.TimingRule;
@ -63,9 +62,8 @@ import mage.game.events.ZoneChangeEvent;
public class UnearthAbility extends ActivatedAbilityImpl { public class UnearthAbility extends ActivatedAbilityImpl {
public UnearthAbility(ManaCosts costs) { public UnearthAbility(ManaCosts costs) {
super(Zone.GRAVEYARD, new ReturnSourceFromGraveyardToBattlefieldEffect(), costs); super(Zone.GRAVEYARD, new ReturnSourceFromGraveyardToBattlefieldEffect(false, true, true), costs);
this.timing = TimingRule.SORCERY; this.timing = TimingRule.SORCERY;
this.addEffect(new GainAbilitySourceEffect(HasteAbility.getInstance(), Duration.Custom));
this.addEffect(new CreateDelayedTriggeredAbilityEffect(new UnearthDelayedTriggeredAbility())); this.addEffect(new CreateDelayedTriggeredAbilityEffect(new UnearthDelayedTriggeredAbility()));
this.addEffect(new UnearthLeavesBattlefieldEffect()); this.addEffect(new UnearthLeavesBattlefieldEffect());
} }

View file

@ -55,6 +55,7 @@ import mage.filter.predicate.mageobject.ConvertedManaCostPredicate;
import mage.filter.predicate.mageobject.NamePredicate; import mage.filter.predicate.mageobject.NamePredicate;
import mage.filter.predicate.mageobject.PowerPredicate; import mage.filter.predicate.mageobject.PowerPredicate;
import mage.game.*; import mage.game.*;
import mage.game.command.CommandObject;
import mage.game.events.GameEvent; import mage.game.events.GameEvent;
import mage.game.events.ZoneChangeEvent; import mage.game.events.ZoneChangeEvent;
import mage.game.permanent.Permanent; import mage.game.permanent.Permanent;
@ -618,14 +619,18 @@ public abstract class CardImpl extends MageObjectImpl implements Card {
stackObject = game.getStack().getSpell(getId()); stackObject = game.getStack().getSpell(getId());
} }
if (stackObject != null) { if (stackObject != null) {
removed = game.getStack().remove(stackObject); removed = game.getStack().remove(stackObject, game);
lkiObject = stackObject; lkiObject = stackObject;
} }
break; break;
case COMMAND: case COMMAND:
lkiObject = game.getObject(objectId); for (CommandObject commandObject : game.getState().getCommand()) {
if (commandObject.getId().equals(objectId)) {
lkiObject = commandObject;
}
}
if (lkiObject != null) { if (lkiObject != null) {
removed = game.getState().getCommand().remove(game.getObject(objectId)); removed = game.getState().getCommand().remove((CommandObject) lkiObject);
} }
break; break;
case OUTSIDE: case OUTSIDE:

View file

@ -5,21 +5,23 @@ package mage.constants;
* @author North * @author North
*/ */
public enum Duration { public enum Duration {
OneUse(""), OneUse("", true),
EndOfGame("for the rest of the game"), EndOfGame("for the rest of the game", false),
WhileOnBattlefield(""), WhileOnBattlefield("", false),
WhileOnStack(""), WhileOnStack("", false),
WhileInGraveyard(""), WhileInGraveyard("", false),
EndOfTurn("until end of turn"), EndOfTurn("until end of turn", true),
UntilYourNextTurn("until your next turn"), UntilYourNextTurn("until your next turn", true),
EndOfCombat("until end of combat"), EndOfCombat("until end of combat", true),
EndOfStep("until end of phase step"), EndOfStep("until end of phase step", true),
Custom(""); Custom("", true);
private final String text; private final String text;
private final boolean onlyValidIfNoZoneChange; // defines if an effect lasts only if the source has not chnaged zone since init of the effect
Duration(String text) { Duration(String text, boolean onlyValidIfNoZoneChange) {
this.text = text; this.text = text;
this.onlyValidIfNoZoneChange = onlyValidIfNoZoneChange;
} }
@Override @Override
@ -27,4 +29,8 @@ public enum Duration {
return text; return text;
} }
public boolean isOnlyValidIfNoZoneChange() {
return onlyValidIfNoZoneChange;
}
} }

View file

@ -328,12 +328,15 @@ public abstract class GameImpl implements Game, Serializable {
MageObject object; MageObject object;
if (state.getBattlefield().containsPermanent(objectId)) { if (state.getBattlefield().containsPermanent(objectId)) {
object = state.getBattlefield().getPermanent(objectId); object = state.getBattlefield().getPermanent(objectId);
state.setZone(objectId, Zone.BATTLEFIELD); // why is this neccessary? // state.setZone(objectId, Zone.BATTLEFIELD); // why is this neccessary?
return object; return object;
} }
if (getPermanentsEntering().containsKey(objectId)) {
return getPermanentEntering(objectId);
}
for (StackObject item : state.getStack()) { for (StackObject item : state.getStack()) {
if (item.getId().equals(objectId)) { if (item.getId().equals(objectId)) {
state.setZone(objectId, Zone.STACK); // why is this neccessary? // state.setZone(objectId, Zone.STACK); // why is this neccessary?
return item; return item;
} }
if (item.getSourceId().equals(objectId) && item instanceof Spell) { if (item.getSourceId().equals(objectId) && item instanceof Spell) {
@ -1421,7 +1424,7 @@ public abstract class GameImpl implements Game, Serializable {
top.resolve(this); top.resolve(this);
} finally { } finally {
if (top != null) { if (top != null) {
state.getStack().remove(top); // seems partly redundant because move card from stack to grave is already done and the stack removed state.getStack().remove(top, this); // seems partly redundant because move card from stack to grave is already done and the stack removed
rememberLKI(top.getSourceId(), Zone.STACK, top); rememberLKI(top.getSourceId(), Zone.STACK, top);
checkInfiniteLoop(top.getSourceId()); checkInfiniteLoop(top.getSourceId());
if (!getTurn().isEndTurnRequested()) { if (!getTurn().isEndTurnRequested()) {
@ -2581,10 +2584,10 @@ public abstract class GameImpl implements Game, Serializable {
it.remove(); it.remove();
} }
} }
if (addPlaneAgain) { if (addPlaneAgain) {
boolean addedAgain = false; boolean addedAgain = false;
for (Player aplayer : state.getPlayers().values()) { for (Player aplayer : state.getPlayers().values()) {
if (!aplayer.hasLeft() && !addedAgain) { if (!aplayer.hasLeft() && !addedAgain) {
addedAgain = true; addedAgain = true;
Plane plane = Plane.getRandomPlane(); Plane plane = Plane.getRandomPlane();

View file

@ -685,7 +685,11 @@ public class GameState implements Serializable, Copyable<GameState> {
} }
public void setZone(UUID id, Zone zone) { public void setZone(UUID id, Zone zone) {
zones.put(id, zone); if (zone == null) {
zones.remove(id);
} else {
zones.put(id, zone);
}
} }
public void addSimultaneousEvent(GameEvent event, Game game) { public void addSimultaneousEvent(GameEvent event, Game game) {

View file

@ -134,12 +134,15 @@ public final class ZonesHandler {
case STACK: case STACK:
// There should never be more than one card here. // There should never be more than one card here.
for (Card card : cards.getCards(game)) { for (Card card : cards.getCards(game)) {
Spell spell;
if (info instanceof ZoneChangeInfo.Stack && ((ZoneChangeInfo.Stack) info).spell != null) { if (info instanceof ZoneChangeInfo.Stack && ((ZoneChangeInfo.Stack) info).spell != null) {
game.getStack().push(((ZoneChangeInfo.Stack) info).spell); spell = ((ZoneChangeInfo.Stack) info).spell;
} else { } else {
game.getStack().push( spell = new Spell(card, card.getSpellAbility().copy(), card.getOwnerId(), event.getFromZone());
new Spell(card, card.getSpellAbility().copy(), card.getOwnerId(), event.getFromZone()));
} }
game.getStack().push(spell);
game.getState().setZone(spell.getId(), Zone.STACK);
game.getState().setZone(card.getId(), Zone.STACK);
} }
break; break;
case BATTLEFIELD: case BATTLEFIELD:

View file

@ -410,7 +410,7 @@ public class Spell extends StackObjImpl implements Card {
} }
} else { } else {
// Copied spell, only remove from stack // Copied spell, only remove from stack
game.getStack().remove(this); game.getStack().remove(this, game);
} }
} }
@ -772,7 +772,7 @@ public class Spell extends StackObjImpl implements Card {
@Override @Override
public boolean moveToExile(UUID exileId, String name, UUID sourceId, Game game, List<UUID> appliedEffects) { public boolean moveToExile(UUID exileId, String name, UUID sourceId, Game game, List<UUID> appliedEffects) {
if (this.isCopiedSpell()) { if (this.isCopiedSpell()) {
game.getStack().remove(this); game.getStack().remove(this, game);
return true; return true;
} }
return this.card.moveToExile(exileId, name, sourceId, game, appliedEffects); return this.card.moveToExile(exileId, name, sourceId, game, appliedEffects);

View file

@ -67,15 +67,16 @@ public class SpellStack extends ArrayDeque<StackObject> {
if (top != null) { if (top != null) {
if (contains(top)) { if (contains(top)) {
logger.warn("StackObject was still on the stack after resoving" + top.getName()); logger.warn("StackObject was still on the stack after resoving" + top.getName());
this.remove(top); this.remove(top, game);
} }
} }
} }
} }
public boolean remove(StackObject object) { public boolean remove(StackObject object, Game game) {
for (StackObject spell : this) { for (StackObject spell : this) {
if (spell.getId().equals(object.getId())) { if (spell.getId().equals(object.getId())) {
game.getState().setZone(spell.getId(), null);
return super.remove(spell); return super.remove(spell);
} }
} }
@ -107,7 +108,7 @@ public class SpellStack extends ArrayDeque<StackObject> {
} }
if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.COUNTER, objectId, sourceId, stackObject.getControllerId()))) { if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.COUNTER, objectId, sourceId, stackObject.getControllerId()))) {
if (!(stackObject instanceof Spell)) { // spells are removed from stack by the card movement if (!(stackObject instanceof Spell)) { // spells are removed from stack by the card movement
this.remove(stackObject); this.remove(stackObject, game);
} }
stackObject.counter(sourceId, game, zone, owner, zoneDetail); stackObject.counter(sourceId, game, zone, owner, zoneDetail);
if (!game.isSimulation()) { if (!game.isSimulation()) {

View file

@ -101,14 +101,14 @@ public class StackAbility extends StackObjImpl implements Ability {
public boolean resolve(Game game) { public boolean resolve(Game game) {
if (ability.getTargets().stillLegal(ability, game) || !canFizzle()) { if (ability.getTargets().stillLegal(ability, game) || !canFizzle()) {
boolean result = ability.resolve(game); boolean result = ability.resolve(game);
game.getStack().remove(this); game.getStack().remove(this, game);
return result; return result;
} }
if (!game.isSimulation()) { if (!game.isSimulation()) {
game.informPlayers("Ability has been fizzled: " + getRule()); game.informPlayers("Ability has been fizzled: " + getRule());
} }
counter(null, game); counter(null, game);
game.getStack().remove(this); game.getStack().remove(this, game);
return false; return false;
} }

View file

@ -284,7 +284,7 @@ public class Turn implements Serializable {
if (stackObject instanceof Spell) { if (stackObject instanceof Spell) {
((Spell) stackObject).moveToExile(null, "", source.getSourceId(), game); ((Spell) stackObject).moveToExile(null, "", source.getSourceId(), game);
} else { } else {
game.getStack().remove(stackObject); // stack ability game.getStack().remove(stackObject, game); // stack ability
} }
} }
// 2) All attacking and blocking creatures are removed from combat. // 2) All attacking and blocking creatures are removed from combat.

View file

@ -3687,7 +3687,7 @@ public abstract class PlayerImpl implements Player, Serializable {
final Spell spell = (Spell) card; final Spell spell = (Spell) card;
if (spell.isCopiedSpell()) { if (spell.isCopiedSpell()) {
// Copied spell, only remove from stack // Copied spell, only remove from stack
game.getStack().remove(spell); game.getStack().remove(spell, game);
} }
} }
game.informPlayers(this.getLogName() + " moves " + (withName ? card.getLogName() + (card.isCopy() ? " (Copy)" : "") : "a card face down") + ' ' game.informPlayers(this.getLogName() + " moves " + (withName ? card.getLogName() + (card.isCopy() ? " (Copy)" : "") : "a card face down") + ' '