refactor: clean, added comments and todos to meld related code, fixed miss copy (related to #12544)

This commit is contained in:
Oleg Agafonov 2024-07-01 22:43:58 +04:00
parent ddb7e21dc2
commit bccf323c0f
5 changed files with 36 additions and 21 deletions

View file

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

View file

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

View file

@ -16,6 +16,7 @@ public class ZoneChangeInfo {
public boolean faceDown;
public ZoneChangeEvent event;
List<ZoneChangeInfo> 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<ZoneChangeInfo> 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);
}
}
}

View file

@ -58,14 +58,19 @@ public final class ZonesHandler {
}
public static List<ZoneChangeInfo> moveCards(List<ZoneChangeInfo> 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<ZoneChangeInfo> 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<ZoneChangeInfo> itr = unmelded.subInfo.iterator(); itr.hasNext(); ) {
ZoneChangeInfo subInfo = itr.next();
if (!maybeRemoveFromSourceZone(subInfo, game, source)) {
for (Iterator<ZoneChangeInfo> 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;
}

View file

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