Handle merged permanent as commander

This commit is contained in:
Lyu Zong-Hong
2021-02-12 00:52:24 +09:00
parent e987ccd743
commit 8128aba4f8
6 changed files with 70 additions and 8 deletions

View File

@@ -132,6 +132,7 @@ public class GameAction {
Card copied = null; Card copied = null;
Card lastKnownInfo = null; Card lastKnownInfo = null;
Card commanderEffect = null; // The effect card of commander replacement effect
// get the LKI from above like ChangeZoneEffect // get the LKI from above like ChangeZoneEffect
if (params != null && params.containsKey(AbilityKey.CardLKI)) { if (params != null && params.containsKey(AbilityKey.CardLKI)) {
@@ -231,6 +232,26 @@ public class GameAction {
copied.updateStateForView(); copied.updateStateForView();
if (!suppress) { 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) { if (zoneFrom == null) {
copied.getOwner().addInboundToken(copied); 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) { 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) { for (final Card card : mergedCards) {
// 721.3e if (card.isRealCommander()) {
if (c.isToken()) { card.setMoveToCommandZone(true);
}
// 721.3e & 903.9a
if (wasToken && !card.isToken() || card.isRealCommander()) {
Map<AbilityKey, Object> repParams = AbilityKey.mapFromAffected(card); Map<AbilityKey, Object> repParams = AbilityKey.mapFromAffected(card);
repParams.put(AbilityKey.CardLKI, card); repParams.put(AbilityKey.CardLKI, card);
repParams.put(AbilityKey.Cause, cause); repParams.put(AbilityKey.Cause, cause);
@@ -389,6 +422,8 @@ public class GameAction {
card.setZone(zoneTo); card.setZone(zoneTo);
} }
} else { } 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) zoneTo.add(copied, position, lastKnownInfo); // the modified state of the card is also reported here (e.g. for Morbid + Awaken)
c.setZone(zoneTo); c.setZone(zoneTo);
} }
@@ -611,7 +646,7 @@ public class GameAction {
AttachEffect.attachAuraOnIndirectEnterBattlefield(c); AttachEffect.attachAuraOnIndirectEnterBattlefield(c);
} }
if (c.isCommander()) { if (c.isRealCommander()) {
c.setMoveToCommandZone(true); c.setMoveToCommandZone(true);
} }
@@ -1234,7 +1269,7 @@ public class GameAction {
} }
private boolean stateBasedAction903_9a(Card c) { private boolean stateBasedAction903_9a(Card c) {
if (c.isCommander() && c.canMoveToCommandZone()) { if (c.isRealCommander() && c.canMoveToCommandZone()) {
c.setMoveToCommandZone(false); 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.")) { 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); moveTo(c.getOwner().getZone(ZoneType.Command), c, null);

View File

@@ -80,6 +80,10 @@ public class MutateEffect extends SpellAbilityEffect {
host.setFlipped(target.isFlipped()); host.setFlipped(target.isFlipped());
target.setTimesMutated(target.getTimesMutated() + 1); target.setTimesMutated(target.getTimesMutated() + 1);
target.updateTokenView(); target.updateTokenView();
if (host.isCommander()) {
target.updateCommanderView();
host.getOwner().updateMergedCommanderCast(target, host);
}
game.getTriggerHandler().runTrigger(TriggerType.Mutates, AbilityKey.mapFromCard(target), false); game.getTriggerHandler().runTrigger(TriggerType.Mutates, AbilityKey.mapFromCard(target), false);
} }

View File

@@ -2757,7 +2757,7 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
// is this "Card" supposed to be a token? // is this "Card" supposed to be a token?
public final boolean isToken() { public final boolean isToken() {
if (hasMergedCard()) { if (isInZone(ZoneType.Battlefield) && hasMergedCard()) {
return getTopMergedCard().token; return getTopMergedCard().token;
} }
return token; return token;
@@ -6240,6 +6240,13 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
public boolean isCommander() { public boolean isCommander() {
if (this.getMeldedWith() != null && this.getMeldedWith().isCommander()) if (this.getMeldedWith() != null && this.getMeldedWith().isCommander())
return true; return true;
if (isInZone(ZoneType.Battlefield) && hasMergedCard()) {
for (final Card c : getMergedCards())
if (c.isCommander) return true;
}
return isCommander;
}
public boolean isRealCommander() {
return isCommander; return isCommander;
} }
public void setCommander(boolean b) { public void setCommander(boolean b) {
@@ -6247,6 +6254,9 @@ public class Card extends GameEntity implements Comparable<Card>, IHasSVars {
isCommander = b; isCommander = b;
view.updateCommander(this); view.updateCommander(this);
} }
public void updateCommanderView() {
view.updateCommander(this);
}
public boolean canMoveToCommandZone() { public boolean canMoveToCommandZone() {
return canMoveToCommandZone; return canMoveToCommandZone;

View File

@@ -101,7 +101,7 @@ public class CardFactory {
for (final Card o : in.getImprintedCards()) { for (final Card o : in.getImprintedCards()) {
out.addImprintedCard(o); out.addImprintedCard(o);
} }
out.setCommander(in.isCommander()); out.setCommander(in.isRealCommander());
//out.setFaceDown(in.isFaceDown()); //out.setFaceDown(in.isFaceDown());
return out; return out;

View File

@@ -2886,6 +2886,10 @@ public class Player extends GameEntity implements Comparable<Player> {
getGame().fireEvent(new GameEventPlayerStatsChanged(this, false)); getGame().fireEvent(new GameEventPlayerStatsChanged(this, false));
} }
public void updateMergedCommanderCast(Card target, Card commander) {
getView().updateMergedCommanderCast(this, target, commander);
}
public int getTotalCommanderCast() { public int getTotalCommanderCast() {
int result = 0; int result = 0;
for (Integer i : commanderCast.values()) { for (Integer i : commanderCast.values()) {

View File

@@ -351,6 +351,15 @@ public class PlayerView extends GameEntityView {
set(TrackableProperty.CommanderCast, map); set(TrackableProperty.CommanderCast, map);
} }
void updateMergedCommanderCast(Player p, Card target, Card commander) {
Map<Integer, Integer> map = get(TrackableProperty.CommanderCast);
if (map == null) {
map = Maps.newHashMap();
}
map.put(target.getId(), p.getCommanderCast(commander));
set(TrackableProperty.CommanderCast, map);
}
public PlayerView getMindSlaveMaster() { public PlayerView getMindSlaveMaster() {
return get(TrackableProperty.MindSlaveMaster); return get(TrackableProperty.MindSlaveMaster);
} }