From ba31b956dc30a90475923a9172500781c29ee0ec Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sat, 18 Jul 2020 16:33:28 +0200 Subject: [PATCH] * Meld Keyword - Fixed that the melt status was not correctly handled related to rollbacks or AI game simulation (fixes #6723). --- .../cards/abilities/keywords/MeldTest.java | 43 ++++++++++++++++--- .../abilities/effects/common/MeldEffect.java | 3 +- Mage/src/main/java/mage/cards/MeldCard.java | 21 ++++----- Mage/src/main/java/mage/game/CardState.java | 10 +++++ .../src/main/java/mage/game/ZonesHandler.java | 4 +- 5 files changed, 60 insertions(+), 21 deletions(-) diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/MeldTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/MeldTest.java index cc47c0511f..d449ddd501 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/MeldTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/MeldTest.java @@ -1,4 +1,3 @@ - package org.mage.test.cards.abilities.keywords; import mage.constants.PhaseStep; @@ -158,12 +157,10 @@ public class MeldTest extends CardTestPlayerBase { // {T}: Add {C}. // {R},{T}: Target creature gains haste until end of turn. - // {3}{R}{R},{T}: If you both own and control Hanweir Battlements and a creature named Hanweir Garrison, exile them, then meld them into Hanweir, the Writhing Township. + // {3}{R}{R},{T}: If you both own and control Hanweir Battlements and a creature named Hanweir Garrison, exile them, + // then meld them into Hanweir, the Writhing Township. addCard(Zone.BATTLEFIELD, playerA, "Hanweir Battlements"); // Land - // Brisela, Voice of Nightmares 9/10 - // Flying, First strike, Vigilance, Lifelink - // Your opponents can't cast spells with converted mana cost 3 or less. addCard(Zone.BATTLEFIELD, playerB, "Island", 1); // Return target creature to its owner's hand. addCard(Zone.HAND, playerB, "Unsummon", 1); // Instant {U} @@ -180,4 +177,40 @@ public class MeldTest extends CardTestPlayerBase { assertHandCount(playerA, "Hanweir Garrison", 1); } + + @Test + public void testUnmeldAfterRollback() { + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 5); + + // Whenever Hanweir Garrison attacks, put two 1/1 red Human creature tokens onto the battlefield tapped and attacking. + // (Melds with Hanweir Battlements.) + addCard(Zone.BATTLEFIELD, playerA, "Hanweir Garrison"); // Creature 2/3 {2}{R} + + // {T}: Add {C}. + // {R},{T}: Target creature gains haste until end of turn. + // {3}{R}{R},{T}: If you both own and control Hanweir Battlements and a creature named Hanweir Garrison, exile them, + // then meld them into Hanweir, the Writhing Township. + addCard(Zone.BATTLEFIELD, playerA, "Hanweir Battlements"); // Land + + addCard(Zone.BATTLEFIELD, playerB, "Island", 1); + // Return target creature to its owner's hand. + addCard(Zone.HAND, playerB, "Unsummon", 1); // Instant {U} + + activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{3}{R}{R}"); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Unsummon", "Hanweir, the Writhing Township"); + + rollbackTurns(2, PhaseStep.BEGIN_COMBAT, playerB, 0); + rollbackAfterActionsStart(); + castSpell(2, PhaseStep.POSTCOMBAT_MAIN, playerB, "Unsummon", "Hanweir, the Writhing Township"); + rollbackAfterActionsEnd(); + setStopAt(2, PhaseStep.END_TURN); + execute(); + + assertGraveyardCount(playerB, "Unsummon", 1); + + assertHandCount(playerA, "Hanweir Battlements", 1); + assertHandCount(playerA, "Hanweir Garrison", 1); + + } } 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 7713284c96..28eb5727ea 100644 --- a/Mage/src/main/java/mage/abilities/effects/common/MeldEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/common/MeldEffect.java @@ -1,4 +1,3 @@ - package mage.abilities.effects.common; import java.util.HashSet; @@ -80,7 +79,7 @@ public class MeldEffect extends OneShotEffect { meldCard.setOwnerId(controller.getId()); meldCard.setTopHalfCard(meldWithCard, game); meldCard.setBottomHalfCard(sourceCard, game); - meldCard.setMelded(true); + meldCard.setMelded(true, game); game.addMeldCard(meldCard.getId(), meldCard); game.getState().addCard(meldCard); meldCard.setZone(Zone.EXILED, game); diff --git a/Mage/src/main/java/mage/cards/MeldCard.java b/Mage/src/main/java/mage/cards/MeldCard.java index 6112d24e0f..ece76ef521 100644 --- a/Mage/src/main/java/mage/cards/MeldCard.java +++ b/Mage/src/main/java/mage/cards/MeldCard.java @@ -1,5 +1,7 @@ package mage.cards; +import java.util.List; +import java.util.UUID; import mage.abilities.Ability; import mage.constants.CardType; import mage.constants.Zone; @@ -8,9 +10,6 @@ import mage.game.Game; import mage.game.events.ZoneChangeEvent; import mage.game.permanent.Permanent; -import java.util.List; -import java.util.UUID; - /** * @author emerald000 */ @@ -20,7 +19,6 @@ public abstract class MeldCard extends CardImpl { protected Card bottomHalfCard; protected int topLastZoneChangeCounter; protected int bottomLastZoneChangeCounter; - protected boolean isMelded; protected Cards halves; public MeldCard(UUID ownerId, CardSetInfo setInfo, CardType[] cardTypes, String costs) { @@ -35,15 +33,14 @@ public abstract class MeldCard extends CardImpl { this.topLastZoneChangeCounter = card.topLastZoneChangeCounter; this.bottomLastZoneChangeCounter = card.bottomLastZoneChangeCounter; this.halves = new CardsImpl(card.halves); - this.isMelded = card.isMelded; } - public void setMelded(boolean isMelded) { - this.isMelded = isMelded; + public void setMelded(boolean isMelded, Game game) { + game.getState().getCardState(getId()).setMelded(isMelded); } - public boolean isMelded() { - return isMelded; + public boolean isMelded(Game game) { + return game.getState().getCardState(getId()).isMelded(); } public Card getTopHalfCard() { @@ -107,7 +104,7 @@ public abstract class MeldCard extends CardImpl { @Override public boolean addCounters(Counter counter, Ability source, Game game, List appliedEffects) { - if (this.isMelded()) { + if (this.isMelded(game)) { return super.addCounters(counter, source, game, appliedEffects); } else { // can this really happen? @@ -163,7 +160,7 @@ public abstract class MeldCard extends CardImpl { if (isCopy()) { return super.removeFromZone(game, fromZone, sourceId); } - if (isMelded() && fromZone == Zone.BATTLEFIELD) { + if (isMelded(game) && fromZone == Zone.BATTLEFIELD) { Permanent permanent = game.getPermanent(objectId); return permanent != null && permanent.removeFromZone(game, fromZone, sourceId); } @@ -182,7 +179,7 @@ public abstract class MeldCard extends CardImpl { @Override public void updateZoneChangeCounter(Game game, ZoneChangeEvent event) { - if (isCopy() || !isMelded()) { + if (isCopy() || !isMelded(game)) { super.updateZoneChangeCounter(game, event); return; } diff --git a/Mage/src/main/java/mage/game/CardState.java b/Mage/src/main/java/mage/game/CardState.java index 7ce8d79f5c..ad89663885 100644 --- a/Mage/src/main/java/mage/game/CardState.java +++ b/Mage/src/main/java/mage/game/CardState.java @@ -19,6 +19,7 @@ public class CardState implements Serializable { protected Counters counters; protected Abilities abilities; protected boolean lostAllAbilities; + protected boolean melded; private static final Map emptyInfo = new HashMap<>(); private static final Abilities emptyAbilities = new AbilitiesImpl<>(); @@ -41,6 +42,7 @@ public class CardState implements Serializable { } } this.lostAllAbilities = state.lostAllAbilities; + this.melded = state.melded; } public CardState copy() { @@ -117,4 +119,12 @@ public class CardState implements Serializable { this.lostAllAbilities = lostAllAbilities; } + public boolean isMelded() { + return melded; + } + + public void setMelded(boolean melded) { + this.melded = melded; + } + } diff --git a/Mage/src/main/java/mage/game/ZonesHandler.java b/Mage/src/main/java/mage/game/ZonesHandler.java index 9a2289ffa6..81f6a57c4d 100644 --- a/Mage/src/main/java/mage/game/ZonesHandler.java +++ b/Mage/src/main/java/mage/game/ZonesHandler.java @@ -56,7 +56,7 @@ public final class ZonesHandler { 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()) { + if (card != null && !card.isMelded(game) && !card.isCopy()) { ZoneChangeInfo.Unmelded unmelded = new ZoneChangeInfo.Unmelded(info, game); if (unmelded.subInfo.isEmpty()) { itr.remove(); @@ -175,7 +175,7 @@ public final class ZonesHandler { game.setZone(event.getTargetId(), event.getToZone()); if (targetCard instanceof MeldCard && cards != null) { if (event.getToZone() != Zone.BATTLEFIELD) { - ((MeldCard) targetCard).setMelded(false); + ((MeldCard) targetCard).setMelded(false, game); } for (Card card : cards.getCards(game)) { game.setZone(card.getId(), event.getToZone());