1
0
forked from 0ad/0ad
0ad/binaries/data/mods/public/gui/gamesetup/gamesetup.js
historic_bruno 0dfc72bc2c Fixes filter behavior in setup when switching map type.
Fixes broken texture in Latium rms.
Adds demo keyword to rms test.
Fixes default rms lighting with new model.

This was SVN commit r9127.
2011-03-27 22:00:49 +00:00

1235 lines
32 KiB
JavaScript

////////////////////////////////////////////////////////////////////////////////////////////////
// Constants
// TODO: Move some of these into common location (other scripts may need)
const MAP_SIZES_TEXT = ["Tiny (2 player)", "Small (3 player)", "Medium (4 player)", "Normal (6 player)", "Large (8 player)", "Very Large", "Giant"];
const MAP_SIZES_DATA = [8, 12, 16, 20, 24, 28, 32];
// Max number of players for any map
const MAX_PLAYERS = 8;
////////////////////////////////////////////////////////////////////////////////////////////////
// Is this is a networked game, or offline
var g_IsNetworked;
// Is this user in control of game settings (i.e. is a network server, or offline player)
var g_IsController;
// Are we currently updating the GUI in response to network messages instead of user input
// (and therefore shouldn't send further messages to the network)
var g_IsInGuiUpdate;
var g_PlayerAssignments = {};
// Default game setup attributes
var g_DefaultPlayerData = [];
var g_GameAttributes = {
mapType: "",
map: "",
mapPath: "",
settings: {
Size: 12,
Seed: 0,
BaseTerrain: "grass1_spring",
BaseHeight: 0,
PlayerData: [],
RevealMap: false,
LockTeams: false,
GameType: "conquest"
}
}
var g_AIs = [];
var g_ChatMessages = [];
// Data caches
var g_MapData = {};
var g_CivData = {};
var g_MapFilters = [];
// To prevent the display locking up while we load the map metadata,
// we'll start with a 'loading' message and switch to the main screen in the
// tick handler
var g_LoadingState = 0; // 0 = not started, 1 = loading, 2 = loaded
////////////////////////////////////////////////////////////////////////////////////////////////
function init(attribs)
{
switch (attribs.type)
{
case "offline":
g_IsNetworked = false;
g_IsController = true;
break;
case "server":
g_IsNetworked = true;
g_IsController = true;
break;
case "client":
g_IsNetworked = true;
g_IsController = false;
break;
default:
error("Unexpected 'type' in gamesetup init: "+attribs.type);
}
}
// Called after the map data is loaded and cached
function initMain()
{
// Load AI list
g_AIs = Engine.GetAIs();
// Sort AIs by displayed name
g_AIs.sort(function (a, b) {
return a.data.name < b.data.name ? -1 : b.data.name < a.data.name ? +1 : 0;
});
// Get default player data - remove gaia
g_DefaultPlayerData = initPlayerDefaults();
g_DefaultPlayerData.shift();
// Build player settings using defaults
g_GameAttributes.settings.PlayerData = g_DefaultPlayerData;
// Init civs
initCivNameList();
// Init map types
var mapTypes = getGUIObjectByName("mapTypeSelection");
mapTypes.list = ["Scenario","Random"];
mapTypes.list_data = ["scenario","random"];
// Setup map filters - will appear in order they are added
addFilter("Default", function(settings) { return settings && !keywordTestOR(settings.Keywords, ["demo", "hidden"]); });
addFilter("Demo Maps", function(settings) { return settings && keywordTestAND(settings.Keywords, ["demo"]); });
addFilter("Old Maps", function(settings) { return !settings; });
addFilter("All Maps", function(settings) { return true; });
// Populate map filters dropdown
var mapFilters = getGUIObjectByName("mapFilterSelection");
mapFilters.list = getFilters();
g_GameAttributes.mapFilter = "Default";
// Setup controls for host only
if (g_IsController)
{
// Generate seed for random maps
// TODO: There's probably a better way to do this
g_GameAttributes.settings.Seed = Math.floor(Math.random() * 65536);
// Set a default map
// TODO: This should be remembered from the last session
if (!g_IsNetworked)
{
g_GameAttributes.map = "Death Canyon";
}
else
{
g_GameAttributes.map = "Median Oasis";
}
mapTypes.selected = 0;
mapFilters.selected = 0;
initMapNameList();
var numPlayersSelect = getGUIObjectByName("numPlayersSelection");
var players = [];
for (var i = 1; i <= MAX_PLAYERS; ++i)
players.push(i);
numPlayersSelect.list = players;
numPlayersSelect.list_data = players;
numPlayersSelect.selected = MAX_PLAYERS - 1;
var victoryConditions = getGUIObjectByName("victoryCondition");
victoryConditions.list = ["Conquest", "None"];
victoryConditions.list_data = ["conquest", "endless"];
victoryConditions.onSelectionChange = function()
{ // Update attributes so other players can see change
if (this.selected != -1)
{
g_GameAttributes.settings.GameType = this.list_data[this.selected];
}
if (!g_IsInGuiUpdate)
{
updateGameAttributes();
}
};
victoryConditions.selected = -1;
var mapSize = getGUIObjectByName("mapSize");
mapSize.list = MAP_SIZES_TEXT;
mapSize.list_data = MAP_SIZES_DATA;
mapSize.onSelectionChange = function()
{ // Update attributes so other players can see change
if (this.selected != -1)
{
g_GameAttributes.settings.Size = this.list_data[this.selected];
}
if (!g_IsInGuiUpdate)
{
updateGameAttributes();
}
};
mapSize.selected = 1;
getGUIObjectByName("revealMap").onPress = function()
{ // Update attributes so other players can see change
g_GameAttributes.settings.RevealMap = this.checked;
if (!g_IsInGuiUpdate)
{
updateGameAttributes();
}
};
getGUIObjectByName("lockTeams").onPress = function()
{ // Update attributes so other players can see change
g_GameAttributes.settings.LockTeams = this.checked;
if (!g_IsInGuiUpdate)
{
updateGameAttributes();
}
};
}
else
{
// If we're a network client, disable all the map controls
// TODO: make them look visually disabled so it's obvious why they don't work
getGUIObjectByName("mapTypeSelection").hidden = true;
getGUIObjectByName("mapFilterSelection").hidden = true;
getGUIObjectByName("mapSelection").enabled = false;
// Disable player and game options controls
// TODO: Shouldn't players be able to choose their own assignment?
for (var i = 0; i < MAX_PLAYERS; ++i)
{
getGUIObjectByName("playerAssignment["+i+"]").enabled = false;
getGUIObjectByName("playerCiv["+i+"]").hidden = true;
getGUIObjectByName("playerTeam["+i+"]").hidden = true;
}
getGUIObjectByName("numPlayersBox").hidden = true;
// Disable "start game" button
// TODO: Perhaps replace this with a "ready" button, and require host to wait?
getGUIObjectByName("startGame").enabled = false;
}
// Set up offline-only bits:
if (!g_IsNetworked)
{
getGUIObjectByName("chatPanel").hidden = true;
}
// Settings for all possible player slots
var boxSpacing = 32;
for (var i = 0; i < MAX_PLAYERS; ++i)
{
// Space player boxes
var box = getGUIObjectByName("playerBox["+i+"]");
var boxSize = box.size;
var h = boxSize.bottom - boxSize.top;
boxSize.top = i * boxSpacing;
boxSize.bottom = i * boxSpacing + h;
box.size = boxSize;
// Populate team dropdowns
var team = getGUIObjectByName("playerTeam["+i+"]");
team.list = ["None", "1", "2", "3", "4"];
team.list_data = [-1, 0, 1, 2, 3];
team.selected = 0;
let playerSlot = i; // declare for inner function use
team.onSelectionChange = function()
{ // Update team
if (this.selected != -1)
{
g_GameAttributes.settings.PlayerData[playerSlot].Team = this.selected - 1;
}
if (!g_IsInGuiUpdate)
{
updateGameAttributes();
}
};
// Set events
var civ = getGUIObjectByName("playerCiv["+i+"]");
civ.onSelectionChange = function()
{ // Update civ
if (this.selected != -1)
{
g_GameAttributes.settings.PlayerData[playerSlot].Civ = this.list_data[this.selected];
}
if (!g_IsInGuiUpdate)
{
updateGameAttributes();
}
};
}
if (g_IsNetworked)
{
// For multiplayer, focus the chat input box by default
getGUIObjectByName("chatInput").focus();
}
else
{
// For single-player, focus the map list by default,
// to allow easy keyboard selection of maps
getGUIObjectByName("mapSelection").focus();
}
}
function handleNetMessage(message)
{
log("Net message: "+uneval(message));
switch (message.type)
{
case "netstatus":
switch (message.status)
{
case "disconnected":
Engine.DisconnectNetworkGame();
Engine.PopGuiPage();
reportDisconnect(message.reason);
break;
default:
error("Unrecognised netstatus type "+message.status);
break;
}
break;
case "gamesetup":
if (message.data) // (the host gets undefined data on first connect, so skip that)
{
g_GameAttributes = message.data;
}
onGameAttributesChange();
break;
case "players":
// Find and report all joinings/leavings
for (var host in message.hosts)
{
if (! g_PlayerAssignments[host])
{
addChatMessage({ "type": "connect", "username": message.hosts[host].name });
}
}
for (var host in g_PlayerAssignments)
{
if (! message.hosts[host])
{
addChatMessage({ "type": "disconnect", "guid": host });
}
}
// Update the player list
g_PlayerAssignments = message.hosts;
updatePlayerList();
break;
case "start":
Engine.SwitchGuiPage("page_loading.xml", {
"attribs": g_GameAttributes,
"isNetworked" : g_IsNetworked,
"playerAssignments": g_PlayerAssignments
});
break;
case "chat":
addChatMessage({ "type": "message", "guid": message.guid, "text": message.text });
break;
default:
error("Unrecognised net message type "+message.type);
}
}
// Get display name from map data
function getMapDisplayName(map)
{
var mapData = loadMapData(map);
if (!mapData || !mapData.settings || !mapData.settings.Name)
{ // Give some msg that map format is unsupported
log("Map data missing in scenario '"+map+"' - likely unsupported format");
return map;
}
return mapData.settings.Name;
}
// Get a setting if it exists or return default
function getSetting(settings, defaults, property)
{
if (settings && (property in settings))
{
return settings[property];
}
// Use defaults
if (defaults && (property in defaults))
{
return defaults[property];
}
return undefined;
}
// Initialize the dropdowns containing all the available civs
function initCivNameList()
{
// Cache civ data
g_CivData = loadCivData();
var civList = [ { "name": civ.Name, "code": civ.Code } for each (civ in g_CivData) ];
// Alphabetically sort the list, ignoring case
civList.sort(sortNameIgnoreCase);
var civListNames = [ civ.name for each (civ in civList) ];
var civListCodes = [ civ.code for each (civ in civList) ];
// Update the dropdowns
for (var i = 0; i < MAX_PLAYERS; ++i)
{
var civ = getGUIObjectByName("playerCiv["+i+"]");
civ.list = civListNames;
civ.list_data = civListCodes;
civ.selected = 0;
}
}
// Initialise the list control containing all the available maps
function initMapNameList()
{
// Get a list of map filenames
// TODO: Should verify these are valid maps before adding to list
var mapSelectionBox = getGUIObjectByName("mapSelection")
var mapFiles;
switch (g_GameAttributes.mapType)
{
case "scenario":
mapFiles = getXMLFileList(g_GameAttributes.mapPath);
break;
case "random":
mapFiles = getJSONFileList(g_GameAttributes.mapPath);
break;
default:
error("initMapNameList: Unexpected map type '"+g_GameAttributes.mapType+"'");
return;
}
// Apply map filter, if any defined
var mapList = [];
for (var i = 0; i < mapFiles.length; ++i)
{
var file = mapFiles[i];
var mapData = loadMapData(file);
if (g_GameAttributes.mapFilter && mapData && testFilter(g_GameAttributes.mapFilter, mapData.settings))
{
mapList.push({ "name": getMapDisplayName(file), "file": file });
}
}
// Alphabetically sort the list, ignoring case
mapList.sort(sortNameIgnoreCase);
var mapListNames = [ map.name for each (map in mapList) ];
var mapListFiles = [ map.file for each (map in mapList) ];
// Select the default map
var selected = mapListFiles.indexOf(g_GameAttributes.map);
// Default to the first element if list is not empty and we can't find the one we searched for
if (selected == -1 && mapList.length)
{
selected = 0;
}
// Update the list control
mapSelectionBox.list = mapListNames;
mapSelectionBox.list_data = mapListFiles;
mapSelectionBox.selected = selected;
}
function loadMapData(name)
{
if (!name)
{
return undefined;
}
if (!g_MapData[name])
{
switch (g_GameAttributes.mapType)
{
case "scenario":
g_MapData[name] = Engine.LoadMapSettings(g_GameAttributes.mapPath+name);
break;
case "random":
g_MapData[name] = parseJSONData(g_GameAttributes.mapPath+name+".json");
break;
default:
error("loadMapData: Unexpected map type '"+g_GameAttributes.mapType+"'");
return undefined;
}
}
return g_MapData[name];
}
////////////////////////////////////////////////////////////////////////////////////////////////
// GUI event handlers
function cancelSetup()
{
Engine.DisconnectNetworkGame();
}
function onTick()
{
// First tick happens before first render, so don't load yet
if (g_LoadingState == 0)
{
g_LoadingState++;
}
else if (g_LoadingState == 1)
{
getGUIObjectByName("loadingWindow").hidden = true;
getGUIObjectByName("setupWindow").hidden = false;
initMain();
g_LoadingState++;
}
else if (g_LoadingState == 2)
{
while (true)
{
var message = Engine.PollNetworkClient();
if (!message)
{
break;
}
handleNetMessage(message);
}
}
}
// Called when user selects number of players
function selectNumPlayers(num)
{
// Avoid recursion
if (g_IsInGuiUpdate)
{
return;
}
// Network clients can't change number of players
if (g_IsNetworked && !g_IsController)
{
return;
}
// Only meaningful for random maps
if (g_GameAttributes.mapType != "random")
{
return;
}
// Update player data
var pData = g_GameAttributes.settings.PlayerData;
if (pData && num < pData.length)
{ // Remove extra player data
g_GameAttributes.settings.PlayerData = pData.slice(0, num);
}
else
{ // Add player data from defaults
for (var i = pData.length; i < num; ++i)
g_GameAttributes.settings.PlayerData.push(g_DefaultPlayerData[i]);
}
updateGameAttributes();
}
// Called when the user selects a map type from the list
function selectMapType(type)
{
// Avoid recursion
if (g_IsInGuiUpdate)
{
return;
}
// Network clients can't change map type
if (g_IsNetworked && !g_IsController)
{
return;
}
g_GameAttributes.mapType = type;
g_GameAttributes.map = undefined;
// Clear old map data
g_MapData = {};
// Select correct path
switch (g_GameAttributes.mapType)
{
case "scenario":
g_GameAttributes.mapPath = "maps/scenarios/";
break;
case "random":
g_GameAttributes.mapPath = "maps/random/";
break;
default:
error("selectMapType: Unexpected map type '"+g_GameAttributes.mapType+"'");
return;
}
initMapNameList();
updateGameAttributes();
}
function selectMapFilter(filterName)
{
// Avoid recursion
if (g_IsInGuiUpdate)
{
return;
}
// Network clients can't change map filter
if (g_IsNetworked && !g_IsController)
{
return;
}
g_GameAttributes.mapFilter = filterName;
initMapNameList();
updateGameAttributes();
}
// Called when the user selects a map from the list
function selectMap(name)
{
// Avoid recursion
if (g_IsInGuiUpdate)
{
return;
}
// Network clients can't change map
if (g_IsNetworked && !g_IsController)
{
return;
}
// Return if we have no map
if (!name)
{
return;
}
g_GameAttributes.map = name;
var mapData = loadMapData(name);
var mapSettings = (mapData && mapData.settings ? mapData.settings : {});
// Copy any new settings
switch (g_GameAttributes.mapType)
{
case "scenario":
g_GameAttributes.settings.PlayerData = (mapSettings.PlayerData ? mapSettings.PlayerData : g_DefaultPlayerData);
break;
case "random":
g_GameAttributes.script = mapSettings.Script;
g_GameAttributes.settings.Size = getSetting(mapSettings, g_GameAttributes.settings, "Size");
g_GameAttributes.settings.BaseTerrain = getSetting(mapSettings, g_GameAttributes.settings, "BaseTerrain");
g_GameAttributes.settings.BaseHeight = getSetting(mapSettings, g_GameAttributes.settings, "BaseHeight");
break;
default:
error("selectMap: Unexpected map type '"+g_GameAttributes.mapType+"'");
return;
}
g_GameAttributes.settings.Description = getSetting(mapSettings, g_GameAttributes.settings, "Description");
g_GameAttributes.settings.RevealMap = getSetting(mapSettings, g_GameAttributes.settings, "RevealMap");
g_GameAttributes.settings.LockTeams = getSetting(mapSettings, g_GameAttributes.settings, "LockTeams");
g_GameAttributes.settings.GameType = getSetting(mapSettings, g_GameAttributes.settings, "GameType");
g_GameAttributes.settings.CircularMap = mapSettings.CircularMap;
// Reset player assignments on map change
if (!g_IsNetworked)
{ // Slot 1
g_PlayerAssignments = { "local": { "name": "You", "player": 1, "civ": "", "team": -1} };
}
else
{
var numPlayers = (mapSettings.PlayerData ? mapSettings.PlayerData.length : g_GameAttributes.settings.PlayerData.length);
for (var guid in g_PlayerAssignments)
{ // Unassign extra players
var player = g_PlayerAssignments[guid].player;
if (player <= MAX_PLAYERS && player > numPlayers)
{
Engine.AssignNetworkPlayer(player, "");
}
}
}
updateGameAttributes();
}
function launchGame()
{
if (g_IsNetworked && !g_IsController)
{
error("Only host can start game");
return;
}
// Check that we have a map
if (!g_GameAttributes.map)
{
return;
}
if (g_IsNetworked)
{
Engine.SetNetworkGameAttributes(g_GameAttributes);
Engine.StartNetworkGame();
}
else
{
// Find the player ID which the user has been assigned to
var numPlayers = g_GameAttributes.settings.PlayerData.length;
var playerID = -1;
for (var i = 0; i < numPlayers; ++i)
{
var assignBox = getGUIObjectByName("playerAssignment["+i+"]");
if (assignBox.list_data[assignBox.selected] == "local")
{
playerID = i+1;
}
}
// Remove extra player data
g_GameAttributes.settings.PlayerData = g_GameAttributes.settings.PlayerData.slice(0, numPlayers);
Engine.StartGame(g_GameAttributes, playerID);
Engine.SwitchGuiPage("page_loading.xml", {
"attribs": g_GameAttributes,
"isNetworked" : g_IsNetworked,
"playerAssignments": g_PlayerAssignments
});
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
function onGameAttributesChange()
{
g_IsInGuiUpdate = true;
// Don't set any attributes here, just show the changes in GUI
var mapName = g_GameAttributes.map || "";
var mapSettings = g_GameAttributes.settings;
var numPlayers = (mapSettings.PlayerData ? mapSettings.PlayerData.length : MAX_PLAYERS);
// Update some controls for clients
if (!g_IsController)
{
var mapFilterHeading = getGUIObjectByName("mapFilterHeading");
mapFilterHeading.caption = "Map Filter: "+g_GameAttributes.mapFilter;
var mapTypeSelection = getGUIObjectByName("mapTypeSelection");
var mapTypeHeading = getGUIObjectByName("mapTypeHeading");
var idx = mapTypeSelection.list_data.indexOf(g_GameAttributes.mapType);
mapTypeHeading.caption = "Match Type: "+mapTypeSelection.list[idx];
var mapSelectionBox = getGUIObjectByName("mapSelection");
mapSelectionBox.selected = mapSelectionBox.list_data.indexOf(mapName);
initMapNameList();
}
// Controls common to all map types
var revealMap = getGUIObjectByName("revealMap");
var victoryCondition = getGUIObjectByName("victoryCondition");
var lockTeams = getGUIObjectByName("lockTeams");
var mapSize = getGUIObjectByName("mapSize");
var revealMapText = getGUIObjectByName("revealMapText");
var victoryConditionText = getGUIObjectByName("victoryConditionText");
var lockTeamsText = getGUIObjectByName("lockTeamsText");
var mapSizeText = getGUIObjectByName("mapSizeText");
var numPlayersBox = getGUIObjectByName("numPlayersBox");
// Handle map type specific logic
switch (g_GameAttributes.mapType)
{
case "random":
var sizeIdx = MAP_SIZES_DATA.indexOf(mapSettings.Size);
if (g_IsController)
{ //Host
getGUIObjectByName("numPlayersSelection").selected = numPlayers - 1;
numPlayersBox.hidden = false;
mapSize.hidden = false;
revealMap.hidden = false;
victoryCondition.hidden = false;
lockTeams.hidden = false;
mapSizeText.caption = "Map size:";
revealMapText.caption = "Reveal map:";
revealMap.checked = (mapSettings.RevealMap ? true : false);
victoryConditionText.caption = "Victory condition:";
victoryCondition.selected = victoryCondition.list_data.indexOf(mapSettings.GameType);
lockTeamsText.caption = "Teams locked:";
lockTeams.checked = (mapSettings.LockTeams === undefined || mapSettings.LockTeams ? true : false);
}
else
{ // Client
mapSizeText.caption = "Map size: " + (sizeIdx != -1 ? MAP_SIZES_TEXT[sizeIdx] : "Other (" + mapSettings.Size + ")");
revealMapText.caption = "Reveal map: " + (mapSettings.RevealMap ? "Yes" : "No");
victoryConditionText.caption = "Victory condition: " + (mapSettings.GameType && mapSettings.GameType == "endless" ? "None" : "Conquest");
lockTeamsText.caption = "Teams locked: " + (mapSettings.LockTeams === undefined || mapSettings.LockTeams ? "Yes" : "No");
}
break;
case "scenario":
// For scenario just reflect settings for the current map
numPlayersBox.hidden = true;
mapSize.hidden = true;
revealMap.hidden = true;
victoryCondition.hidden = true;
lockTeams.hidden = true;
mapSizeText.caption = "Map size: Default";
revealMapText.caption = "Reveal map: " + (mapSettings.RevealMap ? "Yes" : "No");
victoryConditionText.caption = "Victory condition: " + (mapSettings.GameType && mapSettings.GameType == "endless" ? "None" : "Conquest");
lockTeamsText.caption = "Teams locked: " + (mapSettings.LockTeams === undefined || mapSettings.LockTeams ? "Yes" : "No");
break;
default:
error("onGameAttributesChange: Unexpected map type '"+g_GameAttributes.mapType+"'");
return;
}
// Display map name
getGUIObjectByName("mapInfoName").caption = getMapDisplayName(mapName);
// Load the description from the map file, if there is one
var description = mapSettings.Description || "Sorry, no description available.";
// Describe the number of players
var playerString = numPlayers + " " + (numPlayers == 1 ? "player" : "players") + ". ";
for (var i = 0; i < MAX_PLAYERS; ++i)
{
// Show only needed player slots
getGUIObjectByName("playerBox["+i+"]").hidden = (i >= numPlayers);
// Show player data or defaults as necessary
if (i < numPlayers)
{
var pName = getGUIObjectByName("playerName["+i+"]");
var pCiv = getGUIObjectByName("playerCiv["+i+"]");
var pCivText = getGUIObjectByName("playerCivText["+i+"]");
var pTeam = getGUIObjectByName("playerTeam["+i+"]");
var pTeamText = getGUIObjectByName("playerTeamText["+i+"]");
var pColor = getGUIObjectByName("playerColour["+i+"]");
// Player data / defaults
var pData = mapSettings.PlayerData ? mapSettings.PlayerData[i] : {};
var pDefs = g_DefaultPlayerData ? g_DefaultPlayerData[i] : {};
// Common to all game types
var color = iColorToString(getSetting(pData, pDefs, "Colour"));
pColor.sprite = "colour:"+color+" 100";
pName.caption = getSetting(pData, pDefs, "Name");
var team = getSetting(pData, pDefs, "Team");
var civ = getSetting(pData, pDefs, "Civ");
// For clients or scenarios, hide some player dropdowns
if (!g_IsController || g_GameAttributes.mapType == "scenario")
{
pCivText.hidden = false;
pCiv.hidden = true;
pTeamText.hidden = false;
pTeam.hidden = true;
// Set text values
pCivText.caption = g_CivData[civ].Name;
pTeamText.caption = (team !== undefined && team >= 0) ? team+1 : "-";
}
else if (g_GameAttributes.mapType == "random")
{
pCivText.hidden = true;
pCiv.hidden = false;
pTeamText.hidden = true;
pTeam.hidden = false;
// Set dropdown values
pCiv.selected = (civ ? pCiv.list_data.indexOf(civ) : 0);
pTeam.selected = (team !== undefined && team >= 0) ? team+1 : 0;
}
}
}
getGUIObjectByName("mapInfoDescription").caption = playerString + description;
g_IsInGuiUpdate = false;
// Game attributes include AI settings, so update the player list
updatePlayerList();
}
function updateGameAttributes()
{
if (g_IsNetworked)
{
Engine.SetNetworkGameAttributes(g_GameAttributes);
}
else
{
onGameAttributesChange();
}
}
function updatePlayerList()
{
g_IsInGuiUpdate = true;
var hostNameList = [];
var hostGuidList = [];
var assignments = [];
var aiAssignments = {};
var noAssignment;
for (var guid in g_PlayerAssignments)
{
var name = g_PlayerAssignments[guid].name;
var hostID = hostNameList.length;
var player = g_PlayerAssignments[guid].player;
hostNameList.push(name);
hostGuidList.push(guid);
assignments[player] = hostID;
}
for each (var ai in g_AIs)
{ // Give AI a different color so it stands out
aiAssignments[ai.id] = hostNameList.length;
hostNameList.push("[color=\"30 90 30 255\"]AI: " + ai.data.name);
hostGuidList.push("ai:" + ai.id);
}
noAssignment = hostNameList.length;
hostNameList.push("[color=\"90 90 90 255\"]Unassigned");
hostGuidList.push("");
for (var i = 0; i < MAX_PLAYERS; ++i)
{
let playerSlot = i;
let playerID = i+1; // we don't show Gaia, so first slot is ID 1
var selection = assignments[playerID];
var configButton = getGUIObjectByName("playerConfig["+i+"]");
configButton.hidden = true;
// Look for valid player slots
if (playerSlot < g_GameAttributes.settings.PlayerData.length)
{
// If no human is assigned, look for an AI instead
if (selection === undefined)
{
var aiId = g_GameAttributes.settings.PlayerData[playerSlot].AI;
if (aiId)
{
selection = aiAssignments[aiId];
}
else
{
selection = noAssignment;
}
// Since no human is assigned, show the AI config button
if (g_IsController)
{
configButton.hidden = false;
configButton.onpress = function()
{
Engine.PushGuiPage("page_aiconfig.xml", {
ais: g_AIs,
id: g_GameAttributes.settings.PlayerData[playerSlot].AI,
callback: function(ai) {
g_GameAttributes.settings.PlayerData[playerSlot].AI = ai.id;
if (g_IsNetworked)
{
Engine.SetNetworkGameAttributes(g_GameAttributes);
}
else
{
updatePlayerList();
}
}
});
};
}
}
else
{
// There was a human, so make sure we don't have any AI left
// over in their slot, if we're in charge of the attributes
if (g_IsController && g_GameAttributes.settings.PlayerData[playerSlot].AI && g_GameAttributes.settings.PlayerData[playerSlot].AI != "")
{
g_GameAttributes.settings.PlayerData[playerSlot].AI = "";
if (g_IsNetworked)
{
Engine.SetNetworkGameAttributes(g_GameAttributes);
}
}
}
var assignBox = getGUIObjectByName("playerAssignment["+i+"]");
assignBox.list = hostNameList;
assignBox.list_data = hostGuidList;
if (assignBox.selected != selection)
{
assignBox.selected = selection;
}
if (g_IsNetworked && g_IsController)
{
assignBox.onselectionchange = function ()
{
if (!g_IsInGuiUpdate)
{
var guid = hostGuidList[this.selected];
if (guid == "")
{
// Unassign any host from this player slot
Engine.AssignNetworkPlayer(playerID, "");
// Remove AI from this player slot
g_GameAttributes.settings.PlayerData[playerSlot].AI = "";
}
else if (guid.substr(0, 3) == "ai:")
{
// Unassign any host from this player slot
Engine.AssignNetworkPlayer(playerID, "");
// Set the AI for this player slot
g_GameAttributes.settings.PlayerData[playerSlot].AI = guid.substr(3);
}
else
{
// Reassign the host to this player slot
Engine.AssignNetworkPlayer(playerID, guid);
// Remove AI from this player slot
g_GameAttributes.settings.PlayerData[playerSlot].AI = "";
}
Engine.SetNetworkGameAttributes(g_GameAttributes);
}
};
}
else if (!g_IsNetworked)
{
assignBox.onselectionchange = function ()
{
if (!g_IsInGuiUpdate)
{
var guid = hostGuidList[this.selected];
if (guid == "")
{
// Remove AI from this player slot
g_GameAttributes.settings.PlayerData[playerSlot].AI = "";
}
else if (guid.substr(0, 3) == "ai:")
{
// Set the AI for this player slot
g_GameAttributes.settings.PlayerData[playerSlot].AI = guid.substr(3);
}
else
{
// Update the selected host's player ID
g_PlayerAssignments[guid].player = playerID;
// Remove AI from this player slot
g_GameAttributes.settings.PlayerData[playerSlot].AI = "";
}
updatePlayerList();
}
};
}
}
}
g_IsInGuiUpdate = false;
}
function submitChatInput()
{
var input = getGUIObjectByName("chatInput");
var text = input.caption;
if (text.length)
{
Engine.SendNetworkChat(text);
input.caption = "";
}
}
function addChatMessage(msg)
{
var username = escapeText(msg.username || g_PlayerAssignments[msg.guid].name);
var message = escapeText(msg.text);
// TODO: Maybe host should have distinct font/color?
var color = "0 0 0";
if (g_PlayerAssignments[msg.guid] && g_PlayerAssignments[msg.guid].player != 255)
{ // Valid player who has been assigned - get player colour
var player = g_PlayerAssignments[msg.guid].player - 1;
var mapName = g_GameAttributes.map;
var mapData = loadMapData(mapName);
var mapSettings = (mapData && mapData.settings ? mapData.settings : {});
var pData = mapSettings.PlayerData ? mapSettings.PlayerData[player] : {};
var pDefs = g_DefaultPlayerData ? g_DefaultPlayerData[player] : {};
color = iColorToString(getSetting(pData, pDefs, "Colour"));
}
var formatted;
switch (msg.type)
{
case "connect":
formatted = '[font="serif-bold-13"][color="'+ color +'"]' + username + '[/color][/font] [color="64 64 64"]has joined[/color]';
break;
case "disconnect":
formatted = '[font="serif-bold-13"][color="'+ color +'"]' + username + '[/color][/font] [color="64 64 64"]has left[/color]';
break;
case "message":
formatted = '[font="serif-bold-13"]<[color="'+ color +'"]' + username + '[/color]>[/font] ' + message;
break;
default:
error("Invalid chat message '" + uneval(msg) + "'");
return;
}
g_ChatMessages.push(formatted);
getGUIObjectByName("chatText").caption = g_ChatMessages.join("\n");
}
////////////////////////////////////////////////////////////////////////////////////////////////
// Basic map filters API
// Add a new map list filter
function addFilter(name, filterFunc)
{
if (filterFunc instanceof Object)
{ // Basic validity test
var newFilter = {};
newFilter.name = name;
newFilter.filter = filterFunc;
g_MapFilters.push(newFilter);
}
else
{
error("Invalid map filter: "+name);
}
}
// Get array of map filter names
function getFilters()
{
var filters = [];
for (var i = 0; i < g_MapFilters.length; ++i)
{
filters.push(g_MapFilters[i].name);
}
return filters;
}
// Test map filter on given map settings object
function testFilter(name, mapSettings)
{
for (var i = 0; i < g_MapFilters.length; ++i)
{
if (g_MapFilters[i].name == name)
{ // Found filter
return g_MapFilters[i].filter(mapSettings);
}
}
error("Invalid map filter: "+name);
return false;
}
// Test an array of keywords against a match array using AND logic
function keywordTestAND(keywords, matches)
{
if (!keywords || !matches)
{
return false;
}
for (var m = 0; m < matches.length; ++m)
{ // Fail on not match
if (keywords.indexOf(matches[m]) == -1)
{
return false;
}
}
return true;
}
// Test an array of keywords against a match array using OR logic
function keywordTestOR(keywords, matches)
{
if (!keywords || !matches)
{
return false;
}
for (var m = 0; m < matches.length; ++m)
{ // Success on match
if (keywords.indexOf(matches[m]) != -1)
{
return true;
}
}
return false;
}