diff --git a/forge-game/src/main/java/forge/game/GameSnapshot.java b/forge-game/src/main/java/forge/game/GameSnapshot.java index 467a44f8f8f5..c19a95beee84 100644 --- a/forge-game/src/main/java/forge/game/GameSnapshot.java +++ b/forge-game/src/main/java/forge/game/GameSnapshot.java @@ -346,6 +346,10 @@ public void copyGameState(Game fromGame, Game toGame) { newAttachedTo.addAttachedCard(newCard); } } + // Melded or not, the front half has to point at what the snapshot had — the two + // halves are one permanent, and a stale link outlives the meld otherwise. + newCard.setMeldedWith(fromCard.getMeldedWith() == null ? null + : toGame.findById(fromCard.getMeldedWith().getId())); if (fromCard.getCloneOrigin() != null) { newCard.setCloneOrigin(toGame.findById(fromCard.getCloneOrigin().getId())); } @@ -377,7 +381,20 @@ private void setCardInCopiedGame(Game toGame, Player toPlayer, Card fromCard, Ca if (fromType.equals(ZoneType.Stack)) { toGame.getStackZone().add(newCard); newCard.setZone(toGame.getStackZone()); + } else if (isMelded(fromCard)) { + // The back half of a meld lives in the battlefield zone but in its own + // collection rather than the card list. Putting it in the list instead would + // leave it on the battlefield as a permanent of its own. + PlayerZoneBattlefield battlefield = (PlayerZoneBattlefield) toPlayer.getZone(ZoneType.Battlefield); + if (newCard.getZone() == null) { + newCard.setZone(battlefield); + } + battlefield.addToMelded(newCard); } else { + // It may have been melded in the state we are leaving behind. + if (toPlayer.getZone(ZoneType.Battlefield) instanceof PlayerZoneBattlefield battlefield) { + battlefield.removeFromMelded(newCard); + } toPlayer.getZone(fromType).add(newCard); newCard.setZone(toPlayer.getZone(fromType)); } @@ -391,9 +408,16 @@ private void setCardInCopiedGame(Game toGame, Player toPlayer, Card fromCard, Ca newCard.setSickness(fromCard.hasSickness()); //newCard.setForetold(fromCard.isForetold()); //newCard.setForetoldCostByEffect(fromCard.isForetoldCostByEffect()); + newCard.setBackSide(fromCard.isBackSide()); newCard.setState(fromCard.getCurrentStateName(), false); } + /** The back half of a meld: on the battlefield, but held apart from its card list. */ + private static boolean isMelded(Card c) { + return c.getZone() instanceof PlayerZoneBattlefield battlefield + && battlefield.getMeldedCards().contains(c); + } + private static SpellAbility findSAInCard(SpellAbility sa, Card c) { String saDesc = sa.getDescription(); for (SpellAbility cardSa : c.getAllSpellAbilities()) {