1
0
forked from 0ad/0ad

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:
JoshuaJB 2014-03-16 23:29:27 +00:00
parent 94c02ec33c
commit 883f307b40
10 changed files with 116 additions and 64 deletions

View File

@ -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;

View File

@ -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?";

View File

@ -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);

View File

@ -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;
}
}

View File

@ -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]);
}
}
/**

View File

@ -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"

View File

@ -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);

View File

@ -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++)

View File

@ -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()

View File

@ -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])