forked from 0ad/0ad
1949 lines
62 KiB
JavaScript
1949 lines
62 KiB
JavaScript
////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Constants
|
|
const DEFAULT_NETWORKED_MAP = "Acropolis 01";
|
|
const DEFAULT_OFFLINE_MAP = "Acropolis 01";
|
|
|
|
const VICTORY_DEFAULTIDX = 1;
|
|
|
|
// TODO: Move these somewhere like simulation\data\game_types.json, Atlas needs them too
|
|
// Translation: Type of victory condition.
|
|
const POPULATION_CAP = ["50", "100", "150", "200", "250", "300", translate("Unlimited")];
|
|
const POPULATION_CAP_DATA = [50, 100, 150, 200, 250, 300, 10000];
|
|
const POPULATION_CAP_DEFAULTIDX = 5;
|
|
// Translation: Amount of starting resources.
|
|
const STARTING_RESOURCES = [translateWithContext("startingResources", "Very Low"), translateWithContext("startingResources", "Low"), translateWithContext("startingResources", "Medium"), translateWithContext("startingResources", "High"), translateWithContext("startingResources", "Very High"), translateWithContext("startingResources", "Deathmatch")];
|
|
const STARTING_RESOURCES_DATA = [100, 300, 500, 1000, 3000, 50000];
|
|
const STARTING_RESOURCES_DEFAULTIDX = 1;
|
|
// 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;
|
|
|
|
// Server name, if user is a server, connected to the multiplayer lobby
|
|
var g_ServerName;
|
|
|
|
// 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;
|
|
|
|
// Is this user ready
|
|
var g_IsReady;
|
|
|
|
// There are some duplicate orders on init, we can ignore these [bool].
|
|
var g_ReadyInit = true;
|
|
|
|
// If no one has changed ready status, we have no need to spam the settings changed message.
|
|
// 2 - Host's initial ready, suppressed settings message, 1 - Will show settings message, <=0 - Suppressed settings message
|
|
var g_ReadyChanged = 2;
|
|
|
|
// Has the game started?
|
|
var g_GameStarted = false;
|
|
|
|
var g_PlayerAssignments = {};
|
|
|
|
// Default game setup attributes
|
|
var g_DefaultPlayerData = [];
|
|
var g_GameAttributes = {
|
|
settings: {}
|
|
};
|
|
|
|
var g_GameSpeeds = {};
|
|
var g_MapSizes = {};
|
|
|
|
var g_AIs = [];
|
|
|
|
var g_ChatMessages = [];
|
|
|
|
// Data caches
|
|
var g_MapData = {};
|
|
var g_CivData = {};
|
|
|
|
var g_MapFilters = [];
|
|
|
|
// Current number of assigned human players.
|
|
var g_AssignedCount = 0;
|
|
|
|
// 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
|
|
|
|
// Filled by scripts in victory_conditions/
|
|
var g_VictoryConditions = {};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
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(sprintf("Unexpected 'type' in gamesetup init: %(unexpectedType)s", { unexpectedType: attribs.type }));
|
|
}
|
|
|
|
if (attribs.serverName)
|
|
g_ServerName = attribs.serverName;
|
|
|
|
// Init the Cancel Button caption and tooltip
|
|
var cancelButton = Engine.GetGUIObjectByName("cancelGame");
|
|
if (!Engine.HasXmppClient())
|
|
cancelButton.tooltip = translate("Return to the main menu.");
|
|
else
|
|
cancelButton.tooltip = translate("Return to the lobby.");
|
|
}
|
|
|
|
// 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();
|
|
for (var i = 0; i < g_DefaultPlayerData.length; ++i)
|
|
g_DefaultPlayerData[i].Civ = "random";
|
|
|
|
g_GameSpeeds = initGameSpeeds();
|
|
g_MapSizes = initMapSizes();
|
|
|
|
// Init civs
|
|
initCivNameList();
|
|
|
|
// Init map types
|
|
var mapTypes = Engine.GetGUIObjectByName("mapTypeSelection");
|
|
mapTypes.list = [translateWithContext("map", "Skirmish"), translateWithContext("map", "Random"), translate("Scenario")];
|
|
mapTypes.list_data = ["skirmish","random","scenario"];
|
|
|
|
// Setup map filters - will appear in order they are added
|
|
addFilter("default", translate("Default"), function(settings) { return settings && (settings.Keywords === undefined || !keywordTestOR(settings.Keywords, ["naval", "demo", "hidden"])); });
|
|
addFilter("naval", translate("Naval Maps"), function(settings) { return settings && settings.Keywords !== undefined && keywordTestAND(settings.Keywords, ["naval"]); });
|
|
addFilter("demo", translate("Demo Maps"), function(settings) { return settings && settings.Keywords !== undefined && keywordTestAND(settings.Keywords, ["demo"]); });
|
|
addFilter("all", translate("All Maps"), function(settings) { return true; });
|
|
|
|
// Populate map filters dropdown
|
|
var mapFilters = Engine.GetGUIObjectByName("mapFilterSelection");
|
|
mapFilters.list = getFilterNames();
|
|
mapFilters.list_data = getFilterIds();
|
|
g_GameAttributes.mapFilter = "default";
|
|
|
|
// Setup controls for host only
|
|
if (g_IsController)
|
|
{
|
|
mapTypes.selected = 0;
|
|
mapFilters.selected = 0;
|
|
|
|
// Create a unique ID for this match, to be used for identifying the same game reports
|
|
// for the lobby.
|
|
g_GameAttributes.matchID = Engine.GetMatchID();
|
|
|
|
initMapNameList();
|
|
|
|
var numPlayersSelection = Engine.GetGUIObjectByName("numPlayersSelection");
|
|
var players = [];
|
|
for (var i = 1; i <= MAX_PLAYERS; ++i)
|
|
players.push(i);
|
|
numPlayersSelection.list = players;
|
|
numPlayersSelection.list_data = players;
|
|
numPlayersSelection.selected = MAX_PLAYERS - 1;
|
|
|
|
var gameSpeed = Engine.GetGUIObjectByName("gameSpeed");
|
|
gameSpeed.hidden = false;
|
|
Engine.GetGUIObjectByName("gameSpeedText").hidden = true;
|
|
gameSpeed.list = g_GameSpeeds.names;
|
|
gameSpeed.list_data = g_GameSpeeds.speeds;
|
|
gameSpeed.onSelectionChange = function() {
|
|
if (this.selected != -1)
|
|
g_GameAttributes.gameSpeed = g_GameSpeeds.speeds[this.selected];
|
|
|
|
updateGameAttributes();
|
|
}
|
|
gameSpeed.selected = g_GameSpeeds["default"];
|
|
|
|
var populationCaps = Engine.GetGUIObjectByName("populationCap");
|
|
populationCaps.list = POPULATION_CAP;
|
|
populationCaps.list_data = POPULATION_CAP_DATA;
|
|
populationCaps.selected = POPULATION_CAP_DEFAULTIDX;
|
|
populationCaps.onSelectionChange = function() {
|
|
if (this.selected != -1)
|
|
g_GameAttributes.settings.PopulationCap = POPULATION_CAP_DATA[this.selected];
|
|
|
|
updateGameAttributes();
|
|
}
|
|
|
|
var startingResourcesL = Engine.GetGUIObjectByName("startingResources");
|
|
startingResourcesL.list = STARTING_RESOURCES;
|
|
startingResourcesL.list_data = STARTING_RESOURCES_DATA;
|
|
startingResourcesL.selected = STARTING_RESOURCES_DEFAULTIDX;
|
|
startingResourcesL.onSelectionChange = function() {
|
|
if (this.selected != -1)
|
|
g_GameAttributes.settings.StartingResources = STARTING_RESOURCES_DATA[this.selected];
|
|
|
|
updateGameAttributes();
|
|
}
|
|
|
|
var victoryConditions = Engine.GetGUIObjectByName("victoryCondition");
|
|
var victories = getVictoryConditions();
|
|
victoryConditions.list = victories.text;
|
|
victoryConditions.list_data = victories.data;
|
|
victoryConditions.onSelectionChange = function() {
|
|
if (this.selected != -1)
|
|
{
|
|
g_GameAttributes.settings.GameType = victories.data[this.selected];
|
|
g_GameAttributes.settings.VictoryScripts = victories.scripts[this.selected];
|
|
}
|
|
|
|
updateGameAttributes();
|
|
};
|
|
victoryConditions.selected = VICTORY_DEFAULTIDX;
|
|
|
|
var mapSize = Engine.GetGUIObjectByName("mapSize");
|
|
mapSize.list = g_MapSizes.names;
|
|
mapSize.list_data = g_MapSizes.tiles;
|
|
mapSize.onSelectionChange = function() {
|
|
if (this.selected != -1)
|
|
g_GameAttributes.settings.Size = g_MapSizes.tiles[this.selected];
|
|
updateGameAttributes();
|
|
};
|
|
mapSize.selected = 0;
|
|
|
|
Engine.GetGUIObjectByName("revealMap").onPress = function() {
|
|
g_GameAttributes.settings.RevealMap = this.checked;
|
|
updateGameAttributes();
|
|
};
|
|
|
|
Engine.GetGUIObjectByName("exploreMap").onPress = function() {
|
|
g_GameAttributes.settings.ExploreMap = this.checked;
|
|
updateGameAttributes();
|
|
};
|
|
|
|
Engine.GetGUIObjectByName("disableTreasures").onPress = function() {
|
|
g_GameAttributes.settings.DisableTreasures = this.checked;
|
|
updateGameAttributes();
|
|
};
|
|
|
|
Engine.GetGUIObjectByName("lockTeams").onPress = function() {
|
|
g_GameAttributes.settings.LockTeams = this.checked;
|
|
updateGameAttributes();
|
|
};
|
|
|
|
Engine.GetGUIObjectByName("enableCheats").onPress = function() {
|
|
g_GameAttributes.settings.CheatsEnabled = this.checked;
|
|
updateGameAttributes();
|
|
};
|
|
|
|
Engine.GetGUIObjectByName("enableRating").onPress = function() {
|
|
g_GameAttributes.settings.RatingEnabled = this.checked;
|
|
Engine.SetRankedGame(this.checked);
|
|
Engine.GetGUIObjectByName("enableCheats").enabled = !this.checked;
|
|
Engine.GetGUIObjectByName("lockTeams").enabled = !this.checked;
|
|
updateGameAttributes();
|
|
};
|
|
}
|
|
else
|
|
{
|
|
// If we're a network client, disable all the map controls
|
|
Engine.GetGUIObjectByName("mapTypeSelection").hidden = true;
|
|
Engine.GetGUIObjectByName("mapTypeText").hidden = false;
|
|
Engine.GetGUIObjectByName("mapFilterSelection").hidden = true;
|
|
Engine.GetGUIObjectByName("mapFilterText").hidden = false;
|
|
Engine.GetGUIObjectByName("mapSelectionText").hidden = false;
|
|
Engine.GetGUIObjectByName("mapSelection").hidden = true;
|
|
Engine.GetGUIObjectByName("victoryConditionText").hidden = false;
|
|
Engine.GetGUIObjectByName("victoryCondition").hidden = true;
|
|
Engine.GetGUIObjectByName("gameSpeedText").hidden = false;
|
|
Engine.GetGUIObjectByName("gameSpeed").hidden = true;
|
|
|
|
// 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)
|
|
{
|
|
Engine.GetGUIObjectByName("playerAssignment["+i+"]").hidden = true;
|
|
Engine.GetGUIObjectByName("playerCiv["+i+"]").hidden = true;
|
|
Engine.GetGUIObjectByName("playerTeam["+i+"]").hidden = true;
|
|
}
|
|
|
|
Engine.GetGUIObjectByName("numPlayersSelection").hidden = true;
|
|
Engine.GetGUIObjectByName("startGame").enabled = true;
|
|
}
|
|
|
|
// Set up multiplayer/singleplayer bits:
|
|
if (!g_IsNetworked)
|
|
{
|
|
Engine.GetGUIObjectByName("chatPanel").hidden = true;
|
|
Engine.GetGUIObjectByName("enableCheats").checked = true;
|
|
g_GameAttributes.settings.CheatsEnabled = true;
|
|
}
|
|
else
|
|
{
|
|
Engine.GetGUIObjectByName("optionCheats").hidden = false;
|
|
Engine.GetGUIObjectByName("enableCheats").checked = false;
|
|
g_GameAttributes.settings.CheatsEnabled = false;
|
|
// Setup ranked option if we are connected to the lobby.
|
|
if (Engine.HasXmppClient())
|
|
{
|
|
Engine.GetGUIObjectByName("optionRating").hidden = false;
|
|
Engine.GetGUIObjectByName("enableRating").checked = Engine.IsRankedGame();
|
|
g_GameAttributes.settings.RatingEnabled = Engine.IsRankedGame();
|
|
// We force locked teams and disabled cheats in ranked games.
|
|
Engine.GetGUIObjectByName("enableCheats").enabled = !Engine.IsRankedGame();
|
|
Engine.GetGUIObjectByName("lockTeams").enabled = !Engine.IsRankedGame();
|
|
}
|
|
if (g_IsController)
|
|
{
|
|
Engine.GetGUIObjectByName("enableCheatsText").hidden = true;
|
|
Engine.GetGUIObjectByName("enableCheats").hidden = false;
|
|
if (Engine.HasXmppClient())
|
|
{
|
|
Engine.GetGUIObjectByName("enableRatingText").hidden = true;
|
|
Engine.GetGUIObjectByName("enableRating").hidden = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Settings for all possible player slots
|
|
var boxSpacing = 32;
|
|
for (var i = 0; i < MAX_PLAYERS; ++i)
|
|
{
|
|
// Space player boxes
|
|
var box = Engine.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 = Engine.GetGUIObjectByName("playerTeam["+i+"]");
|
|
team.list = [translateWithContext("team", "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() {
|
|
if (this.selected != -1)
|
|
g_GameAttributes.settings.PlayerData[playerSlot].Team = this.selected - 1;
|
|
|
|
updateGameAttributes();
|
|
};
|
|
|
|
// Set events
|
|
var civ = Engine.GetGUIObjectByName("playerCiv["+i+"]");
|
|
civ.onSelectionChange = function() {
|
|
if ((this.selected != -1)&&(g_GameAttributes.mapType !== "scenario"))
|
|
g_GameAttributes.settings.PlayerData[playerSlot].Civ = this.list_data[this.selected];
|
|
|
|
updateGameAttributes();
|
|
};
|
|
}
|
|
|
|
if (g_IsNetworked)
|
|
{
|
|
// For multiplayer, focus the chat input box by default
|
|
Engine.GetGUIObjectByName("chatInput").focus();
|
|
}
|
|
else
|
|
{
|
|
// For single-player, focus the map list by default,
|
|
// to allow easy keyboard selection of maps
|
|
Engine.GetGUIObjectByName("mapSelection").focus();
|
|
}
|
|
|
|
if (g_IsController)
|
|
{
|
|
loadGameAttributes();
|
|
// Sync g_GameAttributes to everyone.
|
|
if (g_IsInGuiUpdate)
|
|
warn("initMain() called while in GUI update");
|
|
|
|
updateGameAttributes();
|
|
}
|
|
}
|
|
|
|
function handleNetMessage(message)
|
|
{
|
|
log("Net message: "+uneval(message));
|
|
|
|
switch (message.type)
|
|
{
|
|
case "netstatus":
|
|
switch (message.status)
|
|
{
|
|
case "disconnected":
|
|
cancelSetup();
|
|
if (Engine.HasXmppClient())
|
|
Engine.SwitchGuiPage("page_lobby.xml");
|
|
else
|
|
Engine.SwitchGuiPage("page_pregame.xml");
|
|
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;
|
|
|
|
// Validate some settings for rated games.
|
|
if (g_GameAttributes.settings.RatingEnabled)
|
|
{
|
|
// Cheats can never be on in rated games.
|
|
g_GameAttributes.settings.CheatsEnabled = false;
|
|
// Teams must be locked in rated games.
|
|
g_GameAttributes.settings.LockTeams = true;
|
|
}
|
|
}
|
|
|
|
onGameAttributesChange();
|
|
break;
|
|
|
|
case "players":
|
|
var resetReady = false;
|
|
var newPlayer = "";
|
|
// Find and report all joinings/leavings
|
|
for (var host in message.hosts)
|
|
{
|
|
if (!g_PlayerAssignments[host])
|
|
{
|
|
// If we have extra player slots and we are the controller, give the player an ID.
|
|
if (g_IsController && message.hosts[host].player === -1 && g_AssignedCount < g_GameAttributes.settings.PlayerData.length)
|
|
Engine.AssignNetworkPlayer(g_AssignedCount + 1, host);
|
|
addChatMessage({ "type": "connect", "username": message.hosts[host].name });
|
|
newPlayer = host;
|
|
}
|
|
}
|
|
|
|
for (var host in g_PlayerAssignments)
|
|
{
|
|
if (!message.hosts[host])
|
|
{
|
|
addChatMessage({ "type": "disconnect", "guid": host });
|
|
if (g_PlayerAssignments[host].player != -1)
|
|
resetReady = true; // Observers shouldn't reset ready.
|
|
}
|
|
}
|
|
|
|
// Update the player list
|
|
g_PlayerAssignments = message.hosts;
|
|
updatePlayerList();
|
|
if (g_PlayerAssignments[newPlayer] && g_PlayerAssignments[newPlayer].player != -1)
|
|
resetReady = true;
|
|
|
|
if (resetReady)
|
|
resetReadyData(); // Observers shouldn't reset ready.
|
|
updateReadyUI();
|
|
if (g_IsController)
|
|
sendRegisterGameStanza();
|
|
break;
|
|
|
|
case "start":
|
|
if (g_IsController && Engine.HasXmppClient())
|
|
{
|
|
var players = [ assignment.name for each (assignment in g_PlayerAssignments) ]
|
|
Engine.SendChangeStateGame(Object.keys(g_PlayerAssignments).length, players.join(", "));
|
|
}
|
|
Engine.SwitchGuiPage("page_loading.xml", {
|
|
"attribs": g_GameAttributes,
|
|
"isNetworked" : g_IsNetworked,
|
|
"playerAssignments": g_PlayerAssignments,
|
|
"isController": g_IsController
|
|
});
|
|
break;
|
|
|
|
case "chat":
|
|
addChatMessage({ "type": "message", "guid": message.guid, "text": message.text });
|
|
break;
|
|
|
|
// Singular client to host message
|
|
case "ready":
|
|
g_ReadyChanged -= 1;
|
|
if (g_ReadyChanged < 1 && g_PlayerAssignments[message.guid].player != -1)
|
|
addChatMessage({ "type": "ready", "guid": message.guid, "ready": +message.status == 1 });
|
|
if (!g_IsController)
|
|
break;
|
|
g_PlayerAssignments[message.guid].status = +message.status == 1;
|
|
Engine.SetNetworkPlayerStatus(message.guid, +message.status);
|
|
updateReadyUI();
|
|
break;
|
|
|
|
default:
|
|
error("Unrecognised net message type "+message.type);
|
|
}
|
|
}
|
|
|
|
function getMapDisplayName(map)
|
|
{
|
|
var mapData = loadMapData(map);
|
|
if (!mapData || !mapData.settings || !mapData.settings.Name)
|
|
{
|
|
log("Map data missing in scenario '"+map+"' - likely unsupported format");
|
|
return map;
|
|
}
|
|
|
|
return mapData.settings.Name;
|
|
}
|
|
|
|
function getMapPreview(map)
|
|
{
|
|
var mapData = loadMapData(map);
|
|
if (!mapData || !mapData.settings || !mapData.settings.Preview)
|
|
return "nopreview.png";
|
|
|
|
return mapData.settings.Preview;
|
|
}
|
|
|
|
// 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();
|
|
|
|
// Extract name/code, and skip civs that are explicitly disabled
|
|
// (intended for unusable incomplete civs)
|
|
var civList = [
|
|
{ "name": civ.Name, "code": civ.Code }
|
|
for each (civ in g_CivData)
|
|
if (civ.SelectableInGameSetup !== false)
|
|
];
|
|
|
|
// 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) ];
|
|
|
|
// Add random civ to beginning of list
|
|
civListNames.unshift('[color="orange"]' + translateWithContext("civilization", "Random") + '[/color]');
|
|
civListCodes.unshift("random");
|
|
|
|
// Update the dropdowns
|
|
for (var i = 0; i < MAX_PLAYERS; ++i)
|
|
{
|
|
var civ = Engine.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 = Engine.GetGUIObjectByName("mapSelection")
|
|
var mapFiles;
|
|
|
|
switch (g_GameAttributes.mapType)
|
|
{
|
|
case "scenario":
|
|
case "skirmish":
|
|
mapFiles = getXMLFileList(g_GameAttributes.mapPath);
|
|
break;
|
|
|
|
case "random":
|
|
mapFiles = getJSONFileList(g_GameAttributes.mapPath);
|
|
break;
|
|
|
|
default:
|
|
error(sprintf("initMapNameList: Unexpected map type '%(mapType)s'", { mapType: g_GameAttributes.mapType }));
|
|
return;
|
|
}
|
|
|
|
// Apply map filter, if any defined
|
|
var mapList = [];
|
|
for (var i = 0; i < mapFiles.length; ++i)
|
|
{
|
|
var file = g_GameAttributes.mapPath + 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
|
|
translateObjectKeys(mapList, ["name"]);
|
|
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
|
|
if (g_GameAttributes.mapType == "random")
|
|
{
|
|
mapListNames.unshift("[color=\"orange\"]" + translateWithContext("map", "Random") + "[/color]");
|
|
mapListFiles.unshift("random");
|
|
}
|
|
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":
|
|
case "skirmish":
|
|
g_MapData[name] = Engine.LoadMapSettings(name);
|
|
break;
|
|
|
|
case "random":
|
|
if (name == "random")
|
|
// To be defined later.
|
|
g_MapData[name] = { settings: { "Name": "", "Description": "" } };
|
|
else
|
|
g_MapData[name] = Engine.ReadJSONFile(name+".json");
|
|
break;
|
|
|
|
default:
|
|
error(sprintf("loadMapData: Unexpected map type '%(mapType)s'", { mapType: g_GameAttributes.mapType }));
|
|
return undefined;
|
|
}
|
|
}
|
|
|
|
return g_MapData[name];
|
|
}
|
|
|
|
const FILEPATH_MATCHSETTINGS_SP = "config/matchsettings.json";
|
|
const FILEPATH_MATCHSETTINGS_MP = "config/matchsettings.mp.json";
|
|
function loadGameAttributes()
|
|
{
|
|
if (Engine.ConfigDB_GetValue("user", "persistmatchsettings") != "true")
|
|
return;
|
|
|
|
var settingsFile = g_IsNetworked ? FILEPATH_MATCHSETTINGS_MP : FILEPATH_MATCHSETTINGS_SP;
|
|
if (!Engine.FileExists(settingsFile))
|
|
return;
|
|
|
|
var attrs = Engine.ReadJSONFile(settingsFile);
|
|
if (!attrs || !attrs.settings)
|
|
return;
|
|
|
|
g_IsInGuiUpdate = true;
|
|
|
|
var mapName = attrs.map || "";
|
|
var mapSettings = attrs.settings;
|
|
|
|
g_GameAttributes = attrs;
|
|
|
|
// Assign new seeds and match id
|
|
g_GameAttributes.matchID = Engine.GetMatchID();
|
|
mapSettings.Seed = Math.floor(Math.random() * 65536);
|
|
mapSettings.AISeed = Math.floor(Math.random() * 65536);
|
|
|
|
// Ensure that cheats are enabled in singleplayer
|
|
if (!g_IsNetworked)
|
|
mapSettings.CheatsEnabled = true;
|
|
|
|
var aiCodes = [ ai.id for each (ai in g_AIs) ];
|
|
var civListCodes = [ civ.Code for each (civ in g_CivData) if (civ.SelectableInGameSetup !== false) ];
|
|
civListCodes.push("random");
|
|
|
|
var playerData = mapSettings.PlayerData;
|
|
|
|
// Validate player civs
|
|
if (playerData)
|
|
{
|
|
for (var i = 0; i < playerData.length; ++i)
|
|
{
|
|
if (civListCodes.indexOf(playerData[i].Civ) < 0)
|
|
playerData[i].Civ = "random";
|
|
}
|
|
}
|
|
|
|
// Refresh probably obsoleted/incomplete map data.
|
|
var newMapData = loadMapData(mapName);
|
|
if (newMapData && newMapData.settings)
|
|
{
|
|
for (var prop in newMapData.settings)
|
|
mapSettings[prop] = newMapData.settings[prop];
|
|
|
|
// Set player data
|
|
if (playerData)
|
|
mapSettings.PlayerData = playerData;
|
|
}
|
|
|
|
var mapFilterSelection = Engine.GetGUIObjectByName("mapFilterSelection");
|
|
mapFilterSelection.selected = mapFilterSelection.list_data.indexOf(attrs.mapFilter);
|
|
|
|
var mapTypeSelection = Engine.GetGUIObjectByName("mapTypeSelection");
|
|
mapTypeSelection.selected = mapTypeSelection.list_data.indexOf(attrs.mapType);
|
|
|
|
initMapNameList();
|
|
|
|
var mapSelectionBox = Engine.GetGUIObjectByName("mapSelection");
|
|
mapSelectionBox.selected = mapSelectionBox.list_data.indexOf(mapName);
|
|
|
|
if (mapSettings.PopulationCap)
|
|
{
|
|
var populationCapBox = Engine.GetGUIObjectByName("populationCap");
|
|
populationCapBox.selected = populationCapBox.list_data.indexOf(mapSettings.PopulationCap);
|
|
}
|
|
if (mapSettings.StartingResources)
|
|
{
|
|
var startingResourcesBox = Engine.GetGUIObjectByName("startingResources");
|
|
startingResourcesBox.selected = startingResourcesBox.list_data.indexOf(mapSettings.StartingResources);
|
|
}
|
|
|
|
if (attrs.gameSpeed)
|
|
{
|
|
var gameSpeedBox = Engine.GetGUIObjectByName("gameSpeed");
|
|
gameSpeedBox.selected = g_GameSpeeds.speeds.indexOf(attrs.gameSpeed);
|
|
}
|
|
|
|
if (!Engine.HasXmppClient())
|
|
{
|
|
g_GameAttributes.settings.RatingEnabled = false;
|
|
Engine.SetRankedGame(false);
|
|
Engine.GetGUIObjectByName("enableRating").checked = false;
|
|
Engine.GetGUIObjectByName("enableCheats").enabled = true;
|
|
Engine.GetGUIObjectByName("lockTeams").enabled = true;
|
|
}
|
|
|
|
g_IsInGuiUpdate = false;
|
|
|
|
onGameAttributesChange();
|
|
}
|
|
|
|
function saveGameAttributes()
|
|
{
|
|
var attributes = Engine.ConfigDB_GetValue("user", "persistmatchsettings") == "true" ? g_GameAttributes : {};
|
|
Engine.WriteJSONFile(g_IsNetworked ? FILEPATH_MATCHSETTINGS_MP : FILEPATH_MATCHSETTINGS_SP, attributes);
|
|
}
|
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// GUI event handlers
|
|
|
|
function cancelSetup()
|
|
{
|
|
if (g_IsController)
|
|
saveGameAttributes();
|
|
|
|
Engine.DisconnectNetworkGame();
|
|
|
|
if (Engine.HasXmppClient())
|
|
{
|
|
// Set player presence
|
|
Engine.LobbySetPlayerPresence("available");
|
|
|
|
// Unregister the game
|
|
if (g_IsController)
|
|
Engine.SendUnregisterGame();
|
|
}
|
|
}
|
|
|
|
function onTick()
|
|
{
|
|
// First tick happens before first render, so don't load yet
|
|
if (g_LoadingState == 0)
|
|
{
|
|
g_LoadingState++;
|
|
}
|
|
else if (g_LoadingState == 1)
|
|
{
|
|
Engine.GetGUIObjectByName("loadingWindow").hidden = true;
|
|
Engine.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]);
|
|
}
|
|
|
|
// Some players may have lost their assigned slot
|
|
for (var guid in g_PlayerAssignments)
|
|
{
|
|
var player = g_PlayerAssignments[guid].player;
|
|
if (player > num)
|
|
{
|
|
if (g_IsNetworked)
|
|
Engine.AssignNetworkPlayer(player, "");
|
|
else
|
|
g_PlayerAssignments = { "local": { "name": translate("You"), "player": 1, "civ": "", "team": -1, "ready": 0} };
|
|
}
|
|
}
|
|
|
|
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;
|
|
|
|
// Reset game attributes
|
|
g_GameAttributes.map = "";
|
|
g_GameAttributes.mapType = type;
|
|
|
|
// Clear old map data
|
|
g_MapData = {};
|
|
|
|
// Select correct path
|
|
switch (g_GameAttributes.mapType)
|
|
{
|
|
case "scenario":
|
|
// Set a default map
|
|
g_GameAttributes.mapPath = "maps/scenarios/";
|
|
g_GameAttributes.map = g_GameAttributes.mapPath + (g_IsNetworked ? DEFAULT_NETWORKED_MAP : DEFAULT_OFFLINE_MAP);
|
|
g_GameAttributes.settings.AISeed = Math.floor(Math.random() * 65536);
|
|
break;
|
|
|
|
case "skirmish":
|
|
g_GameAttributes.mapPath = "maps/skirmishes/";
|
|
g_GameAttributes.settings = {
|
|
PlayerData: g_DefaultPlayerData.slice(0, 4),
|
|
Seed: Math.floor(Math.random() * 65536),
|
|
AISeed: Math.floor(Math.random() * 65536),
|
|
CheatsEnabled: g_GameAttributes.settings.CheatsEnabled
|
|
};
|
|
break;
|
|
|
|
case "random":
|
|
g_GameAttributes.mapPath = "maps/random/";
|
|
g_GameAttributes.settings = {
|
|
PlayerData: g_DefaultPlayerData.slice(0, 4),
|
|
Seed: Math.floor(Math.random() * 65536),
|
|
AISeed: Math.floor(Math.random() * 65536),
|
|
CheatsEnabled: g_GameAttributes.settings.CheatsEnabled
|
|
};
|
|
break;
|
|
|
|
default:
|
|
error(sprintf("selectMapType: Unexpected map type '%(mapType)s'", { mapType: g_GameAttributes.mapType }));
|
|
return;
|
|
}
|
|
|
|
initMapNameList();
|
|
|
|
updateGameAttributes();
|
|
}
|
|
|
|
function selectMapFilter(id)
|
|
{
|
|
// Avoid recursion
|
|
if (g_IsInGuiUpdate)
|
|
return;
|
|
|
|
// Network clients can't change map filter
|
|
if (g_IsNetworked && !g_IsController)
|
|
return;
|
|
|
|
g_GameAttributes.mapFilter = id;
|
|
|
|
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;
|
|
|
|
// reset some map specific properties which are not necessarily redefined on each map
|
|
for (let prop of ["TriggerScripts", "CircularMap", "Garrison"])
|
|
if (g_GameAttributes.settings[prop] !== undefined)
|
|
g_GameAttributes.settings[prop] = undefined;
|
|
|
|
var mapData = loadMapData(name);
|
|
var mapSettings = (mapData && mapData.settings ? deepcopy(mapData.settings) : {});
|
|
|
|
// Reset victory conditions
|
|
var victories = getVictoryConditions();
|
|
var victoryIdx = (mapSettings.GameType !== undefined && victories.data.indexOf(mapSettings.GameType) != -1 ? victories.data.indexOf(mapSettings.GameType) : VICTORY_DEFAULTIDX);
|
|
g_GameAttributes.settings.GameType = victories.data[victoryIdx];
|
|
g_GameAttributes.settings.VictoryScripts = victories.scripts[victoryIdx];
|
|
|
|
// Copy any new settings
|
|
g_GameAttributes.map = name;
|
|
g_GameAttributes.script = mapSettings.Script;
|
|
if (g_GameAttributes.map !== "random")
|
|
for (var prop in mapSettings)
|
|
g_GameAttributes.settings[prop] = mapSettings[prop];
|
|
|
|
// Ignore gaia
|
|
if (g_GameAttributes.settings.PlayerData.length && !g_GameAttributes.settings.PlayerData[0])
|
|
g_GameAttributes.settings.PlayerData.shift();
|
|
|
|
// Use default AI if the map doesn't specify any explicitly
|
|
for (var i = 0; i < g_GameAttributes.settings.PlayerData.length; ++i)
|
|
{
|
|
if (!('AI' in g_GameAttributes.settings.PlayerData[i]))
|
|
g_GameAttributes.settings.PlayerData[i].AI = g_DefaultPlayerData[i].AI;
|
|
if (!('AIDiff' in g_GameAttributes.settings.PlayerData[i]))
|
|
g_GameAttributes.settings.PlayerData[i].AIDiff = g_DefaultPlayerData[i].AIDiff;
|
|
}
|
|
|
|
// Reset player assignments on map change
|
|
if (!g_IsNetworked)
|
|
{ // Slot 1
|
|
g_PlayerAssignments = { "local": { "name": translate("You"), "player": 1, "civ": "", "team": -1, "ready": 0} };
|
|
}
|
|
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;
|
|
|
|
saveGameAttributes();
|
|
|
|
if (g_GameAttributes.map == "random")
|
|
{
|
|
let victoryScriptsSelected = g_GameAttributes.settings.VictoryScripts;
|
|
let gameTypeSelected = g_GameAttributes.settings.GameType;
|
|
selectMap(Engine.GetGUIObjectByName("mapSelection").list_data[Math.floor(Math.random() *
|
|
(Engine.GetGUIObjectByName("mapSelection").list.length - 1)) + 1]);
|
|
g_GameAttributes.settings.VictoryScripts = victoryScriptsSelected;
|
|
g_GameAttributes.settings.GameType = gameTypeSelected;
|
|
}
|
|
if (!g_GameAttributes.settings.TriggerScripts)
|
|
g_GameAttributes.settings.TriggerScripts = g_GameAttributes.settings.VictoryScripts;
|
|
else
|
|
g_GameAttributes.settings.TriggerScripts = g_GameAttributes.settings.VictoryScripts.concat(g_GameAttributes.settings.TriggerScripts);
|
|
g_GameStarted = true;
|
|
g_GameAttributes.settings.mapType = g_GameAttributes.mapType;
|
|
var numPlayers = g_GameAttributes.settings.PlayerData.length;
|
|
// Assign random civilizations to players with that choice
|
|
// (this is synchronized because we're the host)
|
|
var cultures = [];
|
|
for each (var civ in g_CivData)
|
|
if (civ.Culture !== undefined && cultures.indexOf(civ.Culture) < 0 && civ.SelectableInGameSetup !== false)
|
|
cultures.push(civ.Culture);
|
|
var allcivs = new Array(cultures.length);
|
|
for (var i = 0; i < allcivs.length; ++i)
|
|
allcivs[i] = [];
|
|
for each (var civ in g_CivData)
|
|
if (civ.Culture !== undefined && civ.SelectableInGameSetup !== false)
|
|
allcivs[cultures.indexOf(civ.Culture)].push(civ.Code);
|
|
|
|
const romanNumbers = [undefined, "I", "II", "III", "IV", "V", "VI", "VII", "VIII"];
|
|
for (var i = 0; i < numPlayers; ++i)
|
|
{
|
|
var civs = allcivs[Math.floor(Math.random()*allcivs.length)];
|
|
|
|
if (!g_GameAttributes.settings.PlayerData[i].Civ || g_GameAttributes.settings.PlayerData[i].Civ == "random")
|
|
g_GameAttributes.settings.PlayerData[i].Civ = civs[Math.floor(Math.random()*civs.length)];
|
|
// Setting names for AI players. Check if the player is AI and the match is not a scenario
|
|
if (g_GameAttributes.mapType !== "scenario" && g_GameAttributes.settings.PlayerData[i].AI)
|
|
{
|
|
// Get the civ specific names
|
|
if (g_CivData[g_GameAttributes.settings.PlayerData[i].Civ].AINames !== undefined)
|
|
var civAINames = shuffleArray(g_CivData[g_GameAttributes.settings.PlayerData[i].Civ].AINames);
|
|
else
|
|
var civAINames = [g_CivData[g_GameAttributes.settings.PlayerData[i].Civ].Name];
|
|
// Choose the name
|
|
var usedName = 0;
|
|
if (i < civAINames.length)
|
|
var chosenName = civAINames[i];
|
|
else
|
|
var chosenName = civAINames[Math.floor(Math.random() * civAINames.length)];
|
|
for (var j = 0; j < numPlayers; ++j)
|
|
if (g_GameAttributes.settings.PlayerData[j].Name && g_GameAttributes.settings.PlayerData[j].Name.indexOf(chosenName) !== -1)
|
|
usedName++;
|
|
|
|
// Assign civ specific names to AI players
|
|
chosenName = translate(chosenName);
|
|
if (usedName)
|
|
g_GameAttributes.settings.PlayerData[i].Name = sprintf(translate("%(playerName)s %(romanNumber)s"), { playerName: chosenName, romanNumber: romanNumbers[usedName+1]});
|
|
else
|
|
g_GameAttributes.settings.PlayerData[i].Name = chosenName;
|
|
}
|
|
}
|
|
|
|
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 = Engine.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 mapFilterSelection = Engine.GetGUIObjectByName("mapFilterSelection");
|
|
var mapFilterId = mapFilterSelection.list_data.indexOf(g_GameAttributes.mapFilter);
|
|
Engine.GetGUIObjectByName("mapFilterText").caption = mapFilterSelection.list[mapFilterId];
|
|
var mapTypeSelection = Engine.GetGUIObjectByName("mapTypeSelection");
|
|
var idx = mapTypeSelection.list_data.indexOf(g_GameAttributes.mapType);
|
|
Engine.GetGUIObjectByName("mapTypeText").caption = mapTypeSelection.list[idx];
|
|
var mapSelectionBox = Engine.GetGUIObjectByName("mapSelection");
|
|
mapSelectionBox.selected = mapSelectionBox.list_data.indexOf(mapName);
|
|
Engine.GetGUIObjectByName("mapSelectionText").caption = translate(getMapDisplayName(mapName));
|
|
if (mapSettings.PopulationCap)
|
|
{
|
|
var populationCapBox = Engine.GetGUIObjectByName("populationCap");
|
|
populationCapBox.selected = populationCapBox.list_data.indexOf(mapSettings.PopulationCap);
|
|
}
|
|
if (mapSettings.StartingResources)
|
|
{
|
|
var startingResourcesBox = Engine.GetGUIObjectByName("startingResources");
|
|
startingResourcesBox.selected = startingResourcesBox.list_data.indexOf(mapSettings.StartingResources);
|
|
}
|
|
|
|
initMapNameList();
|
|
}
|
|
|
|
// Controls common to all map types
|
|
var numPlayersSelection = Engine.GetGUIObjectByName("numPlayersSelection");
|
|
var revealMap = Engine.GetGUIObjectByName("revealMap");
|
|
var exploreMap = Engine.GetGUIObjectByName("exploreMap");
|
|
var disableTreasures = Engine.GetGUIObjectByName("disableTreasures");
|
|
var victoryCondition = Engine.GetGUIObjectByName("victoryCondition");
|
|
var lockTeams = Engine.GetGUIObjectByName("lockTeams");
|
|
var mapSize = Engine.GetGUIObjectByName("mapSize");
|
|
var enableCheats = Engine.GetGUIObjectByName("enableCheats");
|
|
var enableRating = Engine.GetGUIObjectByName("enableRating");
|
|
var populationCap = Engine.GetGUIObjectByName("populationCap");
|
|
var startingResources = Engine.GetGUIObjectByName("startingResources");
|
|
|
|
var numPlayersText= Engine.GetGUIObjectByName("numPlayersText");
|
|
var mapSizeDesc = Engine.GetGUIObjectByName("mapSizeDesc");
|
|
var mapSizeText = Engine.GetGUIObjectByName("mapSizeText");
|
|
var revealMapText = Engine.GetGUIObjectByName("revealMapText");
|
|
var exploreMapText = Engine.GetGUIObjectByName("exploreMapText");
|
|
var disableTreasuresText = Engine.GetGUIObjectByName("disableTreasuresText");
|
|
var victoryConditionText = Engine.GetGUIObjectByName("victoryConditionText");
|
|
var lockTeamsText = Engine.GetGUIObjectByName("lockTeamsText");
|
|
var enableCheatsText = Engine.GetGUIObjectByName("enableCheatsText");
|
|
var enableRatingText = Engine.GetGUIObjectByName("enableRatingText");
|
|
var populationCapText = Engine.GetGUIObjectByName("populationCapText");
|
|
var startingResourcesText = Engine.GetGUIObjectByName("startingResourcesText");
|
|
var gameSpeedText = Engine.GetGUIObjectByName("gameSpeedText");
|
|
var gameSpeedBox = Engine.GetGUIObjectByName("gameSpeed");
|
|
|
|
// We have to check for undefined on these properties as not all maps define them.
|
|
var sizeIdx = (mapSettings.Size !== undefined && g_MapSizes.tiles.indexOf(mapSettings.Size) != -1 ? g_MapSizes.tiles.indexOf(mapSettings.Size) : g_MapSizes["default"]);
|
|
var speedIdx = (g_GameAttributes.gameSpeed !== undefined && g_GameSpeeds.speeds.indexOf(g_GameAttributes.gameSpeed) != -1) ? g_GameSpeeds.speeds.indexOf(g_GameAttributes.gameSpeed) : g_GameSpeeds["default"];
|
|
var victories = getVictoryConditions();
|
|
var victoryIdx = (mapSettings.GameType !== undefined && victories.data.indexOf(mapSettings.GameType) != -1 ? victories.data.indexOf(mapSettings.GameType) : VICTORY_DEFAULTIDX);
|
|
enableCheats.checked = (mapSettings.CheatsEnabled === undefined || !mapSettings.CheatsEnabled ? false : true)
|
|
enableCheatsText.caption = (enableCheats.checked ? translate("Yes") : translate("No"));
|
|
if (mapSettings.RatingEnabled !== undefined)
|
|
{
|
|
enableRating.checked = mapSettings.RatingEnabled;
|
|
Engine.SetRankedGame(enableRating.checked);
|
|
enableRatingText.caption = (enableRating.checked ? translate("Yes") : translate("No"));
|
|
enableCheats.enabled = !enableRating.checked;
|
|
lockTeams.enabled = !enableRating.checked;
|
|
}
|
|
else
|
|
enableRatingText.caption = "Unknown";
|
|
gameSpeedText.caption = g_GameSpeeds.names[speedIdx];
|
|
gameSpeedBox.selected = speedIdx;
|
|
populationCap.selected = (mapSettings.PopulationCap !== undefined && POPULATION_CAP_DATA.indexOf(mapSettings.PopulationCap) != -1 ? POPULATION_CAP_DATA.indexOf(mapSettings.PopulationCap) : POPULATION_CAP_DEFAULTIDX);
|
|
populationCapText.caption = POPULATION_CAP[populationCap.selected];
|
|
startingResources.selected = (mapSettings.StartingResources !== undefined && STARTING_RESOURCES_DATA.indexOf(mapSettings.StartingResources) != -1 ? STARTING_RESOURCES_DATA.indexOf(mapSettings.StartingResources) : STARTING_RESOURCES_DEFAULTIDX);
|
|
startingResourcesText.caption = STARTING_RESOURCES[startingResources.selected];
|
|
|
|
// Update map preview
|
|
Engine.GetGUIObjectByName("mapPreview").sprite = "cropped:(0.78125,0.5859375)session/icons/mappreview/" + getMapPreview(mapName);
|
|
|
|
// Hide/show settings depending on whether we can change them or not
|
|
var updateDisplay = function(guiObjChg, guiObjDsp, chg) {
|
|
guiObjChg.hidden = !chg;
|
|
guiObjDsp.hidden = chg;
|
|
};
|
|
|
|
// Handle map type specific logic
|
|
switch (g_GameAttributes.mapType)
|
|
{
|
|
case "random":
|
|
mapSizeDesc.hidden = false;
|
|
|
|
updateDisplay(numPlayersSelection, numPlayersText, g_IsController);
|
|
updateDisplay(mapSize, mapSizeText, g_IsController);
|
|
updateDisplay(revealMap, revealMapText, g_IsController);
|
|
updateDisplay(exploreMap, exploreMapText, g_IsController);
|
|
updateDisplay(disableTreasures, disableTreasuresText, g_IsController);
|
|
updateDisplay(victoryCondition, victoryConditionText, g_IsController);
|
|
updateDisplay(lockTeams, lockTeamsText, g_IsController);
|
|
updateDisplay(populationCap, populationCapText, g_IsController);
|
|
updateDisplay(startingResources, startingResourcesText, g_IsController);
|
|
|
|
if (g_IsController)
|
|
{
|
|
//Host
|
|
numPlayersSelection.selected = numPlayers - 1;
|
|
mapSize.selected = sizeIdx;
|
|
revealMap.checked = (mapSettings.RevealMap ? true : false);
|
|
exploreMap.checked = (mapSettings.ExploreMap ? true : false);
|
|
disableTreasures.checked = (mapSettings.DisableTreasures ? true : false);
|
|
victoryCondition.selected = victoryIdx;
|
|
lockTeams.checked = (mapSettings.LockTeams ? true : false);
|
|
}
|
|
else
|
|
{
|
|
// Client
|
|
numPlayersText.caption = numPlayers;
|
|
mapSizeText.caption = g_MapSizes.names[sizeIdx];
|
|
revealMapText.caption = (mapSettings.RevealMap ? translate("Yes") : translate("No"));
|
|
exploreMapText.caption = (mapSettings.ExporeMap ? translate("Yes") : translate("No"));
|
|
disableTreasuresText.caption = (mapSettings.DisableTreasures ? translate("Yes") : translate("No"));
|
|
victoryConditionText.caption = victories.text[victoryIdx];
|
|
lockTeamsText.caption = (mapSettings.LockTeams ? translate("Yes") : translate("No"));
|
|
}
|
|
|
|
break;
|
|
|
|
case "skirmish":
|
|
mapSizeText.caption = translate("Default");
|
|
numPlayersText.caption = numPlayers;
|
|
numPlayersText.hidden = false;
|
|
numPlayersSelection.hidden = true;
|
|
mapSize.hidden = true;
|
|
mapSizeText.hidden = true;
|
|
mapSizeDesc.hidden = true;
|
|
|
|
updateDisplay(revealMap, revealMapText, g_IsController);
|
|
updateDisplay(exploreMap, exploreMapText, g_IsController);
|
|
updateDisplay(disableTreasures, disableTreasuresText, g_IsController);
|
|
updateDisplay(victoryCondition, victoryConditionText, g_IsController);
|
|
updateDisplay(lockTeams, lockTeamsText, g_IsController);
|
|
updateDisplay(populationCap, populationCapText, g_IsController);
|
|
updateDisplay(startingResources, startingResourcesText, g_IsController);
|
|
|
|
if (g_IsController)
|
|
{
|
|
//Host
|
|
revealMap.checked = (mapSettings.RevealMap ? true : false);
|
|
exploreMap.checked = (mapSettings.ExploreMap ? true : false);
|
|
disableTreasures.checked = (mapSettings.DisableTreasures ? true : false);
|
|
victoryCondition.selected = victoryIdx;
|
|
lockTeams.checked = (mapSettings.LockTeams ? true : false);
|
|
}
|
|
else
|
|
{
|
|
// Client
|
|
revealMapText.caption = (mapSettings.RevealMap ? translate("Yes") : translate("No"));
|
|
exploreMapText.caption = (mapSettings.ExploreMap ? translate("Yes") : translate("No"));
|
|
disableTreasuresText.caption = (mapSettings.DisableTreasures ? translate("Yes") : translate("No"));
|
|
victoryConditionText.caption = victories.text[victoryIdx];
|
|
lockTeamsText.caption = (mapSettings.LockTeams ? translate("Yes") : translate("No"));
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
case "scenario":
|
|
// For scenario just reflect settings for the current map
|
|
numPlayersSelection.hidden = true;
|
|
mapSize.hidden = true;
|
|
revealMap.hidden = true;
|
|
exploreMap.hidden = true;
|
|
disableTreasures.hidden = true;
|
|
victoryCondition.hidden = true;
|
|
lockTeams.hidden = true;
|
|
numPlayersText.hidden = false;
|
|
mapSizeText.hidden = true;
|
|
mapSizeDesc.hidden = true;
|
|
revealMapText.hidden = false;
|
|
exploreMapText.hidden = false;
|
|
disableTreasuresText.hidden = false;
|
|
victoryConditionText.hidden = false;
|
|
lockTeamsText.hidden = false;
|
|
populationCap.hidden = true;
|
|
populationCapText.hidden = false;
|
|
startingResources.hidden = true;
|
|
startingResourcesText.hidden = false;
|
|
|
|
numPlayersText.caption = numPlayers;
|
|
mapSizeText.caption = translate("Default");
|
|
revealMapText.caption = (mapSettings.RevealMap ? translate("Yes") : translate("No"));
|
|
exploreMapText.caption = (mapSettings.ExploreMap ? translate("Yes") : translate("No"));
|
|
disableTreasuresText.caption = translate("No");
|
|
victoryConditionText.caption = victories.text[victoryIdx];
|
|
lockTeamsText.caption = (mapSettings.LockTeams ? translate("Yes") : translate("No"));
|
|
Engine.GetGUIObjectByName("populationCap").selected = POPULATION_CAP_DEFAULTIDX;
|
|
|
|
break;
|
|
|
|
default:
|
|
error(sprintf("onGameAttributesChange: Unexpected map type '%(mapType)s'", { mapType: g_GameAttributes.mapType }));
|
|
return;
|
|
}
|
|
|
|
// Display map name
|
|
if (mapName == "random")
|
|
{
|
|
var mapDisplayName = translateWithContext("map", "Random");
|
|
mapSettings.Description = markForTranslation("Randomly selects a map from the list");
|
|
}
|
|
else
|
|
var mapDisplayName = translate(getMapDisplayName(mapName));
|
|
Engine.GetGUIObjectByName("mapInfoName").caption = mapDisplayName;
|
|
|
|
// Load the description from the map file, if there is one
|
|
var description = mapSettings.Description ? translate(mapSettings.Description) : translate("Sorry, no description available.");
|
|
|
|
// Describe the number of players and the victory conditions
|
|
var playerString = sprintf(translatePlural("%(number)s player. ", "%(number)s players. ", numPlayers), { number: numPlayers });
|
|
let victory = translate(victories.text[victoryIdx]);
|
|
if (victoryIdx != VICTORY_DEFAULTIDX)
|
|
victory = "[color=\"orange\"]" + victory + "[/color]";
|
|
playerString += translate("Victory Condition:") + " " + victory + ".\n\n" + description;
|
|
|
|
for (var i = 0; i < MAX_PLAYERS; ++i)
|
|
{
|
|
// Show only needed player slots
|
|
Engine.GetGUIObjectByName("playerBox["+i+"]").hidden = (i >= numPlayers);
|
|
|
|
// Show player data or defaults as necessary
|
|
if (i >= numPlayers)
|
|
continue;
|
|
|
|
var pName = Engine.GetGUIObjectByName("playerName["+i+"]");
|
|
var pAssignment = Engine.GetGUIObjectByName("playerAssignment["+i+"]");
|
|
var pAssignmentText = Engine.GetGUIObjectByName("playerAssignmentText["+i+"]");
|
|
var pCiv = Engine.GetGUIObjectByName("playerCiv["+i+"]");
|
|
var pCivText = Engine.GetGUIObjectByName("playerCivText["+i+"]");
|
|
var pTeam = Engine.GetGUIObjectByName("playerTeam["+i+"]");
|
|
var pTeamText = Engine.GetGUIObjectByName("playerTeamText["+i+"]");
|
|
var pColor = Engine.GetGUIObjectByName("playerColor["+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 = rgbToGuiColor(getSetting(pData, pDefs, "Color"));
|
|
pColor.sprite = "color:" + color + " 100";
|
|
pName.caption = translate(getSetting(pData, pDefs, "Name"));
|
|
|
|
var team = getSetting(pData, pDefs, "Team");
|
|
var civ = getSetting(pData, pDefs, "Civ");
|
|
|
|
// Nobody but the controller can assign people
|
|
pAssignmentText.hidden = g_IsController;
|
|
pAssignment.hidden = !g_IsController;
|
|
if (!pAssignment.list[0])
|
|
pAssignmentText.caption = translate("Loading...");
|
|
else
|
|
pAssignmentText.caption = pAssignment.list[pAssignment.selected === -1 ? 0 : pAssignment.selected];
|
|
|
|
// For clients or scenarios, hide some player dropdowns
|
|
// TODO: Allow clients to choose their own civ and team
|
|
if (!g_IsController || g_GameAttributes.mapType == "scenario")
|
|
{
|
|
pCivText.hidden = false;
|
|
pCiv.hidden = true;
|
|
pTeamText.hidden = false;
|
|
pTeam.hidden = true;
|
|
// Set text values
|
|
if (civ == "random")
|
|
pCivText.caption = "[color=\"orange\"]" + translateWithContext("civilization", "Random");
|
|
else
|
|
pCivText.caption = g_CivData[civ].Name;
|
|
pTeamText.caption = (team !== undefined && team >= 0) ? team+1 : "-";
|
|
}
|
|
else if (g_GameAttributes.mapType != "scenario")
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
|
|
Engine.GetGUIObjectByName("mapInfoDescription").caption = playerString;
|
|
|
|
g_IsInGuiUpdate = false;
|
|
|
|
// Game attributes include AI settings, so update the player list
|
|
updatePlayerList();
|
|
|
|
// We should have everyone confirm that the new settings are acceptable.
|
|
resetReadyData();
|
|
}
|
|
|
|
function updateGameAttributes()
|
|
{
|
|
if (g_IsInGuiUpdate)
|
|
return;
|
|
|
|
if (g_IsNetworked)
|
|
{
|
|
Engine.SetNetworkGameAttributes(g_GameAttributes);
|
|
if (g_IsController && g_LoadingState >= 2)
|
|
sendRegisterGameStanza();
|
|
}
|
|
else
|
|
onGameAttributesChange();
|
|
}
|
|
|
|
function AIConfigCallback(ai)
|
|
{
|
|
g_GameAttributes.settings.PlayerData[ai.playerSlot].AI = ai.id;
|
|
g_GameAttributes.settings.PlayerData[ai.playerSlot].AIDiff = ai.difficulty;
|
|
|
|
if (g_IsNetworked)
|
|
Engine.SetNetworkGameAttributes(g_GameAttributes);
|
|
else
|
|
updatePlayerList();
|
|
}
|
|
|
|
function updatePlayerList()
|
|
{
|
|
g_IsInGuiUpdate = true;
|
|
|
|
var hostNameList = [];
|
|
var hostGuidList = [];
|
|
var assignments = [];
|
|
var aiAssignments = {};
|
|
var noAssignment;
|
|
g_AssignedCount = 0;
|
|
|
|
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;
|
|
|
|
if (player != -1)
|
|
g_AssignedCount++;
|
|
}
|
|
|
|
// Only enable start button if we have enough assigned players
|
|
if (g_IsController)
|
|
Engine.GetGUIObjectByName("startGame").enabled = (g_AssignedCount > 0);
|
|
|
|
for each (var ai in g_AIs)
|
|
{
|
|
if (ai.data.hidden)
|
|
{
|
|
// If the map uses a hidden AI then don't hide it
|
|
var usedByMap = false;
|
|
for (var i = 0; i < MAX_PLAYERS; ++i)
|
|
if (i < g_GameAttributes.settings.PlayerData.length &&
|
|
g_GameAttributes.settings.PlayerData[i].AI == ai.id)
|
|
{
|
|
usedByMap = true;
|
|
break;
|
|
}
|
|
|
|
if (!usedByMap)
|
|
continue;
|
|
}
|
|
// Give AI a different color so it stands out
|
|
aiAssignments[ai.id] = hostNameList.length;
|
|
hostNameList.push("[color=\"70 150 70 255\"]" + sprintf(translate("AI: %(ai)s"), { ai: translate(ai.data.name) }));
|
|
hostGuidList.push("ai:" + ai.id);
|
|
}
|
|
|
|
noAssignment = hostNameList.length;
|
|
hostNameList.push("[color=\"140 140 140 255\"]" + translate("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 = Engine.GetGUIObjectByName("playerConfig["+i+"]");
|
|
configButton.hidden = true;
|
|
|
|
// Look for valid player slots
|
|
if (playerSlot >= g_GameAttributes.settings.PlayerData.length)
|
|
continue;
|
|
|
|
// If no human is assigned, look for an AI instead
|
|
if (selection === undefined)
|
|
{
|
|
var aiId = g_GameAttributes.settings.PlayerData[playerSlot].AI;
|
|
if (aiId)
|
|
{
|
|
// Check for a valid AI
|
|
if (aiId in aiAssignments)
|
|
selection = aiAssignments[aiId];
|
|
else
|
|
{
|
|
g_GameAttributes.settings.PlayerData[playerSlot].AI = "";
|
|
warn(sprintf("AI \"%(id)s\" not present. Defaulting to unassigned.", { id: aiId }));
|
|
}
|
|
}
|
|
|
|
if (!selection)
|
|
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,
|
|
difficulty: g_GameAttributes.settings.PlayerData[playerSlot].AIDiff,
|
|
callback: "AIConfigCallback",
|
|
playerSlot: playerSlot // required by the callback function
|
|
});
|
|
};
|
|
}
|
|
}
|
|
// 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
|
|
else 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 = Engine.GetGUIObjectByName("playerAssignment["+i+"]");
|
|
var assignBoxText = Engine.GetGUIObjectByName("playerAssignmentText["+i+"]");
|
|
assignBox.list = hostNameList;
|
|
assignBox.list_data = hostGuidList;
|
|
if (assignBox.selected != selection)
|
|
assignBox.selected = selection;
|
|
assignBoxText.caption = hostNameList[selection];
|
|
|
|
if (g_IsController)
|
|
assignBox.onselectionchange = function() {
|
|
if (g_IsInGuiUpdate)
|
|
return;
|
|
|
|
var guid = hostGuidList[this.selected];
|
|
if (guid == "")
|
|
{
|
|
if (g_IsNetworked)
|
|
// 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:")
|
|
{
|
|
if (g_IsNetworked)
|
|
// 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
|
|
swapPlayers(guid, playerSlot);
|
|
|
|
if (g_IsNetworked)
|
|
Engine.SetNetworkGameAttributes(g_GameAttributes);
|
|
else
|
|
updatePlayerList();
|
|
updateReadyUI();
|
|
};
|
|
}
|
|
|
|
g_IsInGuiUpdate = false;
|
|
}
|
|
|
|
function swapPlayers(guid, newSlot)
|
|
{
|
|
// Player slots are indexed from 0 as Gaia is omitted.
|
|
var newPlayerID = newSlot + 1;
|
|
var playerID = g_PlayerAssignments[guid].player;
|
|
|
|
// Attempt to swap the player or AI occupying the target slot,
|
|
// if any, into the slot this player is currently in.
|
|
if (playerID != -1)
|
|
{
|
|
for (var i in g_PlayerAssignments)
|
|
{
|
|
// Move the player in the destination slot into the current slot.
|
|
if (g_PlayerAssignments[i].player == newPlayerID)
|
|
{
|
|
if (g_IsNetworked)
|
|
Engine.AssignNetworkPlayer(playerID, i);
|
|
else
|
|
g_PlayerAssignments[i].player = playerID;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Transfer the AI from the target slot to the current slot.
|
|
g_GameAttributes.settings.PlayerData[playerID - 1].AI = g_GameAttributes.settings.PlayerData[newSlot].AI;
|
|
}
|
|
|
|
if (g_IsNetworked)
|
|
Engine.AssignNetworkPlayer(newPlayerID, guid);
|
|
else
|
|
g_PlayerAssignments[guid].player = newPlayerID;
|
|
|
|
// Remove AI from this player slot
|
|
g_GameAttributes.settings.PlayerData[newSlot].AI = "";
|
|
}
|
|
|
|
function submitChatInput()
|
|
{
|
|
var input = Engine.GetGUIObjectByName("chatInput");
|
|
var text = input.caption;
|
|
if (text.length)
|
|
{
|
|
Engine.SendNetworkChat(text);
|
|
input.caption = "";
|
|
}
|
|
}
|
|
|
|
function addChatMessage(msg)
|
|
{
|
|
var username = "";
|
|
if (msg.username)
|
|
username = escapeText(msg.username);
|
|
else if (msg.guid && g_PlayerAssignments[msg.guid])
|
|
username = escapeText(g_PlayerAssignments[msg.guid].name);
|
|
|
|
var message = "";
|
|
if ("text" in msg && msg.text)
|
|
message = escapeText(msg.text);
|
|
|
|
// TODO: Maybe host should have distinct font/color?
|
|
var color = "white";
|
|
|
|
if (msg.guid && g_PlayerAssignments[msg.guid] && g_PlayerAssignments[msg.guid].player != -1)
|
|
{
|
|
// Valid player who has been assigned - get player color
|
|
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 = rgbToGuiColor(getSetting(pData, pDefs, "Color"));
|
|
}
|
|
|
|
var formatted;
|
|
switch (msg.type)
|
|
{
|
|
case "connect":
|
|
var formattedUsername = '[color="'+ color +'"]' + username + '[/color]';
|
|
formatted = '[font="sans-bold-13"] ' + sprintf(translate("== %(message)s"), { message: sprintf(translate("%(username)s has joined"), { username: formattedUsername }) }) + '[/font]';
|
|
break;
|
|
|
|
case "disconnect":
|
|
var formattedUsername = '[color="'+ color +'"]' + username + '[/color]';
|
|
formatted = '[font="sans-bold-13"] ' + sprintf(translate("== %(message)s"), { message: sprintf(translate("%(username)s has left"), { username: formattedUsername }) }) + '[/font]';
|
|
break;
|
|
|
|
case "message":
|
|
var formattedUsername = '[color="'+ color +'"]' + username + '[/color]';
|
|
var formattedUsernamePrefix = '[font="sans-bold-13"]' + sprintf(translate("<%(username)s>"), { username: formattedUsername }) + '[/font]'
|
|
formatted = sprintf(translate("%(username)s %(message)s"), { username: formattedUsernamePrefix, message: message });
|
|
break;
|
|
|
|
case "ready":
|
|
var formattedUsername = '[font="sans-bold-13"][color="'+ color +'"]' + username + '[/color][/font]'
|
|
if (msg.ready)
|
|
formatted = ' ' + sprintf(translate("* %(username)s is ready!"), { username: formattedUsername });
|
|
else
|
|
formatted = ' ' + sprintf(translate("* %(username)s is not ready."), { username: formattedUsername });
|
|
break;
|
|
|
|
case "settings":
|
|
formatted = '[font="sans-bold-13"] ' + sprintf(translate("== %(message)s"), { message: translate('Game settings have been changed') }) + '[/font]';
|
|
break;
|
|
|
|
default:
|
|
error(sprintf("Invalid chat message '%(message)s'", { message: uneval(msg) }));
|
|
return;
|
|
}
|
|
|
|
g_ChatMessages.push(formatted);
|
|
|
|
Engine.GetGUIObjectByName("chatText").caption = g_ChatMessages.join("\n");
|
|
}
|
|
|
|
function toggleMoreOptions()
|
|
{
|
|
Engine.GetGUIObjectByName("moreOptionsFade").hidden = !Engine.GetGUIObjectByName("moreOptionsFade").hidden;
|
|
Engine.GetGUIObjectByName("moreOptions").hidden = !Engine.GetGUIObjectByName("moreOptions").hidden;
|
|
}
|
|
|
|
function toggleReady()
|
|
{
|
|
g_IsReady = !g_IsReady;
|
|
if (g_IsReady)
|
|
{
|
|
Engine.SendNetworkReady(1);
|
|
Engine.GetGUIObjectByName("startGame").caption = translate("I'm not ready");
|
|
Engine.GetGUIObjectByName("startGame").tooltip = translate("State that you are not ready to play.");
|
|
}
|
|
else
|
|
{
|
|
Engine.SendNetworkReady(0);
|
|
Engine.GetGUIObjectByName("startGame").caption = translate("I'm ready!");
|
|
Engine.GetGUIObjectByName("startGame").tooltip = translate("State that you are ready to play!");
|
|
}
|
|
}
|
|
|
|
function updateReadyUI()
|
|
{
|
|
if (!g_IsNetworked)
|
|
return; // Disabled for single-player games.
|
|
var isAI = new Array(MAX_PLAYERS + 1);
|
|
for (var i = 0; i < isAI.length; ++i)
|
|
isAI[i] = true;
|
|
var allReady = true;
|
|
for (var guid in g_PlayerAssignments)
|
|
{
|
|
// We don't really care whether observers are ready.
|
|
if (g_PlayerAssignments[guid].player == -1 || !g_GameAttributes.settings.PlayerData[g_PlayerAssignments[guid].player - 1])
|
|
continue;
|
|
var pData = g_GameAttributes.settings.PlayerData ? g_GameAttributes.settings.PlayerData[g_PlayerAssignments[guid].player - 1] : {};
|
|
var pDefs = g_DefaultPlayerData ? g_DefaultPlayerData[g_PlayerAssignments[guid].player - 1] : {};
|
|
isAI[g_PlayerAssignments[guid].player] = false;
|
|
if (g_PlayerAssignments[guid].status || !g_IsNetworked)
|
|
Engine.GetGUIObjectByName("playerName[" + (g_PlayerAssignments[guid].player - 1) + "]").caption = '[color="0 255 0"]' + translate(getSetting(pData, pDefs, "Name")) + '[/color]';
|
|
else
|
|
{
|
|
Engine.GetGUIObjectByName("playerName[" + (g_PlayerAssignments[guid].player - 1) + "]").caption = translate(getSetting(pData, pDefs, "Name"));
|
|
allReady = false;
|
|
}
|
|
}
|
|
// AIs are always ready.
|
|
for (var playerid = 0; playerid < MAX_PLAYERS; ++playerid)
|
|
{
|
|
if (!g_GameAttributes.settings.PlayerData[playerid])
|
|
continue;
|
|
var pData = g_GameAttributes.settings.PlayerData ? g_GameAttributes.settings.PlayerData[playerid] : {};
|
|
var pDefs = g_DefaultPlayerData ? g_DefaultPlayerData[playerid] : {};
|
|
if (isAI[playerid + 1])
|
|
Engine.GetGUIObjectByName("playerName[" + playerid + "]").caption = '[color="0 255 0"]' + translate(getSetting(pData, pDefs, "Name")) + '[/color]';
|
|
}
|
|
|
|
// The host is not allowed to start until everyone is ready.
|
|
if (g_IsNetworked && g_IsController)
|
|
{
|
|
var startGameButton = Engine.GetGUIObjectByName("startGame");
|
|
startGameButton.enabled = allReady;
|
|
// Add a explanation on to the tooltip if disabled.
|
|
var disabledIndex = startGameButton.tooltip.indexOf('Disabled');
|
|
if (disabledIndex != -1 && allReady)
|
|
startGameButton.tooltip = startGameButton.tooltip.substring(0, disabledIndex - 2);
|
|
else if (disabledIndex == -1 && !allReady)
|
|
startGameButton.tooltip = startGameButton.tooltip + " (Disabled until all players are ready)";
|
|
}
|
|
}
|
|
|
|
function resetReadyData()
|
|
{
|
|
if (g_GameStarted)
|
|
return;
|
|
|
|
if (g_ReadyChanged < 1)
|
|
addChatMessage({ "type": "settings"});
|
|
else if (g_ReadyChanged == 2 && !g_ReadyInit)
|
|
return; // duplicate calls on init
|
|
else
|
|
g_ReadyInit = false;
|
|
|
|
g_ReadyChanged = 2;
|
|
if (!g_IsNetworked)
|
|
g_IsReady = true;
|
|
else if (g_IsController)
|
|
{
|
|
Engine.ClearAllPlayerReady();
|
|
g_IsReady = true;
|
|
Engine.SendNetworkReady(1);
|
|
}
|
|
else
|
|
{
|
|
g_IsReady = false;
|
|
Engine.GetGUIObjectByName("startGame").caption = translate("I'm ready!");
|
|
Engine.GetGUIObjectByName("startGame").tooltip = translate("State that you accept the current settings and are ready to play!");
|
|
}
|
|
}
|
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Basic map filters API
|
|
|
|
// Add a new map list filter
|
|
function addFilter(id, name, filterFunc)
|
|
{
|
|
if (!filterFunc instanceof Object)
|
|
{
|
|
error(sprintf("Invalid map filter: %(name)s", { name: name }));
|
|
return;
|
|
}
|
|
|
|
var newFilter = {};
|
|
newFilter.id = id;
|
|
newFilter.name = name;
|
|
newFilter.filter = filterFunc;
|
|
|
|
g_MapFilters.push(newFilter);
|
|
}
|
|
|
|
// Get array of map filter IDs
|
|
function getFilterIds()
|
|
{
|
|
var filters = [];
|
|
for (var i = 0; i < g_MapFilters.length; ++i)
|
|
filters.push(g_MapFilters[i].id);
|
|
|
|
return filters;
|
|
}
|
|
|
|
// Get array of map filter names
|
|
function getFilterNames()
|
|
{
|
|
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(id, mapSettings)
|
|
{
|
|
for (var i = 0; i < g_MapFilters.length; ++i)
|
|
if (g_MapFilters[i].id == id)
|
|
return g_MapFilters[i].filter(mapSettings);
|
|
|
|
error(sprintf("Invalid map filter: %(id)s", { id: id }));
|
|
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)
|
|
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)
|
|
if (keywords.indexOf(matches[m]) != -1)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
function sendRegisterGameStanza()
|
|
{
|
|
if (!Engine.HasXmppClient())
|
|
return;
|
|
|
|
var selectedMapSize = Engine.GetGUIObjectByName("mapSize").selected;
|
|
var selectedVictoryCondition = Engine.GetGUIObjectByName("victoryCondition").selected;
|
|
|
|
// Map sizes only apply to random maps.
|
|
if (g_GameAttributes.mapType == "random")
|
|
var mapSize = Engine.GetGUIObjectByName("mapSize").list_data[selectedMapSize];
|
|
else
|
|
var mapSize = "Default";
|
|
|
|
var victoryCondition = Engine.GetGUIObjectByName("victoryCondition").list[selectedVictoryCondition];
|
|
var numberOfPlayers = Object.keys(g_PlayerAssignments).length;
|
|
var players = [ assignment.name for each (assignment in g_PlayerAssignments) ].join(", ");
|
|
|
|
var nbp = numberOfPlayers ? numberOfPlayers : 1;
|
|
var tnbp = g_GameAttributes.settings.PlayerData.length;
|
|
|
|
var gameData = {
|
|
"name":g_ServerName,
|
|
"mapName":g_GameAttributes.map,
|
|
"niceMapName":getMapDisplayName(g_GameAttributes.map),
|
|
"mapSize":mapSize,
|
|
"mapType":g_GameAttributes.mapType,
|
|
"victoryCondition":victoryCondition,
|
|
"nbp":nbp,
|
|
"tnbp":tnbp,
|
|
"players":players
|
|
};
|
|
Engine.SendRegisterGame(gameData);
|
|
}
|
|
|
|
function getVictoryConditions()
|
|
{
|
|
var r = {};
|
|
r.text = [translate("None")];
|
|
r.data = ["endless"];
|
|
r.scripts = [[]];
|
|
for (var vc in g_VictoryConditions)
|
|
{
|
|
r.data.push(vc);
|
|
r.text.push(g_VictoryConditions[vc].name);
|
|
r.scripts.push(g_VictoryConditions[vc].scripts);
|
|
}
|
|
return r;
|
|
}
|