Merge pull request #2240 from Dilnu/master

Refactor the core zone change code to use a common code path
This commit is contained in:
LevelX2 2016-09-09 14:47:08 +02:00 committed by GitHub
commit 3e278e2be0
18 changed files with 558 additions and 596 deletions

View file

@ -125,8 +125,11 @@ public class SystemUtil {
* @param card Card to put to player's hand
*/
private static void swapWithAnyCard(Game game, Player player, Card card, Zone zone) {
// Put the card in Exile to start. Otherwise the game doesn't know where to remove the card from.
game.getExile().getPermanentExile().add(card);
game.setZone(card.getId(), Zone.EXILED);
if (zone.equals(Zone.BATTLEFIELD)) {
card.putOntoBattlefield(game, Zone.OUTSIDE, null, player.getId());
card.putOntoBattlefield(game, Zone.EXILED, null, player.getId());
} else if (zone.equals(Zone.LIBRARY)) {
card.setZone(Zone.LIBRARY, game);
player.getLibrary().putOnTop(card, game);

View file

@ -49,6 +49,7 @@ import mage.constants.Zone;
import mage.filter.FilterCard;
import mage.game.ExileZone;
import mage.game.Game;
import mage.game.command.Commander;
import mage.game.events.GameEvent;
import mage.game.events.GameEvent.EventType;
import mage.game.permanent.Permanent;
@ -140,7 +141,8 @@ class KarnLiberatedEffect extends OneShotEffect {
&& !player.getSideboard().contains(card.getId())
&& !cards.contains(card)) { // not the exiled cards
if (card.getId().equals(player.getCommanderId())) {
card.moveToZone(Zone.COMMAND, null, game, true);
game.addCommander(new Commander(card));
game.setZone(card.getId(), Zone.COMMAND);
} else {
player.getLibrary().putOnTop(card, game);
}

View file

@ -77,8 +77,8 @@ public class CounterbalanceTest extends CardTestPlayerBase {
}
/**
* Test that if the top cardis a split card, both casting costs of the split cards
* count to counter the spell. If one of the split cards halfes has the equal casting
* Test that if the top card is a split card, both casting costs of the split cards
* count to counter the spell. If one of the split cards halves has the equal casting
* cost, the spell is countered.
*
*/

View file

@ -86,6 +86,7 @@ public class WorldgorgerDragonTest extends CardTestPlayerBase {
addCard(Zone.BATTLEFIELD, playerA, "Staunch Defenders", 1);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Animate Dead", "Worldgorger Dragon");
setChoice(playerA, "When {this} enters the battlefield, if it's");
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}");
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}");

View file

@ -1238,8 +1238,8 @@ public class TestPlayer implements Player {
}
@Override
public boolean putInGraveyard(Card card, Game game, boolean fromBattlefield) {
return computerPlayer.putInGraveyard(card, game, fromBattlefield);
public boolean putInGraveyard(Card card, Game game) {
return computerPlayer.putInGraveyard(card, game);
}
@Override

View file

@ -593,7 +593,7 @@ public class PlayerStub implements Player {
}
@Override
public boolean putInGraveyard(Card card, Game game, boolean fromBattlefield) {
public boolean putInGraveyard(Card card, Game game) {
return false;
}

View file

@ -121,7 +121,7 @@ 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 cause continuous effects
Player controllingPlayer = null;
if (targetId == null) {
SpellAbility spellAbility = card.getSpellAbility();

View file

@ -101,10 +101,11 @@ public class MeldEffect extends OneShotEffect {
if (!sourceCard.isCopy() && !meldWithCard.isCopy()) {
meldCard.setOwnerId(controller.getId());
meldCard.setTopHalfCard(meldWithCard, game);
meldCard.setbottomHalfCard(sourceCard, game);
meldCard.setBottomHalfCard(sourceCard, game);
meldCard.setMelded(true);
game.addMeldCard(meldCard.getId(), meldCard);
game.getState().addCard(meldCard);
meldCard.setZone(Zone.EXILED, game);
meldCard.moveToZone(Zone.BATTLEFIELD, sourceId, game, false);
}
return true;

View file

@ -28,10 +28,8 @@
package mage.cards;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import java.util.*;
import mage.MageObject;
import mage.MageObjectImpl;
import mage.Mana;
@ -50,10 +48,7 @@ import mage.constants.TimingRule;
import mage.constants.Zone;
import mage.counters.Counter;
import mage.counters.Counters;
import mage.game.CardAttribute;
import mage.game.CardState;
import mage.game.Game;
import mage.game.GameState;
import mage.game.*;
import mage.game.command.Commander;
import mage.game.events.GameEvent;
import mage.game.events.ZoneChangeEvent;
@ -61,6 +56,7 @@ import mage.game.permanent.Permanent;
import mage.game.permanent.PermanentCard;
import mage.game.stack.Spell;
import mage.game.stack.StackObject;
import mage.players.Player;
import mage.util.GameLog;
import mage.watchers.Watcher;
import org.apache.log4j.Logger;
@ -345,79 +341,24 @@ public abstract class CardImpl extends MageObjectImpl implements Card {
public boolean moveToZone(Zone toZone, UUID sourceId, Game game, boolean flag, ArrayList<UUID> appliedEffects) {
Zone fromZone = game.getState().getZone(objectId);
ZoneChangeEvent event = new ZoneChangeEvent(this.objectId, sourceId, ownerId, fromZone, toZone, appliedEffects);
if (!game.replaceEvent(event)) {
removeFromZone(game, fromZone, sourceId);
setFaceDown(false, game);
updateZoneChangeCounter(game);
switch (event.getToZone()) {
case GRAVEYARD:
game.getPlayer(ownerId).putInGraveyard(this, game, !flag);
break;
case HAND:
game.getPlayer(ownerId).getHand().add(this);
break;
case STACK:
game.getStack().push(new Spell(this, this.getSpellAbility().copy(), ownerId, event.getFromZone()));
break;
case EXILED:
game.getExile().getPermanentExile().add(this);
break;
case COMMAND:
game.addCommander(new Commander(this));
break;
case LIBRARY:
if (flag) {
game.getPlayer(ownerId).getLibrary().putOnTop(this, game);
} else {
game.getPlayer(ownerId).getLibrary().putOnBottom(this, game);
}
break;
case BATTLEFIELD:
PermanentCard permanent = new PermanentCard(this, event.getPlayerId(), game); // controller can be replaced (e.g. Gather Specimens)
game.addPermanent(permanent);
game.setZone(objectId, Zone.BATTLEFIELD);
game.setScopeRelevant(true);
game.applyEffects();
boolean entered = permanent.entersBattlefield(sourceId, game, event.getFromZone(), true);
game.setScopeRelevant(false);
game.applyEffects();
if (entered) {
if (flag) {
permanent.setTapped(true);
}
event.setTarget(permanent);
} else {
return false;
}
break;
default:
Card sourceCard = game.getCard(sourceId);
logger.fatal(new StringBuilder("Invalid to zone [").append(toZone)
.append("] for card [").append(this.getName())
.append("] to zone [").append(toZone)
.append("] source [").append(sourceCard != null ? sourceCard.getName() : "null").append("]").toString());
return false;
}
game.setZone(objectId, event.getToZone());
game.addSimultaneousEvent(event);
return game.getState().getZone(objectId) == toZone;
ZoneChangeInfo zoneChangeInfo;
if (toZone == Zone.LIBRARY) {
zoneChangeInfo = new ZoneChangeInfo.Library(event, flag /* put on top */);
} else if (toZone == Zone.BATTLEFIELD) {
zoneChangeInfo = new ZoneChangeInfo.Battlefield(event, flag /* comes into play tapped */);
} else {
zoneChangeInfo = new ZoneChangeInfo(event);
}
return false;
return ZonesHandler.moveCard(zoneChangeInfo, game);
}
@Override
public boolean cast(Game game, Zone fromZone, SpellAbility ability, UUID controllerId) {
Card mainCard = getMainCard();
ZoneChangeEvent event = new ZoneChangeEvent(mainCard.getId(), ability.getId(), controllerId, fromZone, Zone.STACK);
if (!game.replaceEvent(event)) {
mainCard.removeFromZone(game, fromZone, ability.getSourceId());
game.getStack().push(new Spell(this, ability.copy(), controllerId, event.getFromZone()));
updateZoneChangeCounter(game);
setZone(event.getToZone(), game);
game.fireEvent(event);
return game.getState().getZone(mainCard.getId()) == Zone.STACK;
}
return false;
ZoneChangeInfo.Stack info =
new ZoneChangeInfo.Stack(event, new Spell(this, ability.copy(), controllerId, event.getFromZone()));
return ZonesHandler.cast(info, game);
}
@Override
@ -429,20 +370,8 @@ public abstract class CardImpl extends MageObjectImpl implements Card {
public boolean moveToExile(UUID exileId, String name, UUID sourceId, Game game, ArrayList<UUID> appliedEffects) {
Zone fromZone = game.getState().getZone(objectId);
ZoneChangeEvent event = new ZoneChangeEvent(this.objectId, sourceId, ownerId, fromZone, Zone.EXILED, appliedEffects);
if (!game.replaceEvent(event)) {
removeFromZone(game, fromZone, sourceId);
if (exileId == null) {
game.getExile().getPermanentExile().add(this);
} else {
game.getExile().createZone(exileId, name).add(this);
}
setFaceDown(false, game);
updateZoneChangeCounter(game);
game.setZone(objectId, event.getToZone());
game.addSimultaneousEvent(event);
return true;
}
return false;
ZoneChangeInfo.Exile info = new ZoneChangeInfo.Exile(event, exileId, name);
return ZonesHandler.moveCard(info, game);
}
@Override
@ -456,44 +385,15 @@ public abstract class CardImpl extends MageObjectImpl implements Card {
}
@Override
public boolean putOntoBattlefield(Game game, Zone fromZone, UUID sourceId, UUID controllerId, boolean tapped, boolean facedown) {
return this.putOntoBattlefield(game, fromZone, sourceId, controllerId, tapped, facedown, null);
public boolean putOntoBattlefield(Game game, Zone fromZone, UUID sourceId, UUID controllerId, boolean tapped, boolean faceDown) {
return this.putOntoBattlefield(game, fromZone, sourceId, controllerId, tapped, faceDown, null);
}
@Override
public boolean putOntoBattlefield(Game game, Zone fromZone, UUID sourceId, UUID controllerId, boolean tapped, boolean facedown, ArrayList<UUID> appliedEffects) {
ZoneChangeEvent event = new ZoneChangeEvent(this.objectId, sourceId, controllerId, fromZone, Zone.BATTLEFIELD, appliedEffects, tapped);
if (facedown) {
this.setFaceDown(true, game);
}
if (!game.replaceEvent(event)) {
if (facedown) {
this.setFaceDown(false, game);
}
removeFromZone(game, fromZone, sourceId);
updateZoneChangeCounter(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
game.getContinuousEffects().setController(objectId, event.getPlayerId());
game.addPermanent(permanent);
setZone(Zone.BATTLEFIELD, game);
// check if there are counters to add to the permanent (e.g. from non replacement effects like Persist)
checkForCountersToAdd(permanent, game);
game.setScopeRelevant(true);
permanent.setTapped(tapped);
permanent.setFaceDown(facedown, game);
boolean entered = permanent.entersBattlefield(sourceId, game, event.getFromZone(), true);
game.setScopeRelevant(false);
game.applyEffects();
if (entered) {
game.addSimultaneousEvent(new ZoneChangeEvent(permanent, event.getPlayerId(), fromZone, Zone.BATTLEFIELD));
return true;
}
}
if (facedown) {
this.setFaceDown(false, game);
}
return false;
public boolean putOntoBattlefield(Game game, Zone fromZone, UUID sourceId, UUID controllerId, boolean tapped, boolean faceDown, ArrayList<UUID> appliedEffects) {
ZoneChangeEvent event = new ZoneChangeEvent(this.objectId, sourceId, controllerId, fromZone, Zone.BATTLEFIELD, appliedEffects);
ZoneChangeInfo.Battlefield info = new ZoneChangeInfo.Battlefield(event, faceDown, tapped);
return ZonesHandler.moveCard(info, game);
}
@Override

View file

@ -32,13 +32,10 @@ import java.util.UUID;
import mage.constants.CardType;
import mage.constants.Rarity;
import mage.constants.Zone;
import static mage.constants.Zone.EXILED;
import static mage.constants.Zone.GRAVEYARD;
import static mage.constants.Zone.HAND;
import static mage.constants.Zone.LIBRARY;
import mage.counters.Counter;
import mage.game.Game;
import mage.game.events.ZoneChangeEvent;
import mage.game.permanent.Permanent;
import mage.game.permanent.PermanentMeld;
import mage.players.Player;
@ -53,9 +50,11 @@ public abstract class MeldCard extends CardImpl {
protected int topLastZoneChangeCounter;
protected int bottomLastZoneChangeCounter;
protected boolean isMelded;
protected Cards halves;
public MeldCard(UUID ownerId, int cardNumber, String name, Rarity rarity, CardType[] cardTypes, String costs) {
super(ownerId, cardNumber, name, rarity, cardTypes, costs);
halves = new CardsImpl();
}
public MeldCard(MeldCard card) {
@ -64,6 +63,7 @@ public abstract class MeldCard extends CardImpl {
this.bottomHalfCard = card.bottomHalfCard;
this.topLastZoneChangeCounter = card.topLastZoneChangeCounter;
this.bottomLastZoneChangeCounter = card.bottomLastZoneChangeCounter;
this.halves = new CardsImpl(halves);
this.isMelded = card.isMelded;
}
@ -82,6 +82,7 @@ public abstract class MeldCard extends CardImpl {
public void setTopHalfCard(Card topHalfCard, Game game) {
this.topHalfCard = topHalfCard;
this.topLastZoneChangeCounter = topHalfCard.getZoneChangeCounter(game);
halves.add(topHalfCard.getId());
}
public int getTopLastZoneChangeCounter() {
@ -96,9 +97,10 @@ public abstract class MeldCard extends CardImpl {
return bottomHalfCard;
}
public void setbottomHalfCard(Card bottomHalfCard, Game game) {
public void setBottomHalfCard(Card bottomHalfCard, Game game) {
this.bottomHalfCard = bottomHalfCard;
this.bottomLastZoneChangeCounter = bottomHalfCard.getZoneChangeCounter(game);
halves.add(bottomHalfCard.getId());
}
public int getBottomLastZoneChangeCounter() {
@ -116,204 +118,6 @@ public abstract class MeldCard extends CardImpl {
bottomHalfCard.assignNewId();
}
@Override
public void setCopy(boolean isCopy) {
super.setCopy(isCopy);
topHalfCard.setCopy(isCopy);
bottomHalfCard.setCopy(isCopy);
}
@Override
public boolean moveToZone(Zone toZone, UUID sourceId, Game game, boolean flag, ArrayList<UUID> appliedEffects) {
if (this.isMelded()) {
// Initial move to battlefield
if (toZone == Zone.BATTLEFIELD) {
return this.putOntoBattlefield(game, Zone.EXILED, sourceId, this.getOwnerId(), false, false, appliedEffects);
} // Move when melded from the battlefield to elsewhere
else {
ZoneChangeEvent event = new ZoneChangeEvent(this.getId(), sourceId, this.getOwnerId(), Zone.BATTLEFIELD, toZone, appliedEffects);
if (!game.replaceEvent(event)) {
updateZoneChangeCounter(game);
switch (event.getToZone()) {
case GRAVEYARD:
game.getPlayer(this.getOwnerId()).putInGraveyard(topHalfCard, game, true);
game.getPlayer(this.getOwnerId()).putInGraveyard(bottomHalfCard, game, true);
break;
case HAND:
game.getPlayer(this.getOwnerId()).getHand().add(topHalfCard);
game.getPlayer(this.getOwnerId()).getHand().add(bottomHalfCard);
break;
case EXILED:
game.getExile().getPermanentExile().add(topHalfCard);
game.getExile().getPermanentExile().add(bottomHalfCard);
break;
case LIBRARY:
Player controller = game.getPlayer(this.getOwnerId());
if (controller != null) {
CardsImpl cardsToMove = new CardsImpl();
cardsToMove.add(topHalfCard);
cardsToMove.add(bottomHalfCard);
if (flag) {
controller.putCardsOnTopOfLibrary(cardsToMove, game, null, true);
} else {
controller.putCardsOnBottomOfLibrary(cardsToMove, game, null, true);
}
}
break;
default:
return false;
}
this.setMelded(false);
game.setZone(topHalfCard.getId(), event.getToZone());
game.setZone(bottomHalfCard.getId(), event.getToZone());
this.topLastZoneChangeCounter = topHalfCard.getZoneChangeCounter(game);
this.bottomLastZoneChangeCounter = bottomHalfCard.getZoneChangeCounter(game);
game.addSimultaneousEvent(event);
return true;
} else {
return false;
}
}
} else {
// Try to move the former meld cards after it has already left the battlefield.
// If the meld parts didn't move from that zone, move them instead of the meld card.
// Reset the local zcc so the meld card lose track of them.
boolean returnValue = false;
if (topLastZoneChangeCounter == topHalfCard.getZoneChangeCounter(game)) {
topHalfCard.moveToZone(toZone, sourceId, game, flag, appliedEffects);
topLastZoneChangeCounter = topHalfCard.getZoneChangeCounter(game);
returnValue = true;
}
if (bottomLastZoneChangeCounter == bottomHalfCard.getZoneChangeCounter(game)) {
bottomHalfCard.moveToZone(toZone, sourceId, game, flag, appliedEffects);
bottomLastZoneChangeCounter = bottomHalfCard.getZoneChangeCounter(game);
returnValue = true;
}
return returnValue;
}
}
@Override
public boolean moveToExile(UUID exileId, String name, UUID sourceId, Game game, ArrayList<UUID> appliedEffects) {
if (this.isMelded()) {
// Move when melded from the battlefield to exile
ZoneChangeEvent event = new ZoneChangeEvent(this.getId(), sourceId, this.getOwnerId(), Zone.BATTLEFIELD, Zone.EXILED, appliedEffects);
if (!game.replaceEvent(event)) {
updateZoneChangeCounter(game);
switch (event.getToZone()) {
case GRAVEYARD:
game.getPlayer(this.getOwnerId()).putInGraveyard(topHalfCard, game, true);
game.getPlayer(this.getOwnerId()).putInGraveyard(bottomHalfCard, game, true);
break;
case HAND:
game.getPlayer(this.getOwnerId()).getHand().add(topHalfCard);
game.getPlayer(this.getOwnerId()).getHand().add(bottomHalfCard);
break;
case EXILED:
if (exileId == null) {
game.getExile().getPermanentExile().add(topHalfCard);
game.getExile().getPermanentExile().add(bottomHalfCard);
} else {
game.getExile().createZone(exileId, name).add(topHalfCard);
game.getExile().getExileZone(exileId).add(bottomHalfCard);
}
break;
case LIBRARY:
Player controller = game.getPlayer(this.getOwnerId());
if (controller != null) {
CardsImpl cardsToMove = new CardsImpl();
cardsToMove.add(topHalfCard);
cardsToMove.add(bottomHalfCard);
if (event.getFlag()) {
controller.putCardsOnTopOfLibrary(cardsToMove, game, null, true);
} else {
controller.putCardsOnBottomOfLibrary(cardsToMove, game, null, true);
}
}
break;
default:
return false;
}
this.setMelded(false);
game.setZone(topHalfCard.getId(), event.getToZone());
game.setZone(bottomHalfCard.getId(), event.getToZone());
this.topLastZoneChangeCounter = topHalfCard.getZoneChangeCounter(game);
this.bottomLastZoneChangeCounter = bottomHalfCard.getZoneChangeCounter(game);
game.addSimultaneousEvent(event);
return true;
} else {
return false;
}
} else {
// Try to move the former meld cards after it has already left the battlefield.
// If the meld parts didn't move from that zone, move them instead of the meld card.
// Reset the local zcc so the meld card lose track of them.
boolean returnValue = false;
if (topLastZoneChangeCounter == topHalfCard.getZoneChangeCounter(game)) {
topHalfCard.moveToExile(exileId, name, sourceId, game, appliedEffects);
topLastZoneChangeCounter = topHalfCard.getZoneChangeCounter(game);
returnValue = true;
}
if (bottomLastZoneChangeCounter == bottomHalfCard.getZoneChangeCounter(game)) {
bottomHalfCard.moveToExile(exileId, name, sourceId, game, appliedEffects);
bottomLastZoneChangeCounter = bottomHalfCard.getZoneChangeCounter(game);
returnValue = true;
}
return returnValue;
}
}
@Override
public boolean putOntoBattlefield(Game game, Zone fromZone, UUID sourceId, UUID controllerId, boolean tapped, boolean facedown, ArrayList<UUID> appliedEffects) {
// Initial move to battlefield
if (this.isMelded()) {
ZoneChangeEvent event = new ZoneChangeEvent(this.objectId, sourceId, controllerId, Zone.EXILED, Zone.BATTLEFIELD, appliedEffects);
if (!game.replaceEvent(event) && event.getToZone() == Zone.BATTLEFIELD) {
updateZoneChangeCounter(game);
PermanentMeld permanent = new PermanentMeld(this, event.getPlayerId(), game); // controller can be replaced (e.g. Gather Specimens)
game.addPermanent(permanent);
game.setZone(objectId, Zone.BATTLEFIELD);
game.setScopeRelevant(true);
game.applyEffects();
boolean entered = permanent.entersBattlefield(sourceId, game, event.getFromZone(), true);
game.setScopeRelevant(false);
game.applyEffects();
if (entered) {
if (event.getFlag()) {
permanent.setTapped(true);
}
event.setTarget(permanent);
} else {
return false;
}
game.setZone(objectId, event.getToZone());
game.addSimultaneousEvent(event);
game.getExile().removeCard(this.topHalfCard, game);
game.getExile().removeCard(this.bottomHalfCard, game);
return true;
} else {
this.setMelded(false);
return false;
}
} else {
// Try to move the former meld cards after it has already left the battlefield.
// If the meld parts didn't move from that zone, move them instead of the meld card.
// Reset the local zcc so the meld card lose track of them.
boolean returnValue = false;
if (topLastZoneChangeCounter == topHalfCard.getZoneChangeCounter(game)) {
topHalfCard.moveToZone(Zone.BATTLEFIELD, sourceId, game, tapped, appliedEffects);
topLastZoneChangeCounter = topHalfCard.getZoneChangeCounter(game);
returnValue = true;
}
if (bottomLastZoneChangeCounter == bottomHalfCard.getZoneChangeCounter(game)) {
bottomHalfCard.moveToZone(Zone.BATTLEFIELD, sourceId, game, tapped, appliedEffects);
bottomLastZoneChangeCounter = bottomHalfCard.getZoneChangeCounter(game);
returnValue = true;
}
return returnValue;
}
}
@Override
public void setOwnerId(UUID ownerId) {
super.setOwnerId(ownerId);
@ -337,13 +141,76 @@ public abstract class MeldCard extends CardImpl {
} else {
// can this really happen?
boolean returnState = true;
if (topLastZoneChangeCounter == topHalfCard.getZoneChangeCounter(game)) {
if (hasTopHalf(game)) {
returnState |= topHalfCard.addCounters(counter, game, appliedEffects);
}
if (bottomLastZoneChangeCounter == bottomHalfCard.getZoneChangeCounter(game)) {
if (hasBottomHalf(game)) {
returnState |= bottomHalfCard.addCounters(counter, game, appliedEffects);
}
return returnState;
}
}
public boolean hasTopHalf(Game game) {
boolean value = topLastZoneChangeCounter == topHalfCard.getZoneChangeCounter(game)
&& halves.contains(topHalfCard.getId());
if (!value) {
halves.remove(topHalfCard);
}
return value;
}
public boolean hasBottomHalf(Game game) {
boolean value = bottomLastZoneChangeCounter == bottomHalfCard.getZoneChangeCounter(game)
&& halves.contains(bottomHalfCard.getId());
if (!value) {
halves.remove(bottomHalfCard);
}
return value;
}
@Override
public boolean removeFromZone(Game game, Zone fromZone, UUID sourceId) {
if (isCopy()) {
return super.removeFromZone(game, fromZone, sourceId);
}
if (isMelded() && fromZone == Zone.BATTLEFIELD) {
Permanent permanent = game.getPermanent(objectId);
return permanent != null && permanent.removeFromZone(game, fromZone, sourceId);
}
boolean topRemoved = hasTopHalf(game) && topHalfCard.removeFromZone(game, fromZone, sourceId);
if (!topRemoved) {
// The top half isn't being moved with the pair anymore.
halves.remove(topHalfCard);
}
boolean bottomRemoved = hasBottomHalf(game) && bottomHalfCard.removeFromZone(game, fromZone, sourceId);
if (!bottomRemoved) {
// The bottom half isn't being moved with the pair anymore.
halves.remove(bottomHalfCard);
}
return topRemoved || bottomRemoved;
}
@Override
public void updateZoneChangeCounter(Game game) {
if (isCopy() || !isMelded()) {
super.updateZoneChangeCounter(game);
return;
}
game.getState().updateZoneChangeCounter(objectId);
if (topLastZoneChangeCounter == topHalfCard.getZoneChangeCounter(game)
&& halves.contains(topHalfCard.getId())) {
topHalfCard.updateZoneChangeCounter(game);
topLastZoneChangeCounter = topHalfCard.getZoneChangeCounter(game);
}
if (bottomLastZoneChangeCounter == bottomHalfCard.getZoneChangeCounter(game)
&& halves.contains(bottomHalfCard.getId())) {
bottomHalfCard.updateZoneChangeCounter(game);
bottomLastZoneChangeCounter = bottomHalfCard.getZoneChangeCounter(game);
}
}
public Cards getHalves() {
return halves;
}
}

View file

@ -44,7 +44,7 @@ public enum Zone {
if (this.equals(EXILED)) {
return "exile zone";
}
return super.toString(); //To change body of generated methods, choose Tools | Templates.
return super.toString();
}
}

View file

@ -0,0 +1,161 @@
package mage.game;
import mage.cards.MeldCard;
import mage.constants.Zone;
import mage.game.events.ZoneChangeEvent;
import mage.game.stack.Spell;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
/**
* Created by Dilnu on 9/4/16.
*/
public class ZoneChangeInfo {
public boolean faceDown;
public ZoneChangeEvent event;
public ZoneChangeInfo(ZoneChangeEvent event) {
this.event = event;
this.faceDown = false;
}
public ZoneChangeInfo(ZoneChangeEvent event, boolean faceDown) {
this(event);
this.faceDown = faceDown;
}
public ZoneChangeInfo(ZoneChangeInfo info) {
this.event = info.event;
this.faceDown = info.faceDown;
}
public ZoneChangeInfo copy() {
return new ZoneChangeInfo(this);
}
public static class Library extends ZoneChangeInfo {
public boolean top;
public Library(ZoneChangeEvent event, boolean top) {
super(event);
this.top = top;
}
public Library(ZoneChangeEvent event, boolean faceDown, boolean top) {
super(event, faceDown);
this.top = top;
}
public Library(Library info) {
super(info);
this.top = info.top;
}
@Override
public ZoneChangeInfo copy() {
return new Library(this);
}
}
public static class Exile extends ZoneChangeInfo {
public UUID id;
public String name;
public Exile(ZoneChangeEvent event, UUID id, String name) {
super(event);
this.id = id;
this.name = name;
}
public Exile(ZoneChangeEvent event, boolean faceDown, UUID id, String name) {
super(event, faceDown);
this.id = id;
this.name = name;
}
public Exile(Exile info) {
super(info);
this.id = info.id;
this.name = info.name;
}
@Override
public ZoneChangeInfo copy() {
return new Exile(this);
}
}
public static class Battlefield extends ZoneChangeInfo {
public boolean tapped;
public Battlefield (ZoneChangeEvent event, boolean tapped) {
super(event);
this.tapped = tapped;
}
public Battlefield (ZoneChangeEvent event, boolean faceDown, boolean tapped) {
super(event, faceDown);
this.tapped = tapped;
}
public Battlefield(Battlefield info) {
super(info);
this.tapped = info.tapped;
}
@Override
public ZoneChangeInfo copy() {
return new Battlefield(this);
}
}
public static class Stack extends ZoneChangeInfo {
public Spell spell;
public Stack(ZoneChangeEvent event, Spell spell) {
super(event);
this.spell = spell;
}
public Stack(ZoneChangeEvent event, boolean faceDown, Spell spell) {
super(event, faceDown);
this.spell = spell;
}
public Stack(Stack info) {
super(info);
this.spell = info.spell;
}
@Override
public ZoneChangeInfo copy() {
return new Stack(this);
}
}
public static class Unmelded extends ZoneChangeInfo {
List<ZoneChangeInfo> subInfo = new ArrayList<>();
public Unmelded(ZoneChangeInfo info, Game game) {
super(info.event);
MeldCard meld = game.getMeldCard(info.event.getTargetId());
if (meld != null) {
if (meld.hasTopHalf(game)) {
ZoneChangeEvent topEvent = new ZoneChangeEvent(meld.getTopHalfCard().getId(), event.getSourceId(),
event.getPlayerId(), event.getFromZone(), event.getToZone(), event.getAppliedEffects());
ZoneChangeInfo topInfo = info.copy();
topInfo.event = topEvent;
subInfo.add(topInfo);
}
if (meld.hasBottomHalf(game)) {
ZoneChangeEvent bottomEvent = new ZoneChangeEvent(meld.getBottomHalfCard().getId(), event.getSourceId(),
event.getPlayerId(), event.getFromZone(), event.getToZone(), event.getAppliedEffects());
ZoneChangeInfo bottomInfo = info.copy();
bottomInfo.event = bottomEvent;
subInfo.add(bottomInfo);
}
}
}
}
}

View file

@ -0,0 +1,260 @@
package mage.game;
import mage.cards.Card;
import mage.cards.Cards;
import mage.cards.CardsImpl;
import mage.cards.MeldCard;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.filter.FilterCard;
import mage.game.command.Commander;
import mage.game.events.ZoneChangeEvent;
import mage.game.permanent.Permanent;
import mage.game.permanent.PermanentCard;
import mage.game.permanent.PermanentMeld;
import mage.game.stack.Spell;
import mage.players.Player;
import mage.target.TargetCard;
import java.util.*;
/**
* Created by samuelsandeen on 9/6/16.
*/
public class ZonesHandler {
public static boolean cast(ZoneChangeInfo info, Game game) {
if (maybeRemoveFromSourceZone(info, game)) {
placeInDestinationZone(info, game);
game.fireEvent(info.event);
return true;
}
return false;
}
public static boolean moveCard(ZoneChangeInfo info, Game game) {
List<ZoneChangeInfo> list = new ArrayList<ZoneChangeInfo>();
list.add(info);
return moveCards(list, game).size() > 0;
}
public static List<ZoneChangeInfo> moveCards(List<ZoneChangeInfo> zoneChangeInfos, Game game) {
// Handle Unmelded Meld Cards
for(ListIterator<ZoneChangeInfo> itr = zoneChangeInfos.listIterator(); itr.hasNext();) {
ZoneChangeInfo info = itr.next();
MeldCard card = game.getMeldCard(info.event.getTargetId());
// Copies should be handled as normal cards.
if (card != null && !card.isMelded() && !card.isCopy()) {
ZoneChangeInfo.Unmelded unmelded = new ZoneChangeInfo.Unmelded(info, game);
if (unmelded.subInfo.isEmpty()) {
itr.remove();
} else {
itr.set(unmelded);
}
}
}
for (Iterator<ZoneChangeInfo> itr = zoneChangeInfos.iterator(); itr.hasNext();) {
if (!maybeRemoveFromSourceZone(itr.next(), game)) {
itr.remove();
}
}
for (ZoneChangeInfo zoneChangeInfo : zoneChangeInfos) {
placeInDestinationZone(zoneChangeInfo, game);
game.addSimultaneousEvent(zoneChangeInfo.event);
}
return zoneChangeInfos;
}
private static void placeInDestinationZone(ZoneChangeInfo info, Game game) {
// Handle unmelded cards
if (info instanceof ZoneChangeInfo.Unmelded) {
ZoneChangeInfo.Unmelded unmelded = (ZoneChangeInfo.Unmelded) info;
Zone toZone = null;
for (ZoneChangeInfo subInfo : unmelded.subInfo) {
toZone = subInfo.event.getToZone();
placeInDestinationZone(subInfo, game);
}
if (toZone != null) {
game.setZone(unmelded.event.getTargetId(), toZone);
}
return;
}
// Handle normal cases
ZoneChangeEvent event = info.event;
Zone toZone = event.getToZone();
Card targetCard = game.getCard(event.getTargetId());
Cards cards;
if (targetCard instanceof MeldCard) {
cards = ((MeldCard) targetCard).getHalves();
} else {
cards = new CardsImpl(targetCard);
}
Player owner = game.getPlayer(targetCard.getOwnerId());
switch (toZone) {
case HAND:
for (Card card : cards.getCards(game)) {
game.getPlayer(card.getOwnerId()).getHand().add(card);
}
break;
case GRAVEYARD:
for (Card card : chooseOrder(
"order to put in graveyard (last chosen will be on top)", cards, owner, game)) {
game.getPlayer(card.getOwnerId()).getGraveyard().add(card);
}
break;
case LIBRARY:
if (info instanceof ZoneChangeInfo.Library && ((ZoneChangeInfo.Library) info).top) {
for (Card card : chooseOrder(
"order to put on top of library (last chosen will be topmost)", cards, owner, game)) {
game.getPlayer(card.getOwnerId()).getLibrary().putOnTop(card, game);
}
} else {
for (Card card : chooseOrder(
"order to put on bottom of library (last chosen will be bottommost)", cards, owner, game)) {
game.getPlayer(card.getOwnerId()).getLibrary().putOnBottom(card, game);
}
}
break;
case EXILED:
for (Card card : cards.getCards(game)) {
if (info instanceof ZoneChangeInfo.Exile && ((ZoneChangeInfo.Exile) info).id != null) {
ZoneChangeInfo.Exile exileInfo = (ZoneChangeInfo.Exile) info;
game.getExile().createZone(exileInfo.id, exileInfo.name).add(card);
} else {
game.getExile().getPermanentExile().add(card);
}
}
break;
case COMMAND:
// There should never be more than one card here.
for (Card card : cards.getCards(game)) {
game.addCommander(new Commander(card));
}
break;
case STACK:
// There should never be more than one card here.
for (Card card : cards.getCards(game)) {
if (info instanceof ZoneChangeInfo.Stack && ((ZoneChangeInfo.Stack) info).spell != null) {
game.getStack().push(((ZoneChangeInfo.Stack) info).spell);
} else {
game.getStack().push(
new Spell(card, card.getSpellAbility().copy(), card.getOwnerId(), event.getFromZone()));
}
}
break;
case BATTLEFIELD:
Permanent permanent = event.getTarget();
game.addPermanent(permanent);
game.getPermanentsEntering().remove(permanent.getId());
break;
default:
throw new UnsupportedOperationException("to Zone" + toZone.toString() + " not supported yet");
}
game.setZone(event.getTargetId(), event.getToZone());
if (targetCard instanceof MeldCard) {
if (event.getToZone() != Zone.BATTLEFIELD) {
((MeldCard) targetCard).setMelded(false);
}
for (Card card : cards.getCards(game)) {
game.setZone(card.getId(), event.getToZone());
}
}
}
private static boolean maybeRemoveFromSourceZone(ZoneChangeInfo info, Game game) {
// Handle Unmelded Cards
if (info instanceof ZoneChangeInfo.Unmelded) {
ZoneChangeInfo.Unmelded unmelded = (ZoneChangeInfo.Unmelded) info;
MeldCard meld = game.getMeldCard(info.event.getTargetId());
for (Iterator<ZoneChangeInfo> itr = unmelded.subInfo.iterator(); itr.hasNext();) {
ZoneChangeInfo subInfo = itr.next();
if (!maybeRemoveFromSourceZone(subInfo, game)) {
itr.remove();
} else {
if (subInfo.event.getTargetId() == meld.getTopHalfCard().getId()) {
meld.setTopLastZoneChangeCounter(meld.getTopHalfCard().getZoneChangeCounter(game));
} else if (subInfo.event.getTargetId() == meld.getBottomHalfCard().getId()) {
meld.setBottomLastZoneChangeCounter(meld.getBottomHalfCard().getZoneChangeCounter(game));
}
}
}
if (unmelded.subInfo.isEmpty()) {
return false;
}
meld.updateZoneChangeCounter(game);
return true;
}
// Handle all normal cases
ZoneChangeEvent event = info.event;
Card card = game.getCard(event.getTargetId());
boolean success = false;
if (info.faceDown) {
card.setFaceDown(true, game);
}
if(!game.replaceEvent(event)) {
Zone fromZone = event.getFromZone();
if (event.getToZone() == Zone.BATTLEFIELD) {
// controlling player can be replaced so use event player now
Permanent permanent;
if (card instanceof MeldCard) {
permanent = new PermanentMeld(card, event.getPlayerId(), game);
} else {
permanent = new PermanentCard(card, event.getPlayerId(), game);
}
game.getPermanentsEntering().put(permanent.getId(), permanent);
card.checkForCountersToAdd(permanent, game);
permanent.setTapped(
info instanceof ZoneChangeInfo.Battlefield && ((ZoneChangeInfo.Battlefield) info).tapped);
permanent.setFaceDown(info.faceDown, game);
if (info.faceDown) {
card.setFaceDown(false, game);
}
// make sure the controller of all continuous effects of this card are switched to the current controller
game.setScopeRelevant(true);
game.getContinuousEffects().setController(permanent.getId(), permanent.getControllerId());
if (permanent.entersBattlefield(event.getSourceId(), game, fromZone, true)
&& card.removeFromZone(game, fromZone, event.getSourceId())) {
success = true;
event.setTarget(permanent);
} else {
// revert controller to owner if permanent does not enter
game.getContinuousEffects().setController(permanent.getId(), permanent.getOwnerId());
game.getPermanentsEntering().remove(permanent.getId());
}
game.setScopeRelevant(false);
} else if (event.getTarget() != null) {
card.setFaceDown(info.faceDown, game);
Permanent target = event.getTarget();
success = game.getPlayer(target.getControllerId()).removeFromBattlefield(target, game)
&& target.removeFromZone(game, fromZone, event.getSourceId());
} else {
card.setFaceDown(info.faceDown, game);
success = card.removeFromZone(game, fromZone, event.getSourceId());
}
}
if (success) {
if (event.getToZone() == Zone.BATTLEFIELD && event.getTarget() != null) {
event.getTarget().updateZoneChangeCounter(game);
} else {
card.updateZoneChangeCounter(game);
}
}
return success;
}
public static List<Card> chooseOrder(String message, Cards cards, Player player, Game game) {
List<Card> order = new ArrayList<Card>();
TargetCard target = new TargetCard(Zone.ALL, new FilterCard(message));
target.setRequired(true);
while (player.isInGame() && cards.size() > 1) {
player.choose(Outcome.Neutral, cards, target, game);
UUID targetObjectId = target.getFirstTarget();
order.add(cards.get(targetObjectId, game));
cards.remove(targetObjectId);
target.clearChosen();
}
order.add(cards.getCards(game).iterator().next());
return order;
}
}

View file

@ -67,17 +67,12 @@ public class ZoneChangeEvent extends GameEvent {
}
public ZoneChangeEvent(UUID targetId, UUID sourceId, UUID playerId, Zone fromZone, Zone toZone, ArrayList<UUID> appliedEffects) {
this(targetId, sourceId, playerId, fromZone, toZone, appliedEffects, false);
}
public ZoneChangeEvent(UUID targetId, UUID sourceId, UUID playerId, Zone fromZone, Zone toZone, ArrayList<UUID> appliedEffects, boolean comesIntoPlayTapped) {
super(EventType.ZONE_CHANGE, targetId, sourceId, playerId);
this.fromZone = fromZone;
this.toZone = toZone;
if (appliedEffects != null) {
this.appliedEffects = appliedEffects;
}
this.flag = comesIntoPlayTapped;
}
public ZoneChangeEvent(Permanent target, UUID playerId, Zone fromZone, Zone toZone) {
@ -111,9 +106,4 @@ public class ZoneChangeEvent extends GameEvent {
public boolean isDiesEvent() {
return (toZone == Zone.GRAVEYARD && fromZone == Zone.BATTLEFIELD);
}
public boolean comesIntoPlayTapped() {
return this.flag;
}
}

View file

@ -38,7 +38,8 @@ import mage.cards.Card;
import mage.cards.LevelerCard;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.command.Commander;
import mage.game.ZoneChangeInfo;
import mage.game.ZonesHandler;
import mage.game.events.ZoneChangeEvent;
import mage.players.Player;
@ -146,82 +147,29 @@ public class PermanentCard extends PermanentImpl {
return card;
}
@Override
public boolean moveToZone(Zone toZone, UUID sourceId, Game game, boolean flag) {
return moveToZone(toZone, sourceId, game, flag, null);
}
@Override
public boolean moveToZone(Zone toZone, UUID sourceId, Game game, boolean flag, ArrayList<UUID> appliedEffects) {
Zone fromZone = game.getState().getZone(objectId);
Player controller = game.getPlayer(controllerId);
if (controller != null) {
ZoneChangeEvent event = new ZoneChangeEvent(this, sourceId, controllerId, fromZone, toZone, appliedEffects);
if (!game.replaceEvent(event)) {
controller.removeFromBattlefield(this, game);
Player owner = game.getPlayer(ownerId);
game.rememberLKI(objectId, Zone.BATTLEFIELD, this);
if (owner != null) {
card.updateZoneChangeCounter(game);
switch (event.getToZone()) {
case GRAVEYARD:
owner.putInGraveyard(card, game, !flag);
break;
case HAND:
owner.getHand().add(card);
break;
case EXILED:
game.getExile().getPermanentExile().add(card);
break;
case COMMAND:
game.addCommander(new Commander(card));
break;
case LIBRARY:
if (flag) {
owner.getLibrary().putOnTop(card, game);
} else {
owner.getLibrary().putOnBottom(card, game);
}
break;
case BATTLEFIELD:
//should never happen
break;
}
game.setZone(objectId, event.getToZone());
game.addSimultaneousEvent(event);
return game.getState().getZone(objectId) == toZone;
}
ZoneChangeInfo zoneChangeInfo;
if (toZone == Zone.LIBRARY) {
zoneChangeInfo = new ZoneChangeInfo.Library(event, flag /* put on top */);
} else {
zoneChangeInfo = new ZoneChangeInfo(event);
}
return ZonesHandler.moveCard(zoneChangeInfo, game);
}
return false;
}
@Override
public boolean moveToExile(UUID exileId, String name, UUID sourceId, Game game) {
return moveToExile(exileId, name, sourceId, game, null);
}
@Override
public boolean moveToExile(UUID exileId, String name, UUID sourceId, Game game, ArrayList<UUID> appliedEffects) {
Zone fromZone = game.getState().getZone(objectId);
Player controller = game.getPlayer(controllerId);
if (controller != null && controller.removeFromBattlefield(this, game)) {
ZoneChangeEvent event = new ZoneChangeEvent(this, sourceId, ownerId, fromZone, Zone.EXILED, appliedEffects);
if (!game.replaceEvent(event)) {
game.rememberLKI(objectId, Zone.BATTLEFIELD, this);
// update zone change counter of original card
card.updateZoneChangeCounter(game);
if (exileId == null) {
game.getExile().getPermanentExile().add(card);
} else {
game.getExile().createZone(exileId, name).add(card);
}
game.setZone(objectId, event.getToZone());
game.addSimultaneousEvent(event);
return true;
}
}
return false;
ZoneChangeEvent event = new ZoneChangeEvent(this, sourceId, ownerId, fromZone, Zone.EXILED, appliedEffects);
ZoneChangeInfo.Exile info = new ZoneChangeInfo.Exile(event, exileId, name);
return ZonesHandler.moveCard(info, game);
}
@Override

View file

@ -55,108 +55,4 @@ public class PermanentMeld extends PermanentCard {
return this.getCard().getConvertedManaCost();
}
}
@Override
public boolean moveToZone(Zone toZone, UUID sourceId, Game game, boolean flag, ArrayList<UUID> appliedEffects) {
ZoneChangeEvent event = new ZoneChangeEvent(this.getId(), sourceId, this.getOwnerId(), Zone.BATTLEFIELD, toZone, appliedEffects);
if (!game.replaceEvent(event)) {
Player controller = game.getPlayer(this.getControllerId());
if (controller != null) {
controller.removeFromBattlefield(this, game);
updateZoneChangeCounter(game);
MeldCard meldCard = (MeldCard) this.getCard();
Card topHalfCard = meldCard.getTopHalfCard();
Card bottomHalfCard = meldCard.getBottomHalfCard();
switch (event.getToZone()) {
case GRAVEYARD:
game.getPlayer(this.getOwnerId()).putInGraveyard(topHalfCard, game, true);
game.getPlayer(this.getOwnerId()).putInGraveyard(bottomHalfCard, game, true);
break;
case HAND:
game.getPlayer(this.getOwnerId()).getHand().add(topHalfCard);
game.getPlayer(this.getOwnerId()).getHand().add(bottomHalfCard);
break;
case EXILED:
game.getExile().getPermanentExile().add(topHalfCard);
game.getExile().getPermanentExile().add(bottomHalfCard);
break;
case LIBRARY:
CardsImpl cardsToMove = new CardsImpl();
cardsToMove.add(topHalfCard);
cardsToMove.add(bottomHalfCard);
if (flag) {
controller.putCardsOnTopOfLibrary(cardsToMove, game, null, true);
} else {
controller.putCardsOnBottomOfLibrary(cardsToMove, game, null, true);
}
break;
default:
return false;
}
meldCard.setMelded(false);
game.setZone(topHalfCard.getId(), event.getToZone());
game.setZone(bottomHalfCard.getId(), event.getToZone());
meldCard.setTopLastZoneChangeCounter(topHalfCard.getZoneChangeCounter(game));
meldCard.setBottomLastZoneChangeCounter(bottomHalfCard.getZoneChangeCounter(game));
game.addSimultaneousEvent(event);
return true;
}
}
return false;
}
@Override
public boolean moveToExile(UUID exileId, String name, UUID sourceId, Game game, ArrayList<UUID> appliedEffects) {
ZoneChangeEvent event = new ZoneChangeEvent(this.getId(), sourceId, this.getOwnerId(), Zone.BATTLEFIELD, Zone.EXILED, appliedEffects);
if (!game.replaceEvent(event)) {
Player controller = game.getPlayer(this.getControllerId());
if (controller != null) {
controller.removeFromBattlefield(this, game);
updateZoneChangeCounter(game);
MeldCard meldCard = (MeldCard) this.getCard();
Card topHalfCard = meldCard.getTopHalfCard();
Card bottomHalfCard = meldCard.getBottomHalfCard();
switch (event.getToZone()) {
case GRAVEYARD:
game.getPlayer(this.getOwnerId()).putInGraveyard(topHalfCard, game, true);
game.getPlayer(this.getOwnerId()).putInGraveyard(bottomHalfCard, game, true);
break;
case HAND:
game.getPlayer(this.getOwnerId()).getHand().add(topHalfCard);
game.getPlayer(this.getOwnerId()).getHand().add(bottomHalfCard);
break;
case EXILED:
if (exileId == null) {
game.getExile().getPermanentExile().add(topHalfCard);
game.getExile().getPermanentExile().add(bottomHalfCard);
} else {
game.getExile().createZone(exileId, name).add(topHalfCard);
game.getExile().getExileZone(exileId).add(bottomHalfCard);
}
break;
case LIBRARY:
CardsImpl cardsToMove = new CardsImpl();
cardsToMove.add(topHalfCard);
cardsToMove.add(bottomHalfCard);
if (event.getFlag()) {
controller.putCardsOnTopOfLibrary(cardsToMove, game, null, true);
} else {
controller.putCardsOnBottomOfLibrary(cardsToMove, game, null, true);
}
break;
default:
return false;
}
meldCard.setMelded(false);
game.setZone(meldCard.getId(), Zone.OUTSIDE);
game.setZone(topHalfCard.getId(), event.getToZone());
game.setZone(bottomHalfCard.getId(), event.getToZone());
meldCard.setTopLastZoneChangeCounter(topHalfCard.getZoneChangeCounter(game));
meldCard.setBottomLastZoneChangeCounter(bottomHalfCard.getZoneChangeCounter(game));
game.addSimultaneousEvent(event);
return true;
}
}
return false;
}
}

View file

@ -353,7 +353,7 @@ public interface Player extends MageItem, Copyable<Player> {
boolean removeFromBattlefield(Permanent permanent, Game game);
boolean putInGraveyard(Card card, Game game, boolean fromBattlefield);
boolean putInGraveyard(Card card, Game game);
boolean removeFromGraveyard(Card card, Game game);

View file

@ -107,10 +107,7 @@ import mage.filter.common.FilterCreatureForCombat;
import mage.filter.common.FilterCreatureForCombatBlock;
import mage.filter.predicate.Predicates;
import mage.filter.predicate.permanent.PermanentIdPredicate;
import mage.game.ExileZone;
import mage.game.Game;
import mage.game.Graveyard;
import mage.game.Table;
import mage.game.*;
import mage.game.combat.CombatGroup;
import mage.game.command.CommandObject;
import mage.game.events.DamagePlayerEvent;
@ -853,11 +850,11 @@ public abstract class PlayerImpl implements Player, Serializable {
}
@Override
public boolean putInGraveyard(Card card, Game game, boolean fromBattlefield) {
public boolean putInGraveyard(Card card, Game game) {
if (card.getOwnerId().equals(playerId)) {
this.graveyard.add(card);
} else {
return game.getPlayer(card.getOwnerId()).putInGraveyard(card, game, fromBattlefield);
return game.getPlayer(card.getOwnerId()).putInGraveyard(card, game);
}
return true;
}
@ -2769,7 +2766,6 @@ public abstract class PlayerImpl implements Player, Serializable {
* Used to mark the playable cards in GameView
*
* @return A Set of cardIds that are playable
* @see mage.server.GameSessionPlayer#getGameView()
*
* @param game
*
@ -3153,87 +3149,24 @@ public abstract class PlayerImpl implements Player, Serializable {
successfulMovedCards = moveCardsToGraveyardWithInfo(cards, source, game, fromZone);
return successfulMovedCards.size() > 0;
case BATTLEFIELD: // new logic that does not yet add the permanents to battlefield while replacement effects are handled
List<Permanent> permanents = new ArrayList<>();
List<Permanent> permanentsEntered = new ArrayList<>();
// Move meld pieces instead of the meld card if unmelded
Set<Card> meldPiecesToAdd = new HashSet<>(0);
Set<MeldCard> meldCardsRemoved = new HashSet<>(0);
for (Iterator<Card> it = cards.iterator(); it.hasNext();) {
Card card = it.next();
if (card instanceof MeldCard && !((MeldCard) card).isMelded()) {
MeldCard meldCard = (MeldCard) card;
if (meldCard.getTopLastZoneChangeCounter() == meldCard.getTopHalfCard().getZoneChangeCounter(game)) {
meldPiecesToAdd.add(meldCard.getTopHalfCard());
}
if (meldCard.getBottomLastZoneChangeCounter() == meldCard.getBottomHalfCard().getZoneChangeCounter(game)) {
meldPiecesToAdd.add(meldCard.getBottomHalfCard());
}
meldCardsRemoved.add(meldCard);
it.remove();
}
}
cards.addAll(meldPiecesToAdd);
List<ZoneChangeInfo> infoList = new ArrayList<ZoneChangeInfo>();
for (Card card : cards) {
UUID controllingPlayerId = byOwner ? card.getOwnerId() : getId();
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;
if (card instanceof MeldCard) {
permanent = new PermanentMeld(card, event.getPlayerId(), game);// controlling player can be replaced so use event player now
} else {
permanent = new PermanentCard(card, event.getPlayerId(), game);// controlling player can be replaced so use event player now
}
permanents.add(permanent);
game.getPermanentsEntering().put(permanent.getId(), permanent);
card.checkForCountersToAdd(permanent, game);
permanent.setTapped(tapped);
permanent.setFaceDown(faceDown, game);
}
if (faceDown) {
card.setFaceDown(false, game);
}
ZoneChangeEvent event = new ZoneChangeEvent(card.getId(), source.getSourceId(), byOwner ? card.getOwnerId() : getId(), fromZone, Zone.BATTLEFIELD, appliedEffects);
infoList.add(new ZoneChangeInfo.Battlefield(event, faceDown, tapped));
}
game.setScopeRelevant(true);
for (Permanent permanent : permanents) {
fromZone = game.getState().getZone(permanent.getId());
// make sure the controller of all continuous effects of this card are switched to the current controller
game.getContinuousEffects().setController(permanent.getId(), permanent.getControllerId());
if (permanent.entersBattlefield(source.getSourceId(), game, fromZone, true)) {
permanentsEntered.add(permanent);
} else {
// revert controller to owner if permanent does not enter
game.getContinuousEffects().setController(permanent.getId(), permanent.getOwnerId());
game.getPermanentsEntering().remove(permanent.getId());
}
}
game.setScopeRelevant(false);
for (Permanent permanent : permanentsEntered) {
fromZone = game.getState().getZone(permanent.getId());
if (((Card) permanent).removeFromZone(game, fromZone, source.getSourceId())) {
permanent.updateZoneChangeCounter(game);
game.addPermanent(permanent);
permanent.setZone(Zone.BATTLEFIELD, game);
game.getPermanentsEntering().remove(permanent.getId());
infoList = ZonesHandler.moveCards(infoList, game);
for (ZoneChangeInfo info : infoList) {
Permanent permanent = game.getPermanent(info.event.getTargetId());
if (permanent != null) {
successfulMovedCards.add(permanent);
game.addSimultaneousEvent(new ZoneChangeEvent(permanent, permanent.getControllerId(), fromZone, Zone.BATTLEFIELD));
if (!game.isSimulation()) {
game.informPlayers(this.getLogName() + " puts " + (faceDown ? "a card face down " : permanent.getLogName())
+ " from " + fromZone.toString().toLowerCase(Locale.ENGLISH) + " onto the Battlefield");
game.informPlayers(game.getPlayer(info.event.getPlayerId()) + " puts " +
(info.faceDown ? "a card face down " : permanent.getLogName()) + " from " +
fromZone.toString().toLowerCase(Locale.ENGLISH) + " onto the Battlefield");
}
} else {
game.getPermanentsEntering().remove(permanent.getId());
}
}
// Update the lastZoneChangeCounter of meld pieces that were moved
for (MeldCard meldCard : meldCardsRemoved) {
meldCard.setTopLastZoneChangeCounter(meldCard.getTopHalfCard().getZoneChangeCounter(game));
meldCard.setBottomLastZoneChangeCounter(meldCard.getBottomHalfCard().getZoneChangeCounter(game));
}
game.applyEffects();
break;
case HAND: