From a89c9df904f1c9e927f4545a7264803115400fb8 Mon Sep 17 00:00:00 2001 From: drdev Date: Thu, 25 Sep 2014 15:49:23 +0000 Subject: [PATCH] Prevent crash when starting game without name configured Avoid unnecessary duplication of event handling when two human players Avoid duplicate copies of views when two human players --- .../control/FControlGameEventHandler.java | 61 +++++++-- .../src/main/java/forge/match/MatchUtil.java | 40 +++--- .../java/forge/player/GamePlayerUtil.java | 19 ++- .../forge/player/PlayerControllerHuman.java | 5 - .../main/java/forge/view/LocalGameView.java | 116 ++++++++---------- 5 files changed, 146 insertions(+), 95 deletions(-) diff --git a/forge-gui/src/main/java/forge/control/FControlGameEventHandler.java b/forge-gui/src/main/java/forge/control/FControlGameEventHandler.java index 823a52d932b..e80ef67c2bc 100644 --- a/forge-gui/src/main/java/forge/control/FControlGameEventHandler.java +++ b/forge-gui/src/main/java/forge/control/FControlGameEventHandler.java @@ -63,10 +63,12 @@ import forge.view.LocalGameView; import forge.view.PlayerView; public class FControlGameEventHandler extends IGameEventVisitor.Base { - private final LocalGameView gameView; - public FControlGameEventHandler(final LocalGameView gameView0) { + private final boolean isMainHandler; + + public FControlGameEventHandler(final LocalGameView gameView0, final boolean isMainHandler0) { gameView = gameView0; + isMainHandler = isMainHandler0; } @Subscribe @@ -77,7 +79,7 @@ public class FControlGameEventHandler extends IGameEventVisitor.Base { private final AtomicBoolean phaseUpdPlanned = new AtomicBoolean(false); @Override public Void visit(final GameEventTurnPhase ev) { - if (phaseUpdPlanned.getAndSet(true)) return null; + if (!isMainHandler || phaseUpdPlanned.getAndSet(true)) { return null; } FThreads.invokeInEdtNowOrLater(gameView.getGui(), new Runnable() { @Override @@ -95,7 +97,8 @@ public class FControlGameEventHandler extends IGameEventVisitor.Base { private final AtomicBoolean combatUpdPlanned = new AtomicBoolean(false); @Override public Void visit(GameEventPlayerPriority event) { - if (combatUpdPlanned.getAndSet(true)) { return null; } + if (!isMainHandler || combatUpdPlanned.getAndSet(true)) { return null; } + FThreads.invokeInEdtNowOrLater(gameView.getGui(), new Runnable() { @Override public void run() { @@ -109,6 +112,8 @@ public class FControlGameEventHandler extends IGameEventVisitor.Base { private final AtomicBoolean turnUpdPlanned = new AtomicBoolean(false); @Override public Void visit(final GameEventTurnBegan event) { + if (!isMainHandler) { return null; } + if (FModel.getPreferences().getPrefBoolean(FPref.UI_STACK_CREATURES) && event.turnOwner != null) { // anything except stack will get here updateZone(Pair.of(gameView.getPlayerView(event.turnOwner), ZoneType.Battlefield)); @@ -128,6 +133,8 @@ public class FControlGameEventHandler extends IGameEventVisitor.Base { @Override public Void visit(GameEventAnteCardsSelected ev) { + if (!isMainHandler) { return null; } + final List options = Lists.newArrayList(); for (final Entry kv : ev.cards.entries()) { final CardView fakeCard = new CardView(true); //use fake card so real cards appear with proper formatting @@ -176,12 +183,10 @@ public class FControlGameEventHandler extends IGameEventVisitor.Base { InputBase.cancelAwaitNextInput(); //ensure "Waiting for opponent..." doesn't appear behind WinLo MatchUtil.getController().showPromptMessage(localPlayer, ""); //clear prompt behind WinLose overlay ButtonUtil.update(localPlayer, "", "", false, false, false); - - //only finish game once, and only if player is Gui player or Gui player isn't playing - if (localPlayer.getLobbyPlayer() == GamePlayerUtil.getGuiPlayer() || MatchUtil.getHumanCount() == 0) { + if (isMainHandler) { MatchUtil.getController().finishGame(); + gameView.updateAchievements(); } - gameView.updateAchievements(); } }); return null; @@ -198,6 +203,8 @@ public class FControlGameEventHandler extends IGameEventVisitor.Base { @Override public Void visit(GameEventSpellAbilityCast event) { + if (!isMainHandler) { return null; } + if (!stackUpdPlanned.getAndSet(true)) { FThreads.invokeInEdtNowOrLater(gameView.getGui(), updStack); } @@ -205,6 +212,8 @@ public class FControlGameEventHandler extends IGameEventVisitor.Base { } @Override public Void visit(GameEventSpellResolved event) { + if (!isMainHandler) { return null; } + if (!stackUpdPlanned.getAndSet(true)) { FThreads.invokeInEdtNowOrLater(gameView.getGui(), updStack); } @@ -212,6 +221,8 @@ public class FControlGameEventHandler extends IGameEventVisitor.Base { } @Override public Void visit(GameEventSpellRemovedFromStack event) { + if (!isMainHandler) { return null; } + if (!stackUpdPlanned.getAndSet(true)) { FThreads.invokeInEdtNowOrLater(gameView.getGui(), updStack); } @@ -231,6 +242,8 @@ public class FControlGameEventHandler extends IGameEventVisitor.Base { @Override public Void visit(GameEventZone event) { + if (!isMainHandler) { return null; } + if (event.player != null) { // anything except stack will get here updateZone(Pair.of(gameView.getPlayerView(event.player), event.zoneType)); @@ -240,6 +253,8 @@ public class FControlGameEventHandler extends IGameEventVisitor.Base { @Override public Void visit(GameEventCardAttachment event) { + if (!isMainHandler) { return null; } + final Game game = event.equipment.getGame(); final PlayerZone zEq = (PlayerZone)game.getZoneOf(event.equipment); if (event.oldEntiy instanceof Card) { @@ -283,26 +298,36 @@ public class FControlGameEventHandler extends IGameEventVisitor.Base { @Override public Void visit(final GameEventCardTapped event) { + if (!isMainHandler) { return null; } + return updateSingleCard(gameView.getCardView(event.card)); } @Override public Void visit(final GameEventCardPhased event) { + if (!isMainHandler) { return null; } + return updateSingleCard(gameView.getCardView(event.card)); } @Override public Void visit(final GameEventCardDamaged event) { + if (!isMainHandler) { return null; } + return updateSingleCard(gameView.getCardView(event.card)); } @Override public Void visit(final GameEventCardCounters event) { + if (!isMainHandler) { return null; } + return updateSingleCard(gameView.getCardView(event.card)); } @Override public Void visit(final GameEventBlockersDeclared event) { // This is to draw icons on blockers declared by AI + if (!isMainHandler) { return null; } + for (MapOfLists kv : event.blockers.values()) { for (Collection blockers : kv.values()) { updateManyCards(gameView.getCardViews(blockers)); @@ -313,13 +338,15 @@ public class FControlGameEventHandler extends IGameEventVisitor.Base { @Override public Void visit(GameEventAttackersDeclared event) { + if (!isMainHandler) { return null; } + // Skip redraw for GUI player? if (event.player.getLobbyPlayer() == GamePlayerUtil.getGuiPlayer()) { return null; } // Update all attackers. - // Although they might have been updated when they were apped, there could be someone with vigilance, not redrawn yet. + // Although they might have been updated when they were tapped, there could be someone with vigilance, not redrawn yet. updateManyCards(gameView.getCardViews(event.attackersMap.values())); return super.visit(event); @@ -327,6 +354,8 @@ public class FControlGameEventHandler extends IGameEventVisitor.Base { @Override public Void visit(GameEventCombatEnded event) { + if (!isMainHandler) { return null; } + // This should remove sword/shield icons from combatants by the time game moves to M2 updateManyCards(gameView.getCardViews(event.attackers)); updateManyCards(gameView.getCardViews(event.blockers)); @@ -361,6 +390,8 @@ public class FControlGameEventHandler extends IGameEventVisitor.Base { @Override public Void visit(GameEventCardChangeZone event) { + if (!isMainHandler) { return null; } + if (event.from != null) { updateZone(event.from); } @@ -375,6 +406,8 @@ public class FControlGameEventHandler extends IGameEventVisitor.Base { */ @Override public Void visit(GameEventCardStatsChanged event) { + if (!isMainHandler) { return null; } + final Iterable cardViews = gameView.getCardViews(event.cards); MatchUtil.getController().refreshCardDetails(cardViews); return updateManyCards(cardViews); @@ -382,6 +415,8 @@ public class FControlGameEventHandler extends IGameEventVisitor.Base { @Override public Void visit(GameEventPlayerStatsChanged event) { + if (!isMainHandler) { return null; } + for (final Player p : event.players) { MatchUtil.getController().refreshCardDetails(gameView.getCardViews(p.getAllCards())); } @@ -390,6 +425,8 @@ public class FControlGameEventHandler extends IGameEventVisitor.Base { @Override public Void visit(GameEventShuffle event) { + if (!isMainHandler) { return null; } + updateZone(event.player.getZone(ZoneType.Library)); return null; } @@ -407,6 +444,8 @@ public class FControlGameEventHandler extends IGameEventVisitor.Base { @Override public Void visit(GameEventManaPool event) { + if (!isMainHandler) { return null; } + boolean invokeUpdate = false; synchronized (manaPoolUpdate) { if (!manaPoolUpdate.contains(event.player)) { @@ -432,6 +471,8 @@ public class FControlGameEventHandler extends IGameEventVisitor.Base { }; @Override public Void visit(GameEventPlayerLivesChanged event) { + if (!isMainHandler) { return null; } + boolean invokeUpdate = false; synchronized (livesUpdate) { if (!livesUpdate.contains(event.player)) { @@ -447,6 +488,8 @@ public class FControlGameEventHandler extends IGameEventVisitor.Base { @Override public Void visit(GameEventPlayerPoisoned event) { + if (!isMainHandler) { return null; } + boolean invokeUpdate = false; synchronized (livesUpdate) { if (!livesUpdate.contains(event.receiver)) { diff --git a/forge-gui/src/main/java/forge/match/MatchUtil.java b/forge-gui/src/main/java/forge/match/MatchUtil.java index 062ef135f21..e652d8312f3 100644 --- a/forge-gui/src/main/java/forge/match/MatchUtil.java +++ b/forge-gui/src/main/java/forge/match/MatchUtil.java @@ -40,6 +40,8 @@ import forge.game.card.CounterType; import forge.game.phase.PhaseType; import forge.game.player.Player; import forge.game.player.RegisteredPlayer; +import forge.game.spellability.SpellAbility; +import forge.game.spellability.SpellAbilityStackInstance; import forge.game.trigger.TriggerType; import forge.game.zone.ZoneType; import forge.match.input.InputPlaybackControl; @@ -49,7 +51,6 @@ import forge.model.FModel; import forge.player.GamePlayerUtil; import forge.player.LobbyPlayerHuman; import forge.player.PlayerControllerHuman; -import forge.properties.ForgePreferences; import forge.properties.ForgePreferences.FPref; import forge.quest.QuestController; import forge.sound.MusicPlaylist; @@ -57,16 +58,29 @@ import forge.sound.SoundSystem; import forge.util.GuiDisplayUtil; import forge.util.NameGenerator; import forge.util.gui.SOptionPane; +import forge.view.Cache; import forge.view.CardView; import forge.view.GameEntityView; import forge.view.LocalGameView; import forge.view.PlayerView; +import forge.view.SpellAbilityView; +import forge.view.StackItemView; import forge.view.WatchLocalGame; public class MatchUtil { private static IMatchController controller; private static Game game; private static List gameViews = new ArrayList(); + + /** Cache of players. */ + public static final Cache players = new Cache<>(); + /** Cache of cards. */ + public static final Cache cards = new Cache<>(); + /** Cache of spellabilities. */ + public static final Cache spabs = new Cache<>(); + /** Cache of stack items. */ + public static final Cache stackItems = new Cache<>(); + private static int humanCount; private static final EventBus uiEvents; private static FControlGamePlayback playbackControl; @@ -122,16 +136,6 @@ public class MatchUtil { public static void startGame(final Match match) { if (!controller.resetForNewGame()) { return; } - //prompt user for player one name if needed - final ForgePreferences prefs = FModel.getPreferences(); - if (StringUtils.isBlank(prefs.getPref(FPref.PLAYER_NAME))) { - boolean isPlayerOneHuman = match.getPlayers().get(0).getPlayer() instanceof LobbyPlayerHuman; - boolean isPlayerTwoComputer = match.getPlayers().get(1).getPlayer() instanceof LobbyPlayerAi; - if (isPlayerOneHuman && isPlayerTwoComputer) { - GamePlayerUtil.setPlayerName(GuiBase.getInterface()); - } - } - SoundSystem.instance.setBackgroundMusic(MusicPlaylist.MATCH); game = match.createGame(); @@ -147,7 +151,7 @@ public class MatchUtil { game.subscribeToEvents(SoundSystem.instance); - final String[] indices = prefs.getPref(FPref.UI_AVATARS).split(","); + final String[] indices = FModel.getPreferences().getPref(FPref.UI_AVATARS).split(","); // Instantiate all required field slots (user at 0) final List sortedPlayers = new ArrayList(game.getRegisteredPlayers()); @@ -175,7 +179,7 @@ public class MatchUtil { if (p.getController() instanceof PlayerControllerHuman) { final PlayerControllerHuman controller = (PlayerControllerHuman) p.getController(); LocalGameView gameView = controller.getGameView(); - game.subscribeToEvents(new FControlGameEventHandler(gameView)); + game.subscribeToEvents(new FControlGameEventHandler(gameView, humanCount == 0)); gameViews.add(gameView); humanCount++; } @@ -184,7 +188,7 @@ public class MatchUtil { if (humanCount == 0) { //watch game but do not participate LocalGameView gameView = new WatchLocalGame(GuiBase.getInterface(), game); gameView.setLocalPlayer(sortedPlayers.get(0)); - game.subscribeToEvents(new FControlGameEventHandler(gameView)); + game.subscribeToEvents(new FControlGameEventHandler(gameView, true)); gameViews.add(gameView); } else if (humanCount == sortedPlayers.size() && controller.hotSeatMode()) { @@ -207,6 +211,14 @@ public class MatchUtil { game.getAction().invoke(new Runnable() { @Override public void run() { + //prompt user for player one name if needed + if (StringUtils.isBlank(FModel.getPreferences().getPref(FPref.PLAYER_NAME))) { + boolean isPlayerOneHuman = match.getPlayers().get(0).getPlayer() instanceof LobbyPlayerHuman; + boolean isPlayerTwoComputer = match.getPlayers().get(1).getPlayer() instanceof LobbyPlayerAi; + if (isPlayerOneHuman && isPlayerTwoComputer) { + GamePlayerUtil.setPlayerName(GuiBase.getInterface()); + } + } match.startGame(game); } }); diff --git a/forge-gui/src/main/java/forge/player/GamePlayerUtil.java b/forge-gui/src/main/java/forge/player/GamePlayerUtil.java index 069d932839d..18858c010e3 100644 --- a/forge-gui/src/main/java/forge/player/GamePlayerUtil.java +++ b/forge-gui/src/main/java/forge/player/GamePlayerUtil.java @@ -6,6 +6,7 @@ import forge.ai.AiProfileUtil; import forge.ai.LobbyPlayerAi; import forge.game.player.Player; import forge.interfaces.IGuiBase; +import forge.match.MatchUtil; import forge.model.FModel; import forge.properties.ForgePreferences.FPref; import forge.util.GuiDisplayUtil; @@ -73,18 +74,30 @@ public final class GamePlayerUtil { public static void setPlayerName(final IGuiBase gui) { String oldPlayerName = FModel.getPreferences().getPref(FPref.PLAYER_NAME); - String newPlayerName = null; + String newPlayerName; if (StringUtils.isBlank(oldPlayerName)) { newPlayerName = getVerifiedPlayerName(getPlayerNameUsingFirstTimePrompt(gui), oldPlayerName); - } else { + } + else { newPlayerName = getVerifiedPlayerName(getPlayerNameUsingStandardPrompt(gui, oldPlayerName), oldPlayerName); } + //update name for player in active game if needed + if (MatchUtil.getGame() != null) { + for (Player player : MatchUtil.getGame().getPlayers()) { + if (player.getLobbyPlayer() == MatchUtil.getGuiPlayer()) { + player.setName(newPlayerName); + player.getLobbyPlayer().setName(newPlayerName); + break; + } + } + } + FModel.getPreferences().setPref(FPref.PLAYER_NAME, newPlayerName); FModel.getPreferences().save(); - if (StringUtils.isBlank(oldPlayerName) && newPlayerName != "Human") { + if (StringUtils.isBlank(oldPlayerName) && !newPlayerName.equals("Human")) { showThankYouPrompt(gui, newPlayerName); } } diff --git a/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java b/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java index 2ee72c51673..2431ec57589 100644 --- a/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java +++ b/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java @@ -93,7 +93,6 @@ import forge.match.input.InputConfirmMulligan; import forge.match.input.InputPassPriority; import forge.match.input.InputPayMana; import forge.match.input.InputProliferate; -import forge.match.input.InputProxy; import forge.match.input.InputSelectCardsForConvoke; import forge.match.input.InputSelectCardsFromList; import forge.match.input.InputSelectEntitiesFromList; @@ -153,10 +152,6 @@ public class PlayerControllerHuman extends PlayerController { return gameView.getGui(); } - public InputProxy getInputProxy() { - return gameView.getInputProxy(); - } - public LocalGameView getGameView() { return gameView; } diff --git a/forge-gui/src/main/java/forge/view/LocalGameView.java b/forge-gui/src/main/java/forge/view/LocalGameView.java index fe29b230296..9dd477fc810 100644 --- a/forge-gui/src/main/java/forge/view/LocalGameView.java +++ b/forge-gui/src/main/java/forge/view/LocalGameView.java @@ -28,6 +28,7 @@ import forge.game.spellability.SpellAbility; import forge.game.spellability.SpellAbilityStackInstance; import forge.game.zone.ZoneType; import forge.interfaces.IGuiBase; +import forge.match.MatchUtil; import forge.match.input.InputProxy; import forge.match.input.InputQueue; @@ -69,15 +70,6 @@ public abstract class LocalGameView implements IGameView { localPlayerView = getPlayerView(localPlayer); } - /** Cache of players. */ - private final Cache players = new Cache<>(); - /** Cache of cards. */ - private final Cache cards = new Cache<>(); - /** Cache of spellabilities. */ - private final Cache spabs = new Cache<>(); - /** Cache of stack items. */ - private final Cache stackItems = new Cache<>(); - /* (non-Javadoc) * @see forge.view.IGameView#isCommander() */ @@ -261,7 +253,7 @@ public abstract class LocalGameView implements IGameView { final List stack = Lists.newArrayList(game.getStack()); final List items = Collections.unmodifiableList(getStack(stack)); // clear the cache - stackItems.retainAllKeys(stack); + MatchUtil.stackItems.retainAllKeys(stack); return items; } @@ -278,19 +270,17 @@ public abstract class LocalGameView implements IGameView { } private List getStack(final Iterable stack) { - synchronized (this) { - stackItems.retainAllKeys(Lists.newArrayList(stack)); - final List items = Lists.newLinkedList(); - for (final SpellAbilityStackInstance si : stack) { - final int id = si.getId(); - if (stackItems.containsKey(id)) { - items.add(stackItems.get(id)); - } else { - items.add(getStackItemView(si)); - } + MatchUtil.stackItems.retainAllKeys(Lists.newArrayList(stack)); + final List items = Lists.newLinkedList(); + for (final SpellAbilityStackInstance si : stack) { + final int id = si.getId(); + if (MatchUtil.stackItems.containsKey(id)) { + items.add(MatchUtil.stackItems.get(id)); + } else { + items.add(getStackItemView(si)); } - return items; } + return items; } public StackItemView getStackItemView(final SpellAbilityStackInstance si) { @@ -304,7 +294,7 @@ public abstract class LocalGameView implements IGameView { si.getStackDescription(), getCardView(si.getSourceCard()), getPlayerView(si.getActivator()), getCardViews(si.getTargetChoices().getTargetCards()), getPlayerViews(si.getTargetChoices().getTargetPlayers()), si.isAbility(), si.isOptionalTrigger()); - stackItems.put(si, newItem); + MatchUtil.stackItems.put(si, newItem); return newItem; } @@ -312,7 +302,7 @@ public abstract class LocalGameView implements IGameView { if (view == null) { return null; } - return stackItems.getKey(view.getId()); + return MatchUtil.stackItems.getKey(view.getId()); } public final GameEntityView getGameEntityView(final GameEntity e) { @@ -360,13 +350,13 @@ public abstract class LocalGameView implements IGameView { return null; } - final PlayerView view; - if (players.containsKey(p.getId())) { - view = players.get(p.getId()); + PlayerView view = MatchUtil.players.get(p.getId()); + if (view != null) { getPlayerView(p, view); - } else { + } + else { view = new PlayerView(p.getLobbyPlayer(), p.getId()); - players.put(p, view); + MatchUtil.players.put(p, view); getPlayerView(p, view); view.setOpponents(getPlayerViews(p.getOpponents())); } @@ -377,14 +367,14 @@ public abstract class LocalGameView implements IGameView { if (p == null) { return null; } - return players.get(p.getId()); + return MatchUtil.players.get(p.getId()); } public Player getPlayer(final PlayerView p) { if (p == null) { return null; } - return players.getKey(p.getId()); + return MatchUtil.players.getKey(p.getId()); } private void getPlayerView(final Player p, final PlayerView view) { @@ -423,19 +413,19 @@ public abstract class LocalGameView implements IGameView { final boolean isDisplayable = cUi == c; final boolean mayShow = mayShowCard(c); - CardView view = cards.get(c.getId()); + CardView view = MatchUtil.cards.get(c.getId()); final boolean isNewView; if (view != null) { // Update to ensure the Card reference in the cache // is not an outdated Card. if (view.getId() > 0) { - cards.updateKey(view.getId(), c); + MatchUtil.cards.updateKey(view.getId(), c); } isNewView = false; } else if (isDisplayable && mayShow) { view = new CardView(isDisplayable); view.setId(c.getId()); - cards.put(c, view); + MatchUtil.cards.put(c, view); isNewView = true; } else { return CardView.EMPTY; @@ -471,7 +461,7 @@ public abstract class LocalGameView implements IGameView { return ViewUtil.transformIfNotNull(cardViews, new Function() { @Override public CardView apply(final CardView input) { - return cards.getCurrentValue(input); + return MatchUtil.cards.getCurrentValue(input); } }); } @@ -481,7 +471,7 @@ public abstract class LocalGameView implements IGameView { return null; } - final CardView view = cards.get(c.getId()); + final CardView view = MatchUtil.cards.get(c.getId()); if (mayShowCard(c)) { return view; } else if (view.isUiDisplayable()) { @@ -506,7 +496,7 @@ public abstract class LocalGameView implements IGameView { if (c == null) { return null; } - return cards.getKey(c.getId()); + return MatchUtil.cards.getKey(c.getId()); } private final Function FN_GET_CARD = new Function() { @@ -521,30 +511,28 @@ public abstract class LocalGameView implements IGameView { } private void writeCardToView(final Card c, final CardView view) { - synchronized (c) { - // First, write the values independent of other views. - ViewUtil.writeNonDependentCardViewProperties(c, view, mayShowCardFace(c)); - // Next, write the values that depend on other views. - final Combat combat = game.getCombat(); - view.setOwner(getPlayerViewFast(c.getOwner())); - view.setController(getPlayerViewFast(c.getController())); - view.setAttacking(combat != null && combat.isAttacking(c)); - view.setBlocking(combat != null && combat.isBlocking(c)); - view.setChosenPlayer(getPlayerViewFast(c.getChosenPlayer())); - view.setEquipping(getCardViewFast(Iterables.getFirst(c.getEquipping(), null))); - view.setEquippedBy(getCardViewsFast(c.getEquippedBy())); - view.setEnchantingCard(getCardViewFast(c.getEnchantingCard())); - view.setEnchantingPlayer(getPlayerViewFast(c.getEnchantingPlayer())); - view.setEnchantedBy(getCardViewsFast(c.getEnchantedBy())); - view.setFortifiedBy(getCardViewsFast(c.getFortifiedBy())); - view.setGainControlTargets(getCardViewsFast(c.getGainControlTargets())); - view.setCloneOrigin(getCardViewFast(c.getCloneOrigin())); - view.setImprinted(getCardViewsFast(c.getImprinted())); - view.setHauntedBy(getCardViewsFast(c.getHauntedBy())); - view.setHaunting(getCardViewFast(c.getHaunting())); - view.setMustBlock(c.getMustBlockCards() == null ? Collections.emptySet() : getCardViewsFast(c.getMustBlockCards())); - view.setPairedWith(getCardViewFast(c.getPairedWith())); - } + // First, write the values independent of other views. + ViewUtil.writeNonDependentCardViewProperties(c, view, mayShowCardFace(c)); + // Next, write the values that depend on other views. + final Combat combat = game.getCombat(); + view.setOwner(getPlayerViewFast(c.getOwner())); + view.setController(getPlayerViewFast(c.getController())); + view.setAttacking(combat != null && combat.isAttacking(c)); + view.setBlocking(combat != null && combat.isBlocking(c)); + view.setChosenPlayer(getPlayerViewFast(c.getChosenPlayer())); + view.setEquipping(getCardViewFast(Iterables.getFirst(c.getEquipping(), null))); + view.setEquippedBy(getCardViewsFast(c.getEquippedBy())); + view.setEnchantingCard(getCardViewFast(c.getEnchantingCard())); + view.setEnchantingPlayer(getPlayerViewFast(c.getEnchantingPlayer())); + view.setEnchantedBy(getCardViewsFast(c.getEnchantedBy())); + view.setFortifiedBy(getCardViewsFast(c.getFortifiedBy())); + view.setGainControlTargets(getCardViewsFast(c.getGainControlTargets())); + view.setCloneOrigin(getCardViewFast(c.getCloneOrigin())); + view.setImprinted(getCardViewsFast(c.getImprinted())); + view.setHauntedBy(getCardViewsFast(c.getHauntedBy())); + view.setHaunting(getCardViewFast(c.getHaunting())); + view.setMustBlock(c.getMustBlockCards() == null ? Collections.emptySet() : getCardViewsFast(c.getMustBlockCards())); + view.setPairedWith(getCardViewFast(c.getPairedWith())); } public SpellAbilityView getSpellAbilityView(final SpellAbility sa) { @@ -553,13 +541,13 @@ public abstract class LocalGameView implements IGameView { } final SpellAbilityView view; - if (spabs.containsKey(sa.getId())) { - view = spabs.get(sa.getId()); + if (MatchUtil.spabs.containsKey(sa.getId())) { + view = MatchUtil.spabs.get(sa.getId()); writeSpellAbilityToView(sa, view); } else { view = new SpellAbilityView(sa.getId()); writeSpellAbilityToView(sa, view); - spabs.put(sa, view); + MatchUtil.spabs.put(sa, view); } return view; } @@ -582,7 +570,7 @@ public abstract class LocalGameView implements IGameView { return getSpellAbility(spabView.getId()); } public SpellAbility getSpellAbility(final int id) { - return id >= 0 ? spabs.getKey(id) : null; + return id >= 0 ? MatchUtil.spabs.getKey(id) : null; } private final Function FN_GET_SPAB = new Function() {