* Reworked/Cleaned card movement handling.

This commit is contained in:
LevelX2 2015-10-11 03:52:38 +02:00
parent 0f15f4a808
commit 8b8097878c
54 changed files with 714 additions and 687 deletions

View file

@ -436,29 +436,32 @@ public class ComputerPlayer6 extends ComputerPlayer /*implements Player*/ {
} }
protected void resolve(SimulationNode2 node, int depth, Game game) { protected void resolve(SimulationNode2 node, int depth, Game game) {
StackObject ability = game.getStack().pop(); StackObject stackObject = game.getStack().getFirst();
if (ability instanceof StackAbility) { if (stackObject instanceof StackAbility) {
SearchEffect effect = getSearchEffect((StackAbility) ability); SearchEffect effect = getSearchEffect((StackAbility) stackObject);
if (effect != null && ability.getControllerId().equals(playerId)) { if (effect != null && stackObject.getControllerId().equals(playerId)) {
Target target = effect.getTarget(); Target target = effect.getTarget();
if (!target.doneChosing()) { if (!target.doneChosing()) {
for (UUID targetId : target.possibleTargets(ability.getSourceId(), ability.getControllerId(), game)) { for (UUID targetId : target.possibleTargets(stackObject.getSourceId(), stackObject.getControllerId(), game)) {
Game sim = game.copy(); Game sim = game.copy();
StackAbility newAbility = (StackAbility) ability.copy(); StackAbility newAbility = (StackAbility) stackObject.copy();
SearchEffect newEffect = getSearchEffect(newAbility); SearchEffect newEffect = getSearchEffect(newAbility);
newEffect.getTarget().addTarget(targetId, newAbility, sim); newEffect.getTarget().addTarget(targetId, newAbility, sim);
sim.getStack().push(newAbility); sim.getStack().push(newAbility);
SimulationNode2 newNode = new SimulationNode2(node, sim, depth, ability.getControllerId()); SimulationNode2 newNode = new SimulationNode2(node, sim, depth, stackObject.getControllerId());
node.children.add(newNode); node.children.add(newNode);
newNode.getTargets().add(targetId); newNode.getTargets().add(targetId);
logger.trace("Sim search -- node#: " + SimulationNode2.getCount() + " for player: " + sim.getPlayer(ability.getControllerId()).getName()); logger.trace("Sim search -- node#: " + SimulationNode2.getCount() + " for player: " + sim.getPlayer(stackObject.getControllerId()).getName());
} }
return; return;
} }
} }
} }
//logger.info("simulating resolve "); //logger.info("simulating resolve ");
ability.resolve(game); stackObject.resolve(game);
if (stackObject instanceof StackAbility) {
game.getStack().remove(stackObject);
}
game.applyEffects(); game.applyEffects();
game.getPlayers().resetPassed(); game.getPlayers().resetPassed();
game.getPlayerList().setCurrent(game.getActivePlayerId()); game.getPlayerList().setCurrent(game.getActivePlayerId());

View file

@ -27,7 +27,8 @@
*/ */
package mage.sets.avacynrestored; package mage.sets.avacynrestored;
import mage.constants.*; import java.util.UUID;
import mage.MageObject;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.Mode; import mage.abilities.Mode;
import mage.abilities.common.EntersBattlefieldTriggeredAbility; import mage.abilities.common.EntersBattlefieldTriggeredAbility;
@ -37,6 +38,11 @@ import mage.abilities.effects.ReplacementEffectImpl;
import mage.abilities.effects.common.AttachEffect; import mage.abilities.effects.common.AttachEffect;
import mage.abilities.keyword.EnchantAbility; import mage.abilities.keyword.EnchantAbility;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.constants.Rarity;
import mage.constants.Zone;
import mage.filter.FilterPermanent; import mage.filter.FilterPermanent;
import mage.filter.common.FilterControlledCreaturePermanent; import mage.filter.common.FilterControlledCreaturePermanent;
import mage.game.Game; import mage.game.Game;
@ -47,8 +53,6 @@ import mage.target.TargetPermanent;
import mage.target.common.TargetCreaturePermanent; import mage.target.common.TargetCreaturePermanent;
import mage.util.functions.EmptyApplyToPermanent; import mage.util.functions.EmptyApplyToPermanent;
import java.util.UUID;
/** /**
* *
* @author noxx * @author noxx
@ -60,7 +64,6 @@ public class InfiniteReflection extends CardImpl {
this.expansionSetCode = "AVR"; this.expansionSetCode = "AVR";
this.subtype.add("Aura"); this.subtype.add("Aura");
// Enchant creature // Enchant creature
TargetPermanent auraTarget = new TargetCreaturePermanent(); TargetPermanent auraTarget = new TargetCreaturePermanent();
this.getSpellAbility().addTarget(auraTarget); this.getSpellAbility().addTarget(auraTarget);
@ -109,9 +112,9 @@ class InfiniteReflectionTriggeredEffect extends OneShotEffect {
if (sourcePermanent != null && sourcePermanent.getAttachedTo() != null) { if (sourcePermanent != null && sourcePermanent.getAttachedTo() != null) {
Permanent toCopyFromPermanent = game.getPermanent(sourcePermanent.getAttachedTo()); Permanent toCopyFromPermanent = game.getPermanent(sourcePermanent.getAttachedTo());
if (toCopyFromPermanent != null) { if (toCopyFromPermanent != null) {
for (Permanent toCopyToPermanent: game.getBattlefield().getAllActivePermanents(filter, source.getControllerId(), game)) { for (Permanent toCopyToPermanent : game.getBattlefield().getAllActivePermanents(filter, source.getControllerId(), game)) {
if (!toCopyToPermanent.equals(toCopyFromPermanent) && !(toCopyToPermanent instanceof PermanentToken)) { if (!toCopyToPermanent.equals(toCopyFromPermanent) && !(toCopyToPermanent instanceof PermanentToken)) {
game.copyPermanent(toCopyFromPermanent, toCopyToPermanent, source, new EmptyApplyToPermanent()); game.copyPermanent(toCopyFromPermanent, toCopyToPermanent.getId(), source, new EmptyApplyToPermanent());
} }
} }
return true; return true;
@ -135,7 +138,7 @@ class InfiniteReflectionEntersBattlefieldEffect extends ReplacementEffectImpl {
public boolean checksEventType(GameEvent event, Game game) { public boolean checksEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD; return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD;
} }
@Override @Override
public boolean applies(GameEvent event, Ability source, Game game) { public boolean applies(GameEvent event, Ability source, Game game) {
Permanent permanent = game.getPermanent(event.getTargetId()); Permanent permanent = game.getPermanent(event.getTargetId());
@ -144,15 +147,14 @@ class InfiniteReflectionEntersBattlefieldEffect extends ReplacementEffectImpl {
&& !(permanent instanceof PermanentToken); && !(permanent instanceof PermanentToken);
} }
@Override @Override
public boolean replaceEvent(GameEvent event, Ability source, Game game) { public boolean replaceEvent(GameEvent event, Ability source, Game game) {
Permanent toCopyToPermanent = game.getPermanent(event.getTargetId()); MageObject toCopyToObject = game.getObject(event.getTargetId());
Permanent sourcePermanent = game.getPermanent(source.getSourceId()); Permanent sourcePermanent = game.getPermanent(source.getSourceId());
if (sourcePermanent != null && toCopyToPermanent != null && sourcePermanent.getAttachedTo() != null) { if (sourcePermanent != null && toCopyToObject != null && sourcePermanent.getAttachedTo() != null) {
Permanent toCopyFromPermanent = game.getPermanent(sourcePermanent.getAttachedTo()); Permanent toCopyFromPermanent = game.getPermanent(sourcePermanent.getAttachedTo());
if (toCopyFromPermanent != null) { if (toCopyFromPermanent != null) {
game.copyPermanent(toCopyFromPermanent, toCopyToPermanent, source, new EmptyApplyToPermanent()); game.copyPermanent(toCopyFromPermanent, toCopyToObject.getId(), source, new EmptyApplyToPermanent());
} }
} }
return false; return false;

View file

@ -157,7 +157,6 @@ class PossibilityStormEffect extends OneShotEffect {
if (exile != null) { if (exile != null) {
while (exile.size() > 0) { while (exile.size() > 0) {
card = exile.getRandom(game); card = exile.getRandom(game);
exile.remove(card.getId());
spellController.moveCardToLibraryWithInfo(card, source.getSourceId(), game, Zone.EXILED, false, false); spellController.moveCardToLibraryWithInfo(card, source.getSourceId(), game, Zone.EXILED, false, false);
} }
} }

View file

@ -54,14 +54,7 @@ import mage.players.Player;
* @author jeffwadsworth * @author jeffwadsworth
*/ */
public class MycosynthGolem extends CardImpl { public class MycosynthGolem extends CardImpl {
private static final FilterSpell filter = new FilterSpell("Artifact creature spells you cast");
static {
filter.add(new CardTypePredicate(CardType.ARTIFACT));
filter.add(new CardTypePredicate(CardType.CREATURE));
}
public MycosynthGolem(UUID ownerId) { public MycosynthGolem(UUID ownerId) {
super(ownerId, 137, "Mycosynth Golem", Rarity.RARE, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{11}"); super(ownerId, 137, "Mycosynth Golem", Rarity.RARE, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{11}");
this.expansionSetCode = "5DN"; this.expansionSetCode = "5DN";
@ -75,8 +68,8 @@ public class MycosynthGolem extends CardImpl {
// Artifact creature spells you cast have affinity for artifacts. // Artifact creature spells you cast have affinity for artifacts.
this.addAbility(new SimpleStaticAbility( this.addAbility(new SimpleStaticAbility(
Zone.BATTLEFIELD, new MycosynthGolemGainAbilitySpellsEffect(new AffinityForArtifactsAbility(), filter))); Zone.BATTLEFIELD, new MycosynthGolemGainAbilitySpellsEffect()));
} }
public MycosynthGolem(final MycosynthGolem card) { public MycosynthGolem(final MycosynthGolem card) {
@ -91,20 +84,21 @@ public class MycosynthGolem extends CardImpl {
class MycosynthGolemGainAbilitySpellsEffect extends ContinuousEffectImpl { class MycosynthGolemGainAbilitySpellsEffect extends ContinuousEffectImpl {
private final Ability ability; private static final FilterSpell filter = new FilterSpell("Artifact creature spells you cast");
private final FilterSpell filter; private static final Ability ability = new AffinityForArtifactsAbility();
public MycosynthGolemGainAbilitySpellsEffect(Ability ability, FilterSpell filter) { static {
filter.add(new CardTypePredicate(CardType.ARTIFACT));
filter.add(new CardTypePredicate(CardType.CREATURE));
}
public MycosynthGolemGainAbilitySpellsEffect() {
super(Duration.WhileOnBattlefield, Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, Outcome.AddAbility); super(Duration.WhileOnBattlefield, Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, Outcome.AddAbility);
this.ability = ability; staticText = "Artifact creature spells you cast have affinity for artifacts";
this.filter = filter;
staticText = filter.getMessage() + " have " + ability.getRule();
} }
public MycosynthGolemGainAbilitySpellsEffect(final MycosynthGolemGainAbilitySpellsEffect effect) { public MycosynthGolemGainAbilitySpellsEffect(final MycosynthGolemGainAbilitySpellsEffect effect) {
super(effect); super(effect);
this.ability = effect.ability;
this.filter = effect.filter;
} }
@Override @Override
@ -114,17 +108,15 @@ class MycosynthGolemGainAbilitySpellsEffect extends ContinuousEffectImpl {
@Override @Override
public boolean apply(Game game, Ability source) { public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(source.getControllerId()); Player controller = game.getPlayer(source.getControllerId());
Permanent permanent = game.getPermanent(source.getSourceId()); Permanent permanent = game.getPermanent(source.getSourceId());
if (player != null && permanent != null) { if (controller != null && permanent != null) {
for (StackObject stackObject : game.getStack()) { for (StackObject stackObject : game.getStack()) {
// only spells cast, so no copies of spells // only spells cast, so no copies of spells
if ((stackObject instanceof Spell) && !stackObject.isCopy() && stackObject.getControllerId().equals(source.getControllerId())) { if ((stackObject instanceof Spell) && !stackObject.isCopy() && stackObject.getControllerId().equals(source.getControllerId())) {
Spell spell = (Spell) stackObject; Spell spell = (Spell) stackObject;
if (filter.match(spell, game)) { if (filter.match(spell, game)) {
if (!spell.getAbilities().contains(ability)) { game.getState().addOtherAbility(spell.getCard(), ability);
game.getState().addOtherAbility(spell.getCard(), ability);
}
} }
} }
} }

View file

@ -28,10 +28,6 @@
package mage.sets.gatecrash; package mage.sets.gatecrash;
import java.util.UUID; import java.util.UUID;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.Rarity;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.common.TapSourceCost;
@ -39,6 +35,9 @@ import mage.abilities.costs.mana.GenericManaCost;
import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.OneShotEffect;
import mage.abilities.mana.ColorlessManaAbility; import mage.abilities.mana.ColorlessManaAbility;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.Rarity;
import mage.constants.Zone; import mage.constants.Zone;
import mage.game.Game; import mage.game.Game;
import mage.game.permanent.Permanent; import mage.game.permanent.Permanent;
@ -80,7 +79,7 @@ class ThespiansStageCopyEffect extends OneShotEffect {
public ThespiansStageCopyEffect() { public ThespiansStageCopyEffect() {
super(Outcome.Benefit); super(Outcome.Benefit);
this.staticText = "Thespian's Stage becomes a copy of target land and gains this ability"; this.staticText = "{this} becomes a copy of target land and gains this ability";
} }
public ThespiansStageCopyEffect(final ThespiansStageCopyEffect effect) { public ThespiansStageCopyEffect(final ThespiansStageCopyEffect effect) {
@ -97,7 +96,7 @@ class ThespiansStageCopyEffect extends OneShotEffect {
Permanent sourcePermanent = game.getPermanent(source.getSourceId()); Permanent sourcePermanent = game.getPermanent(source.getSourceId());
Permanent copyFromPermanent = game.getPermanent(getTargetPointer().getFirst(game, source)); Permanent copyFromPermanent = game.getPermanent(getTargetPointer().getFirst(game, source));
if (sourcePermanent != null && copyFromPermanent != null) { if (sourcePermanent != null && copyFromPermanent != null) {
Permanent newPermanent = game.copyPermanent(copyFromPermanent, sourcePermanent, source, new EmptyApplyToPermanent()); Permanent newPermanent = game.copyPermanent(copyFromPermanent, sourcePermanent.getId(), source, new EmptyApplyToPermanent());
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ThespiansStageCopyEffect(), new GenericManaCost(2)); Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ThespiansStageCopyEffect(), new GenericManaCost(2));
ability.addCost(new TapSourceCost()); ability.addCost(new TapSourceCost());
ability.addTarget(new TargetLandPermanent()); ability.addTarget(new TargetLandPermanent());

View file

@ -57,7 +57,7 @@ public class MizziumTransreliquat extends CardImpl {
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new MizziumTransreliquatCopyEffect(), new ManaCostsImpl("{3}")); Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new MizziumTransreliquatCopyEffect(), new ManaCostsImpl("{3}"));
ability.addTarget(new TargetArtifactPermanent()); ability.addTarget(new TargetArtifactPermanent());
this.addAbility(ability); this.addAbility(ability);
// {1}{U}{R}: Mizzium Transreliquat becomes a copy of target artifact and gains this ability. // {1}{U}{R}: Mizzium Transreliquat becomes a copy of target artifact and gains this ability.
ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new MizziumTransreliquatCopyAndGainAbilityEffect(), new ManaCostsImpl("{1}{U}{R}")); ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new MizziumTransreliquatCopyAndGainAbilityEffect(), new ManaCostsImpl("{1}{U}{R}"));
ability.addTarget(new TargetArtifactPermanent()); ability.addTarget(new TargetArtifactPermanent());
@ -74,12 +74,11 @@ public class MizziumTransreliquat extends CardImpl {
} }
} }
class MizziumTransreliquatCopyEffect extends OneShotEffect { class MizziumTransreliquatCopyEffect extends OneShotEffect {
public MizziumTransreliquatCopyEffect() { public MizziumTransreliquatCopyEffect() {
super(Outcome.Copy); super(Outcome.Copy);
this.staticText = "Mizzium Transreliquat becomes a copy of target artifact until end of turn"; this.staticText = "{this} becomes a copy of target artifact until end of turn";
} }
public MizziumTransreliquatCopyEffect(final MizziumTransreliquatCopyEffect effect) { public MizziumTransreliquatCopyEffect(final MizziumTransreliquatCopyEffect effect) {
@ -96,17 +95,18 @@ class MizziumTransreliquatCopyEffect extends OneShotEffect {
Permanent sourcePermanent = game.getPermanent(source.getSourceId()); Permanent sourcePermanent = game.getPermanent(source.getSourceId());
Permanent copyFromPermanent = game.getPermanent(getTargetPointer().getFirst(game, source)); Permanent copyFromPermanent = game.getPermanent(getTargetPointer().getFirst(game, source));
if (sourcePermanent != null && copyFromPermanent != null) { if (sourcePermanent != null && copyFromPermanent != null) {
game.copyPermanent(Duration.EndOfTurn, copyFromPermanent, sourcePermanent, source, new EmptyApplyToPermanent()); game.copyPermanent(Duration.EndOfTurn, copyFromPermanent, sourcePermanent.getId(), source, new EmptyApplyToPermanent());
return true; return true;
} }
return false; return false;
} }
} }
class MizziumTransreliquatCopyAndGainAbilityEffect extends OneShotEffect { class MizziumTransreliquatCopyAndGainAbilityEffect extends OneShotEffect {
public MizziumTransreliquatCopyAndGainAbilityEffect() { public MizziumTransreliquatCopyAndGainAbilityEffect() {
super(Outcome.Benefit); super(Outcome.Benefit);
this.staticText = "Mizzium Transreliquat becomes a copy of target artifact and gains this ability"; this.staticText = "{this} becomes a copy of target artifact and gains this ability";
} }
public MizziumTransreliquatCopyAndGainAbilityEffect(final MizziumTransreliquatCopyAndGainAbilityEffect effect) { public MizziumTransreliquatCopyAndGainAbilityEffect(final MizziumTransreliquatCopyAndGainAbilityEffect effect) {
@ -123,7 +123,7 @@ class MizziumTransreliquatCopyAndGainAbilityEffect extends OneShotEffect {
Permanent sourcePermanent = game.getPermanent(source.getSourceId()); Permanent sourcePermanent = game.getPermanent(source.getSourceId());
Permanent copyFromPermanent = game.getPermanent(getTargetPointer().getFirst(game, source)); Permanent copyFromPermanent = game.getPermanent(getTargetPointer().getFirst(game, source));
if (sourcePermanent != null && copyFromPermanent != null) { if (sourcePermanent != null && copyFromPermanent != null) {
Permanent newPermanent = game.copyPermanent(copyFromPermanent, sourcePermanent, source, new EmptyApplyToPermanent()); Permanent newPermanent = game.copyPermanent(copyFromPermanent, sourcePermanent.getId(), source, new EmptyApplyToPermanent());
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new MizziumTransreliquatCopyAndGainAbilityEffect(), new ManaCostsImpl("{1}{U}{R}")); Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new MizziumTransreliquatCopyAndGainAbilityEffect(), new ManaCostsImpl("{1}{U}{R}"));
ability.addTarget(new TargetArtifactPermanent()); ability.addTarget(new TargetArtifactPermanent());
newPermanent.addAbility(ability, source.getSourceId(), game); newPermanent.addAbility(ability, source.getSourceId(), game);
@ -131,4 +131,4 @@ class MizziumTransreliquatCopyAndGainAbilityEffect extends OneShotEffect {
} }
return false; return false;
} }
} }

View file

@ -64,7 +64,7 @@ public class PolymorphousRush extends CardImpl {
// Strive - Polymorphous Rush costs {1}{U} more to cast for each target beyond the first. // Strive - Polymorphous Rush costs {1}{U} more to cast for each target beyond the first.
this.addAbility(new StriveAbility("{1}{U}")); this.addAbility(new StriveAbility("{1}{U}"));
// Choose a creature on the battlefield. Any number of target creatures you control each become a copy of that creature until end of turn. // Choose a creature on the battlefield. Any number of target creatures you control each become a copy of that creature until end of turn.
this.getSpellAbility().addTarget(new TargetCreaturePermanent(0, Integer.MAX_VALUE, filter, false)); this.getSpellAbility().addTarget(new TargetCreaturePermanent(0, Integer.MAX_VALUE, filter, false));
this.getSpellAbility().addEffect(new PolymorphousRushCopyEffect()); this.getSpellAbility().addEffect(new PolymorphousRushCopyEffect());
@ -82,21 +82,21 @@ public class PolymorphousRush extends CardImpl {
} }
class PolymorphousRushCopyEffect extends OneShotEffect { class PolymorphousRushCopyEffect extends OneShotEffect {
public PolymorphousRushCopyEffect() { public PolymorphousRushCopyEffect() {
super(Outcome.Copy); super(Outcome.Copy);
this.staticText = "Choose a creature on the battlefield. Any number of target creatures you control each become a copy of that creature until end of turn"; this.staticText = "Choose a creature on the battlefield. Any number of target creatures you control each become a copy of that creature until end of turn";
} }
public PolymorphousRushCopyEffect(final PolymorphousRushCopyEffect effect) { public PolymorphousRushCopyEffect(final PolymorphousRushCopyEffect effect) {
super(effect); super(effect);
} }
@Override @Override
public PolymorphousRushCopyEffect copy() { public PolymorphousRushCopyEffect copy() {
return new PolymorphousRushCopyEffect(this); return new PolymorphousRushCopyEffect(this);
} }
@Override @Override
public boolean apply(Game game, Ability source) { public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId()); Player controller = game.getPlayer(source.getControllerId());
@ -107,10 +107,10 @@ class PolymorphousRushCopyEffect extends OneShotEffect {
if (target.canChoose(source.getId(), controller.getId(), game) && controller.chooseTarget(outcome, target, source, game)) { if (target.canChoose(source.getId(), controller.getId(), game) && controller.chooseTarget(outcome, target, source, game)) {
Permanent copyFromCreature = game.getPermanent(target.getFirstTarget()); Permanent copyFromCreature = game.getPermanent(target.getFirstTarget());
if (copyFromCreature != null) { if (copyFromCreature != null) {
for (UUID copyToId: getTargetPointer().getTargets(game, source)) { for (UUID copyToId : getTargetPointer().getTargets(game, source)) {
Permanent copyToCreature = game.getPermanent(copyToId); Permanent copyToCreature = game.getPermanent(copyToId);
if (copyToCreature != null) { if (copyToCreature != null) {
game.copyPermanent(Duration.EndOfTurn, copyFromCreature, copyToCreature, source, new EmptyApplyToPermanent()); game.copyPermanent(Duration.EndOfTurn, copyFromCreature, copyToId, source, new EmptyApplyToPermanent());
} }
} }
} }
@ -119,4 +119,5 @@ class PolymorphousRushCopyEffect extends OneShotEffect {
} }
return false; return false;
} }
} }

View file

@ -110,7 +110,7 @@ class WorldgorgerDragonEntersEffect extends OneShotEffect {
Player controller = game.getPlayer(source.getControllerId()); Player controller = game.getPlayer(source.getControllerId());
MageObject sourceObject = source.getSourceObject(game); MageObject sourceObject = source.getSourceObject(game);
if (controller != null) { if (controller != null) {
UUID exileId = CardUtil.getObjectExileZoneId(game, sourceObject); UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter());
if (exileId != null) { if (exileId != null) {
for (Permanent permanent : game.getBattlefield().getAllActivePermanents(filter, source.getControllerId(), game)) { for (Permanent permanent : game.getBattlefield().getAllActivePermanents(filter, source.getControllerId(), game)) {
if (!permanent.getId().equals(source.getSourceId())) { // Another if (!permanent.getId().equals(source.getSourceId())) { // Another
@ -145,7 +145,7 @@ class WorldgorgerDragonLeavesEffect extends OneShotEffect {
Player controller = game.getPlayer(source.getControllerId()); Player controller = game.getPlayer(source.getControllerId());
MageObject sourceObject = source.getSourceObject(game); MageObject sourceObject = source.getSourceObject(game);
if (controller != null && sourceObject != null) { if (controller != null && sourceObject != null) {
int zoneChangeCounter = (sourceObject instanceof PermanentToken) ? source.getSourceObjectZoneChangeCounter() : source.getSourceObjectZoneChangeCounter() -1; int zoneChangeCounter = (sourceObject instanceof PermanentToken) ? source.getSourceObjectZoneChangeCounter() : source.getSourceObjectZoneChangeCounter() - 1;
ExileZone exile = game.getExile().getExileZone(CardUtil.getExileZoneId(game, source.getSourceId(), zoneChangeCounter)); ExileZone exile = game.getExile().getExileZone(CardUtil.getExileZoneId(game, source.getSourceId(), zoneChangeCounter));
if (exile != null) { if (exile != null) {
exile = exile.copy(); exile = exile.copy();

View file

@ -114,15 +114,15 @@ class CopyArtifactEffect extends OneShotEffect {
@Override @Override
public boolean apply(Game game, Ability source) { public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(source.getControllerId()); Player player = game.getPlayer(source.getControllerId());
Permanent sourcePermanent = game.getPermanent(source.getSourceId()); MageObject sourceObject = game.getObject(source.getSourceId());
if (player != null && sourcePermanent != null) { if (player != null && sourceObject != null) {
Target target = new TargetPermanent(filter); Target target = new TargetPermanent(filter);
target.setNotTarget(true); target.setNotTarget(true);
if (target.canChoose(source.getControllerId(), game)) { if (target.canChoose(source.getControllerId(), game)) {
player.choose(Outcome.Copy, target, source.getSourceId(), game); player.choose(Outcome.Copy, target, source.getSourceId(), game);
Permanent copyFromPermanent = game.getPermanent(target.getFirstTarget()); Permanent copyFromPermanent = game.getPermanent(target.getFirstTarget());
if (copyFromPermanent != null) { if (copyFromPermanent != null) {
game.copyPermanent(copyFromPermanent, sourcePermanent, source, applier); game.copyPermanent(copyFromPermanent, sourceObject.getId(), source, applier);
return true; return true;
} }
} }

View file

@ -48,7 +48,6 @@ import mage.target.TargetPermanent;
import mage.target.common.TargetCreaturePermanent; import mage.target.common.TargetCreaturePermanent;
import mage.util.functions.EmptyApplyToPermanent; import mage.util.functions.EmptyApplyToPermanent;
/** /**
* @author duncant * @author duncant
*/ */
@ -68,11 +67,11 @@ public class Shapesharer extends CardImpl {
this.power = new MageInt(1); this.power = new MageInt(1);
this.toughness = new MageInt(1); this.toughness = new MageInt(1);
this.addAbility(ChangelingAbility.getInstance()); this.addAbility(ChangelingAbility.getInstance());
// {2}{U}: Target Shapeshifter becomes a copy of target creature until your next turn. // {2}{U}: Target Shapeshifter becomes a copy of target creature until your next turn.
Ability copyAbility = new SimpleActivatedAbility(Zone.BATTLEFIELD, Ability copyAbility = new SimpleActivatedAbility(Zone.BATTLEFIELD,
new ShapesharerEffect(), new ShapesharerEffect(),
new ManaCostsImpl("{2}{U}")); new ManaCostsImpl("{2}{U}"));
copyAbility.addTarget(new TargetPermanent(filterShapeshifter)); copyAbility.addTarget(new TargetPermanent(filterShapeshifter));
copyAbility.addTarget(new TargetCreaturePermanent()); copyAbility.addTarget(new TargetCreaturePermanent());
this.addAbility(copyAbility); this.addAbility(copyAbility);
@ -89,6 +88,7 @@ public class Shapesharer extends CardImpl {
} }
class ShapesharerEffect extends OneShotEffect { class ShapesharerEffect extends OneShotEffect {
public ShapesharerEffect() { public ShapesharerEffect() {
super(Outcome.Copy); super(Outcome.Copy);
this.staticText = "Target Shapeshifter becomes a copy of target creature until your next turn."; this.staticText = "Target Shapeshifter becomes a copy of target creature until your next turn.";
@ -105,10 +105,12 @@ class ShapesharerEffect extends OneShotEffect {
@Override @Override
public boolean apply(Game game, Ability ability) { public boolean apply(Game game, Ability ability) {
Permanent copyTo = game.getPermanent(ability.getFirstTarget()); Permanent copyTo = game.getPermanent(getTargetPointer().getFirst(game, ability));
if (copyTo != null) { if (copyTo != null) {
Permanent copyFrom = game.getPermanentOrLKIBattlefield(ability.getTargets().get(1).getFirstTarget()); Permanent copyFrom = game.getPermanentOrLKIBattlefield(ability.getTargets().get(1).getFirstTarget());
game.copyPermanent(Duration.EndOfTurn, copyFrom, copyTo, ability, new EmptyApplyToPermanent()); if (copyFrom != null) {
game.copyPermanent(Duration.EndOfTurn, copyFrom, copyTo.getId(), ability, new EmptyApplyToPermanent());
}
} }
return true; return true;
} }

View file

@ -25,12 +25,9 @@
* 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.sets.magic2012; package mage.sets.magic2012;
import java.util.UUID; import java.util.UUID;
import mage.constants.*;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.common.SimpleStaticAbility; import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.common.AttachEffect; import mage.abilities.effects.common.AttachEffect;
@ -38,6 +35,11 @@ import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect;
import mage.abilities.keyword.EnchantAbility; import mage.abilities.keyword.EnchantAbility;
import mage.abilities.keyword.FlyingAbility; import mage.abilities.keyword.FlyingAbility;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.constants.AttachmentType;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.Rarity;
import mage.constants.Zone;
import mage.target.TargetPermanent; import mage.target.TargetPermanent;
import mage.target.common.TargetCreaturePermanent; import mage.target.common.TargetCreaturePermanent;
@ -47,20 +49,23 @@ import mage.target.common.TargetCreaturePermanent;
*/ */
public class Flight extends CardImpl { public class Flight extends CardImpl {
public Flight (UUID ownerId) { public Flight(UUID ownerId) {
super(ownerId, 53, "Flight", Rarity.COMMON, new CardType[]{CardType.ENCHANTMENT}, "{U}"); super(ownerId, 53, "Flight", Rarity.COMMON, new CardType[]{CardType.ENCHANTMENT}, "{U}");
this.expansionSetCode = "M12"; this.expansionSetCode = "M12";
this.subtype.add("Aura"); this.subtype.add("Aura");
// Enchant creature
TargetPermanent auraTarget = new TargetCreaturePermanent(); TargetPermanent auraTarget = new TargetCreaturePermanent();
this.getSpellAbility().addTarget(auraTarget); this.getSpellAbility().addTarget(auraTarget);
this.getSpellAbility().addEffect(new AttachEffect(Outcome.AddAbility)); this.getSpellAbility().addEffect(new AttachEffect(Outcome.AddAbility));
Ability ability = new EnchantAbility(auraTarget.getTargetName()); Ability ability = new EnchantAbility(auraTarget.getTargetName());
this.addAbility(ability); this.addAbility(ability);
// Enchanted creature has flying.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAttachedEffect(FlyingAbility.getInstance(), AttachmentType.AURA))); this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new GainAbilityAttachedEffect(FlyingAbility.getInstance(), AttachmentType.AURA)));
} }
public Flight (final Flight card) { public Flight(final Flight card) {
super(card); super(card);
} }

View file

@ -97,15 +97,15 @@ class PhantasmalImageCopyEffect extends OneShotEffect {
@Override @Override
public boolean apply(Game game, Ability source) { public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(source.getControllerId()); Player player = game.getPlayer(source.getControllerId());
Permanent sourcePermanent = game.getPermanent(source.getSourceId()); MageObject sourceObject = game.getObject(source.getSourceId());
if (player != null && sourcePermanent != null) { if (player != null && sourceObject != null) {
Target target = new TargetPermanent(new FilterCreaturePermanent("creature (you copy from)")); Target target = new TargetPermanent(new FilterCreaturePermanent("creature (you copy from)"));
target.setNotTarget(true); target.setNotTarget(true);
if (target.canChoose(source.getSourceId(), source.getControllerId(), game)) { if (target.canChoose(source.getSourceId(), source.getControllerId(), game)) {
player.choose(Outcome.Copy, target, source.getSourceId(), game); player.choose(Outcome.Copy, target, source.getSourceId(), game);
Permanent copyFromPermanent = game.getPermanent(target.getFirstTarget()); Permanent copyFromPermanent = game.getPermanent(target.getFirstTarget());
if (copyFromPermanent != null) { if (copyFromPermanent != null) {
game.copyPermanent(copyFromPermanent, sourcePermanent, source, new ApplyToPermanent() { game.copyPermanent(copyFromPermanent, sourceObject.getId(), source, new ApplyToPermanent() {
@Override @Override
public Boolean apply(Game game, Permanent permanent) { public Boolean apply(Game game, Permanent permanent) {
if (!permanent.getSubtype().contains("Illusion")) { if (!permanent.getSubtype().contains("Illusion")) {

View file

@ -29,6 +29,7 @@ package mage.sets.magic2015;
import java.util.UUID; import java.util.UUID;
import mage.MageInt; import mage.MageInt;
import mage.MageObject;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.common.SimpleStaticAbility; import mage.abilities.common.SimpleStaticAbility;
@ -96,15 +97,15 @@ class MercurialPretenderCopyEffect extends OneShotEffect {
@Override @Override
public boolean apply(Game game, Ability source) { public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(source.getControllerId()); Player player = game.getPlayer(source.getControllerId());
Permanent sourcePermanent = game.getPermanent(source.getSourceId()); MageObject sourceObject = game.getObject(source.getSourceId());
if (player != null && sourcePermanent != null) { if (player != null && sourceObject != null) {
Target target = new TargetPermanent(new FilterControlledCreaturePermanent()); Target target = new TargetPermanent(new FilterControlledCreaturePermanent());
target.setNotTarget(true); target.setNotTarget(true);
if (target.canChoose(source.getSourceId(), source.getControllerId(), game)) { if (target.canChoose(source.getSourceId(), source.getControllerId(), game)) {
player.choose(Outcome.Copy, target, source.getSourceId(), game); player.choose(Outcome.Copy, target, source.getSourceId(), game);
Permanent copyFromPermanent = game.getPermanent(target.getFirstTarget()); Permanent copyFromPermanent = game.getPermanent(target.getFirstTarget());
if (copyFromPermanent != null) { if (copyFromPermanent != null) {
game.copyPermanent(copyFromPermanent, sourcePermanent, source, game.copyPermanent(copyFromPermanent, sourceObject.getId(), source,
// {2}{U}{U}: Return this creature to its owner's hand. // {2}{U}{U}: Return this creature to its owner's hand.
new AbilityApplier(new SimpleActivatedAbility(Zone.BATTLEFIELD, new ReturnToHandSourceEffect(true), new ManaCostsImpl("{2}{U}{U}"))) new AbilityApplier(new SimpleActivatedAbility(Zone.BATTLEFIELD, new ReturnToHandSourceEffect(true), new ManaCostsImpl("{2}{U}{U}")))
); );

View file

@ -44,6 +44,7 @@ import mage.filter.common.FilterNonlandCard;
import mage.game.Game; 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 mage.game.permanent.PermanentToken;
import mage.game.stack.Spell; import mage.game.stack.Spell;
import mage.players.Player; import mage.players.Player;
import mage.target.common.TargetCardInExile; import mage.target.common.TargetCardInExile;
@ -100,7 +101,8 @@ class KnowledgePoolEffect1 extends OneShotEffect {
Player player = game.getPlayer(playerId); Player player = game.getPlayer(playerId);
if (player != null) { if (player != null) {
player.moveCardsToExile(player.getLibrary().getTopCards(game, 3), source, game, true, player.moveCardsToExile(player.getLibrary().getTopCards(game, 3), source, game, true,
CardUtil.getObjectExileZoneId(game, sourceObject), sourceObject.getIdName() + " (" + sourceObject.getZoneChangeCounter(game) + ")"); CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()),
sourceObject.getIdName() + " (" + sourceObject.getZoneChangeCounter(game) + ")");
} }
} }
return true; return true;
@ -168,7 +170,8 @@ class KnowledgePoolEffect2 extends OneShotEffect {
MageObject sourceObject = game.getObject(source.getSourceId()); MageObject sourceObject = game.getObject(source.getSourceId());
Player controller = game.getPlayer(source.getControllerId()); Player controller = game.getPlayer(source.getControllerId());
if (controller != null && spell != null && sourceObject != null) { if (controller != null && spell != null && sourceObject != null) {
UUID exileZoneId = CardUtil.getObjectExileZoneId(game, sourceObject); int zoneChangeCounter = (sourceObject instanceof PermanentToken) ? source.getSourceObjectZoneChangeCounter() : source.getSourceObjectZoneChangeCounter() - 1;
UUID exileZoneId = CardUtil.getExileZoneId(game, source.getSourceId(), zoneChangeCounter);
if (controller.moveCardsToExile(spell, source, game, true, exileZoneId, sourceObject.getIdName())) { if (controller.moveCardsToExile(spell, source, game, true, exileZoneId, sourceObject.getIdName())) {
Player player = game.getPlayer(spell.getControllerId()); Player player = game.getPlayer(spell.getControllerId());
if (player != null && player.chooseUse(Outcome.PlayForFree, "Cast another nonland card exiled with " + sourceObject.getLogName() + " without paying that card's mana cost?", source, game)) { if (player != null && player.chooseUse(Outcome.PlayForFree, "Cast another nonland card exiled with " + sourceObject.getLogName() + " without paying that card's mana cost?", source, game)) {

View file

@ -88,7 +88,7 @@ public class PhyrexianMetamorph extends CardImpl {
// You may have Phyrexian Metamorph enter the battlefield as a copy of any artifact or creature on the battlefield, except it's an artifact in addition to its other types. // You may have Phyrexian Metamorph enter the battlefield as a copy of any artifact or creature on the battlefield, except it's an artifact in addition to its other types.
Effect effect = new CopyPermanentEffect(filter, phyrexianMetamorphApplier); Effect effect = new CopyPermanentEffect(filter, phyrexianMetamorphApplier);
effect.setText("You may have {this} enter the battlefield as a copy of any artifact or creature on the battlefield, except it's an artifact in addition to its other types"); effect.setText("You may have {this} enter the battlefield as a copy of any artifact or creature on the battlefield, except it's an artifact in addition to its other types");
Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new EntersBattlefieldEffect(effect)); Ability ability = new SimpleStaticAbility(Zone.ALL, new EntersBattlefieldEffect(effect));
this.addAbility(ability); this.addAbility(ability);
} }

View file

@ -92,7 +92,7 @@ class SkyshipWeatherlightEffect extends SearchEffect {
MageObject sourceObject = source.getSourceObject(game); MageObject sourceObject = source.getSourceObject(game);
if (sourceObject != null && controller != null) { if (sourceObject != null && controller != null) {
if (controller.searchLibrary(target, game)) { if (controller.searchLibrary(target, game)) {
UUID exileZone = CardUtil.getObjectExileZoneId(game, sourceObject); UUID exileZone = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter());
if (target.getTargets().size() > 0) { if (target.getTargets().size() > 0) {
for (UUID cardID : target.getTargets()) { for (UUID cardID : target.getTargets()) {
Card card = controller.getLibrary().getCard(cardID, game); Card card = controller.getLibrary().getCard(cardID, game);

View file

@ -46,7 +46,6 @@ import mage.game.permanent.Permanent;
import mage.target.targetpointer.FixedTarget; import mage.target.targetpointer.FixedTarget;
import mage.util.functions.EmptyApplyToPermanent; import mage.util.functions.EmptyApplyToPermanent;
/** /**
* *
* @author North * @author North
@ -142,7 +141,7 @@ class RenegadeDoppelgangerEffect extends OneShotEffect {
return false; return false;
} }
game.copyPermanent(Duration.EndOfTurn, targetCreature, permanent, source, new EmptyApplyToPermanent()); game.copyPermanent(Duration.EndOfTurn, targetCreature, permanent.getId(), source, new EmptyApplyToPermanent());
return false; return false;
} }
} }

View file

@ -98,15 +98,15 @@ class SakashimaTheImpostorCopyEffect extends OneShotEffect {
@Override @Override
public boolean apply(Game game, Ability source) { public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(source.getControllerId()); Player player = game.getPlayer(source.getControllerId());
Permanent sourcePermanent = game.getPermanent(source.getSourceId()); MageObject sourceObject = game.getObject(source.getSourceId());
if (player != null && sourcePermanent != null) { if (player != null && sourceObject != null) {
Target target = new TargetPermanent(new FilterCreaturePermanent()); Target target = new TargetPermanent(new FilterCreaturePermanent());
target.setNotTarget(true); target.setNotTarget(true);
if (target.canChoose(source.getSourceId(), source.getControllerId(), game)) { if (target.canChoose(source.getSourceId(), source.getControllerId(), game)) {
player.choose(Outcome.Copy, target, source.getSourceId(), game); player.choose(Outcome.Copy, target, source.getSourceId(), game);
Permanent copyFromPermanent = game.getPermanent(target.getFirstTarget()); Permanent copyFromPermanent = game.getPermanent(target.getFirstTarget());
if (copyFromPermanent != null) { if (copyFromPermanent != null) {
game.copyPermanent(copyFromPermanent, sourcePermanent, source, new ApplyToPermanent() { game.copyPermanent(copyFromPermanent, sourceObject.getId(), source, new ApplyToPermanent() {
@Override @Override
public Boolean apply(Game game, Permanent permanent) { public Boolean apply(Game game, Permanent permanent) {
if (!permanent.getSupertype().contains("Legendary")) { if (!permanent.getSupertype().contains("Legendary")) {

View file

@ -99,7 +99,7 @@ class CemeteryPucaEffect extends OneShotEffect {
if (copyToCreature != null) { if (copyToCreature != null) {
Permanent copyFromCreature = (Permanent) game.getLastKnownInformation(targetPointer.getFirst(game, source), Zone.BATTLEFIELD); Permanent copyFromCreature = (Permanent) game.getLastKnownInformation(targetPointer.getFirst(game, source), Zone.BATTLEFIELD);
if (copyFromCreature != null) { if (copyFromCreature != null) {
game.copyPermanent(Duration.WhileOnBattlefield, copyFromCreature, copyToCreature, source, new EmptyApplyToPermanent()); game.copyPermanent(Duration.WhileOnBattlefield, copyFromCreature, copyToCreature.getId(), source, new EmptyApplyToPermanent());
ContinuousEffect effect = new GainAbilityTargetEffect(new DiesCreatureTriggeredAbility(new DoIfCostPaid(new CemeteryPucaEffect(), new ManaCostsImpl("{1}")), false, new FilterCreaturePermanent("a creature"), true), Duration.WhileOnBattlefield); ContinuousEffect effect = new GainAbilityTargetEffect(new DiesCreatureTriggeredAbility(new DoIfCostPaid(new CemeteryPucaEffect(), new ManaCostsImpl("{1}")), false, new FilterCreaturePermanent("a creature"), true), Duration.WhileOnBattlefield);
effect.setTargetPointer(new FixedTarget(copyToCreature.getId())); effect.setTargetPointer(new FixedTarget(copyToCreature.getId()));
game.addEffect(effect, source); game.addEffect(effect, source);

View file

@ -30,22 +30,16 @@ package mage.sets.shadowmoor;
import java.util.UUID; import java.util.UUID;
import mage.MageInt; import mage.MageInt;
import mage.ObjectColor; import mage.ObjectColor;
import mage.abilities.Ability;
import mage.abilities.StateTriggeredAbility; import mage.abilities.StateTriggeredAbility;
import mage.abilities.common.AsEntersBattlefieldAbility; import mage.abilities.common.AsEntersBattlefieldAbility;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.SacrificeSourceEffect; import mage.abilities.effects.common.SacrificeSourceEffect;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.choices.ChoiceColor;
import mage.constants.CardType; import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.Rarity; import mage.constants.Rarity;
import mage.constants.Zone; import mage.constants.Zone;
import mage.game.Game; import mage.game.Game;
import mage.game.events.GameEvent; import mage.game.events.GameEvent;
import mage.game.permanent.Permanent; import mage.game.permanent.Permanent;
import mage.game.stack.StackObject;
import mage.players.Player;
/** /**
* *
@ -62,7 +56,7 @@ public class LureboundScarecrow extends CardImpl {
this.toughness = new MageInt(4); this.toughness = new MageInt(4);
// As Lurebound Scarecrow enters the battlefield, choose a color. // As Lurebound Scarecrow enters the battlefield, choose a color.
this.addAbility(new AsEntersBattlefieldAbility(new LureboundScarecrowChooseColorEffect())); this.addAbility(new AsEntersBattlefieldAbility(new ChooseColorEffect()));
// When you control no permanents of the chosen color, sacrifice Lurebound Scarecrow. // When you control no permanents of the chosen color, sacrifice Lurebound Scarecrow.
this.addAbility(new LureboundScarecrowTriggeredAbility()); this.addAbility(new LureboundScarecrowTriggeredAbility());
@ -78,40 +72,6 @@ public class LureboundScarecrow extends CardImpl {
} }
} }
class LureboundScarecrowChooseColorEffect extends OneShotEffect {
public LureboundScarecrowChooseColorEffect() {
super(Outcome.BoostCreature);
staticText = "choose a color";
}
public LureboundScarecrowChooseColorEffect(final LureboundScarecrowChooseColorEffect effect) {
super(effect);
}
@Override
public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(source.getControllerId());
StackObject sourceStackObject = game.getStack().getStackObject(source.getSourceId());
Permanent permanent = game.getPermanent(source.getSourceId());
if (player != null && sourceStackObject != null && permanent != null) {
ChoiceColor colorChoice = new ChoiceColor();
if (player.choose(Outcome.BoostCreature, colorChoice, game)) {
game.informPlayers(sourceStackObject.getName() + ": " + player.getLogName() + " has chosen " + colorChoice.getChoice());
game.getState().setValue(permanent.getId() + "_color", colorChoice.getColor());
permanent.addInfo("chosen color", new StringBuilder("<font color='blue'>Chosen color: ").append(colorChoice.getColor().getDescription()).append("</font>").toString(), game);
}
}
return false;
}
@Override
public LureboundScarecrowChooseColorEffect copy() {
return new LureboundScarecrowChooseColorEffect(this);
}
}
class LureboundScarecrowTriggeredAbility extends StateTriggeredAbility { class LureboundScarecrowTriggeredAbility extends StateTriggeredAbility {
private static final String staticText = "When you control no permanents of the chosen color, sacrifice {this}."; private static final String staticText = "When you control no permanents of the chosen color, sacrifice {this}.";

View file

@ -61,7 +61,6 @@ public class Mirrorweave extends CardImpl {
super(ownerId, 143, "Mirrorweave", Rarity.RARE, new CardType[]{CardType.INSTANT}, "{2}{W/U}{W/U}"); super(ownerId, 143, "Mirrorweave", Rarity.RARE, new CardType[]{CardType.INSTANT}, "{2}{W/U}{W/U}");
this.expansionSetCode = "SHM"; this.expansionSetCode = "SHM";
// Each other creature becomes a copy of target nonlegendary creature until end of turn. // Each other creature becomes a copy of target nonlegendary creature until end of turn.
this.getSpellAbility().addEffect(new MirrorWeaveEffect()); this.getSpellAbility().addEffect(new MirrorWeaveEffect());
this.getSpellAbility().addTarget(new TargetPermanent(filter)); this.getSpellAbility().addTarget(new TargetPermanent(filter));
@ -105,7 +104,7 @@ class MirrorWeaveEffect extends OneShotEffect {
filter.add(Predicates.not(new PermanentIdPredicate(copyFromCreature.getId()))); filter.add(Predicates.not(new PermanentIdPredicate(copyFromCreature.getId())));
for (Permanent copyToCreature : game.getBattlefield().getAllActivePermanents(filter, game)) { for (Permanent copyToCreature : game.getBattlefield().getAllActivePermanents(filter, game)) {
if (copyToCreature != null) { if (copyToCreature != null) {
game.copyPermanent(Duration.EndOfTurn, copyFromCreature, copyToCreature, source, new EmptyApplyToPermanent()); game.copyPermanent(Duration.EndOfTurn, copyFromCreature, copyToCreature.getId(), source, new EmptyApplyToPermanent());
} }
} }
} }

View file

@ -114,7 +114,7 @@ class TidehollowScullerExileEffect extends OneShotEffect {
if (controller.choose(Outcome.Exile, opponent.getHand(), target, game)) { if (controller.choose(Outcome.Exile, opponent.getHand(), target, game)) {
Card card = opponent.getHand().get(target.getFirstTarget(), game); Card card = opponent.getHand().get(target.getFirstTarget(), game);
if (card != null) { if (card != null) {
controller.moveCardToExileWithInfo(card, CardUtil.getObjectExileZoneId(game, sourcePermanent), sourcePermanent.getIdName(), source.getSourceId(), game, Zone.HAND, true); controller.moveCardToExileWithInfo(card, CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()), sourcePermanent.getIdName(), source.getSourceId(), game, Zone.HAND, true);
} }
} }

View file

@ -28,9 +28,6 @@
package mage.sets.tempest; package mage.sets.tempest;
import java.util.UUID; import java.util.UUID;
import mage.constants.CardType;
import mage.constants.Rarity;
import mage.MageInt; import mage.MageInt;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleActivatedAbility;
@ -39,7 +36,9 @@ import mage.abilities.costs.common.TapSourceCost;
import mage.abilities.decorator.ConditionalContinuousEffect; import mage.abilities.decorator.ConditionalContinuousEffect;
import mage.abilities.effects.common.continuous.GainControlTargetEffect; import mage.abilities.effects.common.continuous.GainControlTargetEffect;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.constants.CardType;
import mage.constants.Duration; import mage.constants.Duration;
import mage.constants.Rarity;
import mage.constants.Zone; import mage.constants.Zone;
import mage.target.common.TargetCreaturePermanent; import mage.target.common.TargetCreaturePermanent;
@ -56,9 +55,10 @@ public class RootwaterMatriarch extends CardImpl {
this.power = new MageInt(2); this.power = new MageInt(2);
this.toughness = new MageInt(3); this.toughness = new MageInt(3);
// {TAP}: Gain control of target creature for as long as that creature is enchanted // {T}: Gain control of target creature for as long as that creature is enchanted
ConditionalContinuousEffect effect = new ConditionalContinuousEffect(new GainControlTargetEffect(Duration.OneUse), EnchantedTargetCondition.getInstance(), "Gain control of target creature for as long as that creature is enchanted"); ConditionalContinuousEffect effect = new ConditionalContinuousEffect(new GainControlTargetEffect(Duration.Custom), EnchantedTargetCondition.getInstance(),
"Gain control of target creature for as long as that creature is enchanted");
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, effect, new TapSourceCost()); Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, effect, new TapSourceCost());
ability.addTarget(new TargetCreaturePermanent()); ability.addTarget(new TargetCreaturePermanent());
this.addAbility(ability); this.addAbility(ability);

View file

@ -73,7 +73,6 @@ public class AshiokNightmareWeaver extends CardImpl {
this.expansionSetCode = "THS"; this.expansionSetCode = "THS";
this.subtype.add("Ashiok"); this.subtype.add("Ashiok");
this.addAbility(new EntersBattlefieldAbility(new AddCountersSourceEffect(CounterType.LOYALTY.createInstance(3)), false)); this.addAbility(new EntersBattlefieldAbility(new AddCountersSourceEffect(CounterType.LOYALTY.createInstance(3)), false));
// +2: Exile the top three cards of target opponent's library. // +2: Exile the top three cards of target opponent's library.
@ -121,14 +120,9 @@ class AshiokNightmareWeaverExileEffect extends OneShotEffect {
Player controller = game.getPlayer(source.getControllerId()); Player controller = game.getPlayer(source.getControllerId());
MageObject sourceObject = source.getSourceObject(game); MageObject sourceObject = source.getSourceObject(game);
if (sourceObject != null && opponent != null && controller != null) { if (sourceObject != null && opponent != null && controller != null) {
UUID exileZone = CardUtil.getObjectExileZoneId(game, sourceObject); UUID exileZone = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter());
if (exileZone != null) { if (exileZone != null) {
for (int i = 0; i < 3; i++) { controller.moveCardsToExile(opponent.getLibrary().getTopCards(game, 3), source, game, true, exileZone, sourceObject.getIdName());
Card card = opponent.getLibrary().getFromTop(game);
if (card != null) {
controller.moveCardToExileWithInfo(card, exileZone, sourceObject.getIdName(), source.getSourceId(), game, Zone.LIBRARY, true);
}
}
return true; return true;
} }
} }
@ -167,11 +161,10 @@ class AshiokNightmareWeaverPutIntoPlayEffect extends OneShotEffect {
} }
} }
FilterCard filter = new FilterCreatureCard(new StringBuilder("creature card with converted mana cost {").append(cmc).append("} exiled with Ashiok, Nightmare Weaver").toString()); FilterCard filter = new FilterCreatureCard("creature card with converted mana cost {" + cmc + "} exiled with " + sourceObject.getIdName());
filter.add(new ConvertedManaCostPredicate(Filter.ComparisonType.Equal, cmc)); filter.add(new ConvertedManaCostPredicate(Filter.ComparisonType.Equal, cmc));
Target target = new TargetCardInExile(filter, CardUtil.getObjectExileZoneId(game, sourceObject));
Target target = new TargetCardInExile(filter, CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()));
if (target.canChoose(source.getSourceId(), controller.getId(), game)) { if (target.canChoose(source.getSourceId(), controller.getId(), game)) {
if (controller.chooseTarget(Outcome.PutCreatureInPlay, target, source, game)) { if (controller.chooseTarget(Outcome.PutCreatureInPlay, target, source, game)) {
@ -182,7 +175,7 @@ class AshiokNightmareWeaverPutIntoPlayEffect extends OneShotEffect {
if (permanent != null) { if (permanent != null) {
permanent.changeControllerId(source.getControllerId(), game); permanent.changeControllerId(source.getControllerId(), game);
} }
ContinuousEffectImpl effect = new AshiokNightmareWeaverAddTypeEffect(); ContinuousEffectImpl effect = new AshiokNightmareWeaverAddTypeEffect();
effect.setTargetPointer(new FixedTarget(card.getId())); effect.setTargetPointer(new FixedTarget(card.getId()));
game.addEffect(effect, source); game.addEffect(effect, source);
@ -267,7 +260,7 @@ class AshiokNightmareWeaverExileAllEffect extends OneShotEffect {
if (exileId == null) { if (exileId == null) {
return false; return false;
} }
for (UUID opponentId: game.getOpponents(source.getControllerId())) { for (UUID opponentId : game.getOpponents(source.getControllerId())) {
Player opponent = game.getPlayer(opponentId); Player opponent = game.getPlayer(opponentId);
if (opponent != null) { if (opponent != null) {
Cards cards = new CardsImpl(); Cards cards = new CardsImpl();
@ -280,7 +273,7 @@ class AshiokNightmareWeaverExileAllEffect extends OneShotEffect {
} }
cards.clear(); cards.clear();
cards.addAll(opponent.getGraveyard()); cards.addAll(opponent.getGraveyard());
for (UUID cardId :cards) { for (UUID cardId : cards) {
Card card = game.getCard(cardId); Card card = game.getCard(cardId);
if (card != null) { if (card != null) {
controller.moveCardToExileWithInfo(card, exileId, sourceObject.getIdName(), source.getSourceId(), game, Zone.GRAVEYARD, true); controller.moveCardToExileWithInfo(card, exileId, sourceObject.getIdName(), source.getSourceId(), game, Zone.GRAVEYARD, true);

View file

@ -108,7 +108,7 @@ class VesuvanDoppelgangerCopyEffect extends OneShotEffect {
controller.choose(Outcome.Copy, target, source.getSourceId(), game); controller.choose(Outcome.Copy, target, source.getSourceId(), game);
Permanent copyFromPermanent = game.getPermanent(target.getFirstTarget()); Permanent copyFromPermanent = game.getPermanent(target.getFirstTarget());
if (copyFromPermanent != null) { if (copyFromPermanent != null) {
game.copyPermanent(copyFromPermanent, sourcePermanent, source, new ApplyToPermanent() { game.copyPermanent(copyFromPermanent, sourcePermanent.getId(), source, new ApplyToPermanent() {
@Override @Override
public Boolean apply(Game game, Permanent permanent) { public Boolean apply(Game game, Permanent permanent) {
permanent.getColor(game).setColor(sourcePermanent.getColor(game)); permanent.getColor(game).setColor(sourcePermanent.getColor(game));

View file

@ -16,7 +16,10 @@ public class ProteanHydraTest extends CardTestPlayerBase {
@Test @Test
public void testEnteringWithCounters() { public void testEnteringWithCounters() {
addCard(Zone.BATTLEFIELD, playerA, "Forest", 5); addCard(Zone.BATTLEFIELD, playerA, "Forest", 5);
addCard(Zone.HAND, playerA, "Protean Hydra"); // Protean Hydra enters the battlefield with X +1/+1 counters on it.
// If damage would be dealt to Protean Hydra, prevent that damage and remove that many +1/+1 counters from it.
// Whenever a +1/+1 counter is removed from Protean Hydra, put two +1/+1 counters on it at the beginning of the next end step.
addCard(Zone.HAND, playerA, "Protean Hydra"); // CREATURE - {X}{G}
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Protean Hydra"); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Protean Hydra");

View file

@ -51,8 +51,10 @@ public class MycosynthGolemTest extends CardTestPlayerBase {
public void testSpellsAffinity() { public void testSpellsAffinity() {
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1);
addCard(Zone.BATTLEFIELD, playerA, "Forest", 1); addCard(Zone.BATTLEFIELD, playerA, "Forest", 1);
// Affinity for artifacts
// Artifact creature spells you cast have affinity for artifacts.
addCard(Zone.BATTLEFIELD, playerA, "Mycosynth Golem"); addCard(Zone.BATTLEFIELD, playerA, "Mycosynth Golem");
addCard(Zone.HAND, playerA, "Alpha Myr"); addCard(Zone.HAND, playerA, "Alpha Myr"); // Creature - Myr 2/1
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Alpha Myr"); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Alpha Myr");

View file

@ -66,6 +66,9 @@ public class SoulfireGrandMasterTest extends CardTestPlayerBase {
@Test @Test
public void testSpellsReturnToHand() { public void testSpellsReturnToHand() {
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 5); addCard(Zone.BATTLEFIELD, playerA, "Mountain", 5);
// Lifelink
// Instant and sorcery spells you control have lifelink.
// {2}{U/R}{U/R}: The next time you cast an instant or sorcery spell from your hand this turn, put that card into your hand instead of your graveyard as it resolves.
addCard(Zone.BATTLEFIELD, playerA, "Soulfire Grand Master"); addCard(Zone.BATTLEFIELD, playerA, "Soulfire Grand Master");
addCard(Zone.HAND, playerA, "Lightning Bolt"); addCard(Zone.HAND, playerA, "Lightning Bolt");
@ -199,7 +202,6 @@ public class SoulfireGrandMasterTest extends CardTestPlayerBase {
* Test that if Soulfire Grand Master has left the battlefield spell has no * Test that if Soulfire Grand Master has left the battlefield spell has no
* longer lifelink * longer lifelink
*/ */
@Test @Test
public void testSoulfireLeft() { public void testSoulfireLeft() {
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1);
@ -231,7 +233,6 @@ public class SoulfireGrandMasterTest extends CardTestPlayerBase {
* the elemental, so stoke didnt resolve, but i still got the life from * the elemental, so stoke didnt resolve, but i still got the life from
* lifelink. * lifelink.
*/ */
@Test @Test
public void testSoulfireStokeTheFlames() { public void testSoulfireStokeTheFlames() {
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 8); addCard(Zone.BATTLEFIELD, playerA, "Mountain", 8);
@ -296,7 +297,6 @@ public class SoulfireGrandMasterTest extends CardTestPlayerBase {
* Constructed. * Constructed.
* *
*/ */
@Test @Test
public void testWithDeflectingPalm() { public void testWithDeflectingPalm() {
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1);

View file

@ -1,7 +1,3 @@
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package org.mage.test.cards.conditional; package org.mage.test.cards.conditional;
import mage.constants.PhaseStep; import mage.constants.PhaseStep;
@ -14,10 +10,10 @@ import org.mage.test.serverside.base.CardTestPlayerBase;
* @author jeff * @author jeff
*/ */
public class RootwaterMatriarchTest extends CardTestPlayerBase { public class RootwaterMatriarchTest extends CardTestPlayerBase {
@Test @Test
public void testTargetFail() { public void testTargetFail() {
addCard(Zone.BATTLEFIELD, playerA, "Rootwater Matriarch"); addCard(Zone.BATTLEFIELD, playerA, "Rootwater Matriarch");
addCard(Zone.BATTLEFIELD, playerB, "Memnite"); addCard(Zone.BATTLEFIELD, playerB, "Memnite");
@ -25,73 +21,73 @@ public class RootwaterMatriarchTest extends CardTestPlayerBase {
setStopAt(1, PhaseStep.BEGIN_COMBAT); setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute(); execute();
assertPermanentCount(playerA, "Rootwater Matriarch", 1); assertPermanentCount(playerA, "Rootwater Matriarch", 1);
assertPermanentCount(playerB, "Memnite", 1); assertPermanentCount(playerB, "Memnite", 1);
} }
@Test @Test
public void testTargetSuccess() { public void testTargetSuccess() {
// {T}: Gain control of target creature for as long as that creature is enchanted
addCard(Zone.BATTLEFIELD, playerA, "Rootwater Matriarch"); addCard(Zone.BATTLEFIELD, playerA, "Rootwater Matriarch");
addCard(Zone.BATTLEFIELD, playerA, "Island"); addCard(Zone.BATTLEFIELD, playerA, "Island");
addCard(Zone.BATTLEFIELD, playerB, "Memnite"); addCard(Zone.BATTLEFIELD, playerB, "Memnite");
addCard(Zone.HAND, playerA, "Flight"); addCard(Zone.HAND, playerA, "Flight");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Flight", "Memnite"); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Flight", "Memnite");
activateAbility(1, PhaseStep.BEGIN_COMBAT, playerA, "{T}: Gain control of target creature for as long as that creature is enchanted.", "Memnite"); activateAbility(1, PhaseStep.BEGIN_COMBAT, playerA, "{T}: Gain control of target creature for as long as that creature is enchanted.", "Memnite");
setStopAt(1, PhaseStep.END_COMBAT); setStopAt(1, PhaseStep.END_COMBAT);
execute(); execute();
assertPermanentCount(playerA, "Rootwater Matriarch", 1); assertPermanentCount(playerA, "Rootwater Matriarch", 1);
assertPermanentCount(playerA, "Memnite", 1); assertPermanentCount(playerA, "Memnite", 1);
} }
@Test @Test
public void testGainControlEnchantedTargetAndRWLeavesPlay() { public void testGainControlEnchantedTargetAndRWLeavesPlay() {
addCard(Zone.BATTLEFIELD, playerA, "Rootwater Matriarch"); addCard(Zone.BATTLEFIELD, playerA, "Rootwater Matriarch");
addCard(Zone.BATTLEFIELD, playerA, "Island", 2); addCard(Zone.BATTLEFIELD, playerA, "Island", 2);
addCard(Zone.BATTLEFIELD, playerB, "Memnite"); addCard(Zone.BATTLEFIELD, playerB, "Memnite");
addCard(Zone.HAND, playerA, "Unsummon"); addCard(Zone.HAND, playerA, "Unsummon");
addCard(Zone.HAND, playerA, "Flight"); addCard(Zone.HAND, playerA, "Flight");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Flight", "Memnite"); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Flight", "Memnite");
activateAbility(1, PhaseStep.BEGIN_COMBAT, playerA, "{T}: Gain control of target creature for as long as that creature is enchanted.", "Memnite"); activateAbility(1, PhaseStep.BEGIN_COMBAT, playerA, "{T}: Gain control of target creature for as long as that creature is enchanted.", "Memnite");
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Unsummon", "Rootwater Matriarch"); castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Unsummon", "Rootwater Matriarch");
setStopAt(1, PhaseStep.END_TURN); setStopAt(1, PhaseStep.END_TURN);
execute(); execute();
assertPermanentCount(playerA, "Rootwater Matriarch", 0); assertPermanentCount(playerA, "Rootwater Matriarch", 0);
assertPermanentCount(playerA, "Memnite", 1); assertPermanentCount(playerA, "Memnite", 1);
} }
@Test @Test
public void testGainControlEnchantedTargetAndAuraIsDisenchanted() { public void testGainControlEnchantedTargetAndAuraIsDisenchanted() {
addCard(Zone.BATTLEFIELD, playerA, "Rootwater Matriarch"); addCard(Zone.BATTLEFIELD, playerA, "Rootwater Matriarch");
addCard(Zone.BATTLEFIELD, playerA, "Island"); addCard(Zone.BATTLEFIELD, playerA, "Island");
addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); addCard(Zone.BATTLEFIELD, playerA, "Plains", 2);
addCard(Zone.BATTLEFIELD, playerB, "Memnite"); addCard(Zone.BATTLEFIELD, playerB, "Memnite");
addCard(Zone.HAND, playerA, "Disenchant"); addCard(Zone.HAND, playerA, "Disenchant");
addCard(Zone.HAND, playerA, "Flight"); addCard(Zone.HAND, playerA, "Flight");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Flight", "Memnite"); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Flight", "Memnite");
activateAbility(1, PhaseStep.BEGIN_COMBAT, playerA, "{T}: Gain control of target creature for as long as that creature is enchanted.", "Memnite"); activateAbility(1, PhaseStep.BEGIN_COMBAT, playerA, "{T}: Gain control of target creature for as long as that creature is enchanted.", "Memnite");
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Disenchant", "Flight"); castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Disenchant", "Flight");
setStopAt(1, PhaseStep.END_TURN); setStopAt(1, PhaseStep.END_TURN);
execute(); execute();
assertPermanentCount(playerA, "Rootwater Matriarch", 1); assertPermanentCount(playerA, "Rootwater Matriarch", 1);
assertPermanentCount(playerB, "Memnite", 1); assertPermanentCount(playerB, "Memnite", 1);
} }

View file

@ -202,6 +202,9 @@ public class PhantasmalImageTest extends CardTestPlayerBase {
addCard(Zone.BATTLEFIELD, playerA, "Island", 5); addCard(Zone.BATTLEFIELD, playerA, "Island", 5);
addCard(Zone.BATTLEFIELD, playerA, "Llanowar Elves"); addCard(Zone.BATTLEFIELD, playerA, "Llanowar Elves");
addCard(Zone.HAND, playerA, "Phantasmal Image"); addCard(Zone.HAND, playerA, "Phantasmal Image");
// As Lurebound Scarecrow enters the battlefield, choose a color.
// When you control no permanents of the chosen color, sacrifice Lurebound Scarecrow.
addCard(Zone.HAND, playerA, "Lurebound Scarecrow"); addCard(Zone.HAND, playerA, "Lurebound Scarecrow");
setChoice(playerA, "Green"); setChoice(playerA, "Green");
@ -324,7 +327,7 @@ public class PhantasmalImageTest extends CardTestPlayerBase {
addCard(Zone.HAND, playerB, "Phantasmal Image"); addCard(Zone.HAND, playerB, "Phantasmal Image");
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Phantasmal Image"); // not targeted castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Phantasmal Image"); // not targeted
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerA, "Veil of Secrecy", "Frost Titan"); // so it's no longer targetable castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerA, "Veil of Secrecy", "Frost Titan"); // so it's no longer targetable
setChoice(playerB, "Frost Titan"); setChoice(playerB, "Frost Titan");
castSpell(2, PhaseStep.POSTCOMBAT_MAIN, playerA, "Terror", "Frost Titan"); // of player Bs Phantasmal Image copying Frost Titan castSpell(2, PhaseStep.POSTCOMBAT_MAIN, playerA, "Terror", "Frost Titan"); // of player Bs Phantasmal Image copying Frost Titan
@ -346,7 +349,7 @@ public class PhantasmalImageTest extends CardTestPlayerBase {
} }
// I've casted a Phantasmal Image targeting opponent's Wurmcoil Engine // I've casted a Phantasmal Image targeting opponent's Wurmcoil Engine
// When my Phantasmal Image died, it didn't triggered the Wurmcoil Engine's last ability // When my Phantasmal Image died, it didn't triggered the Wurmcoil Engine's last ability
// (When Wurmcoil Engine dies, put a 3/3 colorless Wurm artifact creature token with deathtouch and // (When Wurmcoil Engine dies, put a 3/3 colorless Wurm artifact creature token with deathtouch and
// a 3/3 colorless Wurm artifact creature token with lifelink onto the battlefield.) // a 3/3 colorless Wurm artifact creature token with lifelink onto the battlefield.)
@Test @Test
@ -418,31 +421,39 @@ public class PhantasmalImageTest extends CardTestPlayerBase {
} }
/** /**
* Action * Action Game State 1 -----------------> Game State 2 (On 'field) (Move to
* Game State 1 -----------------> Game State 2 * GY) (In graveyard)
* (On 'field) (Move to GY) (In graveyard) *
* * LTB abilities such as Persist are expceptional in that they trigger based
* LTB abilities such as Persist are expceptional in that they trigger based on their existence and * on their existence and state of objects before the event (Game State 1,
* state of objects before the event (Game State 1, when the card is on the battlefield) rather than * when the card is on the battlefield) rather than after (Game State 2,
* after (Game State 2, when the card is in the graveyard). It doesn't matter that the LTB ability * when the card is in the graveyard). It doesn't matter that the LTB
* doesn't exist in Game State 2. [CR 603.6d] * ability doesn't exist in Game State 2. [CR 603.6d]
* *
* 603.6d Normally, objects that exist immediately after an event are checked to see if the event matched any trigger conditions. * 603.6d Normally, objects that exist immediately after an event are
* Continuous effects that exist at that time are used to determine what the trigger conditions are and what the objects involved * checked to see if the event matched any trigger conditions. Continuous
* in the event look like. However, some triggered abilities must be treated specially. Leaves-the-battlefield abilities, abilities * effects that exist at that time are used to determine what the trigger
* that trigger when a permanent phases out, abilities that trigger when an object that all players can see is put into a hand or * conditions are and what the objects involved in the event look like.
* library, abilities that trigger specifically when an object becomes unattached, abilities that trigger when a player loses control * However, some triggered abilities must be treated specially.
* of an object, and abilities that trigger when a player planeswalks away from a plane will trigger based on their existence, and * Leaves-the-battlefield abilities, abilities that trigger when a permanent
* the appearance of objects, prior to the event rather than afterward. The game has to look back in time to determine if these abilities trigger. * phases out, abilities that trigger when an object that all players can
* * see is put into a hand or library, abilities that trigger specifically
* Example: Two creatures are on the battlefield along with an artifact that has the ability Whenever a creature dies, you gain 1 life. * when an object becomes unattached, abilities that trigger when a player
* Someone plays a spell that destroys all artifacts, creatures, and enchantments. The artifacts ability triggers twice, even though * loses control of an object, and abilities that trigger when a player
* the artifact goes to its owners graveyard at the same time as the creatures. * planeswalks away from a plane will trigger based on their existence, and
* * the appearance of objects, prior to the event rather than afterward. The
* game has to look back in time to determine if these abilities trigger.
*
* Example: Two creatures are on the battlefield along with an artifact that
* has the ability Whenever a creature dies, you gain 1 life. Someone
* plays a spell that destroys all artifacts, creatures, and enchantments.
* The artifacts ability triggers twice, even though the artifact goes to
* its owners graveyard at the same time as the creatures.
*
*/ */
@Test @Test
public void testPersist() { public void testPersist() {
addCard(Zone.BATTLEFIELD, playerA, "Forest", 3); addCard(Zone.BATTLEFIELD, playerA, "Forest", 3);
// When Kitchen Finks enters the battlefield, you gain 2 life. // When Kitchen Finks enters the battlefield, you gain 2 life.
// Persist (When this creature dies, if it had no -1/-1 counters on it, return it to the battlefield under its owner's control with a -1/-1 counter on it.) // Persist (When this creature dies, if it had no -1/-1 counters on it, return it to the battlefield under its owner's control with a -1/-1 counter on it.)
addCard(Zone.HAND, playerA, "Kitchen Finks"); addCard(Zone.HAND, playerA, "Kitchen Finks");
@ -452,21 +463,20 @@ public class PhantasmalImageTest extends CardTestPlayerBase {
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 6); addCard(Zone.BATTLEFIELD, playerA, "Swamp", 6);
addCard(Zone.BATTLEFIELD, playerB, "Island", 2); addCard(Zone.BATTLEFIELD, playerB, "Island", 2);
// You may have Phantasmal Image enter the battlefield as a copy of any creature // 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 // 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." // it gains "When this creature becomes the target of a spell or ability, sacrifice it."
addCard(Zone.HAND, playerB, "Phantasmal Image"); addCard(Zone.HAND, playerB, "Phantasmal Image");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Kitchen Finks"); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Kitchen Finks");
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Phantasmal Image"); // not targeted castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Phantasmal Image"); // not targeted
setChoice(playerB, "Kitchen Finks"); setChoice(playerB, "Kitchen Finks");
castSpell(2, PhaseStep.POSTCOMBAT_MAIN, playerA, "Public Execution", "Kitchen Finks"); castSpell(2, PhaseStep.POSTCOMBAT_MAIN, playerA, "Public Execution", "Kitchen Finks");
setChoice(playerB, "Kitchen Finks"); setChoice(playerB, "Kitchen Finks");
setStopAt(2, PhaseStep.END_TURN); setStopAt(2, PhaseStep.END_TURN);
execute(); execute();
@ -480,13 +490,13 @@ public class PhantasmalImageTest extends CardTestPlayerBase {
assertHandCount(playerB, "Phantasmal Image", 0); assertHandCount(playerB, "Phantasmal Image", 0);
assertGraveyardCount(playerB, "Phantasmal Image", 0); assertGraveyardCount(playerB, "Phantasmal Image", 0);
assertPermanentCount(playerB, "Kitchen Finks", 1); assertPermanentCount(playerB, "Kitchen Finks", 1);
assertPowerToughness(playerB, "Kitchen Finks", 2, 1); assertPowerToughness(playerB, "Kitchen Finks", 2, 1);
} }
@Test @Test
public void testUndying() { public void testUndying() {
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2); addCard(Zone.BATTLEFIELD, playerA, "Swamp", 2);
// Undying (When this creature dies, if it had no +1/+1 counters on it, return it to the battlefield under its owner's control with a +1/+1 counter on it.) // Undying (When this creature dies, if it had no +1/+1 counters on it, return it to the battlefield under its owner's control with a +1/+1 counter on it.)
addCard(Zone.HAND, playerA, "Butcher Ghoul"); addCard(Zone.HAND, playerA, "Butcher Ghoul");
@ -495,21 +505,20 @@ public class PhantasmalImageTest extends CardTestPlayerBase {
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 6); addCard(Zone.BATTLEFIELD, playerA, "Swamp", 6);
addCard(Zone.BATTLEFIELD, playerB, "Island", 2); addCard(Zone.BATTLEFIELD, playerB, "Island", 2);
// You may have Phantasmal Image enter the battlefield as a copy of any creature // 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 // 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." // it gains "When this creature becomes the target of a spell or ability, sacrifice it."
addCard(Zone.HAND, playerB, "Phantasmal Image"); addCard(Zone.HAND, playerB, "Phantasmal Image");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Butcher Ghoul"); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Butcher Ghoul");
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Phantasmal Image"); // not targeted castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Phantasmal Image"); // not targeted
setChoice(playerB, "Butcher Ghoul"); setChoice(playerB, "Butcher Ghoul");
castSpell(2, PhaseStep.POSTCOMBAT_MAIN, playerA, "Public Execution", "Butcher Ghoul"); castSpell(2, PhaseStep.POSTCOMBAT_MAIN, playerA, "Public Execution", "Butcher Ghoul");
setChoice(playerB, "Butcher Ghoul"); setChoice(playerB, "Butcher Ghoul");
setStopAt(2, PhaseStep.END_TURN); setStopAt(2, PhaseStep.END_TURN);
execute(); execute();
@ -523,29 +532,25 @@ public class PhantasmalImageTest extends CardTestPlayerBase {
assertHandCount(playerB, "Phantasmal Image", 0); assertHandCount(playerB, "Phantasmal Image", 0);
assertGraveyardCount(playerB, "Phantasmal Image", 0); assertGraveyardCount(playerB, "Phantasmal Image", 0);
assertPermanentCount(playerB, "Butcher Ghoul", 1); assertPermanentCount(playerB, "Butcher Ghoul", 1);
assertPowerToughness(playerB, "Butcher Ghoul", 2, 2); assertPowerToughness(playerB, "Butcher Ghoul", 2, 2);
} }
/** /**
* 12:29: Attacker: Wurmcoil Engine [466] (6/6) blocked by Wurmcoil Engine * 12:29: Attacker: Wurmcoil Engine [466] (6/6) blocked by Wurmcoil Engine
* [4ed] (6/6) * [4ed] (6/6) 12:29: yespair gains 6 life 12:29: HipSomHap gains 6 life
* 12:29: yespair gains 6 life * 12:29: Wurmcoil Engine [4ed] died 12:29: Ability triggers: Wurmcoil
* 12:29: HipSomHap gains 6 life * Engine [4ed] - When Wurmcoil Engine [4ed] dies, put a a 3/3 colorless
* 12:29: Wurmcoil Engine [4ed] died
* 12:29: Ability triggers: Wurmcoil Engine [4ed] - When Wurmcoil Engine [4ed] dies, put a a 3/3 colorless
* Wurm artifact creature token with deathtouch onto the battlefield. Put a * Wurm artifact creature token with deathtouch onto the battlefield. Put a
* a 3/3 colorless Wurm artifact creature token with lifelink onto the * a 3/3 colorless Wurm artifact creature token with lifelink onto the
* battlefield. * battlefield. 12:29: Phantasmal Image [466] died 12:29: HipSomHap puts a
* 12:29: Phantasmal Image [466] died * Wurm [7d0] token onto the battlefield 12:29: HipSomHap puts a Wurm [186]
* 12:29: HipSomHap puts a Wurm [7d0] token onto the battlefield * token onto the battlefield
* 12:29: HipSomHap puts a Wurm [186] token onto the battlefield
* *
* To the best of my knowledge, the Phantasmal Image [466], which entered * To the best of my knowledge, the Phantasmal Image [466], which entered
* the battlefield as a Wurmcoil Engine, should grant tokens through the * the battlefield as a Wurmcoil Engine, should grant tokens through the
* Dies-trigger as well, right? * Dies-trigger as well, right?
*/ */
@Test @Test
public void testDiesTriggered2() { public void testDiesTriggered2() {
addCard(Zone.BATTLEFIELD, playerB, "Wurmcoil Engine"); addCard(Zone.BATTLEFIELD, playerB, "Wurmcoil Engine");
@ -570,5 +575,5 @@ public class PhantasmalImageTest extends CardTestPlayerBase {
assertPermanentCount(playerA, "Wurm", 2); assertPermanentCount(playerA, "Wurm", 2);
assertPermanentCount(playerB, "Wurm", 2); assertPermanentCount(playerB, "Wurm", 2);
} }
} }

View file

@ -36,10 +36,8 @@ import org.mage.test.serverside.base.CardTestPlayerBase;
* *
* @author LevelX2 * @author LevelX2
*/ */
public class PhyrexianMetamorphTest extends CardTestPlayerBase { public class PhyrexianMetamorphTest extends CardTestPlayerBase {
@Test @Test
public void testCopyCreature() { public void testCopyCreature() {
addCard(Zone.BATTLEFIELD, playerA, "Island", 1); addCard(Zone.BATTLEFIELD, playerA, "Island", 1);
@ -48,7 +46,7 @@ public class PhyrexianMetamorphTest extends CardTestPlayerBase {
// You may have Phyrexian Metamorph enter the battlefield as a copy of any artifact or creature on the battlefield, except it's an artifact in addition to its other types. // You may have Phyrexian Metamorph enter the battlefield as a copy of any artifact or creature on the battlefield, except it's an artifact in addition to its other types.
addCard(Zone.HAND, playerA, "Phyrexian Metamorph"); // {3}{UP} addCard(Zone.HAND, playerA, "Phyrexian Metamorph"); // {3}{UP}
addCard(Zone.HAND, playerA, "Cloudshift"); addCard(Zone.HAND, playerA, "Cloudshift");
//Flying //Flying
// Vanishing 3 (This permanent enters the battlefield with three time counters on it. At the beginning of your upkeep, remove a time counter from it. When the last is removed, sacrifice it.) // Vanishing 3 (This permanent enters the battlefield with three time counters on it. At the beginning of your upkeep, remove a time counter from it. When the last is removed, sacrifice it.)
// When Aven Riftwatcher enters the battlefield or leaves the battlefield, you gain 2 life. // When Aven Riftwatcher enters the battlefield or leaves the battlefield, you gain 2 life.
@ -68,13 +66,13 @@ public class PhyrexianMetamorphTest extends CardTestPlayerBase {
assertLife(playerA, 24); assertLife(playerA, 24);
assertLife(playerB, 20); assertLife(playerB, 20);
assertGraveyardCount(playerA, "Cloudshift", 1); assertGraveyardCount(playerA, "Cloudshift", 1);
assertPermanentCount(playerA, "Ponyback Brigade", 1); assertPermanentCount(playerA, "Ponyback Brigade", 1);
assertPermanentCount(playerA, "Goblin", 3); assertPermanentCount(playerA, "Goblin", 3);
} }
/** /**
* An opponent cast Phyrexian Metamorph and cloned another opponent's * An opponent cast Phyrexian Metamorph and cloned another opponent's
@ -84,7 +82,6 @@ public class PhyrexianMetamorphTest extends CardTestPlayerBase {
* to choose a new creature to clone when the Phyrexian Metamorph re-entered * to choose a new creature to clone when the Phyrexian Metamorph re-entered
* the battlefield. * the battlefield.
*/ */
@Test @Test
public void testFlickerWithBrago() { public void testFlickerWithBrago() {
addCard(Zone.BATTLEFIELD, playerA, "Island", 4); addCard(Zone.BATTLEFIELD, playerA, "Island", 4);
@ -93,9 +90,9 @@ public class PhyrexianMetamorphTest extends CardTestPlayerBase {
addCard(Zone.HAND, playerA, "Phyrexian Metamorph"); // {3}{UP} addCard(Zone.HAND, playerA, "Phyrexian Metamorph"); // {3}{UP}
// Flying // Flying
// When Brago, King Eternal deals combat damage to a player, exile any number of target nonland permanents you control, then return those cards to the battlefield under their owner's control. // When Brago, King Eternal deals combat damage to a player, exile any number of target nonland permanents you control, then return those cards to the battlefield under their owner's control.
addCard(Zone.BATTLEFIELD, playerA, "Brago, King Eternal"); // 2/4 addCard(Zone.BATTLEFIELD, playerA, "Brago, King Eternal"); // 2/4
// Creatures you control have haste. // Creatures you control have haste.
// Cascade, cascade // Cascade, cascade
addCard(Zone.BATTLEFIELD, playerB, "Maelstrom Wanderer"); // 7/5 addCard(Zone.BATTLEFIELD, playerB, "Maelstrom Wanderer"); // 7/5
@ -106,19 +103,19 @@ public class PhyrexianMetamorphTest extends CardTestPlayerBase {
setChoice(playerA, "Maelstrom Wanderer"); setChoice(playerA, "Maelstrom Wanderer");
attack(3, playerA, "Brago, King Eternal"); attack(3, playerA, "Brago, King Eternal");
addTarget(playerA, "Maelstrom Wanderer"); addTarget(playerA, "Maelstrom Wanderer");
setChoice(playerA, "Ponyback Brigade"); setChoice(playerA, "Ponyback Brigade");
setStopAt(3, PhaseStep.END_COMBAT); setStopAt(3, PhaseStep.END_COMBAT);
execute(); execute();
assertLife(playerA, 20); assertLife(playerA, 20);
assertLife(playerB, 18); assertLife(playerB, 18);
assertPermanentCount(playerA, "Ponyback Brigade", 1); assertPermanentCount(playerA, "Ponyback Brigade", 1);
assertPermanentCount(playerA, "Goblin", 3); assertPermanentCount(playerA, "Goblin", 3);
} }
/** /**
* I had a Harmonic Sliver, my opponent played Phyrexian Metamorph copying * I had a Harmonic Sliver, my opponent played Phyrexian Metamorph copying
@ -126,7 +123,7 @@ public class PhyrexianMetamorphTest extends CardTestPlayerBase {
* destroying ability, where it should have had two of them and triggered * destroying ability, where it should have had two of them and triggered
* twice (the Metamorph might have nothing to do with this) * twice (the Metamorph might have nothing to do with this)
*/ */
@Test @Test
public void testHarmonicSliver() { public void testHarmonicSliver() {
addCard(Zone.BATTLEFIELD, playerA, "Island", 4); addCard(Zone.BATTLEFIELD, playerA, "Island", 4);
@ -137,7 +134,7 @@ public class PhyrexianMetamorphTest extends CardTestPlayerBase {
addCard(Zone.BATTLEFIELD, playerB, "Kitesail", 1); addCard(Zone.BATTLEFIELD, playerB, "Kitesail", 1);
// All Slivers have "When this permanent enters the battlefield, destroy target artifact or enchantment." // All Slivers have "When this permanent enters the battlefield, destroy target artifact or enchantment."
addCard(Zone.BATTLEFIELD, playerB, "Harmonic Sliver"); // 2/4 addCard(Zone.BATTLEFIELD, playerB, "Harmonic Sliver"); // 2/4
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Phyrexian Metamorph"); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Phyrexian Metamorph");
setChoice(playerA, "Harmonic Sliver"); setChoice(playerA, "Harmonic Sliver");
addTarget(playerA, "Alloy Myr"); addTarget(playerA, "Alloy Myr");
@ -145,66 +142,116 @@ public class PhyrexianMetamorphTest extends CardTestPlayerBase {
setStopAt(1, PhaseStep.END_COMBAT); setStopAt(1, PhaseStep.END_COMBAT);
execute(); execute();
assertPermanentCount(playerA, "Harmonic Sliver", 1); assertPermanentCount(playerA, "Harmonic Sliver", 1);
assertGraveyardCount(playerB, "Alloy Myr", 1); assertGraveyardCount(playerB, "Alloy Myr", 1);
assertGraveyardCount(playerB, "Kitesail", 1); assertGraveyardCount(playerB, "Kitesail", 1);
} }
/** /**
* If a Harmonic Sliver enters the battlefield * If a Harmonic Sliver enters the battlefield the controller has to destroy
* the controller has to destroy one artifacts or enchantments * one artifacts or enchantments
*/ */
@Test @Test
public void testHarmonicSliverNative1() { public void testHarmonicSliverNative1() {
addCard(Zone.BATTLEFIELD, playerA, "Forest", 1); addCard(Zone.BATTLEFIELD, playerA, "Forest", 1);
addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); addCard(Zone.BATTLEFIELD, playerA, "Plains", 2);
// All Slivers have "When this permanent enters the battlefield, destroy target artifact or enchantment."
addCard(Zone.HAND, playerA, "Harmonic Sliver");
addCard(Zone.BATTLEFIELD, playerB, "Alloy Myr", 2); // 2/2 // All Slivers have "When this permanent enters the battlefield, destroy target artifact or enchantment."
addCard(Zone.HAND, playerA, "Harmonic Sliver");
addCard(Zone.BATTLEFIELD, playerB, "Alloy Myr", 2); // 2/2
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Harmonic Sliver"); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Harmonic Sliver");
setStopAt(1, PhaseStep.END_COMBAT); setStopAt(1, PhaseStep.END_COMBAT);
execute(); execute();
assertPermanentCount(playerA, "Harmonic Sliver", 1); assertPermanentCount(playerA, "Harmonic Sliver", 1);
assertGraveyardCount(playerB, "Alloy Myr", 1); assertGraveyardCount(playerB, "Alloy Myr", 1);
} }
/** /**
* If a Harmonic Sliver enters the battlefield and there is already one on the battlefield * If a Harmonic Sliver enters the battlefield and there is already one on
* the controller has to destroy two artifacts or enchantments * the battlefield the controller has to destroy two artifacts or
* enchantments
*/ */
@Test @Test
public void testHarmonicSliverNative2() { public void testHarmonicSliverNative2() {
addCard(Zone.BATTLEFIELD, playerA, "Forest", 1); addCard(Zone.BATTLEFIELD, playerA, "Forest", 1);
addCard(Zone.BATTLEFIELD, playerA, "Plains", 2); addCard(Zone.BATTLEFIELD, playerA, "Plains", 2);
addCard(Zone.HAND, playerA, "Harmonic Sliver"); addCard(Zone.HAND, playerA, "Harmonic Sliver");
addCard(Zone.BATTLEFIELD, playerB, "Alloy Myr", 1); addCard(Zone.BATTLEFIELD, playerB, "Alloy Myr", 1);
addCard(Zone.BATTLEFIELD, playerB, "Kitesail", 1); addCard(Zone.BATTLEFIELD, playerB, "Kitesail", 1);
// All Slivers have "When this permanent enters the battlefield, destroy target artifact or enchantment." // All Slivers have "When this permanent enters the battlefield, destroy target artifact or enchantment."
addCard(Zone.BATTLEFIELD, playerB, "Harmonic Sliver"); // 2/4 addCard(Zone.BATTLEFIELD, playerB, "Harmonic Sliver"); // 2/4
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Harmonic Sliver"); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Harmonic Sliver");
addTarget(playerA, "Alloy Myr"); addTarget(playerA, "Alloy Myr");
addTarget(playerA, "Kitesail"); addTarget(playerA, "Kitesail");
setStopAt(1, PhaseStep.END_COMBAT); setStopAt(1, PhaseStep.END_COMBAT);
execute(); execute();
assertPermanentCount(playerA, "Harmonic Sliver", 1); assertPermanentCount(playerA, "Harmonic Sliver", 1);
assertGraveyardCount(playerB, "Alloy Myr", 1); assertGraveyardCount(playerB, "Alloy Myr", 1);
assertGraveyardCount(playerB, "Kitesail", 1); assertGraveyardCount(playerB, "Kitesail", 1);
} }
/**
* I cast Show and Tell, and put Sheoldred, Whispering One into play and my
* opponent put Phyrexian Metamorph into play and he was able to clone my
* Sheoldred, Whispering One.
*
* 6/1/2011 If Phyrexian Metamorph somehow enters the battlefield at the
* same time as another permanent (due to Mass Polymorph or Liliana Vess's
* third ability, for example), Phyrexian Metamorph can't become a copy of
* that permanent. You may only choose a permanent that's already on the
* battlefield.
*
* 400.6. If an object would move from one zone to another, determine what
* event is moving the object. If the object is moving to a public zone, all
* players look at it to see if it has any abilities that would affect the
* move. Then any appropriate replacement effects, whether they come from
* that object or from elsewhere, are applied to that event. If any effects
* or rules try to do two or more contradictory or mutually exclusive things
* to a particular object, that object's controller -- or its owner if it
* has no controller -- chooses which effect to apply, and what that effect
* does. (Note that multiple instances of the same thing may be mutually
* exclusive; for example, two simultaneous "destroy" effects.) Then the
* event moves the object.
*/
@Test
public void testShowAndTell() {
addCard(Zone.BATTLEFIELD, playerA, "Island", 3);
// Each player may put an artifact, creature, enchantment, or land card from his or her hand onto the battlefield.
addCard(Zone.HAND, playerA, "Show and Tell"); // SORCERY {2}{U}
// Swampwalk
// At the beginning of your upkeep, return target creature card from your graveyard to the battlefield.
// At the beginning of each opponent's upkeep, that player sacrifices a creature.
addCard(Zone.HAND, playerA, "Sheoldred, Whispering One");
addCard(Zone.HAND, playerB, "Phyrexian Metamorph");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Show and Tell");
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertPermanentCount(playerA, "Sheoldred, Whispering One", 1);
assertPermanentCount(playerB, "Sheoldred, Whispering One", 0);
assertGraveyardCount(playerB, "Phyrexian Metamorph", 1);
}
} }

View file

@ -554,7 +554,7 @@ public class TestPlayer implements Player {
if (!choices.isEmpty()) { if (!choices.isEmpty()) {
for (String choice : choices) { for (String choice : choices) {
for (int index = 0; index < rEffects.size(); index++) { for (int index = 0; index < rEffects.size(); index++) {
if (choice.equals(rEffects.get(index))) { if (choice.equals(rEffects.get(Integer.toString(index)))) {
choices.remove(choice); choices.remove(choice);
return index; return index;
} }
@ -1951,6 +1951,11 @@ public class TestPlayer implements Player {
return computerPlayer.scry(value, source, game); return computerPlayer.scry(value, source, game);
} }
@Override
public boolean moveCards(Set<Card> cards, Zone toZone, Ability source, Game game, boolean tapped, boolean faceDown, boolean byOwner, ArrayList<UUID> appliedEffects) {
return computerPlayer.moveCards(cards, toZone, source, game, tapped, faceDown, byOwner, appliedEffects);
}
public void setAIPlayer(boolean AIPlayer) { public void setAIPlayer(boolean AIPlayer) {
this.AIPlayer = AIPlayer; this.AIPlayer = AIPlayer;
} }

View file

@ -283,7 +283,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
if (card == null) { if (card == null) {
throw new IllegalArgumentException("[TEST] Couldn't find a card: " + cardName); throw new IllegalArgumentException("[TEST] Couldn't find a card: " + cardName);
} }
PermanentCard p = new PermanentCard(card, null, currentGame); PermanentCard p = new PermanentCard(card, player.getId(), currentGame);
p.setTapped(tapped); p.setTapped(tapped);
getBattlefieldCards(player).add(p); getBattlefieldCards(player).add(p);
} }

View file

@ -60,7 +60,7 @@ public class DiesAttachedTriggeredAbility extends TriggeredAbilityImpl {
// So check here with the LKI of the enchantment // So check here with the LKI of the enchantment
Permanent attachment = game.getPermanentOrLKIBattlefield(getSourceId()); Permanent attachment = game.getPermanentOrLKIBattlefield(getSourceId());
if (attachment != null && attachment.getAttachedTo().equals(zEvent.getTargetId()) if (attachment != null && attachment.getAttachedTo().equals(zEvent.getTargetId())
&& attachment.getAttachedToZoneChangeCounter() == zEvent.getTarget().getZoneChangeCounter(game)) { && attachment.getAttachedToZoneChangeCounter() == zEvent.getTarget().getZoneChangeCounter(game) - 1) {
triggered = true; triggered = true;
} }
} }

View file

@ -1,8 +1,6 @@
package mage.abilities.condition.common; package mage.abilities.condition.common;
import java.util.UUID; import java.util.UUID;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.condition.Condition; import mage.abilities.condition.Condition;
import mage.constants.CardType; import mage.constants.CardType;
@ -15,8 +13,8 @@ import mage.target.Target;
* @author Jeff * @author Jeff
*/ */
public class EnchantedTargetCondition implements Condition { public class EnchantedTargetCondition implements Condition {
private static EnchantedTargetCondition fInstance = new EnchantedTargetCondition(); private static final EnchantedTargetCondition fInstance = new EnchantedTargetCondition();
public static Condition getInstance() { public static Condition getInstance() {
return fInstance; return fInstance;

View file

@ -36,13 +36,13 @@ import mage.cards.Card;
import mage.constants.CardType; import mage.constants.CardType;
import mage.constants.Duration; import mage.constants.Duration;
import mage.constants.Outcome; import mage.constants.Outcome;
import mage.constants.SpellAbilityType;
import mage.constants.Zone; import mage.constants.Zone;
import mage.game.Game; import mage.game.Game;
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;
import mage.game.permanent.PermanentCard; import mage.game.permanent.PermanentCard;
import mage.game.stack.Spell;
import mage.game.stack.StackAbility; import mage.game.stack.StackAbility;
import mage.players.Player; import mage.players.Player;
import mage.target.Target; import mage.target.Target;
@ -103,12 +103,12 @@ public class AuraReplacementEffect extends ReplacementEffectImpl {
UUID targetId = null; UUID targetId = null;
MageObject sourceObject = game.getObject(sourceId); MageObject sourceObject = game.getObject(sourceId);
boolean enchantCardInGraveyard = false; boolean enchantCardInGraveyard = false;
if (sourceObject instanceof Spell) { // if (sourceObject instanceof Spell) {
if (fromZone.equals(Zone.EXILED)) { // if (fromZone.equals(Zone.EXILED)) {
// cast from exile (e.g. Neightveil Spector) -> no replacement // // cast from exile (e.g. Neightveil Spector) -> no replacement
return false; // return false;
} // }
} // }
if (sourceObject instanceof StackAbility) { if (sourceObject instanceof StackAbility) {
StackAbility stackAbility = (StackAbility) sourceObject; StackAbility stackAbility = (StackAbility) sourceObject;
if (!stackAbility.getEffects().isEmpty()) { if (!stackAbility.getEffects().isEmpty()) {
@ -118,25 +118,34 @@ public class AuraReplacementEffect extends ReplacementEffectImpl {
game.applyEffects(); // So continuousEffects are removed if previous effect of the same ability did move objects that cuase continuous effects game.applyEffects(); // So continuousEffects are removed if previous effect of the same ability did move objects that cuase continuous effects
if (targetId == null) { if (targetId == null) {
Target target = card.getSpellAbility().getTargets().get(0).copy(); SpellAbility spellAbility = card.getSpellAbility();
if (spellAbility.getTargets().isEmpty()) {
for (Ability ability : card.getAbilities(game)) {
if ((ability instanceof SpellAbility)
&& SpellAbilityType.BASE_ALTERNATE.equals(((SpellAbility) ability).getSpellAbilityType())
&& !ability.getTargets().isEmpty()) {
spellAbility = (SpellAbility) ability;
break;
}
}
}
if (spellAbility.getTargets().isEmpty()) {
return false;
}
Target target = spellAbility.getTargets().get(0).copy();
Outcome auraOutcome = Outcome.BoostCreature;
for (Effect effect : spellAbility.getEffects()) {
if (effect instanceof AttachEffect) {
auraOutcome = effect.getOutcome();
break;
}
}
enchantCardInGraveyard = target instanceof TargetCardInGraveyard; enchantCardInGraveyard = target instanceof TargetCardInGraveyard;
if (target != null) { if (target != null) {
target.setNotTarget(true); // always not target because this way it's not handled targeted target.setNotTarget(true); // always not target because this way it's not handled targeted
target.clearChosen(); // neccessary if e.g. aura is blinked multiple times target.clearChosen(); // neccessary if e.g. aura is blinked multiple times
} }
Player player = game.getPlayer(card.getOwnerId()); Player player = game.getPlayer(card.getOwnerId());
Outcome auraOutcome = Outcome.BoostCreature;
Ability:
for (Ability ability : card.getAbilities()) {
if (ability instanceof SpellAbility) {
for (Effect effect : ability.getEffects()) {
if (effect instanceof AttachEffect) {
auraOutcome = effect.getOutcome();
break Ability;
}
}
}
}
if (target != null && player != null && player.choose(auraOutcome, target, card.getId(), game)) { if (target != null && player != null && player.choose(auraOutcome, target, card.getId(), game)) {
targetId = target.getFirstTarget(); targetId = target.getFirstTarget();
} }
@ -151,44 +160,27 @@ public class AuraReplacementEffect extends ReplacementEffectImpl {
} }
Player targetPlayer = game.getPlayer(targetId); Player targetPlayer = game.getPlayer(targetId);
if (targetCard != null || targetPermanent != null || targetPlayer != null) { if (targetCard != null || targetPermanent != null || targetPlayer != null) {
switch (fromZone) { card.removeFromZone(game, fromZone, sourceId);
case EXILED: card.updateZoneChangeCounter(game);
game.getExile().removeCard(card, game);
break;
case GRAVEYARD:
game.getPlayer(card.getOwnerId()).removeFromGraveyard(card, game);
break;
case HAND:
game.getPlayer(card.getOwnerId()).removeFromHand(card, game);
break;
case LIBRARY:
game.getPlayer(card.getOwnerId()).removeFromLibrary(card, game);
break;
default:
}
game.rememberLKI(card.getId(), fromZone, card);
PermanentCard permanent = new PermanentCard(card, card.getOwnerId(), game); PermanentCard permanent = new PermanentCard(card, card.getOwnerId(), game);
game.getBattlefield().addPermanent(permanent); game.getBattlefield().addPermanent(permanent);
card.setZone(Zone.BATTLEFIELD, game); card.setZone(Zone.BATTLEFIELD, game);
boolean entered = permanent.entersBattlefield(event.getSourceId(), game, fromZone, true); if (permanent.entersBattlefield(event.getSourceId(), game, fromZone, true)) {
game.applyEffects(); if (targetCard != null) {
if (!entered) { permanent.attachTo(targetCard.getId(), game);
return false; } else if (targetPermanent != null) {
} targetPermanent.addAttachment(permanent.getId(), game);
game.fireEvent(new ZoneChangeEvent(permanent, controllerId, fromZone, Zone.BATTLEFIELD)); } else if (targetPlayer != null) {
targetPlayer.addAttachment(permanent.getId(), game);
}
game.applyEffects();
if (targetCard != null) { game.fireEvent(new ZoneChangeEvent(permanent, controllerId, fromZone, Zone.BATTLEFIELD));
permanent.attachTo(targetCard.getId(), game); return true;
}
if (targetPermanent != null) {
targetPermanent.addAttachment(permanent.getId(), game);
}
if (targetPlayer != null) {
targetPlayer.addAttachment(permanent.getId(), game);
} }
} }
return true; return false;
} }
@Override @Override
@ -199,7 +191,7 @@ public class AuraReplacementEffect extends ReplacementEffectImpl {
@Override @Override
public boolean applies(GameEvent event, Ability source, Game game) { public boolean applies(GameEvent event, Ability source, Game game) {
if (((ZoneChangeEvent) event).getToZone().equals(Zone.BATTLEFIELD) if (((ZoneChangeEvent) event).getToZone().equals(Zone.BATTLEFIELD)
&& !(((ZoneChangeEvent) event).getFromZone().equals(Zone.HAND))) { && !(((ZoneChangeEvent) event).getFromZone().equals(Zone.STACK))) {
Card card = game.getCard(event.getTargetId()); Card card = game.getCard(event.getTargetId());
if (card != null && card.getCardType().contains(CardType.ENCHANTMENT) && card.hasSubtype("Aura")) { if (card != null && card.getCardType().contains(CardType.ENCHANTMENT) && card.hasSubtype("Aura")) {
return true; return true;

View file

@ -1147,18 +1147,18 @@ public class ContinuousEffects implements Serializable {
} }
} }
private void setControllerForEffect(ContinuousEffectsList<?> effects, UUID cardId, UUID controllerId) { private void setControllerForEffect(ContinuousEffectsList<?> effects, UUID sourceId, UUID controllerId) {
for (Effect effect : effects) { for (Effect effect : effects) {
HashSet<Ability> abilities = effects.getAbility(effect.getId()); HashSet<Ability> abilities = effects.getAbility(effect.getId());
if (abilities != null) { if (abilities != null) {
for (Ability ability : abilities) { for (Ability ability : abilities) {
if (ability.getSourceId() != null) { if (ability.getSourceId() != null) {
if (ability.getSourceId().equals(cardId)) { if (ability.getSourceId().equals(sourceId)) {
ability.setControllerId(controllerId); ability.setControllerId(controllerId);
} }
} else { } else {
if (!ability.getZone().equals(Zone.COMMAND)) { if (!ability.getZone().equals(Zone.COMMAND)) {
logger.fatal(new StringBuilder("No sourceId Ability: ").append(ability)); logger.fatal("Continuous effect for ability with no sourceId Ability: " + ability);
} }
} }
} }

View file

@ -1,30 +1,30 @@
/* /*
* Copyright 2012 BetaSteward_at_googlemail.com. All rights reserved. * Copyright 2012 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
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL , EXEMPLARY, OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL , EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 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 * 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 * 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; package mage.abilities.effects;
import java.util.ArrayList; import java.util.ArrayList;
@ -46,18 +46,19 @@ import org.apache.log4j.Logger;
* @param <T> * @param <T>
*/ */
public class ContinuousEffectsList<T extends ContinuousEffect> extends ArrayList<T> { public class ContinuousEffectsList<T extends ContinuousEffect> extends ArrayList<T> {
private static final Logger logger = Logger.getLogger(ContinuousEffectsList.class); private static final Logger logger = Logger.getLogger(ContinuousEffectsList.class);
// the effectAbilityMap holds for each effect all abilities that are connected (used) with this effect // the effectAbilityMap holds for each effect all abilities that are connected (used) with this effect
private final Map<UUID, HashSet<Ability>> effectAbilityMap = new HashMap<>(); private final Map<UUID, HashSet<Ability>> effectAbilityMap = new HashMap<>();
public ContinuousEffectsList() { } public ContinuousEffectsList() {
}
public ContinuousEffectsList(final ContinuousEffectsList<T> effects) { public ContinuousEffectsList(final ContinuousEffectsList<T> effects) {
this.ensureCapacity(effects.size()); this.ensureCapacity(effects.size());
for (ContinuousEffect cost: effects) { for (ContinuousEffect cost : effects) {
this.add((T)cost.copy()); this.add((T) cost.copy());
} }
for (Map.Entry<UUID, HashSet<Ability>> entry : effects.effectAbilityMap.entrySet()) { for (Map.Entry<UUID, HashSet<Ability>> entry : effects.effectAbilityMap.entrySet()) {
HashSet<Ability> newSet = new HashSet<>(); HashSet<Ability> newSet = new HashSet<>();
@ -113,12 +114,12 @@ public class ContinuousEffectsList<T extends ContinuousEffect> extends ArrayList
Ability ability = it.next(); Ability ability = it.next();
if (ability == null) { if (ability == null) {
it.remove(); it.remove();
} else if (ability instanceof MageSingleton) { } else if (ability instanceof MageSingleton) {
return false; return false;
} else if (effect.isDiscarded()) { } else if (effect.isDiscarded()) {
it.remove(); it.remove();
} else { } else {
switch(effect.getDuration()) { switch (effect.getDuration()) {
case WhileOnBattlefield: case WhileOnBattlefield:
case WhileInGraveyard: case WhileInGraveyard:
case WhileOnStack: case WhileOnStack:
@ -133,8 +134,8 @@ public class ContinuousEffectsList<T extends ContinuousEffect> extends ArrayList
break; break;
case Custom: case Custom:
case UntilYourNextTurn: case UntilYourNextTurn:
if (effect.isInactive(ability , game)) { if (effect.isInactive(ability, game)) {
it.remove(); it.remove();
} }
} }
} }
@ -143,9 +144,9 @@ public class ContinuousEffectsList<T extends ContinuousEffect> extends ArrayList
} }
/** /**
* Adds an effect and its connected ability to the list. * Adds an effect and its connected ability to the list. For each effect
* For each effect will be stored, which abilities are connected to the effect. * will be stored, which abilities are connected to the effect. So an effect
* So an effect can be connected to multiple abilities. * can be connected to multiple abilities.
* *
* @param effect - effect to add * @param effect - effect to add
* @param source - connected ability * @param source - connected ability
@ -153,8 +154,8 @@ public class ContinuousEffectsList<T extends ContinuousEffect> extends ArrayList
public void addEffect(T effect, Ability source) { public void addEffect(T effect, Ability source) {
if (effectAbilityMap.containsKey(effect.getId())) { if (effectAbilityMap.containsKey(effect.getId())) {
HashSet<Ability> set = effectAbilityMap.get(effect.getId()); HashSet<Ability> set = effectAbilityMap.get(effect.getId());
for (Ability ability: set) { for (Ability ability : set) {
if (ability.getId().equals(source.getId()) && ability.getSourceId().equals(source.getSourceId()) ) { if (ability.getId().equals(source.getId()) && ability.getSourceId().equals(source.getSourceId())) {
return; return;
} }
} }

View file

@ -1,42 +1,43 @@
/* /*
* 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
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 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 * 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 * 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; package mage.abilities.effects;
import mage.constants.Duration;
import mage.MageObject; import mage.MageObject;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.Mode; import mage.abilities.Mode;
import mage.abilities.condition.Condition; import mage.abilities.condition.Condition;
import mage.constants.Duration;
import mage.constants.Zone;
import mage.game.Game; 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 mage.game.stack.Spell; import mage.game.stack.Spell;
import mage.game.stack.StackObject;
import mage.players.Player; import mage.players.Player;
/** /**
@ -116,12 +117,17 @@ public class EntersBattlefieldEffect extends ReplacementEffectImpl {
} }
} }
Spell spell = game.getStack().getSpell(event.getSourceId()); Spell spell = game.getStack().getSpell(event.getSourceId());
for (Effect effect: baseEffects) { if (spell == null) {
StackObject stackObject = (StackObject) game.getLastKnownInformation(event.getSourceId(), Zone.STACK);
if (stackObject instanceof Spell) {
spell = (Spell) stackObject;
}
}
for (Effect effect : baseEffects) {
if (source.activate(game, false)) { if (source.activate(game, false)) {
if (effect instanceof ContinuousEffect) { if (effect instanceof ContinuousEffect) {
game.addEffect((ContinuousEffect) effect, source); game.addEffect((ContinuousEffect) effect, source);
} } else {
else {
if (spell != null) { if (spell != null) {
effect.setValue(SOURCE_CAST_SPELL_ABILITY, spell.getSpellAbility()); effect.setValue(SOURCE_CAST_SPELL_ABILITY, spell.getSpellAbility());
} }

View file

@ -1,16 +1,16 @@
/* /*
* Copyright 2011 BetaSteward_at_googlemail.com. All rights reserved. * Copyright 2011 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,13 +20,14 @@
* 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; 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;
@ -66,6 +67,7 @@ public class CopyPermanentEffect extends OneShotEffect {
public CopyPermanentEffect(FilterPermanent filter, ApplyToPermanent applier) { public CopyPermanentEffect(FilterPermanent filter, ApplyToPermanent applier) {
this(filter, applier, false); this(filter, applier, false);
} }
public CopyPermanentEffect(FilterPermanent filter, ApplyToPermanent applier, boolean useTarget) { public CopyPermanentEffect(FilterPermanent filter, ApplyToPermanent applier, boolean useTarget) {
super(Outcome.Copy); super(Outcome.Copy);
this.applier = applier; this.applier = applier;
@ -85,8 +87,8 @@ public class CopyPermanentEffect extends OneShotEffect {
@Override @Override
public boolean apply(Game game, Ability source) { public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(source.getControllerId()); Player player = game.getPlayer(source.getControllerId());
Permanent sourcePermanent = game.getPermanent(source.getSourceId()); MageObject sourceObject = game.getObject(source.getSourceId());
if (player != null && sourcePermanent != null) { if (player != null && sourceObject != null) {
Permanent copyFromPermanent = null; Permanent copyFromPermanent = null;
if (useTargetOfAbility) { if (useTargetOfAbility) {
copyFromPermanent = game.getPermanent(getTargetPointer().getFirst(game, source)); copyFromPermanent = game.getPermanent(getTargetPointer().getFirst(game, source));
@ -99,7 +101,7 @@ public class CopyPermanentEffect extends OneShotEffect {
} }
} }
if (copyFromPermanent != null) { if (copyFromPermanent != null) {
bluePrintPermanent = game.copyPermanent(copyFromPermanent, sourcePermanent, source, applier); bluePrintPermanent = game.copyPermanent(copyFromPermanent, sourceObject.getId(), source, applier);
} }
return true; return true;
} }

View file

@ -28,7 +28,6 @@
package mage.abilities.effects.common; package mage.abilities.effects.common;
import java.util.UUID; import java.util.UUID;
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;
@ -56,7 +55,8 @@ public class CounterTargetWithReplacementEffect extends OneShotEffect {
/** /**
* *
* @param targetZone * @param targetZone
* @param flag use to specify when moving card to library <ul><li>true = put on top</li><li>false = put on bottom</li></ul> * @param flag use to specify when moving card to library <ul><li>true = put
* on top</li><li>false = put on bottom</li></ul>
*/ */
public CounterTargetWithReplacementEffect(Zone targetZone, boolean flag) { public CounterTargetWithReplacementEffect(Zone targetZone, boolean flag) {
super(Outcome.Detriment); super(Outcome.Detriment);
@ -83,33 +83,14 @@ public class CounterTargetWithReplacementEffect extends OneShotEffect {
if (controller != null) { if (controller != null) {
StackObject stackObject = game.getStack().getStackObject(objectId); StackObject stackObject = game.getStack().getStackObject(objectId);
if (stackObject != null && !game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.COUNTER, objectId, sourceId, stackObject.getControllerId()))) { if (stackObject != null && !game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.COUNTER, objectId, sourceId, stackObject.getControllerId()))) {
boolean spell = false;
if (stackObject instanceof Spell) { if (stackObject instanceof Spell) {
game.rememberLKI(objectId, Zone.STACK, stackObject); controller.moveCards((Card) stackObject, null, targetZone, source, game);
spell = true; } else {
} game.getStack().remove(stackObject);
game.getStack().remove(stackObject);
if (spell && !((Spell) stackObject).isCopiedSpell()) {
MageObject mageObject = game.getObject(stackObject.getSourceId());
if (mageObject instanceof Card) {
Card card = (Card) mageObject;
switch (targetZone) {
case LIBRARY:
controller.moveCardToLibraryWithInfo(card, sourceId, game, Zone.STACK, flag, true);
break;
case EXILED:
controller.moveCardToExileWithInfo(card, null, "", sourceId, game, Zone.STACK, true);
break;
default:
controller.moveCards(card, Zone.STACK, targetZone, source, game);
}
} else {
return false;
}
} }
game.fireEvent(GameEvent.getEvent(GameEvent.EventType.COUNTERED, objectId, sourceId, stackObject.getControllerId())); game.fireEvent(GameEvent.getEvent(GameEvent.EventType.COUNTERED, objectId, sourceId, stackObject.getControllerId()));
return true; return true;
} }
} }
return false; return false;
} }

View file

@ -40,6 +40,7 @@ import mage.constants.Zone;
import mage.counters.Counter; import mage.counters.Counter;
import mage.counters.Counters; import mage.counters.Counters;
import mage.game.Game; import mage.game.Game;
import mage.game.permanent.Permanent;
public interface Card extends MageObject { public interface Card extends MageObject {
@ -65,6 +66,8 @@ public interface Card extends MageObject {
String getTokenSetCode(); String getTokenSetCode();
void checkForCountersToAdd(Permanent permanent, Game game);
void setFaceDown(boolean value, Game game); void setFaceDown(boolean value, Game game);
boolean isFaceDown(Game game); boolean isFaceDown(Game game);
@ -125,6 +128,8 @@ public interface Card extends MageObject {
boolean cast(Game game, Zone fromZone, SpellAbility ability, UUID controllerId); boolean cast(Game game, Zone fromZone, SpellAbility ability, UUID controllerId);
boolean removeFromZone(Game game, Zone fromZone, UUID sourceId);
boolean putOntoBattlefield(Game game, Zone fromZone, UUID sourceId, UUID controllerId); boolean putOntoBattlefield(Game game, Zone fromZone, UUID sourceId, UUID controllerId);
boolean putOntoBattlefield(Game game, Zone fromZone, UUID sourceId, UUID controllerId, boolean tapped); boolean putOntoBattlefield(Game game, Zone fromZone, UUID sourceId, UUID controllerId, boolean tapped);

View file

@ -54,8 +54,6 @@ import static mage.constants.Zone.EXILED;
import static mage.constants.Zone.GRAVEYARD; import static mage.constants.Zone.GRAVEYARD;
import static mage.constants.Zone.HAND; import static mage.constants.Zone.HAND;
import static mage.constants.Zone.LIBRARY; import static mage.constants.Zone.LIBRARY;
import static mage.constants.Zone.OUTSIDE;
import static mage.constants.Zone.PICK;
import static mage.constants.Zone.STACK; import static mage.constants.Zone.STACK;
import mage.counters.Counter; import mage.counters.Counter;
import mage.counters.Counters; import mage.counters.Counters;
@ -65,6 +63,7 @@ import mage.game.Game;
import mage.game.command.Commander; import mage.game.command.Commander;
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.PermanentCard; import mage.game.permanent.PermanentCard;
import mage.game.stack.Spell; import mage.game.stack.Spell;
import mage.game.stack.StackObject; import mage.game.stack.StackObject;
@ -342,55 +341,7 @@ public abstract class CardImpl extends MageObjectImpl implements Card {
Zone fromZone = game.getState().getZone(objectId); Zone fromZone = game.getState().getZone(objectId);
ZoneChangeEvent event = new ZoneChangeEvent(this.objectId, sourceId, ownerId, fromZone, toZone, appliedEffects); ZoneChangeEvent event = new ZoneChangeEvent(this.objectId, sourceId, ownerId, fromZone, toZone, appliedEffects);
if (!game.replaceEvent(event)) { if (!game.replaceEvent(event)) {
if (event.getFromZone() != null) { removeFromZone(game, fromZone, sourceId);
switch (event.getFromZone()) {
case GRAVEYARD:
game.getPlayer(ownerId).removeFromGraveyard(this, game);
break;
case HAND:
game.getPlayer(ownerId).removeFromHand(this, game);
break;
case LIBRARY:
game.getPlayer(ownerId).removeFromLibrary(this, game);
break;
case EXILED:
game.getExile().removeCard(this, game);
break;
case OUTSIDE:
game.getPlayer(ownerId).getSideboard().remove(this);
break;
case COMMAND:
game.getState().getCommand().remove((Commander) game.getObject(objectId));
break;
case STACK:
StackObject stackObject = game.getStack().getSpell(getSpellAbility().getId());
if (stackObject == null && (this instanceof SplitCard)) { // handle if half of Split cast is on the stack
stackObject = game.getStack().getSpell(((SplitCard) this).getLeftHalfCard().getId());
if (stackObject == null) {
stackObject = game.getStack().getSpell(((SplitCard) this).getRightHalfCard().getId());
}
}
if (stackObject == null) {
stackObject = game.getStack().getSpell(getId());
}
if (stackObject != null) {
game.getStack().remove(stackObject);
}
break;
case PICK:
case BATTLEFIELD: // for sacrificing permanents or putting to library
break;
default:
Card sourceCard = game.getCard(sourceId);
logger.fatal(new StringBuilder("Invalid from zone [").append(fromZone)
.append("] for card [").append(this.getName())
.append("] to zone [").append(toZone)
.append("] source [").append(sourceCard != null ? sourceCard.getName() : "null").append("]").toString());
break;
}
game.rememberLKI(objectId, event.getFromZone(), this);
}
setFaceDown(false, game); setFaceDown(false, game);
updateZoneChangeCounter(game); updateZoneChangeCounter(game);
switch (event.getToZone()) { switch (event.getToZone()) {
@ -454,32 +405,7 @@ public abstract class CardImpl extends MageObjectImpl implements Card {
Card mainCard = getMainCard(); Card mainCard = getMainCard();
ZoneChangeEvent event = new ZoneChangeEvent(mainCard.getId(), ability.getId(), controllerId, fromZone, Zone.STACK); ZoneChangeEvent event = new ZoneChangeEvent(mainCard.getId(), ability.getId(), controllerId, fromZone, Zone.STACK);
if (!game.replaceEvent(event)) { if (!game.replaceEvent(event)) {
if (event.getFromZone() != null) { mainCard.removeFromZone(game, fromZone, ability.getSourceId());
switch (event.getFromZone()) {
case GRAVEYARD:
game.getPlayer(ownerId).removeFromGraveyard(mainCard, game);
break;
case HAND:
game.getPlayer(ownerId).removeFromHand(mainCard, game);
break;
case LIBRARY:
game.getPlayer(ownerId).removeFromLibrary(mainCard, game);
break;
case EXILED:
game.getExile().removeCard(mainCard, game);
break;
case OUTSIDE:
game.getPlayer(ownerId).getSideboard().remove(mainCard);
break;
case COMMAND:
game.getState().getCommand().remove((Commander) game.getObject(mainCard.getId()));
break;
default:
//logger.warning("moveToZone, not fully implemented: from="+event.getFromZone() + ", to="+event.getToZone());
}
game.rememberLKI(mainCard.getId(), event.getFromZone(), this);
}
game.getStack().push(new Spell(this, ability.copy(), controllerId, event.getFromZone())); game.getStack().push(new Spell(this, ability.copy(), controllerId, event.getFromZone()));
updateZoneChangeCounter(game); updateZoneChangeCounter(game);
setZone(event.getToZone(), game); setZone(event.getToZone(), game);
@ -499,36 +425,7 @@ public abstract class CardImpl extends MageObjectImpl implements Card {
Zone fromZone = game.getState().getZone(objectId); Zone fromZone = game.getState().getZone(objectId);
ZoneChangeEvent event = new ZoneChangeEvent(this.objectId, sourceId, ownerId, fromZone, Zone.EXILED, appliedEffects); ZoneChangeEvent event = new ZoneChangeEvent(this.objectId, sourceId, ownerId, fromZone, Zone.EXILED, appliedEffects);
if (!game.replaceEvent(event)) { if (!game.replaceEvent(event)) {
if (fromZone != null) { removeFromZone(game, fromZone, sourceId);
switch (fromZone) {
case GRAVEYARD:
game.getPlayer(ownerId).removeFromGraveyard(this, game);
break;
case HAND:
game.getPlayer(ownerId).removeFromHand(this, game);
break;
case LIBRARY:
game.getPlayer(ownerId).removeFromLibrary(this, game);
break;
case EXILED:
game.getExile().removeCard(this, game);
break;
case STACK:
StackObject stackObject = game.getStack().getSpell(getId());
if (stackObject != null) {
game.getStack().remove(stackObject);
}
break;
case PICK:
// nothing to do
break;
default:
MageObject object = game.getObject(sourceId);
logger.warn(new StringBuilder("moveToExile, not fully implemented: from = ").append(fromZone).append(" - ").append(object != null ? object.getName() : "null"));
}
game.rememberLKI(objectId, event.getFromZone(), this);
}
if (exileId == null) { if (exileId == null) {
game.getExile().getPermanentExile().add(this); game.getExile().getPermanentExile().add(this);
} else { } else {
@ -568,37 +465,7 @@ public abstract class CardImpl extends MageObjectImpl implements Card {
if (facedown) { if (facedown) {
this.setFaceDown(false, game); this.setFaceDown(false, game);
} }
if (fromZone != null) { removeFromZone(game, fromZone, sourceId);
boolean removed = false;
switch (fromZone) {
case GRAVEYARD:
removed = game.getPlayer(ownerId).removeFromGraveyard(this, game);
break;
case HAND:
removed = game.getPlayer(ownerId).removeFromHand(this, game);
break;
case LIBRARY:
removed = game.getPlayer(ownerId).removeFromLibrary(this, game);
break;
case EXILED:
game.getExile().removeCard(this, game);
removed = true;
break;
case COMMAND:
// command object (commander) is only on the stack, so no removing neccessary here
removed = true;
break;
case PICK:
removed = true;
break;
default:
logger.warn("putOntoBattlefield, not fully implemented: fromZone=" + fromZone);
}
game.rememberLKI(objectId, event.getFromZone(), this);
if (!removed) {
logger.warn("Couldn't find card in fromZone, card=" + getName() + ", fromZone=" + fromZone);
}
}
updateZoneChangeCounter(game); updateZoneChangeCounter(game);
PermanentCard permanent = new PermanentCard(this, event.getPlayerId(), game); PermanentCard permanent = new PermanentCard(this, event.getPlayerId(), game);
// make sure the controller of all continuous effects of this card are switched to the current controller // make sure the controller of all continuous effects of this card are switched to the current controller
@ -624,7 +491,76 @@ public abstract class CardImpl extends MageObjectImpl implements Card {
return false; return false;
} }
private void checkForCountersToAdd(PermanentCard permanent, Game game) { @Override
public boolean removeFromZone(Game game, Zone fromZone, UUID sourceId) {
boolean removed = false;
MageObject lkiObject = null;
switch (fromZone) {
case GRAVEYARD:
removed = game.getPlayer(ownerId).removeFromGraveyard(this, game);
break;
case HAND:
removed = game.getPlayer(ownerId).removeFromHand(this, game);
break;
case LIBRARY:
removed = game.getPlayer(ownerId).removeFromLibrary(this, game);
break;
case EXILED:
if (game.getExile().getCard(getId(), game) != null) {
game.getExile().removeCard(this, game);
removed = true;
}
break;
case STACK:
StackObject stackObject = game.getStack().getSpell(getSpellAbility().getId());
if (stackObject == null && (this instanceof SplitCard)) { // handle if half of Split cast is on the stack
stackObject = game.getStack().getSpell(((SplitCard) this).getLeftHalfCard().getId());
if (stackObject == null) {
stackObject = game.getStack().getSpell(((SplitCard) this).getRightHalfCard().getId());
}
}
if (stackObject == null) {
stackObject = game.getStack().getSpell(getId());
}
if (stackObject != null) {
removed = game.getStack().remove(stackObject);
lkiObject = stackObject;
}
break;
case COMMAND:
lkiObject = (Commander) game.getObject(objectId);
if (lkiObject != null) {
removed = game.getState().getCommand().remove((Commander) game.getObject(objectId));
}
break;
case OUTSIDE:
if (isCopy()) { // copied cards have no need to be removed from a previous zone
removed = true;
} else if (game.getPlayer(ownerId).getSideboard().contains(this.getId())) {
game.getPlayer(ownerId).getSideboard().remove(this.getId());
removed = true;
}
break;
case PICK: // Pick should no longer be used
case BATTLEFIELD: // for sacrificing permanents or putting to library
removed = true;
break;
default:
MageObject sourceObject = game.getObject(sourceId);
logger.fatal("Invalid from zone [" + fromZone + "] for card [" + this.getIdName()
+ "] source [" + (sourceObject != null ? sourceObject.getName() : "null") + "]");
break;
}
game.rememberLKI(objectId, fromZone, lkiObject != null ? lkiObject : this);
if (!removed) {
logger.warn("Couldn't find card in fromZone, card=" + getIdName() + ", fromZone=" + fromZone);
}
return removed;
}
@Override
public void checkForCountersToAdd(Permanent permanent, Game game) {
Counters countersToAdd = game.getEnterWithCounters(permanent.getId()); Counters countersToAdd = game.getEnterWithCounters(permanent.getId());
if (countersToAdd != null) { if (countersToAdd != null) {
for (Counter counter : countersToAdd.values()) { for (Counter counter : countersToAdd.values()) {

View file

@ -7,24 +7,25 @@ import mage.abilities.Abilities;
import mage.abilities.AbilitiesImpl; import mage.abilities.AbilitiesImpl;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.counters.Counters; import mage.counters.Counters;
/** /**
* *
* @author BetaSteward * @author BetaSteward
*/ */
public class CardState implements Serializable { public class CardState implements Serializable {
protected boolean faceDown; protected boolean faceDown;
protected Map<String, String> info; protected Map<String, String> info;
protected Counters counters; protected Counters counters;
protected Abilities<Ability> abilities; protected Abilities<Ability> abilities;
private static final Map<String, String> emptyInfo = new HashMap<>(); private static final Map<String, String> emptyInfo = new HashMap<>();
private static final Abilities<Ability> emptyAbilities = new AbilitiesImpl<>(); private static final Abilities<Ability> emptyAbilities = new AbilitiesImpl<>();
public CardState() { public CardState() {
counters = new Counters(); counters = new Counters();
} }
public CardState(final CardState state) { public CardState(final CardState state) {
this.faceDown = state.faceDown; this.faceDown = state.faceDown;
if (state.info != null) { if (state.info != null) {
@ -34,7 +35,7 @@ public class CardState implements Serializable {
counters = state.counters.copy(); counters = state.counters.copy();
if (state.abilities != null) { if (state.abilities != null) {
abilities = new AbilitiesImpl<>(); abilities = new AbilitiesImpl<>();
for (Ability ability: state.abilities) { for (Ability ability : state.abilities) {
abilities.add(ability.copy()); abilities.add(ability.copy());
} }
} }
@ -43,7 +44,7 @@ public class CardState implements Serializable {
public CardState copy() { public CardState copy() {
return new CardState(this); return new CardState(this);
} }
public void setFaceDown(boolean value) { public void setFaceDown(boolean value) {
faceDown = value; faceDown = value;
} }
@ -66,45 +67,45 @@ public class CardState implements Serializable {
info.put(key, value); info.put(key, value);
} }
} }
public Map<String, String> getInfo() { public Map<String, String> getInfo() {
if (info == null) { if (info == null) {
return emptyInfo; return emptyInfo;
} }
return info; return info;
} }
public Abilities<Ability> getAbilities() { public Abilities<Ability> getAbilities() {
if (abilities == null) { if (abilities == null) {
return emptyAbilities; return emptyAbilities;
} }
return abilities; return abilities;
} }
public void addAbility(Ability ability) { public void addAbility(Ability ability) {
if (abilities == null) { if (abilities == null) {
abilities = new AbilitiesImpl<>(); abilities = new AbilitiesImpl<>();
} }
abilities.add(ability); abilities.add(ability);
for (Ability sub: ability.getSubAbilities()) { for (Ability sub : ability.getSubAbilities()) {
abilities.add(sub); abilities.add(sub);
} }
} }
public void clearAbilities() { public void clearAbilities() {
if (abilities != null) { if (abilities != null) {
for (Ability ability: abilities) { // for (Ability ability: abilities) { // Causes problems if temporary (gained) continuous effects are removed
ability.setSourceId(null); // ability.setSourceId(null);
ability.setControllerId(null); // ability.setControllerId(null);
} // }
abilities = null; abilities = null;
} }
} }
public void clear() { public void clear() {
counters.clear(); counters.clear();
info = null; info = null;
clearAbilities(); clearAbilities();
} }
} }

View file

@ -364,14 +364,14 @@ public interface Game extends MageItem, Serializable {
* This version supports copying of copies of any depth. * This version supports copying of copies of any depth.
* *
* @param copyFromPermanent * @param copyFromPermanent
* @param copyToPermanent * @param copyToPermanentId
* @param source * @param source
* @param applier * @param applier
* @return * @return
*/ */
Permanent copyPermanent(Permanent copyFromPermanent, Permanent copyToPermanent, Ability source, ApplyToPermanent applier); Permanent copyPermanent(Permanent copyFromPermanent, UUID copyToPermanentId, Ability source, ApplyToPermanent applier);
Permanent copyPermanent(Duration duration, Permanent copyFromPermanent, Permanent copyToPermanent, Ability source, ApplyToPermanent applier); Permanent copyPermanent(Duration duration, Permanent copyFromPermanent, UUID copyToPermanentId, Ability source, ApplyToPermanent applier);
Card copyCard(Card cardToCopy, Ability source, UUID newController); Card copyCard(Card cardToCopy, Ability source, UUID newController);

View file

@ -49,6 +49,7 @@ import mage.MageObject;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.ActivatedAbility; import mage.abilities.ActivatedAbility;
import mage.abilities.DelayedTriggeredAbility; import mage.abilities.DelayedTriggeredAbility;
import mage.abilities.SpellAbility;
import mage.abilities.TriggeredAbility; import mage.abilities.TriggeredAbility;
import mage.abilities.common.ChancellorAbility; import mage.abilities.common.ChancellorAbility;
import mage.abilities.common.GemstoneCavernsAbility; import mage.abilities.common.GemstoneCavernsAbility;
@ -77,6 +78,7 @@ import mage.constants.Outcome;
import mage.constants.PhaseStep; import mage.constants.PhaseStep;
import mage.constants.PlayerAction; import mage.constants.PlayerAction;
import mage.constants.RangeOfInfluence; import mage.constants.RangeOfInfluence;
import mage.constants.SpellAbilityType;
import mage.constants.Zone; import mage.constants.Zone;
import mage.counters.CounterType; import mage.counters.CounterType;
import mage.counters.Counters; import mage.counters.Counters;
@ -1402,12 +1404,12 @@ public abstract class GameImpl implements Game, Serializable {
} }
@Override @Override
public Permanent copyPermanent(Permanent copyFromPermanent, Permanent copyToPermanent, Ability source, ApplyToPermanent applier) { public Permanent copyPermanent(Permanent copyFromPermanent, UUID copyToPermanentId, Ability source, ApplyToPermanent applier) {
return copyPermanent(Duration.Custom, copyFromPermanent, copyToPermanent, source, applier); return copyPermanent(Duration.Custom, copyFromPermanent, copyToPermanentId, source, applier);
} }
@Override @Override
public Permanent copyPermanent(Duration duration, Permanent copyFromPermanent, Permanent copyToPermanent, Ability source, ApplyToPermanent applier) { public Permanent copyPermanent(Duration duration, Permanent copyFromPermanent, UUID copyToPermanentId, Ability source, ApplyToPermanent applier) {
Permanent newBluePrint = null; Permanent newBluePrint = null;
// handle copies of copies // handle copies of copies
for (Effect effect : getState().getContinuousEffects().getLayeredEffects(this)) { for (Effect effect : getState().getContinuousEffects().getLayeredEffects(this)) {
@ -1440,7 +1442,7 @@ public abstract class GameImpl implements Game, Serializable {
applier.apply(this, newBluePrint); applier.apply(this, newBluePrint);
} }
CopyEffect newEffect = new CopyEffect(duration, newBluePrint, copyToPermanent.getId()); CopyEffect newEffect = new CopyEffect(duration, newBluePrint, copyToPermanentId);
newEffect.newId(); newEffect.newId();
newEffect.setApplier(applier); newEffect.setApplier(applier);
Ability newAbility = source.copy(); Ability newAbility = source.copy();
@ -1686,11 +1688,22 @@ public abstract class GameImpl implements Game, Serializable {
} }
} }
} else { } else {
SpellAbility spellAbility = perm.getSpellAbility();
if (perm.getSpellAbility().getTargets().isEmpty()) { if (perm.getSpellAbility().getTargets().isEmpty()) {
for (Ability ability : perm.getAbilities(this)) {
if ((ability instanceof SpellAbility)
&& SpellAbilityType.BASE_ALTERNATE.equals(((SpellAbility) ability).getSpellAbilityType())
&& !ability.getTargets().isEmpty()) {
spellAbility = (SpellAbility) ability;
break;
}
}
}
if (spellAbility.getTargets().isEmpty()) {
Permanent enchanted = this.getPermanent(perm.getAttachedTo()); Permanent enchanted = this.getPermanent(perm.getAttachedTo());
logger.error("Aura without target: " + perm.getName() + " attached to " + (enchanted == null ? " null" : enchanted.getName())); logger.error("Aura without target: " + perm.getName() + " attached to " + (enchanted == null ? " null" : enchanted.getName()));
} else { } else {
Target target = perm.getSpellAbility().getTargets().get(0); Target target = spellAbility.getTargets().get(0);
if (target instanceof TargetPermanent) { if (target instanceof TargetPermanent) {
Permanent attachedTo = getPermanent(perm.getAttachedTo()); Permanent attachedTo = getPermanent(perm.getAttachedTo());
if (attachedTo == null || !attachedTo.getAttachments().contains(perm.getId())) { if (attachedTo == null || !attachedTo.getAttachments().contains(perm.getId())) {
@ -1706,7 +1719,7 @@ public abstract class GameImpl implements Game, Serializable {
} }
} }
} else { } else {
Filter auraFilter = perm.getSpellAbility().getTargets().get(0).getFilter(); Filter auraFilter = spellAbility.getTargets().get(0).getFilter();
if (auraFilter instanceof FilterControlledCreaturePermanent) { if (auraFilter instanceof FilterControlledCreaturePermanent) {
if (!((FilterControlledCreaturePermanent) auraFilter).match(attachedTo, perm.getId(), perm.getControllerId(), this) if (!((FilterControlledCreaturePermanent) auraFilter).match(attachedTo, perm.getId(), perm.getControllerId(), this)
|| attachedTo.cantBeEnchantedBy(perm, this)) { || attachedTo.cantBeEnchantedBy(perm, this)) {
@ -1737,7 +1750,7 @@ public abstract class GameImpl implements Game, Serializable {
somethingHappened = true; somethingHappened = true;
} }
} else { } else {
Filter auraFilter = perm.getSpellAbility().getTargets().get(0).getFilter(); Filter auraFilter = spellAbility.getTargets().get(0).getFilter();
if (!auraFilter.match(attachedToPlayer, this) || attachedToPlayer.hasProtectionFrom(perm, this)) { if (!auraFilter.match(attachedToPlayer, this) || attachedToPlayer.hasProtectionFrom(perm, this)) {
if (movePermanentToGraveyardWithInfo(perm)) { if (movePermanentToGraveyardWithInfo(perm)) {
somethingHappened = true; somethingHappened = true;

View file

@ -624,7 +624,7 @@ public class GameState implements Serializable, Copyable<GameState> {
public Permanent getPermanent(UUID permanentId) { public Permanent getPermanent(UUID permanentId) {
if (permanentId != null && battlefield.containsPermanent(permanentId)) { if (permanentId != null && battlefield.containsPermanent(permanentId)) {
Permanent permanent = battlefield.getPermanent(permanentId); Permanent permanent = battlefield.getPermanent(permanentId);
setZone(permanent.getId(), Zone.BATTLEFIELD); // shouldn't this be set anyway? (LevelX2) // setZone(permanent.getId(), Zone.BATTLEFIELD); // shouldn't this be set anyway? (LevelX2)
return permanent; return permanent;
} }
return null; return null;

View file

@ -1,31 +1,30 @@
/* /*
* 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
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 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 * 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 * 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.game.permanent; package mage.game.permanent;
import java.util.ArrayList; import java.util.ArrayList;
@ -49,20 +48,21 @@ public class PermanentCard extends PermanentImpl {
protected int maxLevelCounters; protected int maxLevelCounters;
protected Card card; protected Card card;
protected int zoneChangeCounter; // protected int zoneChangeCounter;
public PermanentCard(Card card, UUID controllerId, Game game) { public PermanentCard(Card card, UUID controllerId, Game game) {
super(card.getId(), card.getOwnerId(), controllerId, card.getName()); super(card.getId(), card.getOwnerId(), controllerId, card.getName());
this.card = card.copy(); // this.card = card.copy();
this.card = card;
init(card, game); init(card, game);
} }
private void init(Card card, Game game) { private void init(Card card, Game game) {
copyFromCard(card); copyFromCard(card);
this.zoneChangeCounter = card.getZoneChangeCounter(game); // this.zoneChangeCounter = card.getZoneChangeCounter(game);
/*if (card.getCardType().contains(CardType.PLANESWALKER)) { /*if (card.getCardType().contains(CardType.PLANESWALKER)) {
this.loyalty = new MageInt(card.getLoyalty().getValue()); this.loyalty = new MageInt(card.getLoyalty().getValue());
}*/ }*/
if (card instanceof LevelerCard) { if (card instanceof LevelerCard) {
maxLevelCounters = ((LevelerCard) card).getMaxLevelCounters(); maxLevelCounters = ((LevelerCard) card).getMaxLevelCounters();
} }
@ -79,7 +79,6 @@ public class PermanentCard extends PermanentImpl {
super(permanent); super(permanent);
this.card = permanent.card.copy(); this.card = permanent.card.copy();
this.maxLevelCounters = permanent.maxLevelCounters; this.maxLevelCounters = permanent.maxLevelCounters;
this.zoneChangeCounter = permanent.zoneChangeCounter;
} }
@Override @Override
@ -94,13 +93,12 @@ public class PermanentCard extends PermanentImpl {
this.name = card.getName(); this.name = card.getName();
this.abilities.clear(); this.abilities.clear();
if (this.faceDown) { if (this.faceDown) {
for (Ability ability: card.getAbilities()) { for (Ability ability : card.getAbilities()) {
if (ability.getWorksFaceDown()) { if (ability.getWorksFaceDown()) {
this.abilities.add(ability.copy()); this.abilities.add(ability.copy());
} }
} }
} } else {
else {
this.abilities = card.getAbilities().copy(); this.abilities = card.getAbilities().copy();
} }
this.abilities.setControllerId(this.controllerId); this.abilities.setControllerId(this.controllerId);
@ -135,6 +133,7 @@ public class PermanentCard extends PermanentImpl {
public Card getCard() { public Card getCard() {
return card; return card;
} }
@Override @Override
public boolean moveToZone(Zone toZone, UUID sourceId, Game game, boolean flag) { public boolean moveToZone(Zone toZone, UUID sourceId, Game game, boolean flag) {
return moveToZone(toZone, sourceId, game, flag, null); return moveToZone(toZone, sourceId, game, flag, null);
@ -255,20 +254,20 @@ public class PermanentCard extends PermanentImpl {
} }
return super.getManaCost(); return super.getManaCost();
} }
@Override @Override
public int getZoneChangeCounter(Game game) { public int getZoneChangeCounter(Game game) {
return this.zoneChangeCounter; return card.getZoneChangeCounter(game);
} }
@Override @Override
public void updateZoneChangeCounter(Game game) { public void updateZoneChangeCounter(Game game) {
this.zoneChangeCounter++; card.updateZoneChangeCounter(game);
} }
@Override @Override
public void setZoneChangeCounter(int value, Game game) { public void setZoneChangeCounter(int value, Game game) {
this.zoneChangeCounter = value; card.setZoneChangeCounter(value, game);
} }
} }

View file

@ -221,14 +221,14 @@ public class Spell extends StackObjImpl implements Card {
} else if (this.getCardType().contains(CardType.ENCHANTMENT) && this.getSubtype().contains("Aura")) { } else if (this.getCardType().contains(CardType.ENCHANTMENT) && this.getSubtype().contains("Aura")) {
if (ability.getTargets().stillLegal(ability, game)) { if (ability.getTargets().stillLegal(ability, game)) {
updateOptionalCosts(0); updateOptionalCosts(0);
boolean bestow = this.getSpellAbility() instanceof BestowAbility; boolean bestow = ability instanceof BestowAbility;
if (bestow) { if (bestow) {
// Must be removed first time, after that will be removed by continous effect // Must be removed first time, after that will be removed by continous effect
// Otherwise effects like evolve trigger from creature comes into play event // Otherwise effects like evolve trigger from creature comes into play event
card.getCardType().remove(CardType.CREATURE); card.getCardType().remove(CardType.CREATURE);
card.getSubtype().add("Aura"); card.getSubtype().add("Aura");
} }
if (card.putOntoBattlefield(game, fromZone, ability.getSourceId(), controllerId)) { if (card.putOntoBattlefield(game, Zone.STACK, ability.getSourceId(), controllerId)) {
if (bestow) { if (bestow) {
// card will be copied during putOntoBattlefield, so the card of CardPermanent has to be changed // card will be copied during putOntoBattlefield, so the card of CardPermanent has to be changed
// TODO: Find a better way to prevent bestow creatures from being effected by creature affecting abilities // TODO: Find a better way to prevent bestow creatures from being effected by creature affecting abilities
@ -238,8 +238,6 @@ public class Spell extends StackObjImpl implements Card {
((PermanentCard) permanent).getCard().getCardType().add(CardType.CREATURE); ((PermanentCard) permanent).getCard().getCardType().add(CardType.CREATURE);
((PermanentCard) permanent).getCard().getSubtype().remove("Aura"); ((PermanentCard) permanent).getCard().getSubtype().remove("Aura");
} }
card.getCardType().add(CardType.CREATURE);
card.getSubtype().remove("Aura");
} }
return ability.resolve(game); return ability.resolve(game);
} }
@ -251,7 +249,7 @@ public class Spell extends StackObjImpl implements Card {
// Aura has no legal target and its a bestow enchantment -> Add it to battlefield as creature // Aura has no legal target and its a bestow enchantment -> Add it to battlefield as creature
if (this.getSpellAbility() instanceof BestowAbility) { if (this.getSpellAbility() instanceof BestowAbility) {
updateOptionalCosts(0); updateOptionalCosts(0);
result = card.putOntoBattlefield(game, fromZone, ability.getSourceId(), controllerId); result = card.putOntoBattlefield(game, Zone.STACK, ability.getSourceId(), controllerId);
return result; return result;
} else { } else {
//20091005 - 608.2b //20091005 - 608.2b
@ -263,7 +261,7 @@ public class Spell extends StackObjImpl implements Card {
} }
} else { } else {
updateOptionalCosts(0); updateOptionalCosts(0);
result = card.putOntoBattlefield(game, fromZone, ability.getSourceId(), controllerId, false, faceDown); result = card.putOntoBattlefield(game, Zone.STACK, ability.getSourceId(), controllerId, false, faceDown);
return result; return result;
} }
} }
@ -646,6 +644,11 @@ public class Spell extends StackObjImpl implements Card {
} }
} }
@Override
public boolean removeFromZone(Game game, Zone fromZone, UUID sourceId) {
return card.removeFromZone(game, fromZone, sourceId);
}
@Override @Override
public boolean moveToZone(Zone zone, UUID sourceId, Game game, boolean flag) { public boolean moveToZone(Zone zone, UUID sourceId, Game game, boolean flag) {
return moveToZone(zone, sourceId, game, flag, null); return moveToZone(zone, sourceId, game, flag, null);
@ -847,4 +850,9 @@ public class Spell extends StackObjImpl implements Card {
return countered; return countered;
} }
@Override
public void checkForCountersToAdd(Permanent permanent, Game game) {
throw new UnsupportedOperationException("Not supported for Spell");
}
} }

View file

@ -34,6 +34,7 @@ import mage.MageObject;
import mage.constants.Zone; import mage.constants.Zone;
import mage.game.Game; import mage.game.Game;
import mage.game.events.GameEvent; import mage.game.events.GameEvent;
import org.apache.log4j.Logger;
/** /**
* *
@ -41,6 +42,8 @@ import mage.game.events.GameEvent;
*/ */
public class SpellStack extends ArrayDeque<StackObject> { public class SpellStack extends ArrayDeque<StackObject> {
private static final Logger logger = Logger.getLogger(SpellStack.class);
protected Date dateLastAdded; protected Date dateLastAdded;
public SpellStack() { public SpellStack() {
@ -61,18 +64,21 @@ public class SpellStack extends ArrayDeque<StackObject> {
top.resolve(game); top.resolve(game);
} finally { } finally {
if (top != null) { if (top != null) {
this.remove(top); if (contains(top)) {
logger.warn("StackObject was still on the stack after resoving" + top.getName());
this.remove(top);
}
} }
} }
} }
public void remove(StackObject object) { public boolean remove(StackObject object) {
for (StackObject spell : this) { for (StackObject spell : this) {
if (spell.getId().equals(object.getId())) { if (spell.getId().equals(object.getId())) {
super.remove(spell); return super.remove(spell);
return;
} }
} }
return false;
} }
public boolean counter(UUID objectId, UUID sourceId, Game game) { public boolean counter(UUID objectId, UUID sourceId, Game game) {

View file

@ -104,12 +104,15 @@ public class StackAbility extends StackObjImpl implements Ability {
@Override @Override
public boolean resolve(Game game) { public boolean resolve(Game game) {
if (ability.getTargets().stillLegal(ability, game)) { if (ability.getTargets().stillLegal(ability, game)) {
return ability.resolve(game); boolean result = ability.resolve(game);
game.getStack().remove(this);
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);
return false; return false;
} }

View file

@ -635,6 +635,20 @@ public interface Player extends MageItem, Copyable<Player> {
boolean moveCards(Set<Card> cards, Zone fromZone, Zone toZone, Ability source, Game game); boolean moveCards(Set<Card> cards, Zone fromZone, Zone toZone, Ability source, Game game);
/**
*
* @param cards
* @param toZone
* @param source
* @param game
* @param tapped tha cards are tapped on the battlefield
* @param faceDown the cards are face down in the to zone
* @param byOwner the card is moved (or put onto battlefield) by the owner
* of the card (instead of the controller of the source)
* @return
*/
boolean moveCards(Set<Card> cards, Zone toZone, Ability source, Game game, boolean tapped, boolean faceDown, boolean byOwner, ArrayList<UUID> appliedEffects);
boolean moveCardsToExile(Card card, Ability source, Game game, boolean withName, UUID exileId, String exileZoneName); boolean moveCardsToExile(Card card, Ability source, Game game, boolean withName, UUID exileId, String exileZoneName);
boolean moveCardsToExile(Set<Card> cards, Ability source, Game game, boolean withName, UUID exileId, String exileZoneName); boolean moveCardsToExile(Set<Card> cards, Ability source, Game game, boolean withName, UUID exileId, String exileZoneName);

View file

@ -113,6 +113,7 @@ import mage.game.events.DamagePlayerEvent;
import mage.game.events.DamagedPlayerEvent; import mage.game.events.DamagedPlayerEvent;
import mage.game.events.GameEvent; import mage.game.events.GameEvent;
import mage.game.events.GameEvent.EventType; import mage.game.events.GameEvent.EventType;
import mage.game.events.ZoneChangeEvent;
import mage.game.events.ZoneChangeGroupEvent; import mage.game.events.ZoneChangeGroupEvent;
import mage.game.match.MatchPlayer; import mage.game.match.MatchPlayer;
import mage.game.permanent.Permanent; import mage.game.permanent.Permanent;
@ -671,8 +672,7 @@ public abstract class PlayerImpl implements Player, Serializable {
@Override @Override
public boolean removeFromHand(Card card, Game game) { public boolean removeFromHand(Card card, Game game) {
hand.remove(card); return hand.remove(card.getId());
return true;
} }
@Override @Override
@ -3000,13 +3000,13 @@ public abstract class PlayerImpl implements Player, Serializable {
case HAND: case HAND:
for (Card card : cards) { for (Card card : cards) {
fromZone = game.getState().getZone(card.getId()); fromZone = game.getState().getZone(card.getId());
if (fromZone == Zone.STACK) { // if (fromZone == Zone.STACK) {
// If a spell is returned to its owner's hand, it's removed from the stack and thus will not resolve // // If a spell is returned to its owner's hand, it's removed from the stack and thus will not resolve
Spell spell = game.getStack().getSpell(card.getId()); // Spell spell = game.getStack().getSpell(card.getId());
if (spell != null) { // if (spell != null) {
game.getStack().remove(spell); // game.getStack().remove(spell);
} // }
} // }
boolean hideCard = fromZone.equals(Zone.LIBRARY) boolean hideCard = fromZone.equals(Zone.LIBRARY)
|| (card.isFaceDown(game) && !fromZone.equals(Zone.STACK) && !fromZone.equals(Zone.BATTLEFIELD)); || (card.isFaceDown(game) && !fromZone.equals(Zone.STACK) && !fromZone.equals(Zone.BATTLEFIELD));
if (moveCardToHandWithInfo(card, source == null ? null : source.getSourceId(), game, !hideCard)) { if (moveCardToHandWithInfo(card, source == null ? null : source.getSourceId(), game, !hideCard)) {
@ -3038,6 +3038,69 @@ public abstract class PlayerImpl implements Player, Serializable {
return successfulMovedCards.size() > 0; return successfulMovedCards.size() > 0;
} }
@Override
public boolean moveCards(Set<Card> cards, Zone toZone, Ability source, Game game, boolean tapped, boolean faceDown, boolean byOwner, ArrayList<UUID> appliedEffects) {
if (cards.isEmpty()) {
return true;
}
Set<Card> successfulMovedCards = new LinkedHashSet<>();
Zone fromZone = null;
switch (toZone) {
case BATTLEFIELD:
List<Permanent> permanents = new ArrayList<>();
List<Permanent> permanentsEntered = new ArrayList<>();
for (Card card : cards) {
UUID controllingPlayerId = byOwner ? card.getOwnerId() : source.getControllerId();
fromZone = game.getState().getZone(card.getId());
if (faceDown) {
card.setFaceDown(true, game);
}
ZoneChangeEvent event = new ZoneChangeEvent(card.getId(), source.getSourceId(), controllingPlayerId, fromZone, Zone.BATTLEFIELD, appliedEffects, tapped);
if (!game.replaceEvent(event)) {
// get permanent
Permanent permanent = new PermanentCard(card, controllingPlayerId, game);
permanents.add(permanent);
card.checkForCountersToAdd(permanent, game);
permanent.setTapped(tapped);
permanent.setFaceDown(faceDown, game);
}
if (faceDown) {
card.setFaceDown(false, game);
}
}
game.setScopeRelevant(true);
for (Permanent permanent : permanents) {
fromZone = game.getState().getZone(permanent.getId());
if (permanent.entersBattlefield(source.getSourceId(), game, fromZone, true)) {
permanentsEntered.add(permanent);
}
}
game.setScopeRelevant(false);
game.applyEffects();
for (Permanent permanent : permanentsEntered) {
fromZone = game.getState().getZone(permanent.getId());
if (((Card) permanent).removeFromZone(game, fromZone, source.getSourceId())) {
permanent.updateZoneChangeCounter(game);
// make sure the controller of all continuous effects of this card are switched to the current controller
game.getContinuousEffects().setController(permanent.getId(), permanent.getControllerId());
game.addPermanent(permanent);
permanent.setZone(Zone.BATTLEFIELD, game);
// check if there are counters to add to the permanent (e.g. from non replacement effects like Persist)
game.setScopeRelevant(true);
successfulMovedCards.add(permanent);
game.addSimultaneousEvent(new ZoneChangeEvent(permanent, permanent.getControllerId(), fromZone, Zone.BATTLEFIELD));
}
}
break;
default:
throw new UnsupportedOperationException("to Zone not supported yet");
}
game.fireEvent(new ZoneChangeGroupEvent(successfulMovedCards, source == null ? null : source.getSourceId(), this.getId(), fromZone, toZone));
return successfulMovedCards.size() > 0;
}
@Override @Override
public boolean moveCardsToExile(Card card, Ability source, Game game, boolean withName, UUID exileId, String exileZoneName) { public boolean moveCardsToExile(Card card, Ability source, Game game, boolean withName, UUID exileId, String exileZoneName) {
Set<Card> cards = new HashSet<>(); Set<Card> cards = new HashSet<>();

View file

@ -483,23 +483,6 @@ public class CardUtil {
return getExileZoneId(getCardZoneString(SOURCE_EXILE_ZONE_TEXT, sourceId, game, previous), game); return getExileZoneId(getCardZoneString(SOURCE_EXILE_ZONE_TEXT, sourceId, game, previous), game);
} }
public static UUID getObjectExileZoneId(Game game, MageObject mageObject) {
return getObjectExileZoneId(game, mageObject, false);
}
public static UUID getObjectExileZoneId(Game game, MageObject mageObject, boolean previous) {
int zoneChangeCounter = 0;
if (mageObject instanceof Permanent) {
zoneChangeCounter = ((Permanent) mageObject).getZoneChangeCounter(game);
} else if (mageObject instanceof Card) {
zoneChangeCounter = ((Card) mageObject).getZoneChangeCounter(game);
}
if (zoneChangeCounter > 0 && previous) {
zoneChangeCounter--;
}
return getExileZoneId(getObjectZoneString(SOURCE_EXILE_ZONE_TEXT, mageObject.getId(), game, zoneChangeCounter, false), game);
}
public static UUID getExileZoneId(Game game, UUID objectId, int zoneChangeCounter) { public static UUID getExileZoneId(Game game, UUID objectId, int zoneChangeCounter) {
return getExileZoneId(getObjectZoneString(SOURCE_EXILE_ZONE_TEXT, objectId, game, zoneChangeCounter, false), game); return getExileZoneId(getObjectZoneString(SOURCE_EXILE_ZONE_TEXT, objectId, game, zoneChangeCounter, false), game);
} }