diff --git a/Mage/src/main/java/mage/abilities/effects/common/MeldEffect.java b/Mage/src/main/java/mage/abilities/effects/common/MeldEffect.java index eaabf2f5e90..60e54932be2 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/MeldEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/MeldEffect.java @@ -84,6 +84,13 @@ public class MeldEffect extends OneShotEffect { if (sourcePermanent == null || meldWithPermanent == null) { return false; } + + // melding in exile zone, rules: + // When two cards are exiled and melded, they each leave the battlefield, then return together as one + // new untapped object with no relation to either of the objects that left the battlefield. + // Counters, Auras, Equipment, and other effects that affected those two cards don't affect + // the melded permanent. + Cards cards = new CardsImpl(sourcePermanent); cards.add(meldWithPermanent); controller.moveCards(cards, Zone.EXILED, source, game); diff --git a/Mage/src/main/java/mage/cards/MeldCard.java b/Mage/src/main/java/mage/cards/MeldCard.java index 0dbb52e4d32..5eafe7b3d46 100644 --- a/Mage/src/main/java/mage/cards/MeldCard.java +++ b/Mage/src/main/java/mage/cards/MeldCard.java @@ -7,6 +7,7 @@ import mage.counters.Counter; import mage.game.Game; import mage.game.events.ZoneChangeEvent; import mage.game.permanent.Permanent; +import mage.util.CardUtil; import java.util.List; import java.util.UUID; @@ -30,8 +31,8 @@ public abstract class MeldCard extends CardImpl { protected MeldCard(final MeldCard card) { super(card); - this.topHalfCard = card.topHalfCard; - this.bottomHalfCard = card.bottomHalfCard; + this.topHalfCard = CardUtil.deepCopyObject(card.topHalfCard); + this.bottomHalfCard = CardUtil.deepCopyObject(card.bottomHalfCard); this.topLastZoneChangeCounter = card.topLastZoneChangeCounter; this.bottomLastZoneChangeCounter = card.bottomLastZoneChangeCounter; this.halves = new CardsImpl(card.halves); @@ -135,6 +136,7 @@ public abstract class MeldCard extends CardImpl { boolean value = topLastZoneChangeCounter == topHalfCard.getZoneChangeCounter(game) && halves.contains(topHalfCard.getId()); if (!value) { + // TODO: sync code with deleting halfs ref smells bad - looks like melds have problems with zcc sync (topLastZoneChangeCounter) halves.remove(topHalfCard); } return value; @@ -144,6 +146,7 @@ public abstract class MeldCard extends CardImpl { boolean value = bottomLastZoneChangeCounter == bottomHalfCard.getZoneChangeCounter(game) && halves.contains(bottomHalfCard.getId()); if (!value) { + // TODO: sync code with deleting halfs ref smells bad - looks like melds have problems with zcc sync (bottomLastZoneChangeCounter) halves.remove(bottomHalfCard); } return value; diff --git a/Mage/src/main/java/mage/game/ZoneChangeInfo.java b/Mage/src/main/java/mage/game/ZoneChangeInfo.java index d47f71b282a..93ac1769d84 100644 --- a/Mage/src/main/java/mage/game/ZoneChangeInfo.java +++ b/Mage/src/main/java/mage/game/ZoneChangeInfo.java @@ -16,6 +16,7 @@ public class ZoneChangeInfo { public boolean faceDown; public ZoneChangeEvent event; + List additionalMoves = new ArrayList<>(); // additions objects move (example: meld parts), TODO: must research, can be un-used by real code public ZoneChangeInfo(ZoneChangeEvent event) { this.event = event; @@ -27,9 +28,10 @@ public class ZoneChangeInfo { this.faceDown = faceDown; } - public ZoneChangeInfo(ZoneChangeInfo info) { + private ZoneChangeInfo(final ZoneChangeInfo info) { this.event = info.event; this.faceDown = info.faceDown; + this.additionalMoves = new ArrayList<>(info.additionalMoves); } public ZoneChangeInfo copy() { @@ -146,8 +148,6 @@ public class ZoneChangeInfo { public static class Unmelded extends ZoneChangeInfo { - List subInfo = new ArrayList<>(); - public Unmelded(ZoneChangeInfo info, Game game) { super(info.event); MeldCard meld = game.getMeldCard(info.event.getTargetId()); @@ -157,14 +157,14 @@ public class ZoneChangeInfo { event.getPlayerId(), event.getFromZone(), event.getToZone(), event.getAppliedEffects()); ZoneChangeInfo topInfo = info.copy(); topInfo.event = topEvent; - subInfo.add(topInfo); + additionalMoves.add(topInfo); } if (meld.hasBottomHalf(game)) { ZoneChangeEvent bottomEvent = new ZoneChangeEvent(meld.getBottomHalfCard().getId(), event.getSource(), event.getPlayerId(), event.getFromZone(), event.getToZone(), event.getAppliedEffects()); ZoneChangeInfo bottomInfo = info.copy(); bottomInfo.event = bottomEvent; - subInfo.add(bottomInfo); + additionalMoves.add(bottomInfo); } } } diff --git a/Mage/src/main/java/mage/game/ZonesHandler.java b/Mage/src/main/java/mage/game/ZonesHandler.java index f234e74a580..c4f428eeb57 100644 --- a/Mage/src/main/java/mage/game/ZonesHandler.java +++ b/Mage/src/main/java/mage/game/ZonesHandler.java @@ -58,14 +58,19 @@ public final class ZonesHandler { } public static List moveCards(List zoneChangeInfos, Ability source, Game game) { - // Handle Unmelded Meld Cards + // handle unmelded meld cards (if something moved a melded card to non-battlefield then parts must be moved too) for (ListIterator itr = zoneChangeInfos.listIterator(); itr.hasNext(); ) { ZoneChangeInfo info = itr.next(); + if (info.event.getToZone().equals(Zone.BATTLEFIELD)) { + continue; + } MeldCard card = game.getMeldCard(info.event.getTargetId()); // Copies should be handled as normal cards. if (card != null && !card.isMelded(game) && !card.isCopy()) { + // TODO: WTF, never worked code here. Need research possible typo: !card.isMelded(game) -> card.isMelded(game) ZoneChangeInfo.Unmelded unmelded = new ZoneChangeInfo.Unmelded(info, game); - if (unmelded.subInfo.isEmpty()) { + if (unmelded.additionalMoves.isEmpty()) { + // already moved halves somehow itr.remove(); } else { itr.set(unmelded); @@ -123,9 +128,9 @@ public final class ZonesHandler { 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, createOrder, source, game); + for (ZoneChangeInfo additionalMove : unmelded.additionalMoves) { + toZone = additionalMove.event.getToZone(); + placeInDestinationZone(additionalMove, createOrder, source, game); } // We arbitrarily prefer the bottom half card. This should never be relevant. if (toZone != null) { @@ -303,21 +308,21 @@ public final class ZonesHandler { if (info instanceof ZoneChangeInfo.Unmelded) { ZoneChangeInfo.Unmelded unmelded = (ZoneChangeInfo.Unmelded) info; MeldCard meld = game.getMeldCard(event.getTargetId()); - for (Iterator itr = unmelded.subInfo.iterator(); itr.hasNext(); ) { - ZoneChangeInfo subInfo = itr.next(); - if (!maybeRemoveFromSourceZone(subInfo, game, source)) { + for (Iterator itr = unmelded.additionalMoves.iterator(); itr.hasNext(); ) { + ZoneChangeInfo additionalMove = itr.next(); + if (!maybeRemoveFromSourceZone(additionalMove, game, source)) { itr.remove(); - } else if (Objects.equals(subInfo.event.getTargetId(), meld.getTopHalfCard().getId())) { + } else if (Objects.equals(additionalMove.event.getTargetId(), meld.getTopHalfCard().getId())) { meld.setTopLastZoneChangeCounter(meld.getTopHalfCard().getZoneChangeCounter(game)); - } else if (Objects.equals(subInfo.event.getTargetId(), meld.getBottomHalfCard().getId())) { + } else if (Objects.equals(additionalMove.event.getTargetId(), meld.getBottomHalfCard().getId())) { meld.setBottomLastZoneChangeCounter(meld.getBottomHalfCard().getZoneChangeCounter(game)); } } - if (unmelded.subInfo.isEmpty()) { + if (unmelded.additionalMoves.isEmpty()) { return false; } // We arbitrarily prefer the bottom half card. This should never be relevant. - meld.updateZoneChangeCounter(game, unmelded.subInfo.get(unmelded.subInfo.size() - 1).event); + meld.updateZoneChangeCounter(game, unmelded.additionalMoves.get(unmelded.additionalMoves.size() - 1).event); return true; } diff --git a/Mage/src/main/java/mage/game/events/ZoneChangeEvent.java b/Mage/src/main/java/mage/game/events/ZoneChangeEvent.java index f0895c7d6c0..0ed685d8150 100644 --- a/Mage/src/main/java/mage/game/events/ZoneChangeEvent.java +++ b/Mage/src/main/java/mage/game/events/ZoneChangeEvent.java @@ -14,8 +14,8 @@ import mage.game.permanent.Permanent; public class ZoneChangeEvent extends GameEvent { private Zone fromZone; - private Zone toZone; - private Zone originalToZone; + private Zone toZone; // real toZone after apply some replacements effects + private Zone originalToZone; // original toZone before any replacement effects private Permanent target; private Ability source; // link to source ability, can be null in rare situations