* Meld Keyword - Fixed that the melt status was not correctly handled related to rollbacks or AI game simulation (fixes #6723).

This commit is contained in:
LevelX2 2020-07-18 16:33:28 +02:00
parent ecc05f9535
commit ba31b956dc
5 changed files with 60 additions and 21 deletions

View file

@ -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.
// <i>(Melds with Hanweir Battlements.)</i>
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);
}
}

View file

@ -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);

View file

@ -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<UUID> 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;
}

View file

@ -19,6 +19,7 @@ public class CardState implements Serializable {
protected Counters counters;
protected Abilities<Ability> abilities;
protected boolean lostAllAbilities;
protected boolean melded;
private static final Map<String, String> emptyInfo = new HashMap<>();
private static final Abilities<Ability> 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;
}
}

View file

@ -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());