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.
This commit is contained in:
parent
94c02ec33c
commit
883f307b40
@ -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;
|
||||
|
@ -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?";
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -625,31 +625,31 @@
|
||||
size="10 0 45% 100%"
|
||||
>
|
||||
<!-- Food -->
|
||||
<object size="0 0 90 100%" type="image" style="resourceCounter" tooltip="Food" tooltip_style="sessionToolTipBold">
|
||||
<object name="food" size="0 0 90 100%" type="image" style="resourceCounter" tooltip="Food" tooltip_style="sessionToolTipBold">
|
||||
<object size="0 -4 40 36" type="image" sprite="stretched:session/icons/resources/food.png" ghost="true"/>
|
||||
<object size="32 0 100% 100%-2" type="text" style="resourceText" name="resourceFood"/>
|
||||
</object>
|
||||
|
||||
<!-- Wood -->
|
||||
<object size="90 0 180 100%" type="image" style="resourceCounter" tooltip="Wood" tooltip_style="sessionToolTipBold">
|
||||
<object name="wood" size="90 0 180 100%" type="image" style="resourceCounter" tooltip="Wood" tooltip_style="sessionToolTipBold">
|
||||
<object size="0 -4 40 36" type="image" sprite="stretched:session/icons/resources/wood.png" ghost="true"/>
|
||||
<object size="32 0 100% 100%-2" type="text" style="resourceText" name="resourceWood"/>
|
||||
</object>
|
||||
|
||||
<!-- Stone -->
|
||||
<object size="180 0 270 100%" type="image" style="resourceCounter" tooltip="Stone" tooltip_style="sessionToolTipBold">
|
||||
<object name="stone" size="180 0 270 100%" type="image" style="resourceCounter" tooltip="Stone" tooltip_style="sessionToolTipBold">
|
||||
<object size="0 -4 40 36" type="image" sprite="stretched:session/icons/resources/stone.png" ghost="true"/>
|
||||
<object size="32 0 100% 100%-2" type="text" style="resourceText" name="resourceStone"/>
|
||||
</object>
|
||||
|
||||
<!-- Metal -->
|
||||
<object size="270 0 360 100%" type="image" style="resourceCounter" tooltip="Metal" tooltip_style="sessionToolTipBold">
|
||||
<object name="metal" size="270 0 360 100%" type="image" style="resourceCounter" tooltip="Metal" tooltip_style="sessionToolTipBold">
|
||||
<object size="0 -4 40 36" type="image" sprite="stretched:session/icons/resources/metal.png" ghost="true"/>
|
||||
<object size="32 0 100% 100%-2" type="text" style="resourceText" name="resourceMetal"/>
|
||||
</object>
|
||||
|
||||
<!-- Population -->
|
||||
<object size="360 0 450 100%" type="image" style="resourceCounter" tooltip="Population (current / limit)" tooltip_style="sessionToolTipBold">
|
||||
<object name="population" size="360 0 450 100%" type="image" style="resourceCounter" tooltip="Population (current / limit)" tooltip_style="sessionToolTipBold">
|
||||
<object size="0 -4 40 34" type="image" sprite="stretched:session/icons/resources/population.png" ghost="true"/>
|
||||
<object size="32 0 100% 100%-2" type="text" style="resourceText" name="resourcePop"/>
|
||||
</object>
|
||||
@ -665,10 +665,16 @@
|
||||
<action on="SelectionChange">selectViewPlayer(this.selected);</action>
|
||||
</object>
|
||||
|
||||
<!-- ================================ ================================ -->
|
||||
<!-- Phase -->
|
||||
<!-- ================================ ================================ -->
|
||||
<!--<object size="50%+50 4 50%+300 100%-2" name="PhaseTitleBar" type="text" font="serif-bold-stroke-14" textcolor="white"> Death Match :: Village Phase</object>-->
|
||||
<!-- ================================ ================================ -->
|
||||
<!-- Phase -->
|
||||
<!-- ================================ ================================ -->
|
||||
<!--<object size="50%+50 4 50%+300 100%-2" name="PhaseTitleBar" type="text" font="serif-bold-stroke-14" textcolor="white"> Death Match :: Village Phase</object>-->
|
||||
|
||||
<!-- ================================ ================================ -->
|
||||
<!-- Observer Mode Warning -->
|
||||
<!-- ================================ ================================ -->
|
||||
<object size="50 4 50% 100%-2" name="observerText" type="text" style="ModernLabelText" text_align="left" hidden="true">Observer Mode (experimental)</object>
|
||||
|
||||
|
||||
|
||||
<!-- ================================ ================================ -->
|
||||
@ -1323,7 +1329,7 @@
|
||||
<!-- ================================ ================================ -->
|
||||
<!-- Network status -->
|
||||
<!-- ================================ ================================ -->
|
||||
<object name="netStatus" type="text" style="netStatus" z="100" hidden="true">
|
||||
<object name="netStatus" type="text" style="netStatus" z="200" hidden="true">
|
||||
<object type="button"
|
||||
name="disconnectedExitButton"
|
||||
style="StoneButton"
|
||||
|
@ -25,6 +25,10 @@ PlayerManager.prototype.GetPlayerByID = function(id)
|
||||
if (id in this.playerEntities)
|
||||
return this.playerEntities[id];
|
||||
|
||||
// All players at or below ID 0 get gaia-level data. (Observers for example)
|
||||
if (id <= 0)
|
||||
return this.playerEntities[0];
|
||||
|
||||
var stack = new Error().stack.trimRight().replace(/^/mg, ' '); // indent each line
|
||||
warn("GetPlayerByID: no player defined for id '"+id+"'\n"+stack);
|
||||
|
||||
|
@ -80,10 +80,6 @@ ResourceSupply.prototype.GetMaxGatherers = function()
|
||||
|
||||
ResourceSupply.prototype.GetGatherers = function()
|
||||
{
|
||||
//if (player === undefined)
|
||||
// return this.gatherers;
|
||||
//
|
||||
//
|
||||
var numPlayers = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager).GetNumPlayers();
|
||||
var total = [];
|
||||
for (var playerid = 0; playerid <= numPlayers; playerid++)
|
||||
|
@ -122,7 +122,7 @@ START_NMT_CLASS_(PlayerAssignment, NMT_PLAYER_ASSIGNMENT)
|
||||
NMT_START_ARRAY(m_Hosts)
|
||||
NMT_FIELD(CStr8, m_GUID)
|
||||
NMT_FIELD(CStrW, m_Name)
|
||||
NMT_FIELD_INT(m_PlayerID, u8, 1)
|
||||
NMT_FIELD_INT(m_PlayerID, i8, 1)
|
||||
NMT_END_ARRAY()
|
||||
END_NMT_CLASS()
|
||||
|
||||
|
@ -1398,10 +1398,8 @@ public:
|
||||
virtual bool GetLosRevealAll(player_id_t player)
|
||||
{
|
||||
// Special player value can force reveal-all for every player
|
||||
if(m_LosRevealAll[MAX_LOS_PLAYER_ID+1])
|
||||
if (m_LosRevealAll[MAX_LOS_PLAYER_ID+1] || player == -1)
|
||||
return true;
|
||||
if (player == -1)
|
||||
return false;
|
||||
ENSURE(player >= 0 && player <= MAX_LOS_PLAYER_ID+1);
|
||||
// Otherwise check the player-specific flag
|
||||
if (m_LosRevealAll[player])
|
||||
|
Loading…
Reference in New Issue
Block a user