From 8128aba4f87f8562db577da32c75b68ff8c5b11d Mon Sep 17 00:00:00 2001 From: Lyu Zong-Hong Date: Fri, 12 Feb 2021 00:52:24 +0900 Subject: [PATCH] Handle merged permanent as commander --- .../src/main/java/forge/game/GameAction.java | 47 ++++++++++++++++--- .../game/ability/effects/MutateEffect.java | 4 ++ .../src/main/java/forge/game/card/Card.java | 12 ++++- .../java/forge/game/card/CardFactory.java | 2 +- .../main/java/forge/game/player/Player.java | 4 ++ .../java/forge/game/player/PlayerView.java | 9 ++++ 6 files changed, 70 insertions(+), 8 deletions(-) diff --git a/forge-game/src/main/java/forge/game/GameAction.java b/forge-game/src/main/java/forge/game/GameAction.java index be4b9536c74..0ae3ef9419d 100644 --- a/forge-game/src/main/java/forge/game/GameAction.java +++ b/forge-game/src/main/java/forge/game/GameAction.java @@ -132,6 +132,7 @@ public class GameAction { Card copied = null; Card lastKnownInfo = null; + Card commanderEffect = null; // The effect card of commander replacement effect // get the LKI from above like ChangeZoneEffect if (params != null && params.containsKey(AbilityKey.CardLKI)) { @@ -231,6 +232,26 @@ public class GameAction { copied.updateStateForView(); if (!suppress) { + // Temporary disable commander replacement effect + // 903.9a + if (fromBattlefield && c.isCommander() && c.hasMergedCard()) { + // Find the commander replacement effect "card" + CardCollectionView comCards = c.getOwner().getCardsIn(ZoneType.Command); + for (final Card effCard : comCards) { + for (final ReplacementEffect re : effCard.getReplacementEffects()) { + if (re.getMode() == ReplacementType.Moved && "Card.EffectSource+YouOwn".equals(re.getParam("ValidCard"))) { + commanderEffect = effCard; + break; + } + } + if (commanderEffect != null) break; + } + // Disable the commander replacement effect + for (final ReplacementEffect re : commanderEffect.getReplacementEffects()) { + re.setSuppressed(true); + } + } + if (zoneFrom == null) { copied.getOwner().addInboundToken(copied); } @@ -362,12 +383,24 @@ public class GameAction { } } - // "enter the battlefield as a copy" - apply code here - // but how to query for input here and continue later while the callers assume synchronous result? if (mergedCards != null) { + // Move components of merged permanet here + // Also handle 721.3e and 903.9a + boolean wasToken = c.isToken(); + if (commanderEffect != null) { + for (final ReplacementEffect re : commanderEffect.getReplacementEffects()) { + re.setSuppressed(false); + } + } + // Change zone of original card so components isToken() and isCommander() return correct value + // when running replacement effects here + c.setZone(zoneTo); for (final Card card : mergedCards) { - // 721.3e - if (c.isToken()) { + if (card.isRealCommander()) { + card.setMoveToCommandZone(true); + } + // 721.3e & 903.9a + if (wasToken && !card.isToken() || card.isRealCommander()) { Map repParams = AbilityKey.mapFromAffected(card); repParams.put(AbilityKey.CardLKI, card); repParams.put(AbilityKey.Cause, cause); @@ -389,6 +422,8 @@ public class GameAction { card.setZone(zoneTo); } } else { + // "enter the battlefield as a copy" - apply code here + // but how to query for input here and continue later while the callers assume synchronous result? zoneTo.add(copied, position, lastKnownInfo); // the modified state of the card is also reported here (e.g. for Morbid + Awaken) c.setZone(zoneTo); } @@ -611,7 +646,7 @@ public class GameAction { AttachEffect.attachAuraOnIndirectEnterBattlefield(c); } - if (c.isCommander()) { + if (c.isRealCommander()) { c.setMoveToCommandZone(true); } @@ -1234,7 +1269,7 @@ public class GameAction { } private boolean stateBasedAction903_9a(Card c) { - if (c.isCommander() && c.canMoveToCommandZone()) { + if (c.isRealCommander() && c.canMoveToCommandZone()) { c.setMoveToCommandZone(false); if (c.getOwner().getController().confirmAction(c.getSpellPermanent(), PlayerActionConfirmMode.ChangeZoneToAltDestination, c.getName() + ": If a commander is in a graveyard or in exile and that card was put into that zone since the last time state-based actions were checked, its owner may put it into the command zone.")) { moveTo(c.getOwner().getZone(ZoneType.Command), c, null); diff --git a/forge-game/src/main/java/forge/game/ability/effects/MutateEffect.java b/forge-game/src/main/java/forge/game/ability/effects/MutateEffect.java index ae506f0ca2a..28d1da70390 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/MutateEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/MutateEffect.java @@ -80,6 +80,10 @@ public class MutateEffect extends SpellAbilityEffect { host.setFlipped(target.isFlipped()); target.setTimesMutated(target.getTimesMutated() + 1); target.updateTokenView(); + if (host.isCommander()) { + target.updateCommanderView(); + host.getOwner().updateMergedCommanderCast(target, host); + } game.getTriggerHandler().runTrigger(TriggerType.Mutates, AbilityKey.mapFromCard(target), false); } diff --git a/forge-game/src/main/java/forge/game/card/Card.java b/forge-game/src/main/java/forge/game/card/Card.java index d2d61d64d1a..dfef4b6dad9 100644 --- a/forge-game/src/main/java/forge/game/card/Card.java +++ b/forge-game/src/main/java/forge/game/card/Card.java @@ -2757,7 +2757,7 @@ public class Card extends GameEntity implements Comparable, IHasSVars { // is this "Card" supposed to be a token? public final boolean isToken() { - if (hasMergedCard()) { + if (isInZone(ZoneType.Battlefield) && hasMergedCard()) { return getTopMergedCard().token; } return token; @@ -6240,6 +6240,13 @@ public class Card extends GameEntity implements Comparable, IHasSVars { public boolean isCommander() { if (this.getMeldedWith() != null && this.getMeldedWith().isCommander()) return true; + if (isInZone(ZoneType.Battlefield) && hasMergedCard()) { + for (final Card c : getMergedCards()) + if (c.isCommander) return true; + } + return isCommander; + } + public boolean isRealCommander() { return isCommander; } public void setCommander(boolean b) { @@ -6247,6 +6254,9 @@ public class Card extends GameEntity implements Comparable, IHasSVars { isCommander = b; view.updateCommander(this); } + public void updateCommanderView() { + view.updateCommander(this); + } public boolean canMoveToCommandZone() { return canMoveToCommandZone; diff --git a/forge-game/src/main/java/forge/game/card/CardFactory.java b/forge-game/src/main/java/forge/game/card/CardFactory.java index ddab5c3f357..8a21f092a91 100644 --- a/forge-game/src/main/java/forge/game/card/CardFactory.java +++ b/forge-game/src/main/java/forge/game/card/CardFactory.java @@ -101,7 +101,7 @@ public class CardFactory { for (final Card o : in.getImprintedCards()) { out.addImprintedCard(o); } - out.setCommander(in.isCommander()); + out.setCommander(in.isRealCommander()); //out.setFaceDown(in.isFaceDown()); return out; diff --git a/forge-game/src/main/java/forge/game/player/Player.java b/forge-game/src/main/java/forge/game/player/Player.java index 9604aa34805..c1a483c68c9 100644 --- a/forge-game/src/main/java/forge/game/player/Player.java +++ b/forge-game/src/main/java/forge/game/player/Player.java @@ -2886,6 +2886,10 @@ public class Player extends GameEntity implements Comparable { getGame().fireEvent(new GameEventPlayerStatsChanged(this, false)); } + public void updateMergedCommanderCast(Card target, Card commander) { + getView().updateMergedCommanderCast(this, target, commander); + } + public int getTotalCommanderCast() { int result = 0; for (Integer i : commanderCast.values()) { diff --git a/forge-game/src/main/java/forge/game/player/PlayerView.java b/forge-game/src/main/java/forge/game/player/PlayerView.java index 19e6e1cb6fd..164ff4a92cb 100644 --- a/forge-game/src/main/java/forge/game/player/PlayerView.java +++ b/forge-game/src/main/java/forge/game/player/PlayerView.java @@ -351,6 +351,15 @@ public class PlayerView extends GameEntityView { set(TrackableProperty.CommanderCast, map); } + void updateMergedCommanderCast(Player p, Card target, Card commander) { + Map map = get(TrackableProperty.CommanderCast); + if (map == null) { + map = Maps.newHashMap(); + } + map.put(target.getId(), p.getCommanderCast(commander)); + set(TrackableProperty.CommanderCast, map); + } + public PlayerView getMindSlaveMaster() { return get(TrackableProperty.MindSlaveMaster); }