Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/gamesetup.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/gamesetup.js (revision 15426)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/gamesetup.js (revision 15427)
@@ -1,1849 +1,1874 @@
////////////////////////////////////////////////////////////////////////////////////////////////
// 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 VICTORY_TEXT = [translateWithContext("victory", "Conquest"), translateWithContext("victory", "Wonder"), translateWithContext("victory", "None")];
-const VICTORY_DATA = ["conquest", "wonder", "endless"];
-const VICTORY_DEFAULTIDX = 0;
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 = [];
// Warn about the AI's nonexistent naval map support.
var g_NavalWarning = "\n\n" + sprintf(
translate("%(warning)s The AI does not support naval maps and may cause severe performance issues. Naval maps are recommended to be played with human opponents only. Nonetheless, Petra has now an experimental support for naval maps."),
{ warning: "[font=\"sans-bold-12\"][color=\"orange\"]" + translate("Warning:") + "[/color][/font]" }
);
// 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
////////////////////////////////////////////////////////////////////////////////////////////////
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()
{
// Update attributes so other players can see change
if (this.selected != -1)
g_GameAttributes.gameSpeed = g_GameSpeeds.speeds[this.selected];
if (!g_IsInGuiUpdate)
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];
if (!g_IsInGuiUpdate)
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];
if (!g_IsInGuiUpdate)
updateGameAttributes();
}
var victoryConditions = Engine.GetGUIObjectByName("victoryCondition");
- victoryConditions.list = VICTORY_TEXT;
- victoryConditions.list_data = VICTORY_DATA;
+ var victories = getVictoryConditions();
+ victoryConditions.list = victories.text;
+ victoryConditions.list_data = victories.data;
victoryConditions.onSelectionChange = function()
{ // Update attributes so other players can see change
if (this.selected != -1)
- g_GameAttributes.settings.GameType = VICTORY_DATA[this.selected];
+ {
+ g_GameAttributes.settings.GameType = victories.data[this.selected];
+ g_GameAttributes.settings.VictoryScripts = victories.scripts[this.selected];
+ }
if (!g_IsInGuiUpdate)
updateGameAttributes();
};
victoryConditions.selected = VICTORY_DEFAULTIDX;
var mapSize = Engine.GetGUIObjectByName("mapSize");
mapSize.list = g_MapSizes.names;
mapSize.list_data = g_MapSizes.tiles;
mapSize.onSelectionChange = function()
{
// Update attributes so other players can see change
if (this.selected != -1)
g_GameAttributes.settings.Size = g_MapSizes.tiles[this.selected];
if (!g_IsInGuiUpdate)
updateGameAttributes();
};
mapSize.selected = 0;
Engine.GetGUIObjectByName("revealMap").onPress = function()
{
// Update attributes so other players can see change
g_GameAttributes.settings.RevealMap = this.checked;
if (!g_IsInGuiUpdate)
updateGameAttributes();
};
Engine.GetGUIObjectByName("exploreMap").onPress = function()
{ // Update attributes so other players can see change
g_GameAttributes.settings.ExploreMap = this.checked;
if (!g_IsInGuiUpdate)
updateGameAttributes();
};
Engine.GetGUIObjectByName("lockTeams").onPress = function()
{
// Update attributes so other players can see change
g_GameAttributes.settings.LockTeams = this.checked;
if (!g_IsInGuiUpdate)
updateGameAttributes();
};
Engine.GetGUIObjectByName("enableCheats").onPress = function()
{
// Update attributes so other players can see change
g_GameAttributes.settings.CheatsEnabled = this.checked;
if (!g_IsInGuiUpdate)
updateGameAttributes();
};
Engine.GetGUIObjectByName("enableRating").onPress = function()
{
// Update attributes so other players can see change
g_GameAttributes.settings.RatingEnabled = this.checked;
Engine.SetRankedGame(this.checked);
Engine.GetGUIObjectByName("enableCheats").enabled = !this.checked;
Engine.GetGUIObjectByName("lockTeams").enabled = !this.checked;
if (!g_IsInGuiUpdate)
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+"]").enabled = false;
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()
{ // Update team
if (this.selected != -1)
g_GameAttributes.settings.PlayerData[playerSlot].Team = this.selected - 1;
if (!g_IsInGuiUpdate)
updateGameAttributes();
};
// Set events
var civ = Engine.GetGUIObjectByName("playerCiv["+i+"]");
civ.onSelectionChange = function()
{ // Update civ
if ((this.selected != -1)&&(g_GameAttributes.mapType !== "scenario"))
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
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();
}
// Sync g_GameAttributes to everyone.
if (g_IsController)
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);
}
}
// 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 display name from map data
function getMapPreview(map)
{
var mapData = loadMapData(map);
if (!mapData || !mapData.settings || !mapData.settings.Preview)
{ // Give some msg that map format is unsupported
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"));
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] = parseJSONData(name+".json");
break;
default:
error(sprintf("loadMapData: Unexpected map type '%(mapType)s'", { mapType: g_GameAttributes.mapType }));
return undefined;
}
}
return g_MapData[name];
}
////////////////////////////////////////////////////////////////////////////////////////////////
// GUI event handlers
function cancelSetup()
{
Engine.DisconnectNetworkGame();
if (Engine.HasXmppClient())
{
// Set player presence
Engine.LobbySetPlayerPresence("available");
// Unregister the game
if (g_IsController)
Engine.SendUnregisterGame();
}
}
var lastXmppClientPoll = Date.now();
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
// TODO: This should be remembered from the last session
g_GameAttributes.mapPath = "maps/scenarios/";
g_GameAttributes.map = g_GameAttributes.mapPath + (g_IsNetworked ? DEFAULT_NETWORKED_MAP : DEFAULT_OFFLINE_MAP);
break;
case "skirmish":
g_GameAttributes.mapPath = "maps/skirmishes/";
g_GameAttributes.settings = {
PlayerData: g_DefaultPlayerData.slice(0, 4),
Seed: 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),
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;
var mapData = loadMapData(name);
var mapSettings = (mapData && mapData.settings ? deepcopy(mapData.settings) : {});
// 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];
// 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;
if (g_GameAttributes.map == "random")
selectMap(Engine.GetGUIObjectByName("mapSelection").list_data[Math.floor(Math.random() *
(Engine.GetGUIObjectByName("mapSelection").list.length - 1)) + 1]);
+ if (!g_GameAttributes.settings.TriggerScripts)
+ g_GameAttributes.settings.TriggerScripts = g_GameAttributes.settings.VictoryScripts;
+ else
+ g_GameAttributes.settings.TriggerScripts = g_GameAttributes.settings.VictoryScripts.concat(g_GameAttrbutes.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));
var populationCapBox = Engine.GetGUIObjectByName("populationCap");
populationCapBox.selected = populationCapBox.list_data.indexOf(mapSettings.PopulationCap);
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 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 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");
// 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 victoryIdx = (mapSettings.GameType !== undefined && VICTORY_DATA.indexOf(mapSettings.GameType) != -1 ? VICTORY_DATA.indexOf(mapSettings.GameType) : VICTORY_DEFAULTIDX);
+ 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 ? "Yes" : "No");
if (mapSettings.RatingEnabled !== undefined)
{
enableRating.checked = mapSettings.RatingEnabled;
Engine.SetRankedGame(enableRating.checked);
enableRatingText.caption = (enableRating.checked ? "Yes" : "No");
}
else
enableRatingText.caption = "Unknown";
gameSpeedText.caption = g_GameSpeeds.names[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);
// Handle map type specific logic
switch (g_GameAttributes.mapType)
{
case "random":
mapSizeDesc.hidden = false;
if (g_IsController)
{
//Host
numPlayersSelection.selected = numPlayers - 1;
numPlayersSelection.hidden = false;
mapSize.hidden = false;
revealMap.hidden = false;
exploreMap.hidden = false;
victoryCondition.hidden = false;
lockTeams.hidden = false;
populationCap.hidden = false;
startingResources.hidden = false;
numPlayersText.hidden = true;
mapSizeText.hidden = true;
revealMapText.hidden = true;
exploreMapText.hidden = true;
victoryConditionText.hidden = true;
lockTeamsText.hidden = true;
populationCapText.hidden = true;
startingResourcesText.hidden = true;
mapSizeText.caption = translate("Map Size:");
mapSize.selected = sizeIdx;
revealMapText.caption = translate("Revealed Map:");
exploreMapText.caption = translate("Explored Map:");
revealMap.checked = (mapSettings.RevealMap ? true : false);
exploreMap.checked = (mapSettings.ExploreMap ? true : false);
victoryConditionText.caption = translate("Victory Condition:");
victoryCondition.selected = victoryIdx;
lockTeamsText.caption = translate("Teams Locked:");
lockTeams.checked = (mapSettings.LockTeams ? true : false);
}
else
{
// Client
numPlayersText.hidden = false;
mapSizeText.hidden = false;
revealMapText.hidden = false;
exploreMapText.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 = g_MapSizes.names[sizeIdx];
revealMapText.caption = (mapSettings.RevealMap ? translate("Yes") : translate("No"));
exploreMapText.caption = (mapSettings.ExporeMap ? translate("Yes") : translate("No"));
- victoryConditionText.caption = VICTORY_TEXT[victoryIdx];
+ victoryConditionText.caption = victories.text[victoryIdx];
lockTeamsText.caption = (mapSettings.LockTeams ? translate("Yes") : translate("No"));
}
break;
case "skirmish":
mapSizeText.caption = translate("Default");
numPlayersText.caption = numPlayers;
numPlayersSelection.hidden = true;
mapSize.hidden = true;
mapSizeText.hidden = true;
mapSizeDesc.hidden = true;
if (g_IsController)
{
//Host
revealMap.hidden = false;
exploreMap.hidden = false;
victoryCondition.hidden = false;
lockTeams.hidden = false;
populationCap.hidden = false;
startingResources.hidden = false;
numPlayersText.hidden = false;
revealMapText.hidden = true;
exploreMapText.hidden = true;
victoryConditionText.hidden = true;
lockTeamsText.hidden = true;
populationCapText.hidden = true;
startingResourcesText.hidden = true;
revealMapText.caption = translate("Revealed Map:");
exploreMapText.caption = translate("Explored Map:");
revealMap.checked = (mapSettings.RevealMap ? true : false);
exploreMap.checked = (mapSettings.ExploreMap ? true : false);
victoryConditionText.caption = translate("Victory Condition:");
victoryCondition.selected = victoryIdx;
lockTeamsText.caption = translate("Teams Locked:");
lockTeams.checked = (mapSettings.LockTeams ? true : false);
}
else
{
// Client
numPlayersText.hidden = false;
revealMapText.hidden = false;
exploreMapText.hidden = false;
victoryConditionText.hidden = false;
lockTeamsText.hidden = false;
populationCap.hidden = true;
populationCapText.hidden = false;
startingResources.hidden = true;
startingResourcesText.hidden = false;
revealMapText.caption = (mapSettings.RevealMap ? translate("Yes") : translate("No"));
exploreMapText.caption = (mapSettings.ExploreMap ? translate("Yes") : translate("No"));
- victoryConditionText.caption = VICTORY_TEXT[victoryIdx];
+ 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;
victoryCondition.hidden = true;
lockTeams.hidden = true;
numPlayersText.hidden = false;
mapSizeText.hidden = true;
mapSizeDesc.hidden = true;
revealMapText.hidden = false;
exploreMapText.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"));
- victoryConditionText.caption = VICTORY_TEXT[victoryIdx];
+ 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.");
if (g_GameAttributes.mapFilter == "naval")
description += g_NavalWarning;
// Describe the number of players
var playerString = sprintf(translatePlural("%(number)s player. %(description)s", "%(number)s players. %(description)s", numPlayers), { number: numPlayers, description: 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 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("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 = translate(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
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_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+"]");
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
swapPlayers(guid, playerSlot);
Engine.SetNetworkGameAttributes(g_GameAttributes);
updateReadyUI();
}
};
}
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
swapPlayers(guid, playerSlot);
updatePlayerList();
}
};
}
}
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 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":
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()
{
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.
var startGameButton = Engine.GetGUIObjectByName("startGame");
if (g_IsNetworked && g_IsController)
{
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)
{ // Basic validity test
var newFilter = {};
newFilter.id = id;
newFilter.name = name;
newFilter.filter = filterFunc;
g_MapFilters.push(newFilter);
}
else
{
error(sprintf("Invalid map filter: %(name)s", { name: name }));
}
}
// 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[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;
+}
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/gamesetup.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/gamesetup.xml (revision 15426)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/gamesetup.xml (revision 15427)
@@ -1,374 +1,375 @@
+
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/victory_conditions/conquest.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/victory_conditions/conquest.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/victory_conditions/conquest.js (revision 15427)
@@ -0,0 +1,21 @@
+if (!g_VictoryConditions)
+ var g_VictoryConditions = {};
+
+var vc = {};
+
+vc.name = translate("Conquest");
+vc.description = translate("Defeat all opponents");
+
+/*
+NOT SUPPORTED YET
+vc.requirementsCheck = function(mapSettings)
+{
+ // nothing required
+ return true;
+};
+*/
+
+vc.scripts = ["scripts/Conquest.js"];
+
+g_VictoryConditions.conquest = vc;
+vc = null;
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/victory_conditions/wonder.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/victory_conditions/wonder.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/victory_conditions/wonder.js (revision 15427)
@@ -0,0 +1,21 @@
+if (!g_VictoryConditions)
+ var g_VictoryConditions = {};
+
+var vc = {};
+
+vc.name = translate("Wonder");
+vc.description = translate("Build a wonder to win");
+
+/*
+NOT SUPPORTED YET
+vc.requirementsCheck = function(mapSettings)
+{
+ // nothing required
+ return true;
+};
+*/
+
+vc.scripts = ["scripts/Conquest.js", "scripts/WonderVictory.js"];
+
+g_VictoryConditions.wonder = vc;
+vc = null;
Index: ps/trunk/binaries/data/mods/public/maps/scripts/Conquest.js
===================================================================
--- ps/trunk/binaries/data/mods/public/maps/scripts/Conquest.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/maps/scripts/Conquest.js (revision 15427)
@@ -0,0 +1,70 @@
+/*
+ * Check players the next turn. Avoids problems in Atlas, with promoting entities etc
+ */
+Trigger.prototype.CheckConquestCriticalEntities = function()
+{
+ if (this.checkingConquestCriticalEntities)
+ return;
+ // wait a turn for actually checking the players
+ this.DoAfterDelay(100, "CheckConquestCriticalEntitiesNow", null);
+ this.checkingConquestCriticalEntities = true;
+};
+
+/*
+ * Check players immediately. Might cause problems with converting/promoting entities.
+ */
+Trigger.prototype.CheckConquestCriticalEntitiesNow = function()
+{
+ this.checkingConquestCriticalEntities = false;
+ // for all other game types, defeat that player
+ var cmpPlayerManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager);
+ var cmpEndGameManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_EndGameManager);
+
+ // Ignore gaia
+ var numPlayers = cmpPlayerManager.GetNumPlayers();
+ var cmpPlayers = [];
+
+ var allies = [];
+ var onlyAlliesLeft = true;
+ // If the player is currently active but needs to be defeated,
+ // mark that player as defeated
+ // cache the cmpPlayer instances of the other players and search the allies
+ for (var i = 1; i < numPlayers; i++)
+ {
+ // cmpPlayer should always exist for the player ids from 1 to numplayers
+ // so no tests on the existance of cmpPlayer are needed
+ var playerEntityId = cmpPlayerManager.GetPlayerByID(i);
+ cmpPlayers[i] = Engine.QueryInterface(playerEntityId, IID_Player);
+ if (cmpPlayers[i].GetState() != "active")
+ continue;
+ if (cmpPlayers[i].GetConquestCriticalEntitiesCount() == 0)
+ Engine.PostMessage(playerEntityId, MT_PlayerDefeated, { "playerId": i } );
+ else
+ {
+ if (!allies.length || cmpPlayers[allies[0]].IsMutualAlly(i))
+ allies.push(i);
+ else
+ onlyAlliesLeft = false;
+ }
+ }
+
+ // check if there are winners, or the game needs to continue
+ if (!allies.length || !onlyAlliesLeft || !(cmpEndGameManager.alliedVictory || allies.length == 1))
+ return;
+
+ for each (var p in allies)
+ cmpPlayers[p].SetState("won");
+
+ // Reveal the map to all players
+ var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
+ cmpRangeManager.SetLosRevealAll(-1, true);
+};
+
+var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
+
+var data = {"enabled": true};
+cmpTrigger.RegisterTrigger("OnOwnershipChanged", "CheckConquestCriticalEntities", data);
+// also check at the start of the game
+cmpTrigger.DoAfterDelay(100, "CheckConquestCriticalEntities", null);
+cmpTrigger.checkingConquestCriticalEntities = false;
+
Index: ps/trunk/binaries/data/mods/public/maps/scripts/WonderVictory.js
===================================================================
--- ps/trunk/binaries/data/mods/public/maps/scripts/WonderVictory.js (nonexistent)
+++ ps/trunk/binaries/data/mods/public/maps/scripts/WonderVictory.js (revision 15427)
@@ -0,0 +1,60 @@
+Trigger.prototype.CheckWonderVictory = function(data)
+{
+ warn(uneval(data));
+ var ent = data.entity;
+ var cmpWonder = Engine.QueryInterface(ent, IID_Wonder);
+ if (!cmpWonder)
+ return;
+
+ var timer = this.wonderVictoryTimers[ent];
+ var messages = this.wonderVictoryMessages[ent] || {};
+
+ var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
+ var cmpGuiInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
+ // remove existing messages if any
+ if (timer)
+ {
+ cmpTimer.CancelTimer(timer);
+ cmpGuiInterface.DeleteTimeNotification(messages.ownMessage);
+ cmpGuiInterface.DeleteTimeNotification(messages.otherMessage);
+ }
+
+ if (data.to <= 0)
+ return;
+
+ // create new messages, and start timer to register defeat.
+ var cmpPlayerManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager);
+ var numPlayers = cmpPlayerManager.GetNumPlayers();
+ var cmpPlayer = QueryOwnerInterface(ent, IID_Player);
+ var players = [];
+ for (var i = 1; i < numPlayers; i++)
+ if (i != data.to)
+ players.push(i);
+
+ var time = cmpWonder.GetTimeTillVictory()*1000;
+ messages.otherMessage = cmpGuiInterface.AddTimeNotification({
+ "message": markForTranslation("%(player)s will have won in %(time)s"),
+ "players": players,
+ "duration": time,
+ "parameters": {"player": cmpPlayer.GetName()},
+ "translateMessage": true,
+ "translateParameters": [],
+ });
+ messages.ownMessage = cmpGuiInterface.AddTimeNotification({
+ "message": markForTranslation("You will have won in %(time)s"),
+ "players": [data.to],
+ "duration": time,
+ "translateMessage": true,
+ });
+ timer = cmpTimer.SetTimeout(SYSTEM_ENTITY, IID_EndGameManager, "MarkPlayerAsWon", time, data.to);
+
+ this.wonderVictoryTimers[ent] = timer;
+ this.wonderVictoryMessages[ent] = messages;
+}
+
+var cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger);
+
+var data = {"enabled": true};
+cmpTrigger.RegisterTrigger("OnOwnershipChanged", "CheckWonderVictory", data);
+cmpTrigger.wonderVictoryTimers = {};
+cmpTrigger.wonderVictoryMessages = {};
Index: ps/trunk/binaries/data/mods/public/simulation/components/EndGameManager.js
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/components/EndGameManager.js (revision 15426)
+++ ps/trunk/binaries/data/mods/public/simulation/components/EndGameManager.js (revision 15427)
@@ -1,126 +1,61 @@
// Repetition interval (msecs) for checking end game conditions
var g_ProgressInterval = 1000;
/**
* System component which regularly checks victory/defeat conditions
* and if they are satisfied then it marks the player as victorious/defeated.
*/
function EndGameManager() {}
EndGameManager.prototype.Schema =
"";
EndGameManager.prototype.Init = function()
{
// Game type, initialised from the map settings.
// One of: "conquest" (default) and "endless"
this.gameType = "conquest";
// Allied victory means allied players can win if victory conditions are met for each of them
// Would be false for a "last man standing" game (when diplomacy is fully implemented)
this.alliedVictory = true;
};
EndGameManager.prototype.SetGameType = function(newGameType)
{
this.gameType = newGameType;
Engine.BroadcastMessage(MT_GameTypeChanged, {});
};
EndGameManager.prototype.CheckGameType = function(type)
{
return this.gameType == type;
};
EndGameManager.prototype.MarkPlayerAsWon = function(playerID)
{
var cmpPlayerManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager);
var numPlayers = cmpPlayerManager.GetNumPlayers();
for (var i = 1; i < numPlayers; i++)
{
var playerEntityId = cmpPlayerManager.GetPlayerByID(i);
var cmpPlayer = Engine.QueryInterface(playerEntityId, IID_Player);
if (cmpPlayer.GetState() != "active")
continue;
if (playerID == cmpPlayer.GetPlayerID() || this.alliedVictory && cmpPlayer.IsMutualAlly(playerID))
cmpPlayer.SetState("won")
else
Engine.PostMessage(playerEntityId, MT_PlayerDefeated, { "playerId": i } );
}
// Reveal the map to all players
var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
cmpRangeManager.SetLosRevealAll(-1, true);
};
EndGameManager.prototype.SetAlliedVictory = function(flag)
{
this.alliedVictory = flag;
};
-/*
- * Check players the next turn. Avoids problems in Atlas, with promoting entities etc
- */
-EndGameManager.prototype.CheckConquestCriticalEntities = function()
-{
- if (this.timeout)
- return;
- // wait a turn for actually checking the players
- var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
- this.timeout = cmpTimer.SetTimeout(this.entity, IID_EndGameManager, "CheckConquestCriticalEntitiesNow", 100, null);
-};
-
-/*
- * Check players immediately. Might cause problems with converting/promoting entities.
- */
-EndGameManager.prototype.CheckConquestCriticalEntitiesNow = function()
-{
- if (this.timeout)
- this.timeout = null;
- if (this.gameType == "endless")
- return;
-
- // for all other game types, defeat that player
- var cmpPlayerManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager);
-
- // Ignore gaia
- var numPlayers = cmpPlayerManager.GetNumPlayers();
- var cmpPlayers = [];
-
- var allies = [];
- var onlyAlliesLeft = true;
- // If the player is currently active but needs to be defeated,
- // mark that player as defeated
- // cache the cmpPlayer instances of the other players and search the allies
- for (var i = 1; i < numPlayers; i++)
- {
- // cmpPlayer should always exist for the player ids from 1 to numplayers
- // so no tests on the existance of cmpPlayer are needed
- var playerEntityId = cmpPlayerManager.GetPlayerByID(i);
- cmpPlayers[i] = Engine.QueryInterface(playerEntityId, IID_Player);
- if (cmpPlayers[i].GetState() != "active")
- continue;
- if (cmpPlayers[i].GetConquestCriticalEntitiesCount() == 0)
- Engine.PostMessage(playerEntityId, MT_PlayerDefeated, { "playerId": i } );
- else
- {
- if (!allies.length || cmpPlayers[allies[0]].IsMutualAlly(i))
- allies.push(i);
- else
- onlyAlliesLeft = false;
- }
- }
-
- // check if there are winners, or the game needs to continue
- if (!allies.length || !onlyAlliesLeft || !(this.alliedVictory || allies.length == 1))
- return;
-
- for each (var p in allies)
- cmpPlayers[p].SetState("won");
-
- // Reveal the map to all players
- var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
- cmpRangeManager.SetLosRevealAll(-1, true);
-};
-
Engine.RegisterSystemComponentType(IID_EndGameManager, "EndGameManager", EndGameManager);
Index: ps/trunk/binaries/data/mods/public/simulation/components/Player.js
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/components/Player.js (revision 15426)
+++ ps/trunk/binaries/data/mods/public/simulation/components/Player.js (revision 15427)
@@ -1,707 +1,702 @@
function Player() {}
Player.prototype.Schema =
"";
Player.prototype.Init = function()
{
this.playerID = undefined;
this.name = undefined; // define defaults elsewhere (supporting other languages)
this.civ = undefined;
this.colour = { "r": 0.0, "g": 0.0, "b": 0.0, "a": 1.0 };
this.popUsed = 0; // population of units owned or trained by this player
this.popBonuses = 0; // sum of population bonuses of player's entities
this.maxPop = 300; // maximum population
this.trainingBlocked = false; // indicates whether any training queue is currently blocked
this.resourceCount = {
"food": 300,
"wood": 300,
"metal": 300,
"stone": 300
};
this.tradingGoods = [ // goods for next trade-route and its proba in % (the sum of probas must be 100)
{ "goods": "wood", "proba": 30 },
{ "goods": "stone", "proba": 35 },
{ "goods": "metal", "proba": 35 } ];
this.team = -1; // team number of the player, players on the same team will always have ally diplomatic status - also this is useful for team emblems, scoring, etc.
this.teamsLocked = false;
this.state = "active"; // game state - one of "active", "defeated", "won"
this.diplomacy = []; // array of diplomatic stances for this player with respect to other players (including gaia and self)
this.conquestCriticalEntitiesCount = 0; // number of owned units with ConquestCritical class
this.formations = [];
this.startCam = undefined;
this.controlAllUnits = false;
this.isAI = false;
this.gatherRateMultiplier = 1;
this.cheatsEnabled = false;
this.cheatTimeMultiplier = 1;
this.heroes = [];
this.resourceNames = {
"food": markForTranslation("Food"),
"wood": markForTranslation("Wood"),
"metal": markForTranslation("Metal"),
"stone": markForTranslation("Stone"),
}
- Engine.QueryInterface(SYSTEM_ENTITY, IID_EndGameManager).CheckConquestCriticalEntities();
};
Player.prototype.SetPlayerID = function(id)
{
this.playerID = id;
};
Player.prototype.GetPlayerID = function()
{
return this.playerID;
};
Player.prototype.SetName = function(name)
{
this.name = name;
};
Player.prototype.GetName = function()
{
return this.name;
};
Player.prototype.SetCiv = function(civcode)
{
this.civ = civcode;
};
Player.prototype.GetCiv = function()
{
return this.civ;
};
Player.prototype.SetColour = function(r, g, b)
{
this.colour = { "r": r/255.0, "g": g/255.0, "b": b/255.0, "a": 1.0 };
};
Player.prototype.GetColour = function()
{
return this.colour;
};
// Try reserving num population slots. Returns 0 on success or number of missing slots otherwise.
Player.prototype.TryReservePopulationSlots = function(num)
{
if (num != 0 && num > (this.GetPopulationLimit() - this.GetPopulationCount()))
return num - (this.GetPopulationLimit() - this.GetPopulationCount());
this.popUsed += num;
return 0;
};
Player.prototype.UnReservePopulationSlots = function(num)
{
this.popUsed -= num;
};
Player.prototype.GetPopulationCount = function()
{
return this.popUsed;
};
Player.prototype.SetPopulationBonuses = function(num)
{
this.popBonuses = num;
};
Player.prototype.AddPopulationBonuses = function(num)
{
this.popBonuses += num;
};
Player.prototype.GetPopulationLimit = function()
{
return Math.min(this.GetMaxPopulation(), this.popBonuses);
};
Player.prototype.SetMaxPopulation = function(max)
{
this.maxPop = max;
};
Player.prototype.GetMaxPopulation = function()
{
return Math.round(ApplyValueModificationsToPlayer("Player/MaxPopulation", this.maxPop, this.entity));
};
Player.prototype.SetGatherRateMultiplier = function(value)
{
this.gatherRateMultiplier = value;
};
Player.prototype.GetGatherRateMultiplier = function()
{
return this.gatherRateMultiplier;
};
Player.prototype.GetHeroes = function()
{
return this.heroes;
};
Player.prototype.IsTrainingBlocked = function()
{
return this.trainingBlocked;
};
Player.prototype.BlockTraining = function()
{
this.trainingBlocked = true;
};
Player.prototype.UnBlockTraining = function()
{
this.trainingBlocked = false;
};
Player.prototype.SetResourceCounts = function(resources)
{
if (resources.food !== undefined)
this.resourceCount.food = resources.food;
if (resources.wood !== undefined)
this.resourceCount.wood = resources.wood;
if (resources.stone !== undefined)
this.resourceCount.stone = resources.stone;
if (resources.metal !== undefined)
this.resourceCount.metal = resources.metal;
};
Player.prototype.GetResourceCounts = function()
{
return this.resourceCount;
};
/**
* Add resource of specified type to player
* @param type Generic type of resource (string)
* @param amount Amount of resource, which should be added (integer)
*/
Player.prototype.AddResource = function(type, amount)
{
this.resourceCount[type] += (+amount);
};
/**
* Add resources to player
*/
Player.prototype.AddResources = function(amounts)
{
for (var type in amounts)
{
this.resourceCount[type] += (+amounts[type]);
}
};
Player.prototype.GetNeededResources = function(amounts)
{
// Check if we can afford it all
var amountsNeeded = {};
for (var type in amounts)
if (this.resourceCount[type] != undefined && amounts[type] > this.resourceCount[type])
amountsNeeded[type] = amounts[type] - Math.floor(this.resourceCount[type]);
if (Object.keys(amountsNeeded).length == 0)
return undefined;
return amountsNeeded;
};
Player.prototype.SubtractResourcesOrNotify = function(amounts)
{
var amountsNeeded = this.GetNeededResources(amounts);
// If we don't have enough resources, send a notification to the player
if (amountsNeeded)
{
var parameters = {};
var i = 0;
for (var type in amountsNeeded)
{
i++;
parameters["resourceType"+i] = this.resourceNames[type];
parameters["resourceAmount"+i] = amountsNeeded[type];
}
var msg = "";
// when marking strings for translations, you need to include the actual string,
// not some way to derive the string
if (i < 1)
warn("Amounts needed but no amounts given?");
else if (i == 1)
msg = markForTranslation("Insufficient resources - %(resourceAmount1)s %(resourceType1)s");
else if (i == 2)
msg = markForTranslation("Insufficient resources - %(resourceAmount1)s %(resourceType1)s, %(resourceAmount2)s %(resourceType2)s");
else if (i == 3)
msg = markForTranslation("Insufficient resources - %(resourceAmount1)s %(resourceType1)s, %(resourceAmount2)s %(resourceType2)s, %(resourceAmount3)s %(resourceType3)s");
else if (i == 4)
msg = markForTranslation("Insufficient resources - %(resourceAmount1)s %(resourceType1)s, %(resourceAmount2)s %(resourceType2)s, %(resourceAmount3)s %(resourceType3)s, %(resourceAmount4)s %(resourceType4)s");
else
warn("Localisation: Strings are not localised for more than 4 resources");
var notification = {
"players": [this.playerID],
"message": msg,
"parameters": parameters,
"translateMessage": true,
"translateParameters": {
"resourceType1": "withinSentence",
"resourceType2": "withinSentence",
"resourceType3": "withinSentence",
"resourceType4": "withinSentence",
},
};
var cmpGUIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
cmpGUIInterface.PushNotification(notification);
return false;
}
// Subtract the resources
for (var type in amounts)
this.resourceCount[type] -= amounts[type];
return true;
};
Player.prototype.TrySubtractResources = function(amounts)
{
if (!this.SubtractResourcesOrNotify(amounts))
return false;
var cmpStatisticsTracker = QueryPlayerIDInterface(this.playerID, IID_StatisticsTracker);
if (cmpStatisticsTracker)
for (var type in amounts)
cmpStatisticsTracker.IncreaseResourceUsedCounter(type, amounts[type]);
return true;
};
Player.prototype.GetNextTradingGoods = function()
{
var value = 100*Math.random();
var last = this.tradingGoods.length - 1;
var sumProba = 0;
for (var i = 0; i < last; ++i)
{
sumProba += this.tradingGoods[i].proba;
if (value < sumProba)
return this.tradingGoods[i].goods;
}
return this.tradingGoods[last].goods;
};
Player.prototype.GetTradingGoods = function()
{
var tradingGoods = {};
for each (var resource in this.tradingGoods)
tradingGoods[resource.goods] = resource.proba;
return tradingGoods;
};
Player.prototype.SetTradingGoods = function(tradingGoods)
{
var sumProba = 0;
for (var resource in tradingGoods)
sumProba += tradingGoods[resource];
if (sumProba != 100) // consistency check
{
error("Player.js SetTradingGoods: " + uneval(tradingGoods));
tradingGoods = { "food": 20, "wood":20, "stone":30, "metal":30 };
}
this.tradingGoods = [];
for (var resource in tradingGoods)
this.tradingGoods.push( {"goods": resource, "proba": tradingGoods[resource]} );
};
Player.prototype.GetState = function()
{
return this.state;
};
Player.prototype.SetState = function(newState)
{
this.state = newState;
};
Player.prototype.GetConquestCriticalEntitiesCount = function()
{
return this.conquestCriticalEntitiesCount;
};
Player.prototype.GetTeam = function()
{
return this.team;
};
Player.prototype.SetTeam = function(team)
{
if (!this.teamsLocked)
{
this.team = team;
var cmpPlayerManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager);
if (cmpPlayerManager && this.team != -1)
{
// Set all team members as allies
for (var i = 0; i < cmpPlayerManager.GetNumPlayers(); ++i)
{
var cmpPlayer = Engine.QueryInterface(cmpPlayerManager.GetPlayerByID(i), IID_Player);
if (this.team == cmpPlayer.GetTeam())
{
this.SetAlly(i);
cmpPlayer.SetAlly(this.playerID);
}
}
}
Engine.BroadcastMessage(MT_DiplomacyChanged, {"player": this.playerID});
}
};
Player.prototype.SetLockTeams = function(value)
{
this.teamsLocked = value;
};
Player.prototype.GetLockTeams = function()
{
return this.teamsLocked;
};
Player.prototype.GetDiplomacy = function()
{
return this.diplomacy;
};
Player.prototype.SetDiplomacy = function(dipl)
{
// Should we check for teamsLocked here?
this.diplomacy = dipl;
Engine.BroadcastMessage(MT_DiplomacyChanged, {"player": this.playerID});
};
Player.prototype.SetDiplomacyIndex = function(idx, value)
{
var cmpPlayerManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager);
if (!cmpPlayerManager)
return;
var cmpPlayer = Engine.QueryInterface(cmpPlayerManager.GetPlayerByID(idx), IID_Player);
if (!cmpPlayer)
return;
if (this.state != "active" || cmpPlayer.state != "active")
return;
// You can have alliances with other players,
if (this.teamsLocked)
{
// but can't stab your team members in the back
if (this.team == -1 || this.team != cmpPlayer.GetTeam())
{
// Break alliance or declare war
if (Math.min(this.diplomacy[idx],cmpPlayer.diplomacy[this.playerID]) > value)
{
this.diplomacy[idx] = value;
cmpPlayer.SetDiplomacyIndex(this.playerID, value);
}
else
{
this.diplomacy[idx] = value;
}
Engine.BroadcastMessage(MT_DiplomacyChanged, {"player": this.playerID});
}
}
else
{
// Break alliance or declare war (worsening of relations is mutual)
if (Math.min(this.diplomacy[idx],cmpPlayer.diplomacy[this.playerID]) > value)
{
// This is duplicated because otherwise we get too much recursion
this.diplomacy[idx] = value;
cmpPlayer.SetDiplomacyIndex(this.playerID, value);
}
else
{
this.diplomacy[idx] = value;
}
Engine.BroadcastMessage(MT_DiplomacyChanged, {"player": this.playerID});
}
};
Player.prototype.UpdateSharedLos = function()
{
var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
if (!cmpRangeManager)
return;
var cmpPlayerManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager);
if (!cmpPlayerManager)
return;
var sharedLos = [];
for (var i = 0; i < cmpPlayerManager.GetNumPlayers(); ++i)
if (this.IsMutualAlly(i))
sharedLos.push(i);
cmpRangeManager.SetSharedLos(this.playerID, sharedLos);
};
Player.prototype.GetFormations = function()
{
return this.formations;
};
Player.prototype.SetFormations = function(formations)
{
this.formations = formations;
};
Player.prototype.GetStartingCameraPos = function()
{
return this.startCam.position;
};
Player.prototype.GetStartingCameraRot = function()
{
return this.startCam.rotation;
};
Player.prototype.SetStartingCamera = function(pos, rot)
{
this.startCam = {"position": pos, "rotation": rot};
};
Player.prototype.HasStartingCamera = function()
{
return (this.startCam !== undefined);
};
Player.prototype.SetControlAllUnits = function(c)
{
this.controlAllUnits = c;
};
Player.prototype.CanControlAllUnits = function()
{
return this.controlAllUnits;
};
Player.prototype.SetAI = function(flag)
{
this.isAI = flag;
};
Player.prototype.IsAI = function()
{
return this.isAI;
};
Player.prototype.SetAlly = function(id)
{
this.SetDiplomacyIndex(id, 1);
};
/**
* Check if given player is our ally
*/
Player.prototype.IsAlly = function(id)
{
return this.diplomacy[id] > 0;
};
/**
* Check if given player is our ally, and we are its ally
*/
Player.prototype.IsMutualAlly = function(id)
{
var cmpPlayerManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager);
if (!cmpPlayerManager)
return false;
var cmpPlayer = Engine.QueryInterface(cmpPlayerManager.GetPlayerByID(id), IID_Player);
return this.IsAlly(id) && cmpPlayer && cmpPlayer.IsAlly(this.playerID);
};
Player.prototype.SetEnemy = function(id)
{
this.SetDiplomacyIndex(id, -1);
};
/**
* Get all enemies of a given player.
*/
Player.prototype.GetEnemies = function()
{
var enemies = [];
for (var i = 0; i < this.diplomacy.length; i++)
if (this.diplomacy[i] < 0)
enemies.push(i);
return enemies;
};
/**
* Check if given player is our enemy
*/
Player.prototype.IsEnemy = function(id)
{
return this.diplomacy[id] < 0;
};
Player.prototype.SetNeutral = function(id)
{
this.SetDiplomacyIndex(id, 0);
};
/**
* Check if given player is neutral
*/
Player.prototype.IsNeutral = function(id)
{
return this.diplomacy[id] == 0;
};
/**
* Keep track of population effects of all entities that
* become owned or unowned by this player
*/
Player.prototype.OnGlobalOwnershipChanged = function(msg)
{
if (msg.from != this.playerID && msg.to != this.playerID)
return;
var cmpIdentity = Engine.QueryInterface(msg.entity, IID_Identity);
var cmpCost = Engine.QueryInterface(msg.entity, IID_Cost);
var cmpFoundation = Engine.QueryInterface(msg.entity, IID_Foundation);
if (msg.from == this.playerID)
{
if (!cmpFoundation && cmpIdentity && cmpIdentity.HasClass("ConquestCritical"))
this.conquestCriticalEntitiesCount--;
- if (this.conquestCriticalEntitiesCount == 0) // end game when needed
- Engine.QueryInterface(SYSTEM_ENTITY, IID_EndGameManager).CheckConquestCriticalEntities();
-
if (cmpCost)
this.popUsed -= cmpCost.GetPopCost();
if (cmpIdentity && cmpIdentity.HasClass("Hero"))
{
//Remove from Heroes list
var index = this.heroes.indexOf(msg.entity);
if (index >= 0)
this.heroes.splice(index, 1);
}
}
if (msg.to == this.playerID)
{
if (!cmpFoundation && cmpIdentity && cmpIdentity.HasClass("ConquestCritical"))
this.conquestCriticalEntitiesCount++;
if (cmpCost)
this.popUsed += cmpCost.GetPopCost();
if (cmpIdentity && cmpIdentity.HasClass("Hero"))
this.heroes.push(msg.entity);
}
};
Player.prototype.OnPlayerDefeated = function(msg)
{
this.state = "defeated";
// TODO: Tribute all resources to this player's active allies (if any)
// Reassign all player's entities to Gaia
var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
var entities = cmpRangeManager.GetEntitiesByPlayer(this.playerID);
// The ownership change is done in two steps so that entities don't hit idle
// (and thus possibly look for "enemies" to attack) before nearby allies get
// converted to Gaia as well.
for each (var entity in entities)
{
var cmpOwnership = Engine.QueryInterface(entity, IID_Ownership);
cmpOwnership.SetOwnerQuiet(0);
}
// With the real ownership change complete, send OwnershipChanged messages.
for each (var entity in entities)
Engine.PostMessage(entity, MT_OwnershipChanged, { "entity": entity,
"from": this.playerID, "to": 0 });
// Reveal the map for this player.
cmpRangeManager.SetLosRevealAll(this.playerID, true);
// Send a chat message notifying of the player's defeat.
var notification = {"type": "defeat", "players": [this.playerID]};
var cmpGUIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
cmpGUIInterface.PushNotification(notification);
};
Player.prototype.OnDiplomacyChanged = function()
{
this.UpdateSharedLos();
- Engine.QueryInterface(SYSTEM_ENTITY, IID_EndGameManager).CheckConquestCriticalEntities();
};
Player.prototype.SetCheatsEnabled = function(flag)
{
this.cheatsEnabled = flag;
};
Player.prototype.GetCheatsEnabled = function()
{
return this.cheatsEnabled;
};
Player.prototype.SetCheatTimeMultiplier = function(time)
{
this.cheatTimeMultiplier = time;
};
Player.prototype.GetCheatTimeMultiplier = function()
{
return this.cheatTimeMultiplier;
};
Player.prototype.TributeResource = function(player, amounts)
{
var cmpPlayerManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager);
if (!cmpPlayerManager)
return;
var cmpPlayer = Engine.QueryInterface(cmpPlayerManager.GetPlayerByID(player), IID_Player);
if (!cmpPlayer)
return;
if (this.state != "active" || cmpPlayer.state != "active")
return;
if (!this.SubtractResourcesOrNotify(amounts))
return;
cmpPlayer.AddResources(amounts);
var total = Object.keys(amounts).reduce(function (sum, type){ return sum + amounts[type]; }, 0);
var cmpOurStatisticsTracker = QueryPlayerIDInterface(this.playerID, IID_StatisticsTracker);
if (cmpOurStatisticsTracker)
cmpOurStatisticsTracker.IncreaseTributesSentCounter(total);
var cmpTheirStatisticsTracker = QueryPlayerIDInterface(player, IID_StatisticsTracker);
if (cmpTheirStatisticsTracker)
cmpTheirStatisticsTracker.IncreaseTributesReceivedCounter(total);
var notification = {"type": "tribute", "players": [player], "donator": this.playerID, "amounts": amounts};
var cmpGUIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
if (cmpGUIInterface)
cmpGUIInterface.PushNotification(notification);
};
Engine.RegisterComponentType(IID_Player, "Player", Player);
Index: ps/trunk/binaries/data/mods/public/simulation/components/Trigger.js
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/components/Trigger.js (revision 15426)
+++ ps/trunk/binaries/data/mods/public/simulation/components/Trigger.js (revision 15427)
@@ -1,265 +1,265 @@
function Trigger() {}
Trigger.prototype.Schema =
"";
/**
* Events we're able to receive and call handlers for
*/
Trigger.prototype.eventNames =
[
"StructureBuilt",
"ConstructionStarted",
"TrainingFinished",
"TrainingQueued",
"ResearchFinished",
"ResearchQueued",
"OwnershipChanged",
"PlayerCommand",
"Interval",
"Range",
];
Trigger.prototype.Init = function()
{
this.triggerPoints = {};
// Each event has its own set of actions determined by the map maker.
for each (var eventName in this.eventNames)
this["On" + eventName + "Actions"] = {};
};
Trigger.prototype.RegisterTriggerPoint = function(ref, ent)
{
if (!this.triggerPoints[ref])
this.triggerPoints[ref] = [];
this.triggerPoints[ref].push(ent);
};
Trigger.prototype.RemoveRegisteredTriggerPoint = function(ref, ent)
{
if (!this.triggerPoints[ref])
{
warn("no trigger points found with ref "+ref);
return;
}
var i = this.triggerPoints[ref].indexOf(ent);
if (i == -1)
{
warn("entity " + ent + " wasn't found under the trigger points with ref "+ref);
return;
}
this.triggerPoints[ref].splice(i, 1);
};
Trigger.prototype.GetTriggerPoints = function(ref)
{
return this.triggerPoints[ref] || [];
};
/** Binds the "action" function to one of the implemented events.
*
* @param event Name of the event (see the list in init)
* @param action Functionname of a function available under this object
* @param Extra Data for the trigger (enabled or not, delay for timers, range for range triggers ...)
*
* Interval triggers example:
* data = {enabled: true, interval: 1000, delay: 500}
*
* Range trigger:
* data.entities = [id1, id2] * Ids of the source
* data.players = [1,2,3,...] * list of player ids
* data.minRange = 0 * Minimum range for the query
* data.maxRange = -1 * Maximum range for the query (-1 = no maximum)
* data.requiredComponent = 0 * Required component id the entities will have
* data.enabled = false * If the query is enabled by default
*/
Trigger.prototype.RegisterTrigger = function(event, action, data)
{
var eventString = event + "Actions";
if (!this[eventString])
{
warn("Trigger.js: Invalid trigger event \"" + event + "\".")
return;
}
if (this[eventString][action])
{
warn("Trigger.js: Trigger has been registered before. Aborting...");
return;
}
// clone the data to be sure it's only modified locally
// We could run into triggers overwriting each other's data otherwise.
// F.e. getting the wrong timer tag
data = clone(data) || {"enabled": false};
this[eventString][action] = data;
// setup range query
if (event == "OnRange")
{
if (!data.entities)
{
warn("Trigger.js: Range triggers should carry extra data");
return;
}
data.queries = [];
for (var ent of data.entities)
{
var cmpTriggerPoint = Engine.QueryInterface(ent, IID_TriggerPoint);
if (!cmpTriggerPoint)
{
warn("Trigger.js: Range triggers must be defined on trigger points");
continue;
}
data.queries.push(cmpTriggerPoint.RegisterRangeTrigger(action, data));
}
}
if (data.enabled)
this.EnableTrigger(event, action);
};
// Disable trigger
Trigger.prototype.DisableTrigger = function(event, action)
{
var eventString = event + "Actions";
if (!this[eventString][action])
{
warn("Trigger.js: Disabling unknown trigger");
return;
}
var data = this[eventString][action];
// special casing interval and range triggers for performance
if (event == "OnInterval")
{
if (!data.timer) // don't disable it a second time
return;
var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
cmpTimer.CancelTimer(data.timer);
data.timer = null;
}
else if (event == "OnRange")
{
if (!data.queries)
{
warn("Trigger.js: Range query wasn't set up before trying to disable it.");
return;
}
var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
for (var query of data.queries)
cmpRangeManager.DisableActiveQuery(query);
}
data.enabled = false;
};
// Enable trigger
Trigger.prototype.EnableTrigger = function(event, action)
{
var eventString = event + "Actions";
if (!this[eventString][action])
{
warn("Trigger.js: Enabling unknown trigger");
return;
}
var data = this[eventString][action];
// special casing interval and range triggers for performance
if (event == "OnInterval")
{
if (data.timer) // don't enable it a second time
return;
if (!data.interval)
{
warn("Trigger.js: An interval trigger should have an intervel in its data")
return;
}
var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
var timer = cmpTimer.SetInterval(this.entity, IID_Trigger, "DoAction", data.delay || 0, data.interval, {"action" : action});
data.timer = timer;
}
else if (event == "OnRange")
{
if (!data.queries)
{
warn("Trigger.js: Range query wasn't set up before");
return;
}
var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
for (var query of data.queries)
cmpRangeManager.EnableActiveQuery(query);
}
data.enabled = true;
};
/**
* This function executes the actions bound to the events
* It's either called directlty from other simulation scripts,
* or from message listeners in this file
*
* @param event Name of the event (see the list in init)
* @param data Data object that will be passed to the actions
*/
Trigger.prototype.CallEvent = function(event, data)
{
var eventString = "On" + event + "Actions";
if (!this[eventString])
{
warn("Trigger.js: Unknown trigger event called:\"" + event + "\".");
return;
}
for (var action in this[eventString])
{
if (this[eventString][action].enabled)
this.DoAction({"action": action, "data":data});
}
};
// Handles "OnStructureBuilt" event.
Trigger.prototype.OnGlobalConstructionFinished = function(msg)
{
this.CallEvent("StructureBuilt", {"building": msg.newentity}); // The data for this one is {"building": constructedBuilding}
};
// Handles "OnTrainingFinished" event.
Trigger.prototype.OnGlobalTrainingFinished = function(msg)
{
this.CallEvent("TrainingFinished", msg);
// The data for this one is {"entities": createdEnts,
// "owner": cmpOwnership.GetOwner(),
// "metadata": metadata}
// See function "SpawnUnits" in ProductionQueue for more details
};
Trigger.prototype.OnGlobalOwnershipChanged = function(msg)
{
this.CallEvent("OwnershipChanged", msg);
- // data is {"entities": ents, "from": playerId, "to": playerId}
+ // data is {"entity": ent, "from": playerId, "to": playerId}
};
/**
* Execute a function after a certain delay
* @param time The delay expressed in milleseconds
* @param action Name of the action function
* @param data Data object that will be passed to the action function
*/
Trigger.prototype.DoAfterDelay = function(miliseconds, action, data)
{
var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
return cmpTimer.SetTimeout(SYSTEM_ENTITY, IID_Trigger, "DoAction", miliseconds, {"action": action, "data": data});
};
/**
* Called by the trigger listeners to exucute the actual action. Including sanity checks.
*/
Trigger.prototype.DoAction = function(msg)
{
if (this[msg.action])
this[msg.action](msg.data || null);
else
warn("Trigger.js: called a trigger action '" + msg.action + "' that wasn't found");
};
Engine.RegisterSystemComponentType(IID_Trigger, "Trigger", Trigger);
Index: ps/trunk/binaries/data/mods/public/simulation/components/Wonder.js
===================================================================
--- ps/trunk/binaries/data/mods/public/simulation/components/Wonder.js (revision 15426)
+++ ps/trunk/binaries/data/mods/public/simulation/components/Wonder.js (revision 15427)
@@ -1,69 +1,17 @@
function Wonder() {}
Wonder.prototype.Schema =
"" +
"" +
"";
Wonder.prototype.Init = function()
{
};
-Wonder.prototype.OnOwnershipChanged = function(msg)
+Wonder.prototype.GetTimeTillVictory = function()
{
- this.ResetTimer(msg.to);
-};
-
-Wonder.prototype.OnGameTypeChanged = function()
-{
- var cmpPlayer = QueryOwnerInterface(this.entity, IID_Player);
- if (!cmpPlayer)
- return;
- this.ResetTimer(cmpPlayer.GetPlayerID());
-};
-
-Wonder.prototype.ResetTimer = function(ownerID)
-{
- var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
- var cmpGuiInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
- // remove existing messages if any
- if (this.timer)
- {
- cmpTimer.CancelTimer(this.timer);
- cmpGuiInterface.DeleteTimeNotification(this.ownMessage);
- cmpGuiInterface.DeleteTimeNotification(this.otherMessage);
- }
- if (ownerID <= 0)
- return;
-
- var cmpEndGameManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_EndGameManager);
- if (!cmpEndGameManager.CheckGameType("wonder"))
- return;
-
- // create new messages, and start timer to register defeat.
- var cmpPlayerManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager);
- var numPlayers = cmpPlayerManager.GetNumPlayers();
- var cmpPlayer = QueryOwnerInterface(this.entity, IID_Player);
- var players = [];
- for (var i = 1; i < numPlayers; i++)
- if (i != ownerID)
- players.push(i);
-
- this.otherMessage = cmpGuiInterface.AddTimeNotification({
- "message": markForTranslation("%(player)s will have won in %(time)s"),
- "players": players,
- "duration": +this.template.TimeTillVictory*1000,
- "parameters": {"player": cmpPlayer.GetName()},
- "translateMessage": true,
- "translateParameters": [],
- });
- this.ownMessage = cmpGuiInterface.AddTimeNotification({
- "message": markForTranslation("You will have won in %(time)s"),
- "players": [ownerID],
- "duration": +this.template.TimeTillVictory*1000,
- "translateMessage": true,
- });
- this.timer = cmpTimer.SetTimeout(SYSTEM_ENTITY, IID_EndGameManager, "MarkPlayerAsWon", +this.template.TimeTillVictory*1000, ownerID);
+ return +this.template.TimeTillVictory;
};
Engine.RegisterComponentType(IID_Wonder, "Wonder", Wonder);