From 883f307b40926cb70e2cf9c6cf7eda7e2afd3909 Mon Sep 17 00:00:00 2001 From: JoshuaJB Date: Sun, 16 Mar 2014 23:29:27 +0000 Subject: [PATCH] Implement a basic observer mode. Any player which joins a multiplayer game but is not assigned a player slot automatically becomes an observer. Refs #69 This was SVN commit r14849. --- .../mods/public/gui/gamesetup/gamesetup.js | 6 +- binaries/data/mods/public/gui/session/menu.js | 2 +- .../data/mods/public/gui/session/messages.js | 5 +- .../public/gui/session/selection_details.js | 24 ++-- .../data/mods/public/gui/session/session.js | 103 ++++++++++++------ .../data/mods/public/gui/session/session.xml | 26 +++-- .../simulation/components/PlayerManager.js | 4 + .../simulation/components/ResourceSupply.js | 4 - source/network/NetMessages.h | 2 +- .../components/CCmpRangeManager.cpp | 4 +- 10 files changed, 116 insertions(+), 64 deletions(-) diff --git a/binaries/data/mods/public/gui/gamesetup/gamesetup.js b/binaries/data/mods/public/gui/gamesetup/gamesetup.js index 64bd881b29..f236ae4cd2 100644 --- a/binaries/data/mods/public/gui/gamesetup/gamesetup.js +++ b/binaries/data/mods/public/gui/gamesetup/gamesetup.js @@ -1225,7 +1225,7 @@ function updatePlayerList() hostGuidList.push(guid); assignments[player] = hostID; - if (player != 255) + if (player != -1) assignedCount++; } @@ -1386,7 +1386,7 @@ function swapPlayers(guid, newSlot) // Attempt to swap the player or AI occupying the target slot, // if any, into the slot this player is currently in. - if (playerID != 255) + if (playerID != -1) { for (var i in g_PlayerAssignments) { @@ -1433,7 +1433,7 @@ function addChatMessage(msg) // TODO: Maybe host should have distinct font/color? var color = "white"; - if (g_PlayerAssignments[msg.guid] && g_PlayerAssignments[msg.guid].player != 255) + if (g_PlayerAssignments[msg.guid] && g_PlayerAssignments[msg.guid].player != -1) { // Valid player who has been assigned - get player colour var player = g_PlayerAssignments[msg.guid].player - 1; var mapName = g_GameAttributes.map; diff --git a/binaries/data/mods/public/gui/session/menu.js b/binaries/data/mods/public/gui/session/menu.js index 5c305ad1a7..d252f40588 100644 --- a/binaries/data/mods/public/gui/session/menu.js +++ b/binaries/data/mods/public/gui/session/menu.js @@ -151,7 +151,7 @@ function exitMenuButton() var btCode = [leaveGame, resumeGame]; var message = "Are you sure you want to quit? Leaving will disconnect all other players."; } - else if (g_IsNetworked && !g_GameEnded) + else if (g_IsNetworked && !g_GameEnded && !g_IsObserver) { var btCode = [networkReturnQuestion, resumeGame]; var message = "Are you sure you want to quit?"; diff --git a/binaries/data/mods/public/gui/session/messages.js b/binaries/data/mods/public/gui/session/messages.js index b472055274..18d00df385 100644 --- a/binaries/data/mods/public/gui/session/messages.js +++ b/binaries/data/mods/public/gui/session/messages.js @@ -270,7 +270,7 @@ function submitChatInput() var isCheat = false; if (text.length) { - if (g_Players[Engine.GetPlayerID()].cheatsEnabled) + if (!g_IsObserver && g_Players[Engine.GetPlayerID()].cheatsEnabled) { for each (var cheat in Object.keys(cheats)) { @@ -350,6 +350,9 @@ function addChatMessage(msg, playerAssignments) if (playerAssignments[msg.guid]) { var n = playerAssignments[msg.guid].player; + // Observers have an ID of -1 which is not a valid index. + if (n < 0) + n = 0; playerColor = g_Players[n].color.r + " " + g_Players[n].color.g + " " + g_Players[n].color.b; username = escapeText(playerAssignments[msg.guid].name); diff --git a/binaries/data/mods/public/gui/session/selection_details.js b/binaries/data/mods/public/gui/session/selection_details.js index 47da1871f1..223b8f8f67 100644 --- a/binaries/data/mods/public/gui/session/selection_details.js +++ b/binaries/data/mods/public/gui/session/selection_details.js @@ -306,13 +306,13 @@ function updateSelectionDetails() g_Selection.update(); var selection = g_Selection.toList(); - + if (selection.length == 0) { Engine.GetGUIObjectByName("detailsAreaMultiple").hidden = true; Engine.GetGUIObjectByName("detailsAreaSingle").hidden = true; hideUnitCommands(); - + supplementalDetailsPanel.hidden = true; detailsPanel.hidden = true; commandsPanel.hidden = true; @@ -334,11 +334,21 @@ function updateSelectionDetails() else displayMultiple(selection, template); - // Show Panels - supplementalDetailsPanel.hidden = false; + // Show basic details. detailsPanel.hidden = false; - commandsPanel.hidden = false; - // Fill out commands panel for specific unit selected (or first unit of primary group) - updateUnitCommands(entState, supplementalDetailsPanel, commandsPanel, selection); + if (g_IsObserver) + { + // Observers don't need these displayed. + supplementalDetailsPanel.hidden = true; + commandsPanel.hidden = true; + } + else + { + // Fill out commands panel for specific unit selected (or first unit of primary group) + updateUnitCommands(entState, supplementalDetailsPanel, commandsPanel, selection); + // Show panels + supplementalDetailsPanel.hidden = false; + commandsPanel.hidden = false; + } } diff --git a/binaries/data/mods/public/gui/session/session.js b/binaries/data/mods/public/gui/session/session.js index f06b96c692..7cebbe856e 100644 --- a/binaries/data/mods/public/gui/session/session.js +++ b/binaries/data/mods/public/gui/session/session.js @@ -5,6 +5,8 @@ var g_IsNetworked = false; var g_IsController; // Match ID for tracking var g_MatchID; +// Is this user an observer? +var g_IsObserver = false; // Cache the basic player data (name, civ, color) var g_Players = []; @@ -151,7 +153,29 @@ function init(initData, hotloadData) // Cache civ data g_CivData = loadCivData(); - g_CivData["gaia"] = { "Code": "gaia", "Name": "Gaia" }; + g_CivData["gaia"] = { "Code": "gaia", "Name": "Gaia"}; + + if (Engine.GetPlayerID() <= 0) + { + g_IsObserver = true; + // Hide stuff observers don't use. + Engine.GetGUIObjectByName("food").hidden = true; + Engine.GetGUIObjectByName("wood").hidden = true; + Engine.GetGUIObjectByName("stone").hidden = true; + Engine.GetGUIObjectByName("metal").hidden = true; + Engine.GetGUIObjectByName("population").hidden = true; + Engine.GetGUIObjectByName("diplomacyButton1").hidden = true; + Engine.GetGUIObjectByName("tradeButton1").hidden = true; + Engine.GetGUIObjectByName("menuResignButton").enabled = false; + Engine.GetGUIObjectByName("pauseButton").enabled = false; + Engine.GetGUIObjectByName("observerText").hidden = false; + } + else + { + // TODO: Get a civ icon for gaia/observers. + Engine.GetGUIObjectByName("civIcon").sprite = "stretched:" + g_CivData[g_Players[Engine.GetPlayerID()].civ].Emblem; + Engine.GetGUIObjectByName("civIcon").tooltip = g_CivData[g_Players[Engine.GetPlayerID()].civ].Name; + } g_GameSpeeds = initGameSpeeds(); g_CurrentSpeed = Engine.GetSimRate(); @@ -161,9 +185,6 @@ function init(initData, hotloadData) var idx = g_GameSpeeds.speeds.indexOf(g_CurrentSpeed); gameSpeed.selected = idx != -1 ? idx : g_GameSpeeds["default"]; gameSpeed.onSelectionChange = function() { changeGameSpeed(+this.list_data[this.selected]); } - - Engine.GetGUIObjectByName("civIcon").sprite = "stretched:" + g_CivData[g_Players[Engine.GetPlayerID()].civ].Emblem; - Engine.GetGUIObjectByName("civIcon").tooltip = g_CivData[g_Players[Engine.GetPlayerID()].civ].Name; initMenuPosition(); // set initial position // Populate player selection dropdown @@ -191,9 +212,11 @@ function init(initData, hotloadData) else { // Starting for the first time: - var civMusic = g_CivData[g_Players[Engine.GetPlayerID()].civ].Music; initMusic(); - global.music.storeTracks(civMusic); + if (!g_IsObserver){ + var civMusic = g_CivData[g_Players[Engine.GetPlayerID()].civ].Music; + global.music.storeTracks(civMusic); + } global.music.setState(global.music.states.PEACE); playRandomAmbient("temperate"); } @@ -214,7 +237,7 @@ function init(initData, hotloadData) function selectViewPlayer(playerID) { Engine.SetPlayerID(playerID); - if (playerID != 0) { + if (playerID > 0) { Engine.GetGUIObjectByName("civIcon").sprite = "stretched:" + g_CivData[g_Players[playerID].civ].Emblem; Engine.GetGUIObjectByName("civIcon").tooltip = g_CivData[g_Players[playerID].civ].Name; } @@ -266,31 +289,40 @@ function resignGame(leaveGameAfterResign) function leaveGame(willRejoin) { var extendedSimState = Engine.GuiInterfaceCall("GetExtendedSimulationState"); - var playerState = extendedSimState.players[Engine.GetPlayerID()]; var mapSettings = Engine.GetMapSettings(); var gameResult; - if (g_Disconnected) + if (g_IsObserver) { - gameResult = "You have been disconnected." + // Observers don't win/lose. + gameResult = "You have left the game."; + global.music.setState(global.music.states.VICTORY); } - else if (playerState.state == "won") + else { - gameResult = "You have won the battle!"; - } - else if (playerState.state == "defeated") - { - gameResult = "You have been defeated..."; - } - else // "active" - { - global.music.setState(global.music.states.DEFEAT); - if (willRejoin) - gameResult = "You have left the game."; - else + var playerState = extendedSimState.players[Engine.GetPlayerID()]; + if (g_Disconnected) { - gameResult = "You have abandoned the game."; - resignGame(true); + gameResult = "You have been disconnected." + } + else if (playerState.state == "won") + { + gameResult = "You have won the battle!"; + } + else if (playerState.state == "defeated") + { + gameResult = "You have been defeated..."; + } + else // "active" + { + global.music.setState(global.music.states.DEFEAT); + if (willRejoin) + gameResult = "You have left the game."; + else + { + gameResult = "You have abandoned the game."; + resignGame(true); + } } } @@ -372,7 +404,8 @@ function onTick() onSimulationUpdate(); // Display rally points for selected buildings - Engine.GuiInterfaceCall("DisplayRallyPoint", { "entities": g_Selection.toList() }); + if (!g_IsObserver) + Engine.GuiInterfaceCall("DisplayRallyPoint", { "entities": g_Selection.toList() }); } // Run timers @@ -401,7 +434,7 @@ function onTick() function checkPlayerState() { // Once the game ends, we're done here. - if (g_GameEnded) + if (g_GameEnded || g_IsObserver) return; // Send a game report for each player in this game. @@ -499,16 +532,18 @@ function onSimulationUpdate() updateDebug(); updatePlayerDisplay(); updateSelectionDetails(); - updateResearchDisplay(); updateBuildingPlacementPreview(); updateTimeElapsedCounter(); - updateTimeNotifications(); + updateTimeNotifications(); - // Update music state on basis of battle state. - var battleState = Engine.GuiInterfaceCall("GetBattleState", Engine.GetPlayerID()); - if (battleState) - global.music.setState(global.music.states[battleState]); - + if (!g_IsObserver) + { + updateResearchDisplay(); + // Update music state on basis of battle state. + var battleState = Engine.GuiInterfaceCall("GetBattleState", Engine.GetPlayerID()); + if (battleState) + global.music.setState(global.music.states[battleState]); + } } /** diff --git a/binaries/data/mods/public/gui/session/session.xml b/binaries/data/mods/public/gui/session/session.xml index 2c159151c2..14442f48c8 100644 --- a/binaries/data/mods/public/gui/session/session.xml +++ b/binaries/data/mods/public/gui/session/session.xml @@ -625,31 +625,31 @@ size="10 0 45% 100%" > - + - + - + - + - + @@ -665,10 +665,16 @@ selectViewPlayer(this.selected); - - - - + + + + + + + + + + @@ -1323,7 +1329,7 @@ -