mirror of
https://github.com/correl/mage.git
synced 2025-01-13 19:11:33 +00:00
Merge pull request #2240 from Dilnu/master
Refactor the core zone change code to use a common code path
This commit is contained in:
commit
3e278e2be0
18 changed files with 558 additions and 596 deletions
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
*/
|
||||
|
|
|
@ -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}");
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
161
Mage/src/main/java/mage/game/ZoneChangeInfo.java
Normal file
161
Mage/src/main/java/mage/game/ZoneChangeInfo.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
260
Mage/src/main/java/mage/game/ZonesHandler.java
Normal file
260
Mage/src/main/java/mage/game/ZonesHandler.java
Normal 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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
Loading…
Reference in a new issue