Index: ps/trunk/binaries/data/mods/public/gui/aiconfig/aiconfig.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/aiconfig/aiconfig.js (revision 14495)
+++ ps/trunk/binaries/data/mods/public/gui/aiconfig/aiconfig.js (revision 14496)
@@ -1,54 +1,54 @@
var g_AIs; // [ {"id": ..., "data": {"name": ..., "description": ..., ...} }, ... ]
var g_Callback; // for the OK button
function init(settings)
{
g_Callback = settings.callback;
g_AIs = [
{id: "", data: {name: "None", description: "AI will be disabled for this player."}}
].concat(settings.ais);
- var aiSelection = getGUIObjectByName("aiSelection");
+ var aiSelection = Engine.GetGUIObjectByName("aiSelection");
aiSelection.list = [ ai.data.name for each (ai in g_AIs) ];
var selected = 0;
for (var i = 0; i < g_AIs.length; ++i)
{
if (g_AIs[i].id == settings.id)
{
selected = i;
break;
}
}
aiSelection.selected = selected;
- var aiDiff = getGUIObjectByName("aiDifficulty");
+ var aiDiff = Engine.GetGUIObjectByName("aiDifficulty");
aiDiff.list = [ "Sandbox", "Easy", "Medium", "Hard", "Very Hard" ];
aiDiff.selected = settings.difficulty;
}
function selectAI(idx)
{
var id = g_AIs[idx].id;
var name = g_AIs[idx].data.name;
var description = g_AIs[idx].data.description;
- getGUIObjectByName("aiDescription").caption = description;
+ Engine.GetGUIObjectByName("aiDescription").caption = description;
}
function returnAI()
{
- var aiSelection = getGUIObjectByName("aiSelection");
+ var aiSelection = Engine.GetGUIObjectByName("aiSelection");
var idx = aiSelection.selected;
var id = g_AIs[idx].id;
var name = g_AIs[idx].data.name;
- var difficulty = getGUIObjectByName("aiDifficulty").selected;
+ var difficulty = Engine.GetGUIObjectByName("aiDifficulty").selected;
// Pop the page before calling the callback, so the callback runs
// in the parent GUI page's context
Engine.PopGuiPage();
g_Callback({"id": id, "name": name, "difficulty" : difficulty});
}
Index: ps/trunk/binaries/data/mods/public/gui/civinfo/civinfo.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/civinfo/civinfo.js (revision 14495)
+++ ps/trunk/binaries/data/mods/public/gui/civinfo/civinfo.js (revision 14496)
@@ -1,155 +1,155 @@
var g_CivData = {};
var TEXTCOLOR = "white"
function init(settings)
{
// Initialize civ list
initCivNameList();
// TODO: Separate control for factions?
}
// Sort by culture, then by code equals culture and then by name ignoring case
function sortByCultureAndName(a, b)
{
if (a.culture < b.culture)
return -1;
if (a.culture > b.culture)
return 1;
// Same culture
// First code == culture
if (a.code == a.culture)
return -1;
if (b.code == b.culture)
return 1;
// Then alphabetically by name (ignoring case)
return sortNameIgnoreCase(a, b);
}
// Initialize the dropdown containing all the available civs
function initCivNameList()
{
// Cache map data
g_CivData = loadCivData();
var civList = [ { "name": civ.Name, "culture": civ.Culture, "code": civ.Code } for each (civ in g_CivData) ];
// Alphabetically sort the list, ignoring case
civList.sort(sortByCultureAndName);
// Indent sub-factions
var civListNames = [ ((civ.code == civ.culture)?"":" ")+civ.name for each (civ in civList) ];
var civListCodes = [ civ.code for each (civ in civList) ];
// Set civ control
- var civSelection = getGUIObjectByName("civSelection");
+ var civSelection = Engine.GetGUIObjectByName("civSelection");
civSelection.list = civListNames;
civSelection.list_data = civListCodes;
civSelection.selected = 0;
}
// Function to make first char of string big
function bigFirstLetter(str, size)
{
return '[font="serif-bold-'+(size+6)+'"]' + str[0] + '[/font]' + '[font="serif-bold-'+size+'"]' + str.substring(1) + '[/font]';
}
// Heading font - bold and mixed caps
function heading(string, size)
{
var textArray = string.split(" ");
for(var i = 0; i < textArray.length; ++i)
{
var word = textArray[i];
var wordCaps = word.toUpperCase();
// Check if word is capitalized, if so assume it needs a big first letter
if (wordCaps[0] == word[0])
textArray[i] = bigFirstLetter(wordCaps, size);
else
textArray[i] = '[font="serif-bold-'+size+'"]' + wordCaps + '[/font]'; // TODO: Would not be necessary if we could do nested tags
}
return textArray.join(" ");
}
// Called when user selects civ from dropdown
function selectCiv(code)
{
var civInfo = g_CivData[code];
if(!civInfo)
error("Error loading civ data for \""+code+"\"");
// Update civ gameplay display
- getGUIObjectByName("civGameplayHeading").caption = heading(civInfo.Name+" Gameplay", 16);
+ Engine.GetGUIObjectByName("civGameplayHeading").caption = heading(civInfo.Name+" Gameplay", 16);
// Bonuses
var bonusCaption = heading("Civilization Bonus"+(civInfo.CivBonuses.length == 1 ? "" : "es"), 12) + '\n';
for(var i = 0; i < civInfo.CivBonuses.length; ++i)
{
bonusCaption += '[color="' + TEXTCOLOR + '"][font="serif-bold-14"]' + civInfo.CivBonuses[i].Name + '[/font] [icon="iconInfo" tooltip="'
+ civInfo.CivBonuses[i].History + '" tooltip_style="civInfoTooltip"]\n ' + civInfo.CivBonuses[i].Description + '\n[/color]';
}
bonusCaption += heading("Team Bonus"+(civInfo.TeamBonuses.length == 1 ? "" : "es"), 12) + '\n';
for(var i = 0; i < civInfo.TeamBonuses.length; ++i)
{
bonusCaption += '[color="' + TEXTCOLOR + '"][font="serif-bold-14"]' + civInfo.TeamBonuses[i].Name + '[/font] [icon="iconInfo" tooltip="'
+ civInfo.TeamBonuses[i].History + '" tooltip_style="civInfoTooltip"]\n ' + civInfo.TeamBonuses[i].Description + '\n[/color]';
}
- getGUIObjectByName("civBonuses").caption = bonusCaption;
+ Engine.GetGUIObjectByName("civBonuses").caption = bonusCaption;
// Special techs / buildings
var techCaption = heading("Special Technologies", 12) + '\n';
for(var i = 0; i < civInfo.Factions.length; ++i)
{
var faction = civInfo.Factions[i];
for(var j = 0; j < faction.Technologies.length; ++j)
{
techCaption += '[color="' + TEXTCOLOR + '"][font="serif-bold-14"]' + faction.Technologies[j].Name + '[/font] [icon="iconInfo" tooltip="'
+ faction.Technologies[j].History + '" tooltip_style="civInfoTooltip"]\n ' + faction.Technologies[j].Description + '\n[/color]';
}
}
techCaption += heading("Special Building"+(civInfo.Structures.length == 1 ? "" : "s"), 12) + '\n';
for(var i = 0; i < civInfo.Structures.length; ++i)
{
techCaption += '[color="' + TEXTCOLOR + '"][font="serif-bold-14"]' + civInfo.Structures[i].Name + '[/font][/color] [icon="iconInfo" tooltip="'
+ civInfo.Structures[i].History + '" tooltip_style="civInfoTooltip"]\n';
}
- getGUIObjectByName("civTechs").caption = techCaption;
+ Engine.GetGUIObjectByName("civTechs").caption = techCaption;
// Heroes
var heroCaption = heading("Heroes", 12) + '\n';
for(var i = 0; i < civInfo.Factions.length; ++i)
{
var faction = civInfo.Factions[i];
for(var j = 0; j < faction.Heroes.length; ++j)
{
heroCaption += '[color="' + TEXTCOLOR + '"][font="serif-bold-14"]' + faction.Heroes[j].Name + '[/font][/color] [icon="iconInfo" tooltip="'
+ faction.Heroes[j].History + '" tooltip_style="civInfoTooltip"]\n';
}
heroCaption += '\n';
}
- getGUIObjectByName("civHeroes").caption = heroCaption;
+ Engine.GetGUIObjectByName("civHeroes").caption = heroCaption;
// Update civ history display
- getGUIObjectByName("civHistoryHeading").caption = heading("History of the " + civInfo.Name, 16);
- getGUIObjectByName("civHistoryText").caption = civInfo.History;
+ Engine.GetGUIObjectByName("civHistoryHeading").caption = heading("History of the " + civInfo.Name, 16);
+ Engine.GetGUIObjectByName("civHistoryText").caption = civInfo.History;
}
Index: ps/trunk/binaries/data/mods/public/gui/common/functions_civinfo.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/common/functions_civinfo.js (revision 14495)
+++ ps/trunk/binaries/data/mods/public/gui/common/functions_civinfo.js (revision 14496)
@@ -1,23 +1,23 @@
/*
DESCRIPTION : Functions related to reading civ info
NOTES :
*/
// ====================================================================
function loadCivData()
{ // Load all JSON files containing civ data
var civData = {};
- var civFiles = buildDirEntList("civs/", "*.json", false);
+ var civFiles = Engine.BuildDirEntList("civs/", "*.json", false);
for each (var filename in civFiles)
{ // Parse data if valid file
var data = parseJSONData(filename);
civData[data.Code] = data;
}
return civData;
}
// ====================================================================
\ No newline at end of file
Index: ps/trunk/binaries/data/mods/public/gui/common/functions_global_object.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/common/functions_global_object.js (revision 14495)
+++ ps/trunk/binaries/data/mods/public/gui/common/functions_global_object.js (revision 14496)
@@ -1,33 +1,67 @@
/*
DESCRIPTION : Contains global GUI functions, which will later be accessible from every GUI script/file.
NOTES : So far, only the message box-related functions are implemented.
*/
// *******************************************
// messageBox
// *******************************************
-// @params: int mbWidth, int mbHeight, string mbMessage, string mbTitle, int mbMode, arr mbButtonCaptions, arr mbButtonsCode
+// @params: int mbWidth, int mbHeight, string mbMessage, string mbTitle, int mbMode, arr mbButtonCaptions, function mbBtnCode, var mbCallbackArgs
// @return: void
// @desc: Displays a new modal message box.
// *******************************************
-function messageBox (mbWidth, mbHeight, mbMessage, mbTitle, mbMode, mbButtonCaptions, mbButtonsCode)
+
+// We want to pass callback functions for the different buttons in a convenient way.
+// Because passing functions accross compartment boundaries is a pain, we just store them here together with some optional arguments.
+// The messageBox page will return the code of the pressed button and the according function will be called.
+var g_messageBoxBtnFunctions = [];
+var g_messageBoxCallbackArgs = [];
+
+var g_messageBoxCallbackFunction = function(btnCode)
+{
+ if (btnCode !== undefined && g_messageBoxBtnFunctions[btnCode])
+ {
+ if (g_messageBoxCallbackArgs[btnCode])
+ g_messageBoxBtnFunctions[btnCode](g_messageBoxCallbackArgs[btnCode]);
+ else
+ g_messageBoxBtnFunctions[btnCode]();
+ }
+
+ g_messageBoxBtnFunctions = [];
+ g_messageBoxCallbackArgs = [];
+}
+
+function messageBox (mbWidth, mbHeight, mbMessage, mbTitle, mbMode, mbButtonCaptions, mbBtnCode, mbCallbackArgs)
{
+ if (g_messageBoxBtnFunctions.length != 0)
+ {
+ warn("A messagebox was called when a previous callback function is still set, aborting!");
+ return;
+ }
- Engine.PushGuiPage("page_msgbox.xml", {
+ g_messageBoxBtnFunctions = mbBtnCode;
+ if (mbCallbackArgs)
+ g_messageBoxCallbackArgs = mbCallbackArgs;
+
+ var initData = {
width: mbWidth,
height: mbHeight,
message: mbMessage,
title: mbTitle,
mode: mbMode,
buttonCaptions: mbButtonCaptions,
- buttonCode: mbButtonsCode
- });
+ }
+ if (mbBtnCode)
+ initData.callback = "g_messageBoxCallbackFunction";
+
+
+ Engine.PushGuiPage("page_msgbox.xml", initData);
}
// ====================================================================
function updateFPS()
{
- getGUIObjectByName("fpsCounter").caption = "FPS: " + getFPS();
+ Engine.GetGUIObjectByName("fpsCounter").caption = "FPS: " + Engine.GetFPS();
}
Index: ps/trunk/binaries/data/mods/public/gui/common/functions_utility.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/common/functions_utility.js (revision 14495)
+++ ps/trunk/binaries/data/mods/public/gui/common/functions_utility.js (revision 14496)
@@ -1,313 +1,313 @@
/*
DESCRIPTION : Generic utility functions.
NOTES :
*/
// ====================================================================
function getRandom(randomMin, randomMax)
{
// Returns a random whole number in a min..max range.
// NOTE: There should probably be an engine function for this,
// since we'd need to keep track of random seeds for replays.
var randomNum = randomMin + (randomMax-randomMin)*Math.random(); // num is random, from A to B
return Math.round(randomNum);
}
// ====================================================================
// Get list of XML files in pathname with recursion, excepting those starting with _
function getXMLFileList(pathname)
{
- var files = buildDirEntList(pathname, "*.xml", true);
+ var files = Engine.BuildDirEntList(pathname, "*.xml", true);
var result = [];
// Get only subpath from filename and discard extension
for (var i = 0; i < files.length; ++i)
{
var file = files[i];
file = file.substring(pathname.length, file.length-4);
// Split path into directories so we can check for beginning _ character
var tokens = file.split("/");
if (tokens[tokens.length-1][0] != "_")
result.push(file);
}
return result;
}
// ====================================================================
// Get list of JSON files in pathname
function getJSONFileList(pathname)
{
- var files = buildDirEntList(pathname, "*.json", false);
+ var files = Engine.BuildDirEntList(pathname, "*.json", false);
// Remove the path and extension from each name, since we just want the filename
files = [ n.substring(pathname.length, n.length-5) for each (n in files) ];
return files;
}
// ====================================================================
// Parse JSON data
function parseJSONData(pathname)
{
var data = {};
- var rawData = readFile(pathname);
+ var rawData = Engine.ReadFile(pathname);
if (!rawData)
{
error("Failed to read file: "+pathname);
}
else
{
try
{ // Catch nasty errors from JSON parsing
// TODO: Need more info from the parser on why it failed: line number, position, etc!
data = JSON.parse(rawData);
if (!data)
error("Failed to parse JSON data in: "+pathname+" (check for valid JSON data)");
}
catch(err)
{
error(err.toString()+": parsing JSON data in "+pathname);
}
}
return data;
}
// ====================================================================
// A sorting function for arrays of objects with 'name' properties, ignoring case
function sortNameIgnoreCase(x, y)
{
var lowerX = x.name.toLowerCase();
var lowerY = y.name.toLowerCase();
if (lowerX < lowerY)
return -1;
else if (lowerX > lowerY)
return 1;
else
return 0;
}
// ====================================================================
// Escape text tags and whitespace, so users can't use special formatting in their chats
// Limit string length to 256 characters
function escapeText(text)
{
if (!text)
return text;
var out = text.replace(/[\[\]]+/g,"");
out = out.replace(/\s+/g, " ");
return out.substr(0, 255);
}
// ====================================================================
function toTitleCase (string)
{
if (!string)
return string;
// Returns the title-case version of a given string.
string = string.toString();
string = string[0].toUpperCase() + string.substring(1).toLowerCase();
return string;
}
// ====================================================================
// Parse and return JSON data from file in simulation/data/*
// returns valid object or undefined on error
function parseJSONFromDataFile(filename)
{
var path = "simulation/data/"+filename;
- var rawData = readFile(path);
+ var rawData = Engine.ReadFile(path);
if (!rawData)
error("Failed to read file: "+path);
try
{
// Catch nasty errors from JSON parsing
// TODO: Need more info from the parser on why it failed: line number, position, etc!
var data = JSON.parse(rawData);
return data;
}
catch(err)
{
error(err.toString()+": parsing JSON data in "+path);
}
return undefined;
}
// ====================================================================
// Load default player data, for when it's not otherwise specified
function initPlayerDefaults()
{
var defaults = [];
var data = parseJSONFromDataFile("player_defaults.json");
if (!data || !data.PlayerData)
error("Failed to parse player defaults in player_defaults.json (check for valid JSON data)");
else
defaults = data.PlayerData;
return defaults;
}
// ====================================================================
// Load map size data
function initMapSizes()
{
var sizes = {
"shortNames":[],
"names":[],
"tiles": [],
"default": 0
};
var data = parseJSONFromDataFile("map_sizes.json");
if (!data || !data.Sizes)
error("Failed to parse map sizes in map_sizes.json (check for valid JSON data)");
else
{
for (var i = 0; i < data.Sizes.length; ++i)
{
sizes.shortNames.push(data.Sizes[i].Name);
sizes.names.push(data.Sizes[i].LongName);
sizes.tiles.push(data.Sizes[i].Tiles);
if (data.Sizes[i].Default)
sizes["default"] = i;
}
}
return sizes;
}
// ====================================================================
// Load game speed data
function initGameSpeeds()
{
var gameSpeeds = {
"names": [],
"speeds": [],
"default": 0
};
var data = parseJSONFromDataFile("game_speeds.json");
if (!data || !data.Speeds)
error("Failed to parse game speeds in game_speeds.json (check for valid JSON data)");
else
{
for (var i = 0; i < data.Speeds.length; ++i)
{
gameSpeeds.names.push(data.Speeds[i].Name);
gameSpeeds.speeds.push(data.Speeds[i].Speed);
if (data.Speeds[i].Default)
gameSpeeds["default"] = i;
}
}
return gameSpeeds;
}
// ====================================================================
// Convert integer color values to string (for use in GUI objects)
function iColorToString(color)
{
var string = "0 0 0";
if (color && ("r" in color) && ("g" in color) && ("b" in color))
string = color.r + " " + color.g + " " + color.b;
return string;
}
// ====================================================================
/**
* Convert time in milliseconds to hh:mm:ss string representation.
* @param time Time period in milliseconds (integer)
* @return String representing time period
*/
function timeToString(time)
{
var hours = Math.floor(time / 1000 / 60 / 60);
var minutes = Math.floor(time / 1000 / 60) % 60;
var seconds = Math.floor(time / 1000) % 60;
return hours + ':' + (minutes < 10 ? '0' + minutes : minutes) + ':' + (seconds < 10 ? '0' + seconds : seconds);
}
// ====================================================================
function removeDupes(array)
{
for (var i = 0; i < array.length; i++)
{
if (array.indexOf(array[i]) < i)
{
array.splice(i, 1);
i--;
}
}
}
// ====================================================================
// "Inside-out" implementation of Fisher-Yates shuffle
function shuffleArray(source)
{
if (!source.length)
return [];
var result = [source[0]];
for (var i = 1; i < source.length; ++i)
{
var j = Math.floor(Math.random() * i);
result[i] = result[j];
result[j] = source[i];
}
return result;
}
// ====================================================================
// Filter out conflicting characters and limit the length of a given name.
// @param name Name to be filtered.
// @param stripUnicode Whether or not to remove unicode characters.
// @param stripSpaces Whether or not to remove whitespace.
function sanitizePlayerName(name, stripUnicode, stripSpaces)
{
// We delete the '[', ']' characters (GUI tags) and delete the ',' characters (player name separators) by default.
var sanitizedName = name.replace(/[\[\],]/g, "");
// Optionally strip unicode
if (stripUnicode)
sanitizedName = sanitizedName.replace(/[^\x20-\x7f]/g, "");
// Optionally strip whitespace
if (stripSpaces)
sanitizedName = sanitizedName.replace(/\s/g, "");
// Limit the length to 20 characters
return sanitizedName.substr(0,20);
}
Index: ps/trunk/binaries/data/mods/public/gui/common/functions_utility_error.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/common/functions_utility_error.js (revision 14495)
+++ ps/trunk/binaries/data/mods/public/gui/common/functions_utility_error.js (revision 14496)
@@ -1,29 +1,29 @@
/*
DESCRIPTION : Error-handling utility functions.
NOTES :
*/
// ====================================================================
function cancelOnError(msg)
{
// Delete game objects
- endGame();
+ Engine.EndGame();
// Return to pregame
Engine.SwitchGuiPage("page_pregame.xml");
// Display error dialog if message given
if (msg)
{
Engine.PushGuiPage("page_msgbox.xml", {
width: 500,
height: 200,
message: '[font="serif-bold-18"]' + msg + '[/font]',
title: "Loading Aborted",
mode: 2
});
}
// Reset cursor
- setCursor("arrow-default");
+ Engine.SetCursor("arrow-default");
}
Index: ps/trunk/binaries/data/mods/public/gui/common/functions_utility_list.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/common/functions_utility_list.js (revision 14495)
+++ ps/trunk/binaries/data/mods/public/gui/common/functions_utility_list.js (revision 14496)
@@ -1,164 +1,164 @@
/*
DESCRIPTION : Functions to manipulate objects with a 'list' property
(used to handle the items in list, dropdown, etc.)
NOTES : To ensure the selection is properly updated, it is important to
use these functions and not manually access the list.
*/
// ====================================================================
// Remove the item at the given index (pos) from the given list object (objectName).
function removeItem (objectName, pos)
{
- if (getGUIObjectByName (objectName) == null)
+ if (Engine.GetGUIObjectByName (objectName) == null)
Engine.Console_Write ("removeItem(): " + objectName + " not found.");
- var list = getGUIObjectByName (objectName).list;
- var selected = getGUIObjectByName (objectName).selected;
+ var list = Engine.GetGUIObjectByName (objectName).list;
+ var selected = Engine.GetGUIObjectByName (objectName).selected;
list.splice(pos, 1);
- getGUIObjectByName (objectName).list = list;
+ Engine.GetGUIObjectByName (objectName).list = list;
// It's important that we update the selection *after*
// we've committed the changes to the list.
// Update the selected so the same element remains selected.
if (selected == pos)
{
- getGUIObjectByName (objectName).selected = -1;
+ Engine.GetGUIObjectByName (objectName).selected = -1;
}
else
if (selected > pos)
{
- getGUIObjectByName (objectName).selected = selected - 1;
+ Engine.GetGUIObjectByName (objectName).selected = selected - 1;
}
}
// ====================================================================
// Add the item at the given index (pos) to the given list object (objectName) with the given value (value).
function addItem (objectName, pos, value)
{
- if (getGUIObjectByName (objectName) == null)
+ if (Engine.GetGUIObjectByName (objectName) == null)
Engine.Console_Write ("addItem(): " + objectName + " not found.");
- var list = getGUIObjectByName (objectName).list;
- var selected = getGUIObjectByName (objectName).selected;
+ var list = Engine.GetGUIObjectByName (objectName).list;
+ var selected = Engine.GetGUIObjectByName (objectName).selected;
list.splice (pos, 0, value);
- getGUIObjectByName (objectName).list = list;
+ Engine.GetGUIObjectByName (objectName).list = list;
// It's important that we update the selection *after*
// we've committed the changes to the list.
// Update the selected so the same element remains selected.
if (selected >= pos)
{
- getGUIObjectByName (objectName).selected = selected + 1;
+ Engine.GetGUIObjectByName (objectName).selected = selected + 1;
}
}
// ====================================================================
// Adds an element to the end of the list
function pushItem (objectName, value)
{
- if (getGUIObjectByName (objectName) == null)
+ if (Engine.GetGUIObjectByName (objectName) == null)
Engine.Console_Write ("pushItem(): " + objectName + " not found.");
- var list = getGUIObjectByName (objectName).list;
+ var list = Engine.GetGUIObjectByName (objectName).list;
list.push (value);
- getGUIObjectByName (objectName).list = list;
+ Engine.GetGUIObjectByName (objectName).list = list;
// Point to the new item.
- getGUIObjectByName(objectName).selected = getNumItems(objectName)-1;
+ Engine.GetGUIObjectByName(objectName).selected = getNumItems(objectName)-1;
}
// ====================================================================
// Removes the last element
function popItem (objectName)
{
- if (getGUIObjectByName (objectName) == null)
+ if (Engine.GetGUIObjectByName (objectName) == null)
Engine.Console_Write ("popItem(): " + objectName + " not found.");
- var selected = getGUIObjectByName (objectName).selected;
+ var selected = Engine.GetGUIObjectByName (objectName).selected;
removeItem(objectName, getNumItems(objectName)-1);
if (selected == getNumItems(objectName)-1)
{
- getGUIObjectByName(objectName).selected = -1;
+ Engine.GetGUIObjectByName(objectName).selected = -1;
}
}
// ====================================================================
// Retrieves the number of elements in the list
function getNumItems (objectName)
{
- if (getGUIObjectByName (objectName) == null)
+ if (Engine.GetGUIObjectByName (objectName) == null)
Engine.Console_Write ("getNumItems(): " + objectName + " not found.");
- var list = getGUIObjectByName(objectName).list;
+ var list = Engine.GetGUIObjectByName(objectName).list;
return list.length;
}
// ====================================================================
// Retrieves the value of the item at 'pos'
function getItemValue (objectName, pos)
{
- if (getGUIObjectByName (objectName) == null)
+ if (Engine.GetGUIObjectByName (objectName) == null)
Engine.Console_Write ("getItemValue(): " + objectName + " not found.");
- var list = getGUIObjectByName(objectName).list;
+ var list = Engine.GetGUIObjectByName(objectName).list;
return list[pos];
}
// ====================================================================
// Retrieves the value of the currently selected item
function getCurrItemValue (objectName)
{
- if (getGUIObjectByName (objectName) == null)
+ if (Engine.GetGUIObjectByName (objectName) == null)
Engine.Console_Write ("getCurrItemValue(): " + objectName + " not found.");
- if (getGUIObjectByName(objectName).selected == -1)
+ if (Engine.GetGUIObjectByName(objectName).selected == -1)
return "";
- var list = getGUIObjectByName(objectName).list;
- return list[getGUIObjectByName(objectName).selected];
+ var list = Engine.GetGUIObjectByName(objectName).list;
+ return list[Engine.GetGUIObjectByName(objectName).selected];
}
// ====================================================================
// Sets current item to a given string (which must be one of those
// already in the list).
function setCurrItemValue (objectName, string)
{
- if (getGUIObjectByName(objectName) == null) {
+ if (Engine.GetGUIObjectByName(objectName) == null) {
Engine.Console_Write ("setCurrItemValue(): " + objectName + " not found.");
return -1;
}
- if (getGUIObjectByName(objectName).selected == -1)
+ if (Engine.GetGUIObjectByName(objectName).selected == -1)
return -1; // Return -1 if nothing selected.
- var list = getGUIObjectByName(objectName).list;
+ var list = Engine.GetGUIObjectByName(objectName).list;
// Seek through list.
for (var ctr = 0; ctr < list.length; ctr++)
{
// If we have found the string in the list,
if (list[ctr] == string)
{
// Point selected to this item.
- getGUIObjectByName(objectName).selected = ctr;
+ Engine.GetGUIObjectByName(objectName).selected = ctr;
return ctr; // Return position of item.
}
}
// Return -2 if failed to find value in list.
Engine.Console_Write ("Requested string '" + string + "' not found in " + objectName + "'s list.");
return -2;
}
// ====================================================================
Index: ps/trunk/binaries/data/mods/public/gui/common/functions_utility_music.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/common/functions_utility_music.js (revision 14495)
+++ ps/trunk/binaries/data/mods/public/gui/common/functions_utility_music.js (revision 14496)
@@ -1,171 +1,171 @@
/*
DESCRIPTION : Audio functions (eg "pick a random sound from a list", "build a playlist") go here.
NOTES :
*/
// ====================================================================
// Quick run-down of the basic audio commands:
// Save the specified audio file to handle "s".
// s = new Sound( "audio/music/menu_track.ogg" );
// Play the sound stored at handle "s" one time (it'll be automatically freed when the playback ends):
// s.play();
// Play the sound stored at handle "s" continuously:
// s.loop();
// Close "s" and free it from memory (use in conjunction with loop()):
// s.free();
// Adjust the gain (volume) of a sound (floating point range between 0 (silent) and 1 (max volume)).
// s.setGain(value);
// ====================================================================
function newRandomSound(soundType, soundSubType, soundPrePath)
{
// Return a random audio file by category, to be assigned to a handle.
var randomSoundPath;
switch (soundType)
{
case "music":
randomSoundPath = "audio/music/"
break;
case "ambient":
randomSoundPath = "audio/ambient/" + soundPrePath + "/";
break;
case "effect":
randomSoundPath = soundPrePath + "/";
break;
default:
break;
}
// Get names of sounds (attack, command, select, hit, pain).
// or
// Get names of "peace", "menu" (theme) and "battle" tracks.
- var soundArray = buildDirEntList(randomSoundPath, "*" + soundSubType + "*", false);
+ var soundArray = Engine.BuildDirEntList(randomSoundPath, "*" + soundSubType + "*", false);
if (soundArray.length == 0)
{
Engine.Console_Write ("Failed to find sounds matching '*"+soundSubType+"*'");
return undefined;
}
// Get a random number within the sound's range.
var randomSound = getRandom (0, soundArray.length-1);
// Set name of track.
var randomFileName = soundArray[randomSound];
// Build path to random audio file.
randomSoundPath = randomFileName;
//Engine.Console_Write("Playing " + randomSoundPath + " ...");
switch (soundType)
{
case "music":
return new MusicSound(randomSoundPath);
break;
case "ambient":
return new AmbientSound(randomSoundPath);
break;
case "effect":
Engine.Console_Write("am loading effect '*"+randomSoundPath+"*'");
break;
default:
break;
}
return new Sound(randomSoundPath);
}
// ====================================================================
function fadeOut (soundHandle, fadeDuration)
{
// Adjust the gain of a sound until it is zero.
// The sound is automatically freed when finished fading.
soundHandle.fade(-1, 0, fadeDuration)
return true;
}
// ====================================================================
function fadeIn (soundHandle, finalGain, fadeDuration)
{
// Adjust the gain of a sound from zero up to the given value.
soundHandle.fade(0, finalGain, fadeDuration)
return true;
}
// ====================================================================
function crossFade (outHandle, inHandle, fadeDuration)
{
// Accepts two sound handles. Over the given duration,
// fades out the first while fading in the second.
// Note that it plays the in and frees the out while it's at it.
if (outHandle)
fadeOut(outHandle, fadeDuration);
if (inHandle)
{
inHandle.play();
fadeIn(inHandle, Engine.ConfigDB_GetValue("user", "sound.mastergain"), fadeDuration);
}
return true;
}
// ====================================================================
//const AMBIENT_SOUND = "audio/ambient/dayscape/day_temperate_gen_03.ogg";
//const AMBIENT_TEMPERATE = "temperate";
//var currentAmbient;
//function playRandomAmbient(type)
//{
// switch (type)
// {
// case AMBIENT_TEMPERATE:
// // Seem to need the underscore at the end of "temperate" to avoid crash
// // (Might be caused by trying to randomly load day_temperate.xml)
// currentAmbient = newRandomSound("ambient", "temperate_", "dayscape");
// if (currentAmbient)
// {
// currentAmbient.loop();
// currentAmbient.setGain(0.8);
// }
// break;
//
// default:
// Engine.Console_Write("Unrecognized ambient type: " + type);
// break;
// }
//}
//
//function stopAmbient()
//{
// if (currentAmbient)
// {
// currentAmbient.fade(-1, 0.0, 5.0);
// currentAmbient = null;
// }
//}
//const BUTTON_SOUND = "audio/interface/ui/ui_button_longclick.ogg";
//function playButtonSound()
//{
// var buttonSound = new Sound(BUTTON_SOUND);
// buttonSound.play();
//}
Index: ps/trunk/binaries/data/mods/public/gui/common/functions_utility_test.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/common/functions_utility_test.js (revision 14495)
+++ ps/trunk/binaries/data/mods/public/gui/common/functions_utility_test.js (revision 14496)
@@ -1,44 +1,44 @@
function updateOrbital()
{
- if( !getGUIObjectByName( 'arena' ).hidden )
+ if( !Engine.GetGUIObjectByName( 'arena' ).hidden )
{
g_ballx += g_balldx;
g_bally += g_balldy;
if (g_ballx > 600)
{
g_balldx *= -0.9;
g_ballx = 600-(g_ballx-600);
}
else if (g_ballx < 0)
{
g_balldx *= -0.9;
g_ballx = -g_ballx;
}
if (g_bally > 400)
{
g_balldy *= -0.9;
g_bally = 400-(g_bally-400);
}
else if (g_bally < 0)
{
g_balldy *= -0.9;
g_bally = -g_bally;
}
// Gravitate towards the mouse
var vect_x = g_ballx-g_mousex;
var vect_y = g_bally-g_mousey;
var dsquared = vect_x*vect_x + vect_y*vect_y;
if (dsquared < 1000) dsquared = 1000;
var force = 10000.0 / dsquared;
var mag = Math.sqrt(dsquared);
vect_x /= mag; vect_y /= mag;
g_balldx -= force * vect_x;
g_balldy -= force * vect_y;
- var ball = getGUIObjectByName('ball');
+ var ball = Engine.GetGUIObjectByName('ball');
var r=5;
ball.size = new GUISize(g_ballx-r, g_bally-r, g_ballx+r, g_bally+r);
}
}
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/gamesetup.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/gamesetup.js (revision 14495)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/gamesetup.js (revision 14496)
@@ -1,1565 +1,1565 @@
////////////////////////////////////////////////////////////////////////////////////////////////
// Constants
const DEFAULT_NETWORKED_MAP = "Acropolis 01";
const DEFAULT_OFFLINE_MAP = "Acropolis 01";
// TODO: Move these somewhere like simulation\data\game_types.json, Atlas needs them too
const VICTORY_TEXT = ["Conquest", "Wonder", "None"];
const VICTORY_DATA = ["conquest", "wonder", "endless"];
const VICTORY_DEFAULTIDX = 0;
const POPULATION_CAP = ["50", "100", "150", "200", "250", "300", "Unlimited"];
const POPULATION_CAP_DATA = [50, 100, 150, 200, 250, 300, 10000];
const POPULATION_CAP_DEFAULTIDX = 5;
const STARTING_RESOURCES = ["Very Low", "Low", "Medium", "High", "Very High", "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;
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[font=\"serif-bold-12\"][color=\"orange\"]Warning:[/color][/font] \
The AI does not support naval maps and may cause severe performance issues. \
Naval maps are recommended to be played with human opponents only.";
// To prevent the display locking up while we load the map metadata,
// we'll start with a 'loading' message and switch to the main screen in the
// tick handler
var g_LoadingState = 0; // 0 = not started, 1 = loading, 2 = loaded
////////////////////////////////////////////////////////////////////////////////////////////////
function init(attribs)
{
switch (attribs.type)
{
case "offline":
g_IsNetworked = false;
g_IsController = true;
break;
case "server":
g_IsNetworked = true;
g_IsController = true;
break;
case "client":
g_IsNetworked = true;
g_IsController = false;
break;
default:
error("Unexpected 'type' in gamesetup init: "+attribs.type);
}
if (attribs.serverName)
g_ServerName = attribs.serverName;
// Init the Cancel Button caption and tooltip
- var cancelButton = getGUIObjectByName("cancelGame");
+ var cancelButton = Engine.GetGUIObjectByName("cancelGame");
if(!Engine.HasXmppClient())
{
cancelButton.tooltip = "Return to the main menu."
}
else
{
cancelButton.tooltip = "Return to the lobby."
}
}
// Called after the map data is loaded and cached
function initMain()
{
// Load AI list and hide deprecated AIs
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 = getGUIObjectByName("mapTypeSelection");
+ var mapTypes = Engine.GetGUIObjectByName("mapTypeSelection");
mapTypes.list = ["Skirmish","Random","Scenario"];
mapTypes.list_data = ["skirmish","random","scenario"];
// Setup map filters - will appear in order they are added
addFilter("Default", function(settings) { return settings && !keywordTestOR(settings.Keywords, ["naval", "demo", "hidden"]); });
addFilter("Naval Maps", function(settings) { return settings && keywordTestAND(settings.Keywords, ["naval"]); });
addFilter("Demo Maps", function(settings) { return settings && keywordTestAND(settings.Keywords, ["demo"]); });
addFilter("All Maps", function(settings) { return true; });
// Populate map filters dropdown
- var mapFilters = getGUIObjectByName("mapFilterSelection");
+ var mapFilters = Engine.GetGUIObjectByName("mapFilterSelection");
mapFilters.list = getFilters();
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 = getGUIObjectByName("numPlayersSelection");
+ 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 = getGUIObjectByName("gameSpeed");
+ var gameSpeed = Engine.GetGUIObjectByName("gameSpeed");
gameSpeed.hidden = false;
- getGUIObjectByName("gameSpeedText").hidden = true;
+ 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 = getGUIObjectByName("populationCap");
+ 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 = getGUIObjectByName("startingResources");
+ 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 = getGUIObjectByName("victoryCondition");
+ var victoryConditions = Engine.GetGUIObjectByName("victoryCondition");
victoryConditions.list = VICTORY_TEXT;
victoryConditions.list_data = VICTORY_DATA;
victoryConditions.onSelectionChange = function()
{ // Update attributes so other players can see change
if (this.selected != -1)
g_GameAttributes.settings.GameType = VICTORY_DATA[this.selected];
if (!g_IsInGuiUpdate)
updateGameAttributes();
};
victoryConditions.selected = VICTORY_DEFAULTIDX;
- var mapSize = getGUIObjectByName("mapSize");
+ 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;
- getGUIObjectByName("revealMap").onPress = function()
+ Engine.GetGUIObjectByName("revealMap").onPress = function()
{ // Update attributes so other players can see change
g_GameAttributes.settings.RevealMap = this.checked;
if (!g_IsInGuiUpdate)
updateGameAttributes();
};
- getGUIObjectByName("lockTeams").onPress = function()
+ Engine.GetGUIObjectByName("lockTeams").onPress = function()
{ // Update attributes so other players can see change
g_GameAttributes.settings.LockTeams = this.checked;
if (!g_IsInGuiUpdate)
updateGameAttributes();
};
- getGUIObjectByName("enableCheats").onPress = function()
+ Engine.GetGUIObjectByName("enableCheats").onPress = function()
{ // Update attributes so other players can see change
g_GameAttributes.settings.CheatsEnabled = this.checked;
if (!g_IsInGuiUpdate)
updateGameAttributes();
};
}
else
{
// If we're a network client, disable all the map controls
// TODO: make them look visually disabled so it's obvious why they don't work
- getGUIObjectByName("mapTypeSelection").hidden = true;
- getGUIObjectByName("mapTypeText").hidden = false;
- getGUIObjectByName("mapFilterSelection").hidden = true;
- getGUIObjectByName("mapFilterText").hidden = false;
- getGUIObjectByName("mapSelectionText").hidden = false;
- getGUIObjectByName("mapSelection").hidden = true;
- getGUIObjectByName("victoryConditionText").hidden = false;
- getGUIObjectByName("victoryCondition").hidden = true;
- getGUIObjectByName("gameSpeedText").hidden = false;
- getGUIObjectByName("gameSpeed").hidden = true;
+ 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)
{
- getGUIObjectByName("playerAssignment["+i+"]").enabled = false;
- getGUIObjectByName("playerCiv["+i+"]").hidden = true;
- getGUIObjectByName("playerTeam["+i+"]").hidden = true;
+ Engine.GetGUIObjectByName("playerAssignment["+i+"]").enabled = false;
+ Engine.GetGUIObjectByName("playerCiv["+i+"]").hidden = true;
+ Engine.GetGUIObjectByName("playerTeam["+i+"]").hidden = true;
}
- getGUIObjectByName("numPlayersSelection").hidden = true;
+ Engine.GetGUIObjectByName("numPlayersSelection").hidden = true;
}
// Set up multiplayer/singleplayer bits:
if (!g_IsNetworked)
{
- getGUIObjectByName("chatPanel").hidden = true;
- getGUIObjectByName("enableCheats").checked = true;
+ Engine.GetGUIObjectByName("chatPanel").hidden = true;
+ Engine.GetGUIObjectByName("enableCheats").checked = true;
g_GameAttributes.settings.CheatsEnabled = true;
}
else
{
- getGUIObjectByName("enableCheatsDesc").hidden = false;
- getGUIObjectByName("enableCheats").checked = false;
+ Engine.GetGUIObjectByName("enableCheatsDesc").hidden = false;
+ Engine.GetGUIObjectByName("enableCheats").checked = false;
g_GameAttributes.settings.CheatsEnabled = false;
if (g_IsController)
- getGUIObjectByName("enableCheats").hidden = false;
+ Engine.GetGUIObjectByName("enableCheats").hidden = false;
else
- getGUIObjectByName("enableCheatsText").hidden = false;
+ Engine.GetGUIObjectByName("enableCheatsText").hidden = false;
}
// Settings for all possible player slots
var boxSpacing = 32;
for (var i = 0; i < MAX_PLAYERS; ++i)
{
// Space player boxes
- var box = getGUIObjectByName("playerBox["+i+"]");
+ var 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 = getGUIObjectByName("playerTeam["+i+"]");
+ var team = Engine.GetGUIObjectByName("playerTeam["+i+"]");
team.list = ["None", "1", "2", "3", "4"];
team.list_data = [-1, 0, 1, 2, 3];
team.selected = 0;
let playerSlot = i; // declare for inner function use
team.onSelectionChange = function()
{ // Update team
if (this.selected != -1)
g_GameAttributes.settings.PlayerData[playerSlot].Team = this.selected - 1;
if (!g_IsInGuiUpdate)
updateGameAttributes();
};
// Set events
- var civ = getGUIObjectByName("playerCiv["+i+"]");
+ 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
- getGUIObjectByName("chatInput").focus();
+ Engine.GetGUIObjectByName("chatInput").focus();
}
else
{
// For single-player, focus the map list by default,
// to allow easy keyboard selection of maps
- getGUIObjectByName("mapSelection").focus();
+ Engine.GetGUIObjectByName("mapSelection").focus();
}
}
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;
onGameAttributesChange();
break;
case "players":
// Find and report all joinings/leavings
for (var host in message.hosts)
if (! g_PlayerAssignments[host])
addChatMessage({ "type": "connect", "username": message.hosts[host].name });
for (var host in g_PlayerAssignments)
if (! message.hosts[host])
addChatMessage({ "type": "disconnect", "guid": host });
// Update the player list
g_PlayerAssignments = message.hosts;
updatePlayerList();
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;
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\"]Random");
civListCodes.unshift("random");
// Update the dropdowns
for (var i = 0; i < MAX_PLAYERS; ++i)
{
- var civ = getGUIObjectByName("playerCiv["+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 = getGUIObjectByName("mapSelection")
+ 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("initMapNameList: Unexpected map type '"+g_GameAttributes.mapType+"'");
return;
}
// Apply map filter, if any defined
var mapList = [];
for (var i = 0; i < mapFiles.length; ++i)
{
var file = 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
mapList.sort(sortNameIgnoreCase);
if (g_GameAttributes.mapType == "random")
mapList.unshift({ "name": "[color=\"orange\"]Random[/color]", "file": "random" });
var mapListNames = [ map.name for each (map in mapList) ];
var mapListFiles = [ map.file for each (map in mapList) ];
// Select the default map
var selected = mapListFiles.indexOf(g_GameAttributes.map);
// Default to the first element if list is not empty and we can't find the one we searched for
if (selected == -1 && mapList.length)
{
selected = 0;
}
// Update the list control
mapSelectionBox.list = mapListNames;
mapSelectionBox.list_data = mapListFiles;
mapSelectionBox.selected = selected;
}
function loadMapData(name)
{
if (!name)
return undefined;
if (!g_MapData[name])
{
switch (g_GameAttributes.mapType)
{
case "scenario":
case "skirmish":
g_MapData[name] = Engine.LoadMapSettings(name);
break;
case "random":
if (name == "random")
g_MapData[name] = {settings : {"Name" : "Random", "Description" : "Randomly selects a map from the list"}};
else
g_MapData[name] = parseJSONData(name+".json");
break;
default:
error("loadMapData: Unexpected map type '"+g_GameAttributes.mapType+"'");
return undefined;
}
}
return g_MapData[name];
}
////////////////////////////////////////////////////////////////////////////////////////////////
// GUI event handlers
function cancelSetup()
{
Engine.DisconnectNetworkGame();
if (Engine.HasXmppClient())
{
// Set player presence
Engine.LobbySetPlayerPresence("available");
// Unregister the game
if (g_IsController)
Engine.SendUnregisterGame();
}
}
function onTick()
{
// First tick happens before first render, so don't load yet
if (g_LoadingState == 0)
{
g_LoadingState++;
}
else if (g_LoadingState == 1)
{
- getGUIObjectByName("loadingWindow").hidden = true;
- getGUIObjectByName("setupWindow").hidden = false;
+ 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": "You", "player": 1, "civ": "", "team": -1} };
}
}
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("selectMapType: Unexpected map type '"+g_GameAttributes.mapType+"'");
return;
}
initMapNameList();
updateGameAttributes();
}
function selectMapFilter(filterName)
{
// Avoid recursion
if (g_IsInGuiUpdate)
return;
// Network clients can't change map filter
if (g_IsNetworked && !g_IsController)
return;
g_GameAttributes.mapFilter = filterName;
initMapNameList();
updateGameAttributes();
}
// Called when the user selects a map from the list
function selectMap(name)
{
// Avoid recursion
if (g_IsInGuiUpdate)
return;
// Network clients can't change map
if (g_IsNetworked && !g_IsController)
return;
// Return if we have no map
if (!name)
return;
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 (mapData !== "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": "You", "player": 1, "civ": "", "team": -1} };
}
else
{
var numPlayers = (mapSettings.PlayerData ? mapSettings.PlayerData.length : g_GameAttributes.settings.PlayerData.length);
for (var guid in g_PlayerAssignments)
{ // Unassign extra players
var player = g_PlayerAssignments[guid].player;
if (player <= MAX_PLAYERS && player > numPlayers)
Engine.AssignNetworkPlayer(player, "");
}
}
updateGameAttributes();
}
function launchGame()
{
if (g_IsNetworked && !g_IsController)
{
error("Only host can start game");
return;
}
// Check that we have a map
if (!g_GameAttributes.map)
return;
if (g_GameAttributes.map == "random")
- selectMap(getGUIObjectByName("mapSelection").list_data[Math.floor(Math.random() *
- (getGUIObjectByName("mapSelection").list.length - 1)) + 1]);
+ selectMap(Engine.GetGUIObjectByName("mapSelection").list_data[Math.floor(Math.random() *
+ (Engine.GetGUIObjectByName("mapSelection").list.length - 1)) + 1]);
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 === undefined || civ.SelectableInGameSetup))
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 === undefined || civ.SelectableInGameSetup))
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
if (usedName)
g_GameAttributes.settings.PlayerData[i].Name = chosenName + " " + 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 = getGUIObjectByName("playerAssignment["+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)
{
- getGUIObjectByName("mapFilterText").caption = g_GameAttributes.mapFilter;
- var mapTypeSelection = getGUIObjectByName("mapTypeSelection");
+ Engine.GetGUIObjectByName("mapFilterText").caption = g_GameAttributes.mapFilter;
+ var mapTypeSelection = Engine.GetGUIObjectByName("mapTypeSelection");
var idx = mapTypeSelection.list_data.indexOf(g_GameAttributes.mapType);
- getGUIObjectByName("mapTypeText").caption = mapTypeSelection.list[idx];
- var mapSelectionBox = getGUIObjectByName("mapSelection");
+ Engine.GetGUIObjectByName("mapTypeText").caption = mapTypeSelection.list[idx];
+ var mapSelectionBox = Engine.GetGUIObjectByName("mapSelection");
mapSelectionBox.selected = mapSelectionBox.list_data.indexOf(mapName);
- getGUIObjectByName("mapSelectionText").caption = getMapDisplayName(mapName);
- var populationCapBox = getGUIObjectByName("populationCap");
+ Engine.GetGUIObjectByName("mapSelectionText").caption = getMapDisplayName(mapName);
+ var populationCapBox = Engine.GetGUIObjectByName("populationCap");
populationCapBox.selected = populationCapBox.list_data.indexOf(mapSettings.PopulationCap);
- var startingResourcesBox = getGUIObjectByName("startingResources");
+ var startingResourcesBox = Engine.GetGUIObjectByName("startingResources");
startingResourcesBox.selected = startingResourcesBox.list_data.indexOf(mapSettings.StartingResources);
initMapNameList();
}
// Controls common to all map types
- var numPlayersSelection = getGUIObjectByName("numPlayersSelection");
- var revealMap = getGUIObjectByName("revealMap");
- var victoryCondition = getGUIObjectByName("victoryCondition");
- var lockTeams = getGUIObjectByName("lockTeams");
- var mapSize = getGUIObjectByName("mapSize");
- var enableCheats = getGUIObjectByName("enableCheats");
- var populationCap = getGUIObjectByName("populationCap");
- var startingResources = getGUIObjectByName("startingResources");
-
- var numPlayersText= getGUIObjectByName("numPlayersText");
- var mapSizeDesc = getGUIObjectByName("mapSizeDesc");
- var mapSizeText = getGUIObjectByName("mapSizeText");
- var revealMapText = getGUIObjectByName("revealMapText");
- var victoryConditionText = getGUIObjectByName("victoryConditionText");
- var lockTeamsText = getGUIObjectByName("lockTeamsText");
- var enableCheatsText = getGUIObjectByName("enableCheatsText");
- var populationCapText = getGUIObjectByName("populationCapText");
- var startingResourcesText = getGUIObjectByName("startingResourcesText");
- var gameSpeedText = getGUIObjectByName("gameSpeedText");
+ var numPlayersSelection = Engine.GetGUIObjectByName("numPlayersSelection");
+ var revealMap = Engine.GetGUIObjectByName("revealMap");
+ var victoryCondition = Engine.GetGUIObjectByName("victoryCondition");
+ var lockTeams = Engine.GetGUIObjectByName("lockTeams");
+ var mapSize = Engine.GetGUIObjectByName("mapSize");
+ var enableCheats = Engine.GetGUIObjectByName("enableCheats");
+ 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 victoryConditionText = Engine.GetGUIObjectByName("victoryConditionText");
+ var lockTeamsText = Engine.GetGUIObjectByName("lockTeamsText");
+ var enableCheatsText = Engine.GetGUIObjectByName("enableCheatsText");
+ var populationCapText = Engine.GetGUIObjectByName("populationCapText");
+ var startingResourcesText = Engine.GetGUIObjectByName("startingResourcesText");
+ var gameSpeedText = Engine.GetGUIObjectByName("gameSpeedText");
var sizeIdx = (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 = (VICTORY_DATA.indexOf(mapSettings.GameType) != -1 ? VICTORY_DATA.indexOf(mapSettings.GameType) : VICTORY_DEFAULTIDX);
enableCheats.checked = (g_GameAttributes.settings.CheatsEnabled === undefined || !g_GameAttributes.settings.CheatsEnabled ? false : true)
enableCheatsText.caption = (enableCheats.checked ? "Yes" : "No");
gameSpeedText.caption = g_GameSpeeds.names[speedIdx];
populationCap.selected = (POPULATION_CAP_DATA.indexOf(mapSettings.PopulationCap) != -1 ? POPULATION_CAP_DATA.indexOf(mapSettings.PopulationCap) : POPULATION_CAP_DEFAULTIDX);
populationCapText.caption = POPULATION_CAP[populationCap.selected];
startingResources.selected = (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
- getGUIObjectByName("mapPreview").sprite = "cropped:(0.78125,0.5859375)session/icons/mappreview/" + getMapPreview(mapName);
+ 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;
victoryCondition.hidden = false;
lockTeams.hidden = false;
populationCap.hidden = false;
startingResources.hidden = false;
numPlayersText.hidden = true;
mapSizeText.hidden = true;
revealMapText.hidden = true;
victoryConditionText.hidden = true;
lockTeamsText.hidden = true;
populationCapText.hidden = true;
startingResourcesText.hidden = true;
mapSizeText.caption = "Map size:";
mapSize.selected = sizeIdx;
revealMapText.caption = "Reveal map:";
revealMap.checked = (mapSettings.RevealMap ? true : false);
victoryConditionText.caption = "Victory condition:";
victoryCondition.selected = victoryIdx;
lockTeamsText.caption = "Teams locked:";
lockTeams.checked = (mapSettings.LockTeams ? true : false);
}
else
{
// Client
numPlayersText.hidden = false;
mapSizeText.hidden = false;
revealMapText.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 ? "Yes" : "No");
victoryConditionText.caption = VICTORY_TEXT[victoryIdx];
lockTeamsText.caption = (mapSettings.LockTeams ? "Yes" : "No");
}
break;
case "skirmish":
mapSizeText.caption = "Default";
numPlayersText.caption = numPlayers;
numPlayersSelection.hidden = true;
mapSize.hidden = true;
mapSizeText.hidden = true;
mapSizeDesc.hidden = true;
if (g_IsController)
{
//Host
revealMap.hidden = false;
victoryCondition.hidden = false;
lockTeams.hidden = false;
populationCap.hidden = false;
startingResources.hidden = false;
numPlayersText.hidden = false;
revealMapText.hidden = true;
victoryConditionText.hidden = true;
lockTeamsText.hidden = true;
populationCapText.hidden = true;
startingResourcesText.hidden = true;
revealMapText.caption = "Reveal map:";
revealMap.checked = (mapSettings.RevealMap ? true : false);
victoryConditionText.caption = "Victory condition:";
victoryCondition.selected = victoryIdx;
lockTeamsText.caption = "Teams locked:";
lockTeams.checked = (mapSettings.LockTeams ? true : false);
}
else
{
// Client
numPlayersText.hidden = false;
revealMapText.hidden = false;
victoryConditionText.hidden = false;
lockTeamsText.hidden = false;
populationCap.hidden = true;
populationCapText.hidden = false;
startingResources.hidden = true;
startingResourcesText.hidden = false;
revealMapText.caption = (mapSettings.RevealMap ? "Yes" : "No");
victoryConditionText.caption = VICTORY_TEXT[victoryIdx];
lockTeamsText.caption = (mapSettings.LockTeams ? "Yes" : "No");
}
break;
case "scenario":
// For scenario just reflect settings for the current map
numPlayersSelection.hidden = true;
mapSize.hidden = true;
revealMap.hidden = true;
victoryCondition.hidden = true;
lockTeams.hidden = true;
numPlayersText.hidden = false;
mapSizeText.hidden = true;
mapSizeDesc.hidden = true;
revealMapText.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 = "Default";
revealMapText.caption = (mapSettings.RevealMap ? "Yes" : "No");
victoryConditionText.caption = VICTORY_TEXT[victoryIdx];
lockTeamsText.caption = (mapSettings.LockTeams ? "Yes" : "No");
- getGUIObjectByName("populationCap").selected = POPULATION_CAP_DEFAULTIDX;
+ Engine.GetGUIObjectByName("populationCap").selected = POPULATION_CAP_DEFAULTIDX;
break;
default:
error("onGameAttributesChange: Unexpected map type '"+g_GameAttributes.mapType+"'");
return;
}
// Display map name
- getGUIObjectByName("mapInfoName").caption = getMapDisplayName(mapName);
+ Engine.GetGUIObjectByName("mapInfoName").caption = getMapDisplayName(mapName);
// Load the description from the map file, if there is one
var description = mapSettings.Description || "Sorry, no description available.";
if (g_GameAttributes.mapFilter == "Naval Maps")
description += g_NavalWarning;
// Describe the number of players
var playerString = numPlayers + " " + (numPlayers == 1 ? "player" : "players") + ". ";
for (var i = 0; i < MAX_PLAYERS; ++i)
{
// Show only needed player slots
- getGUIObjectByName("playerBox["+i+"]").hidden = (i >= numPlayers);
+ Engine.GetGUIObjectByName("playerBox["+i+"]").hidden = (i >= numPlayers);
// Show player data or defaults as necessary
if (i >= numPlayers)
continue;
- var pName = getGUIObjectByName("playerName["+i+"]");
- var pCiv = getGUIObjectByName("playerCiv["+i+"]");
- var pCivText = getGUIObjectByName("playerCivText["+i+"]");
- var pTeam = getGUIObjectByName("playerTeam["+i+"]");
- var pTeamText = getGUIObjectByName("playerTeamText["+i+"]");
- var pColor = getGUIObjectByName("playerColour["+i+"]");
+ 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 = 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\"]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;
}
}
- getGUIObjectByName("mapInfoDescription").caption = playerString + description;
+ Engine.GetGUIObjectByName("mapInfoDescription").caption = playerString + description;
g_IsInGuiUpdate = false;
// Game attributes include AI settings, so update the player list
updatePlayerList();
}
function updateGameAttributes()
{
if (g_IsNetworked)
{
Engine.SetNetworkGameAttributes(g_GameAttributes);
if (g_IsController && g_LoadingState >= 2)
sendRegisterGameStanza();
}
else
{
onGameAttributesChange();
}
}
function updatePlayerList()
{
g_IsInGuiUpdate = true;
var hostNameList = [];
var hostGuidList = [];
var assignments = [];
var aiAssignments = {};
var noAssignment;
var 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 != 255)
assignedCount++;
}
// Only enable start button if we have enough assigned players
if (g_IsController)
- getGUIObjectByName("startGame").enabled = (assignedCount > 0);
+ Engine.GetGUIObjectByName("startGame").enabled = (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\"]AI: " + ai.data.name);
hostGuidList.push("ai:" + ai.id);
}
noAssignment = hostNameList.length;
hostNameList.push("[color=\"140 140 140 255\"]Unassigned");
hostGuidList.push("");
for (var i = 0; i < MAX_PLAYERS; ++i)
{
let playerSlot = i;
let playerID = i+1; // we don't show Gaia, so first slot is ID 1
var selection = assignments[playerID];
- var configButton = getGUIObjectByName("playerConfig["+i+"]");
+ 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
warn("AI \""+aiId+"\" not present. Defaulting to unassigned.");
}
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: function(ai) {
g_GameAttributes.settings.PlayerData[playerSlot].AI = ai.id;
g_GameAttributes.settings.PlayerData[playerSlot].AIDiff = ai.difficulty;
if (g_IsNetworked)
Engine.SetNetworkGameAttributes(g_GameAttributes);
else
updatePlayerList();
}
});
};
}
}
// 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 = getGUIObjectByName("playerAssignment["+i+"]");
+ 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);
}
};
}
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 != 255)
{
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 = getGUIObjectByName("chatInput");
+ var input = Engine.GetGUIObjectByName("chatInput");
var text = input.caption;
if (text.length)
{
Engine.SendNetworkChat(text);
input.caption = "";
}
}
function addChatMessage(msg)
{
var username = escapeText(msg.username || g_PlayerAssignments[msg.guid].name);
var message = escapeText(msg.text);
// TODO: Maybe host should have distinct font/color?
var color = "white";
if (g_PlayerAssignments[msg.guid] && g_PlayerAssignments[msg.guid].player != 255)
{ // Valid player who has been assigned - get player colour
var player = g_PlayerAssignments[msg.guid].player - 1;
var mapName = g_GameAttributes.map;
var mapData = loadMapData(mapName);
var mapSettings = (mapData && mapData.settings ? mapData.settings : {});
var pData = mapSettings.PlayerData ? mapSettings.PlayerData[player] : {};
var pDefs = g_DefaultPlayerData ? g_DefaultPlayerData[player] : {};
color = iColorToString(getSetting(pData, pDefs, "Colour"));
}
var formatted;
switch (msg.type)
{
case "connect":
formatted = '[font="serif-bold-13"][color="'+ color +'"]' + username + '[/color][/font] [color="gold"]has joined[/color]';
break;
case "disconnect":
formatted = '[font="serif-bold-13"][color="'+ color +'"]' + username + '[/color][/font] [color="gold"]has left[/color]';
break;
case "message":
formatted = '[font="serif-bold-13"]<[color="'+ color +'"]' + username + '[/color]>[/font] ' + message;
break;
default:
error("Invalid chat message '" + uneval(msg) + "'");
return;
}
g_ChatMessages.push(formatted);
- getGUIObjectByName("chatText").caption = g_ChatMessages.join("\n");
+ Engine.GetGUIObjectByName("chatText").caption = g_ChatMessages.join("\n");
}
function toggleMoreOptions()
{
- getGUIObjectByName("moreOptions").hidden = !getGUIObjectByName("moreOptions").hidden;
+ Engine.GetGUIObjectByName("moreOptions").hidden = !Engine.GetGUIObjectByName("moreOptions").hidden;
}
////////////////////////////////////////////////////////////////////////////////////////////////
// Basic map filters API
// Add a new map list filter
function addFilter(name, filterFunc)
{
if (filterFunc instanceof Object)
{ // Basic validity test
var newFilter = {};
newFilter.name = name;
newFilter.filter = filterFunc;
g_MapFilters.push(newFilter);
}
else
{
error("Invalid map filter: "+name);
}
}
// Get array of map filter names
function getFilters()
{
var filters = [];
for (var i = 0; i < g_MapFilters.length; ++i)
filters.push(g_MapFilters[i].name);
return filters;
}
// Test map filter on given map settings object
function testFilter(name, mapSettings)
{
for (var i = 0; i < g_MapFilters.length; ++i)
if (g_MapFilters[i].name == name)
return g_MapFilters[i].filter(mapSettings);
error("Invalid map filter: "+name);
return false;
}
// Test an array of keywords against a match array using AND logic
function keywordTestAND(keywords, matches)
{
if (!keywords || !matches)
return false;
for (var m = 0; m < matches.length; ++m)
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 = getGUIObjectByName("mapSize").selected;
- var selectedVictoryCondition = getGUIObjectByName("victoryCondition").selected;
+ 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 = getGUIObjectByName("mapSize").list[selectedMapSize];
+ var mapSize = Engine.GetGUIObjectByName("mapSize").list[selectedMapSize];
else
var mapSize = "Default";
- var victoryCondition = getGUIObjectByName("victoryCondition").list[selectedVictoryCondition];
+ 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);
}
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/gamesetup_mp.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/gamesetup_mp.js (revision 14495)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/gamesetup_mp.js (revision 14496)
@@ -1,229 +1,229 @@
var g_IsConnecting = false;
var g_GameType; // "server" or "client"
var g_ServerName = "";
var g_IsRejoining = false;
var g_GameAttributes; // used when rejoining
var g_PlayerAssignments; // used when rejoining
function init(attribs)
{
switch (attribs.multiplayerGameType)
{
case "join":
if(Engine.HasXmppClient())
{
if (startJoin(attribs.name, attribs.ip))
switchSetupPage("pageJoin", "pageConnecting");
}
else
{
- getGUIObjectByName("pageJoin").hidden = false;
- getGUIObjectByName("pageHost").hidden = true;
+ Engine.GetGUIObjectByName("pageJoin").hidden = false;
+ Engine.GetGUIObjectByName("pageHost").hidden = true;
}
break;
case "host":
- getGUIObjectByName("pageJoin").hidden = true;
- getGUIObjectByName("pageHost").hidden = false;
+ Engine.GetGUIObjectByName("pageJoin").hidden = true;
+ Engine.GetGUIObjectByName("pageHost").hidden = false;
if(Engine.HasXmppClient())
{
- getGUIObjectByName("hostServerNameWrapper").hidden = false;
- getGUIObjectByName("hostPlayerName").caption = attribs.name;
- getGUIObjectByName("hostServerName").caption = attribs.name + "'s game";
+ Engine.GetGUIObjectByName("hostServerNameWrapper").hidden = false;
+ Engine.GetGUIObjectByName("hostPlayerName").caption = attribs.name;
+ Engine.GetGUIObjectByName("hostServerName").caption = attribs.name + "'s game";
}
else
- getGUIObjectByName("hostPlayerNameWrapper").hidden = false;
+ Engine.GetGUIObjectByName("hostPlayerNameWrapper").hidden = false;
break;
default:
error("Unrecognised multiplayer game type : " + attribs.multiplayerGameType);
break;
}
}
function cancelSetup()
{
if (g_IsConnecting)
Engine.DisconnectNetworkGame();
// Set player lobby presence
if (Engine.HasXmppClient())
Engine.LobbySetPlayerPresence("available");
Engine.PopGuiPage();
}
function startConnectionStatus(type)
{
g_GameType = type;
g_IsConnecting = true;
g_IsRejoining = false;
- getGUIObjectByName("connectionStatus").caption = "Connecting to server...";
+ Engine.GetGUIObjectByName("connectionStatus").caption = "Connecting to server...";
}
function onTick()
{
if (!g_IsConnecting)
return;
pollAndHandleNetworkClient();
}
function pollAndHandleNetworkClient()
{
while (true)
{
var message = Engine.PollNetworkClient();
if (!message)
break;
log("Net message: "+uneval(message));
// If we're rejoining an active game, we don't want to actually display
// the game setup screen, so perform similar processing to gamesetup.js
// in this screen
if (g_IsRejoining)
{
switch (message.type)
{
case "netstatus":
switch (message.status)
{
case "disconnected":
cancelSetup();
reportDisconnect(message.reason);
return;
default:
error("Unrecognised netstatus type "+message.status);
break;
}
break;
case "gamesetup":
g_GameAttributes = message.data;
break;
case "players":
g_PlayerAssignments = message.hosts;
break;
case "start":
Engine.SwitchGuiPage("page_loading.xml", {
"attribs": g_GameAttributes,
"isNetworked" : true,
"playerAssignments": g_PlayerAssignments
});
break;
case "chat":
// Ignore, since we have nowhere to display chat messages
break;
default:
error("Unrecognised net message type "+message.type);
}
}
else
{
// Not rejoining - just trying to connect to server
switch (message.type)
{
case "netstatus":
switch (message.status)
{
case "connected":
- getGUIObjectByName("connectionStatus").caption = "Registering with server...";
+ Engine.GetGUIObjectByName("connectionStatus").caption = "Registering with server...";
break;
case "authenticated":
if (message.rejoining)
{
- getGUIObjectByName("connectionStatus").caption = "Game has already started - rejoining...";
+ Engine.GetGUIObjectByName("connectionStatus").caption = "Game has already started - rejoining...";
g_IsRejoining = true;
return; // we'll process the game setup messages in the next tick
}
else
{
Engine.SwitchGuiPage("page_gamesetup.xml", { "type": g_GameType, "serverName": g_ServerName });
return; // don't process any more messages - leave them for the game GUI loop
}
case "disconnected":
cancelSetup();
reportDisconnect(message.reason);
return;
default:
error("Unrecognised netstatus type "+message.status);
break;
}
break;
default:
error("Unrecognised net message type "+message.type);
break;
}
}
}
}
function switchSetupPage(oldpage, newpage)
{
- getGUIObjectByName(oldpage).hidden = true;
- getGUIObjectByName(newpage).hidden = false;
+ Engine.GetGUIObjectByName(oldpage).hidden = true;
+ Engine.GetGUIObjectByName(newpage).hidden = false;
}
function startHost(playername, servername)
{
// Disallow identically named games in the multiplayer lobby
if (Engine.HasXmppClient())
{
for each (g in Engine.GetGameList())
{
if (g.name === servername)
{
- getGUIObjectByName("hostFeedback").caption = "Game name already in use.";
+ Engine.GetGUIObjectByName("hostFeedback").caption = "Game name already in use.";
return false;
}
}
}
try
{
Engine.StartNetworkHost(playername);
}
catch (e)
{
cancelSetup();
messageBox(400, 200,
"Cannot host game: " + e.message + ".",
"Error", 2);
return false;
}
startConnectionStatus("server");
g_ServerName = servername;
// Set player lobby presence
if (Engine.HasXmppClient())
Engine.LobbySetPlayerPresence("playing");
return true;
}
function startJoin(playername, ip)
{
try
{
Engine.StartNetworkJoin(playername, ip);
}
catch (e)
{
cancelSetup();
messageBox(400, 200,
"Cannot join game: " + e.message + ".",
"Error", 2);
return false;
}
startConnectionStatus("client");
// Set player lobby presence
if (Engine.HasXmppClient())
Engine.LobbySetPlayerPresence("playing");
return true;
}
Index: ps/trunk/binaries/data/mods/public/gui/gamesetup/gamesetup_mp.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/gamesetup/gamesetup_mp.xml (revision 14495)
+++ ps/trunk/binaries/data/mods/public/gui/gamesetup/gamesetup_mp.xml (revision 14496)
@@ -1,121 +1,121 @@
Index: ps/trunk/binaries/data/mods/public/gui/loading/loading.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/loading/loading.js (revision 14495)
+++ ps/trunk/binaries/data/mods/public/gui/loading/loading.js (revision 14496)
@@ -1,116 +1,116 @@
var g_Data;
const END_PIECE_WIDTH = 16;
function init(data)
{
g_Data = data;
// Set to "hourglass" cursor.
- setCursor("cursor-wait");
+ Engine.SetCursor("cursor-wait");
// Get tip image and corresponding tip text
- var tipTextLoadingArray = buildDirEntList("gui/text/tips/", "*.txt", false);
+ var tipTextLoadingArray = Engine.BuildDirEntList("gui/text/tips/", "*.txt", false);
if (tipTextLoadingArray.length > 0)
{
// Set tip text
var tipTextFilePath = tipTextLoadingArray[getRandom (0, tipTextLoadingArray.length-1)];
- var tipText = readFile(tipTextFilePath);
+ var tipText = Engine.ReadFile(tipTextFilePath);
if (tipText)
{
var index = tipText.indexOf("\n");
var tipTextTitle = tipText.substring(0, index);
var tipTextMessage = tipText.substring(index);
- getGUIObjectByName("tipTitle").caption = tipTextTitle? tipTextTitle : "";
- getGUIObjectByName("tipText").caption = tipTextMessage? tipTextMessage : "";
+ Engine.GetGUIObjectByName("tipTitle").caption = tipTextTitle? tipTextTitle : "";
+ Engine.GetGUIObjectByName("tipText").caption = tipTextMessage? tipTextMessage : "";
}
// Set tip image
var fileName = tipTextFilePath.substring(tipTextFilePath.lastIndexOf("/")+1).replace(".txt", ".png");
var tipImageFilePath = "loading/tips/" + fileName;
var sprite = "stretched:" + tipImageFilePath;
- getGUIObjectByName("tipImage").sprite = sprite? sprite : "";
+ Engine.GetGUIObjectByName("tipImage").sprite = sprite? sprite : "";
}
else
{
error("Failed to find any matching tips for the loading screen.")
}
// janwas: main loop now sets progress / description, but that won't
// happen until the first timeslice completes, so set initial values.
- var loadingMapName = getGUIObjectByName ("loadingMapName");
+ var loadingMapName = Engine.GetGUIObjectByName ("loadingMapName");
if (data)
{
var mapName = data.attribs.settings.Name;
switch (data.attribs.mapType)
{
case "skirmish":
case "scenario":
loadingMapName.caption = "Loading \"" + mapName + "\"";
break;
case "random":
loadingMapName.caption = "Generating \"" + mapName + "\"";
break;
default:
error("Unknown map type: " + data.attribs.mapType);
}
}
- getGUIObjectByName("progressText").caption = "";
- getGUIObjectByName("progressbar").caption = 0;
+ Engine.GetGUIObjectByName("progressText").caption = "";
+ Engine.GetGUIObjectByName("progressbar").caption = 0;
// Pick a random quote of the day (each line is a separate tip).
- var quoteArray = readFileLines("gui/text/quotes.txt");
- getGUIObjectByName("quoteText").caption = quoteArray[getRandom(0, quoteArray.length-1)];
+ var quoteArray = Engine.ReadFileLines("gui/text/quotes.txt");
+ Engine.GetGUIObjectByName("quoteText").caption = quoteArray[getRandom(0, quoteArray.length-1)];
}
// ====================================================================
function displayProgress()
{
// Make the progessbar finish a little early so that the user can actually see it finish
if (g_Progress < 100)
{
// Show 100 when it is really 99
var progress = g_Progress + 1;
- getGUIObjectByName("progressbar").caption = progress; // display current progress
- getGUIObjectByName("progressText").caption = progress + "%";
+ Engine.GetGUIObjectByName("progressbar").caption = progress; // display current progress
+ Engine.GetGUIObjectByName("progressText").caption = progress + "%";
// Displays detailed loading info rather than a percent
- // getGUIObjectByName("progressText").caption = g_LoadDescription; // display current progess details
+ // Engine.GetGUIObjectByName("progressText").caption = g_LoadDescription; // display current progess details
// Keep curved right edge of progress bar in sync with the rest of the progress bar
- var middle = getGUIObjectByName("progressbar");
- var rightSide = getGUIObjectByName("progressbar_right");
+ var middle = Engine.GetGUIObjectByName("progressbar");
+ var rightSide = Engine.GetGUIObjectByName("progressbar_right");
var middleLength = (middle.size.right - middle.size.left) - (END_PIECE_WIDTH / 2);
var increment = Math.round(progress * middleLength / 100);
var size = rightSide.size;
size.left = increment;
size.right = increment + END_PIECE_WIDTH;
rightSide.size = size;
}
}
// ====================================================================
function reallyStartGame()
{
// Stop the music
// if (global.curr_music)
// global.curr_music.fade(-1, 0.0, 5.0); // fade to 0 over 5 seconds
// This is a reserved function name that is executed by the engine when it is ready
// to start the game (i.e. loading progress has reached 100%).
// Switch GUI from loading screen to game session.
Engine.SwitchGuiPage("page_session.xml", g_Data);
// Restore default cursor.
- setCursor("arrow-default");
+ Engine.SetCursor("arrow-default");
}
Index: ps/trunk/binaries/data/mods/public/gui/lobby/lobby.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/lobby/lobby.js (revision 14495)
+++ ps/trunk/binaries/data/mods/public/gui/lobby/lobby.js (revision 14496)
@@ -1,803 +1,803 @@
var g_ChatMessages = [];
var g_Name = "unknown";
var g_GameList = {};
var g_specialKey = Math.random();
var g_spamMonitor = {};
var g_spammers = {};
var g_timestamp = Engine.ConfigDB_GetValue("user", "lobby.chattimestamp") == "true";
var g_mapSizes = {};
////////////////////////////////////////////////////////////////////////////////////////////////
function init(attribs)
{
// Play menu music
initMusic();
global.music.setState(global.music.states.MENU);
g_Name = Engine.LobbyGetNick();
g_mapSizes = initMapSizes();
g_mapSizes.shortNames.splice(0, 0, "Any");
g_mapSizes.tiles.splice(0, 0, "");
- var mapSizeFilter = getGUIObjectByName("mapSizeFilter");
+ var mapSizeFilter = Engine.GetGUIObjectByName("mapSizeFilter");
mapSizeFilter.list = g_mapSizes.shortNames;
mapSizeFilter.list_data = g_mapSizes.tiles;
- var playersNumberFilter = getGUIObjectByName("playersNumberFilter");
+ var playersNumberFilter = Engine.GetGUIObjectByName("playersNumberFilter");
playersNumberFilter.list = ["Any",2,3,4,5,6,7,8];
playersNumberFilter.list_data = ["",2,3,4,5,6,7,8];
- var mapTypeFilter = getGUIObjectByName("mapTypeFilter");
+ var mapTypeFilter = Engine.GetGUIObjectByName("mapTypeFilter");
mapTypeFilter.list = ["Any", "Skirmish", "Random", "Scenario"];
mapTypeFilter.list_data = ["", "skirmish", "random", "scenario"];
Engine.LobbySetPlayerPresence("available");
Engine.SendGetGameList();
Engine.SendGetBoardList();
updatePlayerList();
resetFilters();
setTimeout(clearSpamMonitor, 5000);
setTimeout(clearSpammers, 30000);
}
////////////////////////////////////////////////////////////////////////////////////////////////
// Xmpp client connection management
////////////////////////////////////////////////////////////////////////////////////////////////
function lobbyStop()
{
Engine.StopXmppClient();
}
function lobbyConnect()
{
Engine.ConnectXmppClient();
}
function lobbyDisconnect()
{
Engine.DisconnectXmppClient();
}
////////////////////////////////////////////////////////////////////////////////////////////////
// Update functions
////////////////////////////////////////////////////////////////////////////////////////////////
function resetFilters()
{
// Reset states of gui objects
- getGUIObjectByName("mapSizeFilter").selected = 0
- getGUIObjectByName("playersNumberFilter").selected = 0;
- getGUIObjectByName("mapTypeFilter").selected = 0;
- getGUIObjectByName("showFullFilter").checked = true;
+ Engine.GetGUIObjectByName("mapSizeFilter").selected = 0
+ Engine.GetGUIObjectByName("playersNumberFilter").selected = 0;
+ Engine.GetGUIObjectByName("mapTypeFilter").selected = 0;
+ Engine.GetGUIObjectByName("showFullFilter").checked = true;
// Update the list of games
updateGameList();
// Update info box about the game currently selected
- selectGame(getGUIObjectByName("gamesBox").selected);
+ selectGame(Engine.GetGUIObjectByName("gamesBox").selected);
}
function applyFilters()
{
// Update the list of games
updateGameList();
// Update info box about the game currently selected
- selectGame(getGUIObjectByName("gamesBox").selected);
+ selectGame(Engine.GetGUIObjectByName("gamesBox").selected);
}
function displayGame(g, mapSizeFilter, playersNumberFilter, mapTypeFilter, showFullFilter)
{
if(mapSizeFilter != "" && g.mapSize != mapSizeFilter)
return false;
if(playersNumberFilter != "" && g.tnbp != playersNumberFilter)
return false;
if(mapTypeFilter != "" && g.mapType != mapTypeFilter)
return false;
if(!showFullFilter && g.tnbp <= g.nbp)
return false;
return true;
}
// Do a full update of the player listing **Only call on init**
function updatePlayerList()
{
- var playersBox = getGUIObjectByName("playersBox");
+ var playersBox = Engine.GetGUIObjectByName("playersBox");
[playerList, presenceList, nickList] = [[],[],[]];
for each (var p in Engine.GetPlayerList())
{
var [name, status] = formatPlayerListEntry(p.name, p.presence);
playerList.push(name);
presenceList.push(status);
nickList.push(p.name);
}
playersBox.list_name = playerList;
playersBox.list_status = presenceList;
playersBox.list = nickList;
if (playersBox.selected >= playersBox.list.length)
playersBox.selected = -1;
return [playerList, presenceList, nickList];
}
// Update leaderboard listing
function updateBoardList()
{
// Get list from C++
var boardList = Engine.GetBoardList();
// Get GUI leaderboard object
- var leaderboard = getGUIObjectByName("leaderboardBox");
+ var leaderboard = Engine.GetGUIObjectByName("leaderboardBox");
// Sort list in acending order by rating
boardList.sort(function(a, b) b.rating - a.rating);
var list = [];
var list_name = [];
var list_rank = [];
var list_rating = [];
// Push changes
for (var i = 0; i < boardList.length; i++)
{
list_name.push(boardList[i].name);
list_rating.push(boardList[i].rating);
list_rank.push(i+1);
list.push(boardList[i].name);
}
leaderboard.list_name = list_name;
leaderboard.list_rating = list_rating;
leaderboard.list_rank = list_rank;
leaderboard.list = list;
if (leaderboard.selected >= leaderboard.list.length)
leaderboard.selected = -1;
}
// Update game listing
function updateGameList()
{
- var gamesBox = getGUIObjectByName("gamesBox");
+ var gamesBox = Engine.GetGUIObjectByName("gamesBox");
var gameList = Engine.GetGameList();
// Store the game whole game list data so that we can access it later
// to update the game info panel.
g_GameList = gameList;
// Sort the list of games to that games 'waiting' are displayed at the top
g_GameList.sort(function (a,b) {
return a.state == 'waiting' ? -1 : b.state == 'waiting' ? +1 : 0;
});
var list_name = [];
var list_ip = [];
var list_mapName = [];
var list_mapSize = [];
var list_mapType = [];
var list_nPlayers = [];
var list = [];
var list_data = [];
- var mapSizeFilterDD = getGUIObjectByName("mapSizeFilter");
- var playersNumberFilterDD = getGUIObjectByName("playersNumberFilter");
- var mapTypeFilterDD = getGUIObjectByName("mapTypeFilter");
- var showFullFilterCB = getGUIObjectByName("showFullFilter");
+ var mapSizeFilterDD = Engine.GetGUIObjectByName("mapSizeFilter");
+ var playersNumberFilterDD = Engine.GetGUIObjectByName("playersNumberFilter");
+ var mapTypeFilterDD = Engine.GetGUIObjectByName("mapTypeFilter");
+ var showFullFilterCB = Engine.GetGUIObjectByName("showFullFilter");
// Get filter values
var mapSizeFilter = mapSizeFilterDD.selected >= 0 ? mapSizeFilterDD.list_data[mapSizeFilterDD.selected] : "";
var playersNumberFilter = playersNumberFilterDD.selected >=0 ? playersNumberFilterDD.list_data[playersNumberFilterDD.selected] : "";
var mapTypeFilter = mapTypeFilterDD.selected >= 0 ? mapTypeFilterDD.list_data[mapTypeFilterDD.selected] : "";
var showFullFilter = showFullFilterCB.checked;
var c = 0;
for each (g in gameList)
{
if(displayGame(g, mapSizeFilter, playersNumberFilter, mapTypeFilter, showFullFilter))
{
// Highlight games 'waiting' for this player, otherwise display as green
var name = (g.state != 'waiting') ? '[color="0 125 0"]' + g.name + '[/color]' : '[color="orange"]' + g.name + '[/color]';
list_name.push(name);
list_ip.push(g.ip);
list_mapName.push(g.niceMapName);
list_mapSize.push(g.mapSize.split("(")[0]);
list_mapType.push(toTitleCase(g.mapType));
list_nPlayers.push(g.nbp + "/" +g.tnbp);
list.push(g.name);
list_data.push(c);
}
c++;
}
gamesBox.list_name = list_name;
//gamesBox.list_ip = list_ip;
gamesBox.list_mapName = list_mapName;
gamesBox.list_mapSize = list_mapSize;
gamesBox.list_mapType = list_mapType;
gamesBox.list_nPlayers = list_nPlayers;
gamesBox.list = list;
gamesBox.list_data = list_data;
if (gamesBox.selected >= gamesBox.list_name.length)
gamesBox.selected = -1;
// If game selected, update info box about the game.
- if(getGUIObjectByName("gamesBox").selected != -1)
- selectGame(getGUIObjectByName("gamesBox").selected)
+ if(Engine.GetGUIObjectByName("gamesBox").selected != -1)
+ selectGame(Engine.GetGUIObjectByName("gamesBox").selected)
}
// The following function colorizes and formats the entries in the player list.
function formatPlayerListEntry(nickname, presence)
{
// Set colors based on player status
var color_close = '[/color]';
switch (presence)
{
case "playing":
var color = '[color="125 0 0"]';
var status = color + "Busy" + color_close;
break;
case "away":
var color = '[color="229 76 13"]';
var status = color + "Away" + color_close;
break;
case "available":
var color = '[color="0 125 0"]';
var status = color + "Online" + color_close;
break;
case "offline":
var color = '[color="0 0 0"]';
var status = color + "Offline" + color_close;
break;
default:
warn("Unknown presence '"+presence+"'");
break;
}
var name = colorPlayerName(nickname);
// Push this player's name and status onto the list
return [name, status];
}
function selectGame(selected)
{
if (selected == -1)
{
// Hide the game info panel if a game is not selected
- getGUIObjectByName("gameInfo").hidden = true;
- getGUIObjectByName("gameInfoEmpty").hidden = false;
- getGUIObjectByName("joinGameButton").hidden = true;
+ Engine.GetGUIObjectByName("gameInfo").hidden = true;
+ Engine.GetGUIObjectByName("gameInfoEmpty").hidden = false;
+ Engine.GetGUIObjectByName("joinGameButton").hidden = true;
return;
}
var mapData;
- var g = getGUIObjectByName("gamesBox").list_data[selected];
+ var g = Engine.GetGUIObjectByName("gamesBox").list_data[selected];
// Load map data
if (g_GameList[g].mapType == "random" && g_GameList[g].mapName == "random")
mapData = {"settings": {"Description": "A randomly selected map."}};
- else if (g_GameList[g].mapType == "random" && fileExists(g_GameList[g].mapName + ".json"))
+ else if (g_GameList[g].mapType == "random" && Engine.FileExists(g_GameList[g].mapName + ".json"))
mapData = parseJSONData(g_GameList[g].mapName + ".json");
- else if (fileExists(g_GameList[g].mapName + ".xml"))
+ else if (Engine.FileExists(g_GameList[g].mapName + ".xml"))
mapData = Engine.LoadMapSettings(g_GameList[g].mapName + ".xml");
else
// Warn the player if we can't find the map.
warn("Map '"+ g_GameList[g].mapName +"' not found");
// Show the game info paneland join button.
- getGUIObjectByName("gameInfo").hidden = false;
- getGUIObjectByName("gameInfoEmpty").hidden = true;
- getGUIObjectByName("joinGameButton").hidden = false;
+ Engine.GetGUIObjectByName("gameInfo").hidden = false;
+ Engine.GetGUIObjectByName("gameInfoEmpty").hidden = true;
+ Engine.GetGUIObjectByName("joinGameButton").hidden = false;
// Display the map name, number of players, the names of the players, the map size and the map type.
- getGUIObjectByName("sgMapName").caption = g_GameList[g].niceMapName;
- getGUIObjectByName("sgNbPlayers").caption = g_GameList[g].nbp + "/" + g_GameList[g].tnbp;
- getGUIObjectByName("sgPlayersNames").caption = g_GameList[g].players;
- getGUIObjectByName("sgMapSize").caption = g_GameList[g].mapSize.split("(")[0];
- getGUIObjectByName("sgMapType").caption = toTitleCase(g_GameList[g].mapType);
+ Engine.GetGUIObjectByName("sgMapName").caption = g_GameList[g].niceMapName;
+ Engine.GetGUIObjectByName("sgNbPlayers").caption = g_GameList[g].nbp + "/" + g_GameList[g].tnbp;
+ Engine.GetGUIObjectByName("sgPlayersNames").caption = g_GameList[g].players;
+ Engine.GetGUIObjectByName("sgMapSize").caption = g_GameList[g].mapSize.split("(")[0];
+ Engine.GetGUIObjectByName("sgMapType").caption = toTitleCase(g_GameList[g].mapType);
// Display map description if it exists, otherwise display a placeholder.
if (mapData && mapData.settings.Description)
var mapDescription = mapData.settings.Description;
else
var mapDescription = "Sorry, no description available.";
// Display map preview if it exists, otherwise display a placeholder.
if (mapData && mapData.settings.Preview)
var mapPreview = mapData.settings.Preview;
else
var mapPreview = "nopreview.png";
- getGUIObjectByName("sgMapDescription").caption = mapDescription;
- getGUIObjectByName("sgMapPreview").sprite = "cropped:(0.7812,0.5859)session/icons/mappreview/" + mapPreview;
+ Engine.GetGUIObjectByName("sgMapDescription").caption = mapDescription;
+ Engine.GetGUIObjectByName("sgMapPreview").sprite = "cropped:(0.7812,0.5859)session/icons/mappreview/" + mapPreview;
}
function joinSelectedGame()
{
- var gamesBox = getGUIObjectByName("gamesBox");
+ var gamesBox = Engine.GetGUIObjectByName("gamesBox");
if (gamesBox.selected >= 0)
{
var g = gamesBox.list_data[gamesBox.selected];
var sname = g_Name;
var sip = g_GameList[g].ip;
// TODO: What about valid host names?
// Check if it looks like an ip address
if (sip.split('.').length != 4)
{
addChatMessage({ "from": "system", "text": "This game does not have a valid address" });
return;
}
// Open Multiplayer connection window with join option.
Engine.PushGuiPage("page_gamesetup_mp.xml", { multiplayerGameType: "join", name: sname, ip: sip });
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
// Utils
////////////////////////////////////////////////////////////////////////////////////////////////
function twoDigits(n)
{
return n < 10 ? "0" + n : n;
}
////////////////////////////////////////////////////////////////////////////////////////////////
// GUI event handlers
////////////////////////////////////////////////////////////////////////////////////////////////
function onTick()
{
// Wake up XmppClient
Engine.RecvXmppClient();
updateTimers();
// Receive messages
while (true)
{
var message = Engine.LobbyGuiPollMessage();
// Clean Message
if (!message)
break;
message.from = escapeText(message.from);
message.text = escapeText(message.text);
switch (message.type)
{
case "mucmessage": // For room messages
addChatMessage({ "from": message.from, "text": message.text });
break;
case "message": // For private messages
addChatMessage({ "from": message.from, "text": message.text });
break;
case "muc":
var nick = message.text;
var presence = Engine.LobbyGetPlayerPresence(nick);
- var playersBox = getGUIObjectByName("playersBox");
+ var playersBox = Engine.GetGUIObjectByName("playersBox");
var playerList = playersBox.list_name;
var presenceList = playersBox.list_status;
var nickList = playersBox.list;
var nickIndex = nickList.indexOf(nick);
switch(message.level)
{
case "join":
if (nick == g_Name)
{
// We just joined, we need to get the full player list
[playerList, presenceList, nickList] = updatePlayerList();
break;
}
var [name, status] = formatPlayerListEntry(nick, presence);
playerList.push(name);
presenceList.push(status);
nickList.push(nick);
addChatMessage({ "text": "/special " + nick + " has joined.", "key": g_specialKey });
break;
case "leave":
if (nickIndex == -1) // Left, but not present (TODO: warn about this?)
break;
playerList.splice(nickIndex, 1);
presenceList.splice(nickIndex, 1);
nickList.splice(nickIndex, 1);
addChatMessage({ "text": "/special " + nick + " has left.", "key": g_specialKey });
break;
case "nick":
if (nickIndex == -1) // This shouldn't ever happen
break;
if (!isValidNick(message.data))
{
addChatMessage({ "from": "system", "text": "Invalid nickname: " + message.data });
break;
}
var [name, status] = formatPlayerListEntry(message.data, presence); // TODO: actually we don't want to change the presence here, so use what was used before
playerList[nickIndex] = name;
// presence stays the same
nickList[nickIndex] = message.data;
addChatMessage({ "text": "/special " + nick + " is now known as " + message.data + ".", "key": g_specialKey });
break;
case "presence":
if (nickIndex == -1) // This shouldn't ever happen
break;
var [name, status] = formatPlayerListEntry(nick, presence);
presenceList[nickIndex] = status;
playerList[nickIndex] = name;
break;
default:
warn("Unknown message.level '" + message.level + "'");
break;
}
// Push new data to GUI
playersBox.list_name = playerList;
playersBox.list_status = presenceList;
playersBox.list = nickList;
if (playersBox.selected >= playersBox.list.length)
playersBox.selected = -1;
break;
case "system":
switch (message.level)
{
case "standard":
addChatMessage({ "from": "system", "text": message.text, "color": "150 0 0" });
if (message.text == "disconnected")
{
// Clear the list of games and the list of players
updateGameList();
updateBoardList();
updatePlayerList();
// Disable the 'host' button
- getGUIObjectByName("hostButton").enabled = false;
+ Engine.GetGUIObjectByName("hostButton").enabled = false;
}
else if (message.text == "connected")
{
- getGUIObjectByName("hostButton").enabled = true;
+ Engine.GetGUIObjectByName("hostButton").enabled = true;
}
break;
case "error":
addChatMessage({ "from": "system", "text": message.text, "color": "150 0 0" });
break;
case "internal":
switch (message.text)
{
case "gamelist updated":
updateGameList();
break;
case "boardlist updated":
updateBoardList();
break;
}
break
}
break;
default:
error("Unrecognised message type "+message.type);
}
}
}
/* Messages */
function submitChatInput()
{
- var input = getGUIObjectByName("chatInput");
+ var input = Engine.GetGUIObjectByName("chatInput");
var text = escapeText(input.caption);
if (text.length)
{
if (!handleSpecialCommand(text))
Engine.LobbySendMessage(text);
input.caption = "";
}
}
function completeNick()
{
- var input = getGUIObjectByName("chatInput");
+ var input = Engine.GetGUIObjectByName("chatInput");
var text = escapeText(input.caption);
if (text.length)
{
var matched = false;
for each (var playerObj in Engine.GetPlayerList()) {
var player = playerObj.name;
var breaks = text.match(/(\s+)/g) || [];
text.split(/\s+/g).reduceRight(function (wordsSoFar, word, index) {
if (matched)
return null;
var matchCandidate = word + (breaks[index - 1] || "") + wordsSoFar;
if (player.toUpperCase().indexOf(matchCandidate.toUpperCase().trim()) == 0) {
input.caption = text.replace(matchCandidate.trim(), player);
matched = true;
}
return matchCandidate;
}, "");
if (matched)
break;
}
}
}
function isValidNick(nick)
{
var prohibitedNicks = ["system"];
return prohibitedNicks.indexOf(nick) == -1;
}
function handleSpecialCommand(text)
{
if (text[0] != '/')
return false;
var [cmd, nick] = ircSplit(text);
switch (cmd)
{
case "away":
// TODO: Should we handle away messages?
Engine.LobbySetPlayerPresence("away");
break;
case "back":
Engine.LobbySetPlayerPresence("available");
break;
case "nick":
if (g_spammers[g_Name] != undefined)
break;
// Strip invalid characters.
nick = sanitizePlayerName(nick, true, true);
Engine.LobbySetNick(nick);
g_Name = nick;
break;
case "kick": // TODO: Split reason from nick and pass it too, for now just support "/kick nick"
// also allow quoting nicks (and/or prevent users from changing it here, but that doesn't help if the spammer uses a different client)
Engine.LobbyKick(nick, "");
break;
case "ban": // TODO: Split reason from nick and pass it too, for now just support "/ban nick"
Engine.LobbyBan(nick, "");
break;
case "quit":
lobbyStop();
Engine.SwitchGuiPage("page_pregame.xml");
break;
case "say":
case "me":
return false;
default:
addChatMessage({ "from":"system", "text":"We're sorry, the '" + cmd + "' command is not supported."});
}
return true;
}
function addChatMessage(msg)
{
// Highlight local user's nick
if (msg.text.indexOf(g_Name) != -1 && g_Name != msg.from)
msg.text = msg.text.replace(new RegExp('\\b' + '\\' + g_Name + '\\b', "g"), colorPlayerName(g_Name));
// Run spam test
if (updateSpamandDetect(msg.text, msg.from))
return;
// Format Text
var formatted = ircFormat(msg.text, msg.from, msg.color, msg.key);
// If there is text, add it to the chat box.
if (formatted)
{
g_ChatMessages.push(formatted);
- getGUIObjectByName("chatText").caption = g_ChatMessages.join("\n");
+ Engine.GetGUIObjectByName("chatText").caption = g_ChatMessages.join("\n");
}
}
function ircSplit(string)
{
var idx = string.indexOf(' ');
if (idx != -1)
return [string.substr(1,idx-1), string.substr(idx+1)];
return [string.substr(1), ""];
}
// The following formats text in an IRC-like way
function ircFormat(text, from, color, key)
{
// Generate and apply color to uncolored names,
if (!color && from)
var coloredFrom = colorPlayerName(from);
else if (color && from)
var coloredFrom = '[color="' + color + '"]' + from + "[/color]";
// Time for optional time header
var time = new Date(Date.now());
// Build time header if enabled
if (g_timestamp)
var formatted = '[font="serif-bold-13"]\x5B' + twoDigits(time.getHours() % 12) + ":" + twoDigits(time.getMinutes()) + '\x5D[/font] '
else
var formatted = "";
// Handle commands allowed past handleSpecialCommand.
if (text[0] == '/')
{
var [command, message] = ircSplit(text);
switch (command)
{
case "me":
return formatted + '[font="serif-bold-13"]* ' + coloredFrom + '[/font] ' + message;
case "say":
return formatted + '[font="serif-bold-13"]<' + coloredFrom + '>[/font] ' + message;
case "special":
if (key === g_specialKey)
return formatted + '[font="serif-bold-13"] == ' + message + '[/font]';
break;
default:
// This should never happen.
return "";
}
}
return formatted + '[font="serif-bold-13"]<' + coloredFrom + '>[/font] ' + text;
}
// The following function tracks message stats and returns true if the input text is spam.
function updateSpamandDetect(text, from)
{
// Check for blank lines.
if (text == " ")
return true;
// Update the spam monitor.
if (g_spamMonitor[from])
g_spamMonitor[from]++;
else
g_spamMonitor[from] = 1;
if (g_spamMonitor[from] > 5)
g_spammers[from] = true
// Block spammers and notify the player if they are blocked.
if(from in g_spammers)
{
if (from == g_Name)
addChatMessage({ "from": "system", "text": "Please do not spam. You have been blocked for thirty seconds." });
return true;
}
// Return false if everything is clear.
return false;
}
// Timers to clear spammers after some time.
function clearSpamMonitor()
{
g_spamMonitor = {};
setTimeout(clearSpamMonitor, 5000);
}
function clearSpammers()
{
g_spammers = {};
setTimeout(clearSpammers, 30000);
}
/* Utilities */
// Generate a (mostly) unique color for this player based on their name.
// See http://stackoverflow.com/questions/3426404/create-a-hexadecimal-colour-based-on-a-string-with-jquery-javascript
function getPlayerColor(playername)
{
// Generate a probably-unique hash for the player name and use that to create a color.
var hash = 0;
for (var i = 0; i < playername.length; i++)
hash = playername.charCodeAt(i) + ((hash << 5) - hash);
// First create the color in RGB then HSL, clamp the lightness so it's not too dark to read, and then convert back to RGB to display.
// The reason for this roundabout method is this algorithm can generate values from 0 to 255 for RGB but only 0 to 100 for HSL; this gives
// us much more variety if we generate in RGB. Unfortunately, enforcing that RGB values are a certain lightness is very difficult, so
// we convert to HSL to do the computation. Since our GUI code only displays RGB colors, we have to convert back.
var [h, s, l] = rgbToHsl(hash >> 24 & 0xFF, hash >> 16 & 0xFF, hash >> 8 & 0xFF);
return hslToRgb(h, s, Math.max(0.4, l)).join(" ");
}
function repeatString(times, string) {
return Array(times + 1).join(string);
}
// Some names are special and should always appear in certain colors.
var fixedColors = { "system": repeatString(7, "255.0.0."), "WFGbot": repeatString(6, "255.24.24."),
"pyrogenesis": repeatString(2, "97.0.0.") + repeatString(2, "124.0.0.") + "138.0.0." +
repeatString(2, "174.0.0.") + repeatString(2, "229.40.0.") + repeatString(2, "243.125.15.") };
function colorPlayerName(playername)
{
var color = fixedColors[playername];
if (color) {
color = color.split(".");
return ('[color="' + playername.split("").map(function (c, i) color.slice(i * 3, i * 3 + 3).join(" ") + '"]' + c + '[/color][color="')
.join("") + '"]').slice(0, -10);
}
return '[color="' + getPlayerColor(playername) + '"]' + playername + '[/color]';
}
// Ensure `value` is between 0 and 1.
function clampColorValue(value)
{
return Math.abs(1 - Math.abs(value - 1));
}
// See http://stackoverflow.com/questions/2353211/hsl-to-rgb-color-conversion
function rgbToHsl(r, g, b)
{
r /= 255;
g /= 255;
b /= 255;
var max = Math.max(r, g, b), min = Math.min(r, g, b);
var h, s, l = (max + min) / 2;
if (max == min)
h = s = 0; // achromatic
else
{
var d = max - min;
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
switch (max)
{
case r: h = (g - b) / d + (g < b ? 6 : 0); break;
case g: h = (b - r) / d + 2; break;
case b: h = (r - g) / d + 4; break;
}
h /= 6;
}
return [h, s, l];
}
function hslToRgb(h, s, l)
{
function hue2rgb(p, q, t)
{
if (t < 0) t += 1;
if (t > 1) t -= 1;
if (t < 1/6) return p + (q - p) * 6 * t;
if (t < 1/2) return q;
if (t < 2/3) return p + (q - p) * (2/3 - t) * 6;
return p;
}
[h, s, l] = [h, s, l].map(clampColorValue);
var r, g, b;
if (s == 0)
r = g = b = l; // achromatic
else {
var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
var p = 2 * l - q;
r = hue2rgb(p, q, h + 1/3);
g = hue2rgb(p, q, h);
b = hue2rgb(p, q, h - 1/3);
}
return [r, g, b].map(function (n) Math.round(n * 255));
}
(function () {
function hexToRgb(hex) {
return parseInt(hex.slice(0, 2), 16) + "." + parseInt(hex.slice(2, 4), 16) + "." + parseInt(hex.slice(4, 6), 16) + ".";
}
function r(times, hex) {
return repeatString(times, hexToRgb(hex));
}
fixedColors["Twilight_Sparkle"] = r(2, "d19fe3") + r(2, "b689c8") + r(2, "a76bc2") +
r(4, "263773") + r(2, "131f46") + r(2, "662d8a") + r(2, "ed438a");
fixedColors["Applejack"] = r(3, "ffc261") + r(3, "efb05d") + r(3, "f26f31");
fixedColors["Rarity"] = r(1, "ebeff1") + r(1, "dee3e4") + r(1, "bec2c3") +
r(1, "83509f") + r(1, "4b2568") + r(1, "4917d6");
fixedColors["Rainbow_Dash"] = r(2, "ee4144") + r(1, "f37033") + r(1, "fdf6af") +
r(1, "62bc4d") + r(1, "1e98d3") + r(2, "672f89") + r(1, "9edbf9") +
r(1, "88c4eb") + r(1, "77b0e0") + r(1, "1e98d3");
fixedColors["Pinkie_Pie"] = r(2, "f3b6cf") + r(2, "ec9dc4") + r(4, "eb81b4") +
r(1, "ed458b") + r(1, "be1d77");
fixedColors["Fluttershy"] = r(2, "fdf6af") + r(2, "fee78f") + r(2, "ead463") +
r(2, "f3b6cf") + r(2, "eb81b4");
fixedColors["Sweetie_Belle"] = r(2, "efedee") + r(3, "e2dee3") + r(3, "cfc8d1") +
r(2, "b28dc0") + r(2, "f6b8d2") + r(1, "795b8a");
fixedColors["Apple_Bloom"] = r(2, "f4f49b") + r(2, "e7e793") + r(2, "dac582") +
r(2, "f46091") + r(2, "f8415f") + r(1, "c52451");
fixedColors["Scootaloo"] = r(2, "fbba64") + r(2, "f2ab56") + r(2, "f37003") +
r(2, "bf5d95") + r(1, "bf1f79");
fixedColors["Luna"] = r(1, "7ca7fa") + r(1, "5d6fc1") + r(1, "656cb9") + r(1, "393993");
fixedColors["Celestia"] = r(1, "fdfafc") + r(1, "f7eaf2") + r(1, "d99ec5") +
r(1, "00aec5") + r(1, "f7c6dc") + r(1, "98d9ef") + r(1, "ced7ed") + r(1, "fed17b");
})();
Index: ps/trunk/binaries/data/mods/public/gui/lobby/lobby.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/lobby/lobby.xml (revision 14495)
+++ ps/trunk/binaries/data/mods/public/gui/lobby/lobby.xml (revision 14496)
@@ -1,185 +1,185 @@
Multiplayer Lobby
onTick();
Leaderboard
- getGUIObjectByName("leaderboard").hidden = false;getGUIObjectByName("leaderboardFade").hidden = false;
+ Engine.GetGUIObjectByName("leaderboard").hidden = false;Engine.GetGUIObjectByName("leaderboardFade").hidden = false;Map Type:Map Size:Players:
Join Game
joinSelectedGame();
Host Game
// Open Multiplayer connection window with host option.
Engine.PushGuiPage("page_gamesetup_mp.xml", { multiplayerGameType: "host", name: g_Name });
Main Menu
lobbyStop();
Engine.SwitchGuiPage("page_pregame.xml");
selectGame(this.selected);applyFilters();applyFilters();applyFilters();Show full gamesapplyFilters();submitChatInput();completeNick();Leaderboard
Back
- getGUIObjectByName("leaderboard").hidden = true;getGUIObjectByName("leaderboardFade").hidden = true;
+ Engine.GetGUIObjectByName("leaderboard").hidden = true;Engine.GetGUIObjectByName("leaderboardFade").hidden = true;
Update
Engine.SendGetBoardList();
Index: ps/trunk/binaries/data/mods/public/gui/lobby/prelobby.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/lobby/prelobby.js (revision 14495)
+++ ps/trunk/binaries/data/mods/public/gui/lobby/prelobby.js (revision 14496)
@@ -1,185 +1,185 @@
var g_LobbyIsConnecting = false;
var g_EncrytedPassword = "";
var g_PasswordInputIsHidden = false;
function init()
{
g_EncrytedPassword = Engine.ConfigDB_GetValue("user", "lobby.password");
}
function lobbyStop()
{
- getGUIObjectByName("feedback").caption = "";
+ Engine.GetGUIObjectByName("feedback").caption = "";
if (g_LobbyIsConnecting == false)
return;
g_LobbyIsConnecting = false;
Engine.StopXmppClient();
}
function lobbyStart()
{
if (g_LobbyIsConnecting)
return;
if (Engine.HasXmppClient())
Engine.StopXmppClient();
- var username = getGUIObjectByName("connectUsername").caption;
- var password = getGUIObjectByName("connectPassword").caption;
- var feedback = getGUIObjectByName("feedback");
+ var username = Engine.GetGUIObjectByName("connectUsername").caption;
+ var password = Engine.GetGUIObjectByName("connectPassword").caption;
+ var feedback = Engine.GetGUIObjectByName("feedback");
var room = Engine.ConfigDB_GetValue("user", "lobby.room");
feedback.caption = "Connecting....";
// If they enter a different password, re-encrypt.
if (password != g_EncrytedPassword.substring(0, 10))
g_EncrytedPassword = Engine.EncryptPassword(password, username);
// We just use username as nick for simplicity.
Engine.StartXmppClient(username, g_EncrytedPassword, room, username);
g_LobbyIsConnecting = true;
Engine.ConnectXmppClient();
}
function lobbyStartRegister()
{
if (g_LobbyIsConnecting != false)
return;
if (Engine.HasXmppClient())
Engine.StopXmppClient();
- var account = getGUIObjectByName("connectUsername").caption;
- var password = getGUIObjectByName("connectPassword").caption;
- var passwordAgain = getGUIObjectByName("registerPasswordAgain").caption;
- var feedback = getGUIObjectByName("feedback");
+ var account = Engine.GetGUIObjectByName("connectUsername").caption;
+ var password = Engine.GetGUIObjectByName("connectPassword").caption;
+ var passwordAgain = Engine.GetGUIObjectByName("registerPasswordAgain").caption;
+ var feedback = Engine.GetGUIObjectByName("feedback");
// Check the passwords match.
if (password != passwordAgain)
{
feedback.caption = "Passwords do not match";
- getGUIObjectByName("connectPassword").caption = "";
- getGUIObjectByName("registerPasswordAgain").caption = "";
+ Engine.GetGUIObjectByName("connectPassword").caption = "";
+ Engine.GetGUIObjectByName("registerPasswordAgain").caption = "";
switchRegister();
return;
}
feedback.caption = "Registering...";
if (password != g_EncrytedPassword.substring(0, 10))
g_EncrytedPassword = Engine.EncryptPassword(password, account);
Engine.StartRegisterXmppClient(account, g_EncrytedPassword);
g_LobbyIsConnecting = true;
Engine.ConnectXmppClient();
}
function switchRegister()
{
- if (getGUIObjectByName("pageRegister").hidden)
+ if (Engine.GetGUIObjectByName("pageRegister").hidden)
{
lobbyStop();
- getGUIObjectByName("pageRegister").hidden = false;
- getGUIObjectByName("pageConnect").hidden = true;
- getGUIObjectByName("connect").enabled = false;
+ Engine.GetGUIObjectByName("pageRegister").hidden = false;
+ Engine.GetGUIObjectByName("pageConnect").hidden = true;
+ Engine.GetGUIObjectByName("connect").enabled = false;
}
else
{
- getGUIObjectByName("pageRegister").hidden = true;
- getGUIObjectByName("pageConnect").hidden = false;
- getGUIObjectByName("connect").enabled = true;
+ Engine.GetGUIObjectByName("pageRegister").hidden = true;
+ Engine.GetGUIObjectByName("pageConnect").hidden = false;
+ Engine.GetGUIObjectByName("connect").enabled = true;
}
}
function onTick()
{
//
- var username = getGUIObjectByName("connectUsername").caption;
- var password = getGUIObjectByName("connectPassword").caption;
- var passwordAgain = getGUIObjectByName("registerPasswordAgain").caption;
- var feedback = getGUIObjectByName("feedback");
- var pageRegisterHidden = getGUIObjectByName("pageRegister").hidden;
- var connectButton = getGUIObjectByName("connect");
- var registerButton = getGUIObjectByName("register");
+ var username = Engine.GetGUIObjectByName("connectUsername").caption;
+ var password = Engine.GetGUIObjectByName("connectPassword").caption;
+ var passwordAgain = Engine.GetGUIObjectByName("registerPasswordAgain").caption;
+ var feedback = Engine.GetGUIObjectByName("feedback");
+ var pageRegisterHidden = Engine.GetGUIObjectByName("pageRegister").hidden;
+ var connectButton = Engine.GetGUIObjectByName("connect");
+ var registerButton = Engine.GetGUIObjectByName("register");
var sanitizedName = sanitizePlayerName(username, true, true)
// If there aren't a username and password entered, we can't start registration or connection.
if (!username || !password)
{
connectButton.enabled = false;
registerButton.enabled = false;
if (!username && !password)
feedback.caption = "Please enter existing login or desired registration credentials.";
}
// Check they are using a valid account name.
else if (username != sanitizedName)
{
feedback.caption = "Sorry, you can't use [, ], unicode, whitespace, or commas.";
connectButton.enabled = false;
registerButton.enabled = false;
}
// Allow them to connect/begin registation if there aren't any problems.
else if (pageRegisterHidden)
{
if (feedback.caption == "Sorry, you can't use [, ], unicode, whitespace, or commas." ||
feedback.caption == "Please enter existing login or desired registration credentials.")
feedback.caption = "";
connectButton.enabled = true;
registerButton.enabled = true;
}
// If the password hasn't been entered again, we can't complete registation.
if (!pageRegisterHidden && !passwordAgain)
registerButton.enabled = false;
else if (!pageRegisterHidden)
registerButton.enabled = true;
if (!g_LobbyIsConnecting)
// The Xmpp Client has not been created
return;
// The XmppClient has been created, we are waiting
// to be connected or to receive an error.
//Wake up XmppClient
Engine.RecvXmppClient();
//Receive messages
while (true)
{
var message = Engine.LobbyGuiPollMessage();
if (!message)
break;
if (message.type == "muc" && message.level == "join")
{
// We are connected, switch to the lobby page
Engine.PopGuiPage();
// Use username as nick.
var nick = sanitizePlayerName(username, true, true);
// Switch to lobby
Engine.SwitchGuiPage("page_lobby.xml");
// Store nick, login, and password
Engine.ConfigDB_CreateValue("user", "playername", nick);
Engine.ConfigDB_CreateValue("user", "lobby.login", username);
// We only store the encrypted password, so make sure to re-encrypt it if changed before saving.
if (password != g_EncrytedPassword.substring(0, 10))
g_EncrytedPassword = Engine.EncryptPassword(password, username);
Engine.ConfigDB_CreateValue("user", "lobby.password", g_EncrytedPassword);
Engine.ConfigDB_WriteFile("user", "config/user.cfg");
return;
}
else if (message.type == "system" && message.text == "registered")
{
// Great, we are registered. Switch to the connection window.
feedback.caption = toTitleCase(message.text);
Engine.StopXmppClient();
g_LobbyIsConnecting = false;
switchRegister();
}
else if(message.type == "system" && (message.level == "error" || message.text == "disconnected"))
{
feedback.caption = toTitleCase(message.text);
Engine.StopXmppClient();
g_LobbyIsConnecting = false;
}
}
}
Index: ps/trunk/binaries/data/mods/public/gui/lobby/prelobby.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/lobby/prelobby.xml (revision 14495)
+++ ps/trunk/binaries/data/mods/public/gui/lobby/prelobby.xml (revision 14496)
@@ -1,98 +1,98 @@
onTick();
[Connection status]
Multiplayer Lobby
Connect to the game lobby
Login:
this.caption = Engine.ConfigDB_GetValue("user", "lobby.login");
Password:
// We only show 10 characters to make it look decent.
this.caption = Engine.ConfigDB_GetValue("user", "lobby.password").substring(0, 10);
lobbyStart();
Registration
Password again:
lobbyStartRegister()
Cancel
- if (getGUIObjectByName("pageRegister").hidden)
+ if (Engine.GetGUIObjectByName("pageRegister").hidden)
{
lobbyStop();
Engine.PopGuiPage();
}
else
switchRegister();
Register
- if (getGUIObjectByName("pageRegister").hidden)
+ if (Engine.GetGUIObjectByName("pageRegister").hidden)
{
switchRegister();
}
else
lobbyStartRegister()
Connect
lobbyStart();
Index: ps/trunk/binaries/data/mods/public/gui/manual/manual.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/manual/manual.js (revision 14495)
+++ ps/trunk/binaries/data/mods/public/gui/manual/manual.js (revision 14496)
@@ -1,14 +1,14 @@
var closeCallback;
function init(data)
{
- getGUIObjectByName("mainText").caption = readFile("gui/manual/" + data.page + ".txt");
+ Engine.GetGUIObjectByName("mainText").caption = Engine.ReadFile("gui/manual/" + data.page + ".txt");
closeCallback = data.closeCallback;
}
function closeManual()
{
Engine.PopGuiPage();
if (closeCallback)
closeCallback();
}
Index: ps/trunk/binaries/data/mods/public/gui/msgbox/msgbox.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/msgbox/msgbox.xml (revision 14495)
+++ ps/trunk/binaries/data/mods/public/gui/msgbox/msgbox.xml (revision 14496)
@@ -1,132 +1,150 @@
Engine.PopGuiPage();
Index: ps/trunk/binaries/data/mods/public/gui/options/options.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/options/options.xml (revision 14495)
+++ ps/trunk/binaries/data/mods/public/gui/options/options.xml (revision 14496)
@@ -1,104 +1,104 @@
Game OptionsSystem SettingsWindowed ModeEngine.ConfigDB_GetValue("user", "windowed") === "true" ? this.checked = true : this.checked = false;Engine.ConfigDB_CreateValue("user", "windowed", String(this.checked));Background PauseEngine.ConfigDB_GetValue("user", "pauseonfocusloss") === "true" ? this.checked = true : this.checked = false;Engine.ConfigDB_CreateValue("user", "pauseonfocusloss", String(this.checked));Graphics SettingsEnable Shadowsthis.checked = Engine.Renderer_GetShadowsEnabled();Engine.Renderer_SetShadowsEnabled(this.checked);Enable Shadow Filteringthis.checked = Engine.Renderer_GetShadowPCFEnabled();Engine.Renderer_SetShadowPCFEnabled(this.checked);Sound SettingsMaster Gainthis.caption = Engine.ConfigDB_GetValue("user", "sound.mastergain");Save
- Engine.ConfigDB_CreateValue("user", "sound.mastergain", String(getGUIObjectByName("SMasterCFG").caption));
+ Engine.ConfigDB_CreateValue("user", "sound.mastergain", String(Engine.GetGUIObjectByName("SMasterCFG").caption));Music Gainthis.caption = Engine.ConfigDB_GetValue("user", "sound.musicgain");Save
- Engine.ConfigDB_CreateValue("user", "sound.musicgain", String(getGUIObjectByName("SMusicCFG").caption));
+ Engine.ConfigDB_CreateValue("user", "sound.musicgain", String(Engine.GetGUIObjectByName("SMusicCFG").caption));
Cancel
Engine.PopGuiPage();
Save
Engine.ConfigDB_WriteFile("user", "config/user.cfg");Engine.PopGuiPage();
Index: ps/trunk/binaries/data/mods/public/gui/pregame/mainmenu.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/pregame/mainmenu.js (revision 14495)
+++ ps/trunk/binaries/data/mods/public/gui/pregame/mainmenu.js (revision 14496)
@@ -1,321 +1,332 @@
var userReportEnabledText; // contains the original version with "$status" placeholder
var currentSubmenuType; // contains submenu type
const MARGIN = 4; // menu border size
const background = "hellenes1"; // Background type. Currently: 'hellenes1', 'persians1'.
var g_ShowSplashScreens;
function init(initData, hotloadData)
{
initMusic();
// Play main menu music
global.music.setState(global.music.states.MENU);
- userReportEnabledText = getGUIObjectByName("userReportEnabledText").caption;
+ userReportEnabledText = Engine.GetGUIObjectByName("userReportEnabledText").caption;
// initialize currentSubmenuType with placeholder to avoid null when switching
currentSubmenuType = "submenuSinglePlayer";
EnableUserReport(Engine.IsUserReportEnabled());
// Only show splash screen(s) once at startup, but not again after hotloading
g_ShowSplashScreens = hotloadData ? hotloadData.showSplashScreens : initData && initData.isStartup;
}
function getHotloadData()
{
return { "showSplashScreens": g_ShowSplashScreens };
}
var t0 = new Date;
function scrollBackgrounds(background)
{
if (background == "hellenes1")
{
- var layer1 = getGUIObjectByName("backgroundHele1-1");
- var layer2 = getGUIObjectByName("backgroundHele1-2");
- var layer3 = getGUIObjectByName("backgroundHele1-3");
+ var layer1 = Engine.GetGUIObjectByName("backgroundHele1-1");
+ var layer2 = Engine.GetGUIObjectByName("backgroundHele1-2");
+ var layer3 = Engine.GetGUIObjectByName("backgroundHele1-3");
layer1.hidden = false;
layer2.hidden = false;
layer3.hidden = false;
var screen = layer1.parent.getComputedSize();
var h = screen.bottom - screen.top; // height of screen
var w = h*16/9; // width of background image
// Offset the layers by oscillating amounts
var t = (t0 - new Date) / 700;
var speed = 1/20;
var off1 = 0.02 * w * (1+Math.cos(t*speed));
var off2 = 0.12 * w * (1+Math.cos(t*speed)) - h*6/9;
var off3 = 0.16 * w * (1+Math.cos(t*speed));
var left = screen.right - w * (1 + Math.ceil(screen.right / w));
layer1.size = new GUISize(left + off1, screen.top, screen.right + off1, screen.bottom);
layer2.size = new GUISize(screen.right/2 - h + off2, screen.top, screen.right/2 + h + off2, screen.bottom);
layer3.size = new GUISize(screen.right - h + off3, screen.top, screen.right + off3, screen.bottom);
}
if (background == "persians1")
{
- var layer1 = getGUIObjectByName("backgroundPers1-1");
- var layer2 = getGUIObjectByName("backgroundPers1-2");
- var layer3 = getGUIObjectByName("backgroundPers1-3");
- var layer4 = getGUIObjectByName("backgroundPers1-4");
+ var layer1 = Engine.GetGUIObjectByName("backgroundPers1-1");
+ var layer2 = Engine.GetGUIObjectByName("backgroundPers1-2");
+ var layer3 = Engine.GetGUIObjectByName("backgroundPers1-3");
+ var layer4 = Engine.GetGUIObjectByName("backgroundPers1-4");
layer1.hidden = false;
layer2.hidden = false;
layer3.hidden = false;
layer4.hidden = false;
var screen = layer1.parent.getComputedSize();
var h = screen.bottom - screen.top; // height of screen
var screenWidth = screen.right - screen.left;
var w = h*16/9;
var t = (t0 - new Date) / 1000;
var speed = 1/20;
var off1 = 0.01 * w * (Math.cos(t*speed));
var off2 = 0.03 * w * (Math.cos(t*speed));
var off3 = 0.07 * w * (1+Math.cos(t*speed)) + 0.5 * screenWidth - h*1.1;
var off4 = 0.16 * w * (1+Math.cos(t*speed)) - h*6/9;
var left = screen.right - w * (1 + Math.ceil(screen.right / w)) - 0.5 * screenWidth + h;
layer1.size = new GUISize(left + off1, screen.top, screen.right + off1 + h, screen.bottom);
layer2.size = new GUISize(left + off2, screen.top, screen.right + off2 + h, screen.bottom);
layer3.size = new GUISize(screen.left + off3, screen.top, screen.left + 2 * h + off3, screen.bottom);
layer4.size = new GUISize(screen.left + off4, screen.top, screen.left + 2 * h + off4, screen.bottom);
}
}
function submitUserReportMessage()
{
- var input = getGUIObjectByName("userReportMessageInput");
+ var input = Engine.GetGUIObjectByName("userReportMessageInput");
var msg = input.caption;
if (msg.length)
Engine.SubmitUserReport("message", 1, msg);
input.caption = "";
}
function formatUserReportStatus(status)
{
var d = status.split(/:/, 3);
if (d[0] == "disabled")
return "disabled";
if (d[0] == "connecting")
return "connecting to server";
if (d[0] == "sending")
{
var done = d[1];
return "uploading (" + Math.floor(100*done) + "%)";
}
if (d[0] == "completed")
{
var httpCode = d[1];
if (httpCode == 200)
return "upload succeeded";
else
return "upload failed (" + httpCode + ")";
}
if (d[0] == "failed")
{
var errCode = d[1];
var errMessage = d[2];
return "upload failed (" + errMessage + ")";
}
return "unknown";
}
var lastTickTime = new Date;
function onTick()
{
var now = new Date;
var tickLength = new Date - lastTickTime;
lastTickTime = now;
// Animate backgrounds
scrollBackgrounds(background);
// Animate submenu
updateMenuPosition(tickLength);
if (Engine.IsUserReportEnabled())
{
- getGUIObjectByName("userReportEnabledText").caption =
+ Engine.GetGUIObjectByName("userReportEnabledText").caption =
userReportEnabledText.replace(/\$status/,
formatUserReportStatus(Engine.GetUserReportStatus()));
}
// Show splash screens here, so we don't interfere with main menu hotloading
if (g_ShowSplashScreens)
{
g_ShowSplashScreens = false;
if (Engine.ConfigDB_GetValue("user", "splashscreenenable") !== "false")
- Engine.PushGuiPage("page_splashscreen.xml", { "page": "splashscreen" } );
-
- // Warn about removing fixed render path
- if (Engine.Renderer_GetRenderPath() == "fixed")
- messageBox(
- 600,
- 300,
- "[font=\"serif-bold-16\"][color=\"200 20 20\"]Warning:[/color] You appear to be using non-shader (fixed function) graphics. This option will be removed in a future 0 A.D. release, to allow for more advanced graphics features. We advise upgrading your graphics card to a more recent, shader-compatible model.\n\nPlease press \"Read More\" for more information or \"Ok\" to continue.",
- "WARNING!",
- 0,
- ["Ok", "Read More"],
- [null, function() { Engine.OpenURL("http://www.wildfiregames.com/forum/index.php?showtopic=16734"); }]
- );
+ Engine.PushGuiPage("page_splashscreen.xml", { "page": "splashscreen", callback : "SplashScreenClosedCallback" } );
+ else
+ ShowRenderPathMessage();
+
}
}
+function ShowRenderPathMessage()
+{
+ // Warn about removing fixed render path
+ if (Engine.Renderer_GetRenderPath() == "fixed")
+ messageBox(
+ 600,
+ 300,
+ "[font=\"serif-bold-16\"][color=\"200 20 20\"]Warning:[/color] You appear to be using non-shader (fixed function) graphics. This option will be removed in a future 0 A.D. release, to allow for more advanced graphics features. We advise upgrading your graphics card to a more recent, shader-compatible model.\n\nPlease press \"Read More\" for more information or \"Ok\" to continue.",
+ "WARNING!",
+ 0,
+ ["Ok", "Read More"],
+ [ null, function() { Engine.OpenURL("http://www.wildfiregames.com/forum/index.php?showtopic=16734"); } ]
+ );
+}
+
+function SplashScreenClosedCallback()
+{
+ ShowRenderPathMessage();
+}
+
function EnableUserReport(Enabled)
{
- getGUIObjectByName("userReportDisabled").hidden = Enabled;
- getGUIObjectByName("userReportEnabled").hidden = !Enabled;
+ Engine.GetGUIObjectByName("userReportDisabled").hidden = Enabled;
+ Engine.GetGUIObjectByName("userReportEnabled").hidden = !Enabled;
Engine.SetUserReportEnabled(Enabled);
}
/*
* MENU FUNCTIONS
*/
// Temporarily adding this here
//const BUTTON_SOUND = "audio/interface/ui/ui_button_longclick.ogg";
//function playButtonSound()
//{
// var buttonSound = new Sound(BUTTON_SOUND);
// buttonSound.play();
//}
// Slide menu
function updateMenuPosition(dt)
{
- var submenu = getGUIObjectByName("submenu");
+ var submenu = Engine.GetGUIObjectByName("submenu");
if (submenu.hidden == false)
{
// Number of pixels per millisecond to move
const SPEED = 1.2;
- var maxOffset = getGUIObjectByName("mainMenu").size.right - submenu.size.left;
+ var maxOffset = Engine.GetGUIObjectByName("mainMenu").size.right - submenu.size.left;
if (maxOffset > 0)
{
var offset = Math.min(SPEED * dt, maxOffset);
var size = submenu.size;
size.left += offset;
size.right += offset;
submenu.size = size;
}
}
}
// Opens the menu by revealing the screen which contains the menu
function openMenu(newSubmenu, position, buttonHeight, numButtons)
{
// switch to new submenu type
currentSubmenuType = newSubmenu;
- getGUIObjectByName(currentSubmenuType).hidden = false;
+ Engine.GetGUIObjectByName(currentSubmenuType).hidden = false;
// set position of new submenu
- var submenu = getGUIObjectByName("submenu");
+ var submenu = Engine.GetGUIObjectByName("submenu");
var top = position - MARGIN;
var bottom = position + ((buttonHeight + MARGIN) * numButtons);
submenu.size = submenu.size.left + " " + top + " " + submenu.size.right + " " + bottom;
// Blend in right border of main menu into the left border of the submenu
blendSubmenuIntoMain(top, bottom);
// Reveal submenu
- getGUIObjectByName("submenu").hidden = false;
+ Engine.GetGUIObjectByName("submenu").hidden = false;
}
// Closes the menu and resets position
function closeMenu()
{
// playButtonSound();
// remove old submenu type
- getGUIObjectByName(currentSubmenuType).hidden = true;
+ Engine.GetGUIObjectByName(currentSubmenuType).hidden = true;
// hide submenu and reset position
- var submenu = getGUIObjectByName("submenu");
+ var submenu = Engine.GetGUIObjectByName("submenu");
submenu.hidden = true;
- submenu.size = getGUIObjectByName("mainMenu").size;
+ submenu.size = Engine.GetGUIObjectByName("mainMenu").size;
// reset main menu panel right border
- getGUIObjectByName("MainMenuPanelRightBorderTop").size = "100%-2 0 100% 100%";
+ Engine.GetGUIObjectByName("MainMenuPanelRightBorderTop").size = "100%-2 0 100% 100%";
}
// Sizes right border on main menu panel to match the submenu
function blendSubmenuIntoMain(topPosition, bottomPosition)
{
- var topSprite = getGUIObjectByName("MainMenuPanelRightBorderTop");
+ var topSprite = Engine.GetGUIObjectByName("MainMenuPanelRightBorderTop");
topSprite.size = "100%-2 0 100% " + (topPosition + MARGIN);
- var bottomSprite = getGUIObjectByName("MainMenuPanelRightBorderBottom");
+ var bottomSprite = Engine.GetGUIObjectByName("MainMenuPanelRightBorderBottom");
bottomSprite.size = "100%-2 " + (bottomPosition) + " 100% 100%";
}
/*
* FUNCTIONS BELOW DO NOT WORK YET
*/
//// Switch to a given options tab window.
//function openOptionsTab(tabName)
//{
// // Hide the other tabs.
// for (var i = 1; i <= 3; i++)
// {
// switch (i)
// {
// case 1:
// var tmpName = "pgOptionsAudio";
// break;
// case 2:
// var tmpName = "pgOptionsVideo";
// break;
// case 3:
// var tmpName = "pgOptionsGame";
// break;
// default:
// break;
// }
//
// if (tmpName != tabName)
// {
-// getGUIObjectByName (tmpName + "Window").hidden = true;
-// getGUIObjectByName (tmpName + "Button").enabled = true;
+// Engine.GetGUIObjectByName (tmpName + "Window").hidden = true;
+// Engine.GetGUIObjectByName (tmpName + "Button").enabled = true;
// }
// }
//
// // Make given tab visible.
-// getGUIObjectByName (tabName + "Window").hidden = false;
-// getGUIObjectByName (tabName + "Button").enabled = false;
+// Engine.GetGUIObjectByName (tabName + "Window").hidden = false;
+// Engine.GetGUIObjectByName (tabName + "Button").enabled = false;
//}
//
//// Move the credits up the screen.
//function updateCredits()
//{
// // If there are still credit lines to remove, remove them.
// if (getNumItems("pgCredits") > 0)
// removeItem ("pgCredits", 0);
// else
// {
// // When we've run out of credit,
//
// // Stop the increment timer if it's still active.
// cancelInterval();
//
// // Close the credits screen and return.
// closeMainMenuSubWindow ("pgCredits");
// guiUnHide ("pg");
// }
//}
Index: ps/trunk/binaries/data/mods/public/gui/pregame/mainmenu.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/pregame/mainmenu.xml (revision 14495)
+++ ps/trunk/binaries/data/mods/public/gui/pregame/mainmenu.xml (revision 14496)
@@ -1,558 +1,558 @@
onTick();
tooltipText
[font="serif-bold-16"]Help improve 0 A.D.![/font]
You can automatically send us anonymous feedback that will help us fix bugs, and improve performance and compatibility.
Enable feedback
EnableUserReport(true);
Technical details
Engine.PushGuiPage("page_manual.xml", { "page": "userreport" });[font="serif-bold-16"]Thank you for helping improve 0 A.D.![/font]
Anonymous feedback is currently enabled.
Status: $status.
Disable feedback
EnableUserReport(false);
Technical details
Engine.PushGuiPage("page_manual.xml", { "page": "userreport" });
closeMenu();
Matches
Engine.SwitchGuiPage("page_gamesetup.xml", { type: "offline" });
Campaigns
closeMenu();
Load Game
closeMenu();
Engine.PushGuiPage("page_loadgame.xml", { type: "offline" });
Join Game
closeMenu();
// Open Multiplayer connection window with join option.
Engine.PushGuiPage("page_gamesetup_mp.xml", { multiplayerGameType: "join" });
Host Game
closeMenu();
// Open Multiplayer connection window with host option.
Engine.PushGuiPage("page_gamesetup_mp.xml", { multiplayerGameType: "host" });
Game Lobby
closeMenu();
// Open Multiplayer game lobby.
Engine.PushGuiPage("page_prelobby.xml", []);
if (!Engine.StartXmppClient)
{
this.enabled = false;
this.tooltip = "Launch the multiplayer lobby. [DISABLED BY BUILD]";
}
Options
closeMenu();
Scenario Editor
closeMenu();
Learn To Play
closeMenu();
Single Player
closeMenu();
openMenu("submenuSinglePlayer", (this.parent.size.top+this.size.top), (this.size.bottom-this.size.top), 3);
Multiplayer
closeMenu();
openMenu("submenuMultiplayer", (this.parent.size.top+this.size.top), (this.size.bottom-this.size.top), 3);
Tools Options
closeMenu();
openMenu("submenuToolsAndOptions", (this.parent.size.top+this.size.top), (this.size.bottom-this.size.top), 2);
History
closeMenu();
Exit
closeMenu();
[font="serif-bold-16"]Alpha XV: Osiris[/font]
WARNING: This is an early development version of the game. Many features have not been added yet.
Get involved at: play0ad.com
Website
Chat
Report a Bug
WILDFIRE GAMES
Index: ps/trunk/binaries/data/mods/public/gui/savedgames/load.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/savedgames/load.js (revision 14495)
+++ ps/trunk/binaries/data/mods/public/gui/savedgames/load.js (revision 14496)
@@ -1,68 +1,68 @@
function init()
{
- var gameSelection = getGUIObjectByName("gameSelection");
+ var gameSelection = Engine.GetGUIObjectByName("gameSelection");
var savedGames = Engine.GetSavedGames();
if (savedGames.length == 0)
{
gameSelection.list = [ "No saved games found" ];
gameSelection.selected = 0;
- getGUIObjectByName("loadGameButton").enabled = false;
- getGUIObjectByName("deleteGameButton").enabled = false;
+ Engine.GetGUIObjectByName("loadGameButton").enabled = false;
+ Engine.GetGUIObjectByName("deleteGameButton").enabled = false;
return;
}
savedGames.sort(sortDecreasingDate);
var gameListIDs = [ game.id for each (game in savedGames) ];
var gameListLabels = [ generateLabel(game.metadata) for each (game in savedGames) ];
gameSelection.list = gameListLabels;
gameSelection.list_data = gameListIDs;
gameSelection.selected = 0;
}
function loadGame()
{
- var gameSelection = getGUIObjectByName("gameSelection");
+ var gameSelection = Engine.GetGUIObjectByName("gameSelection");
var gameID = gameSelection.list_data[gameSelection.selected];
var metadata = Engine.StartSavedGame(gameID);
if (!metadata)
{
// Probably the file wasn't found
// Show error and refresh saved game list
error("Could not load saved game '"+gameID+"'");
init();
}
else
{
Engine.SwitchGuiPage("page_loading.xml", {
"attribs": metadata.initAttributes,
"isNetworked" : false,
"playerAssignments": metadata.gui.playerAssignments,
"savedGUIData": metadata.gui
});
}
}
function deleteGame()
{
- var gameSelection = getGUIObjectByName("gameSelection");
+ var gameSelection = Engine.GetGUIObjectByName("gameSelection");
var gameLabel = gameSelection.list[gameSelection.selected];
var gameID = gameSelection.list_data[gameSelection.selected];
// Ask for confirmation
var btCaptions = ["Yes", "No"];
var btCode = [function(){ reallyDeleteGame(gameID); }, null];
messageBox(500, 200, "\""+gameLabel+"\"\nSaved game will be permanently deleted, are you sure?", "DELETE", 0, btCaptions, btCode);
}
function reallyDeleteGame(gameID)
{
if (!Engine.DeleteSavedGame(gameID))
error("Could not delete saved game '"+gameID+"'");
// Run init again to refresh saved game list
init();
}
Index: ps/trunk/binaries/data/mods/public/gui/savedgames/save.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/savedgames/save.js (revision 14495)
+++ ps/trunk/binaries/data/mods/public/gui/savedgames/save.js (revision 14496)
@@ -1,112 +1,108 @@
var g_Descriptions;
-var closeCallback;
-var gameDataCallback;
+var savedGameData;
function selectDescription()
{
- var gameSelection = getGUIObjectByName("gameSelection");
+ var gameSelection = Engine.GetGUIObjectByName("gameSelection");
if (gameSelection.selected != -1)
{
- getGUIObjectByName("deleteGameButton").enabled = true;
+ Engine.GetGUIObjectByName("deleteGameButton").enabled = true;
var gameID = gameSelection.list_data[gameSelection.selected];
- getGUIObjectByName("saveGameDesc").caption = g_Descriptions[gameID];
+ Engine.GetGUIObjectByName("saveGameDesc").caption = g_Descriptions[gameID];
}
}
function init(data)
{
if (data)
{
- if (data.closeCallback)
- closeCallback = data.closeCallback;
- if (data.gameDataCallback)
- gameDataCallback = data.gameDataCallback;
+ if (data.savedGameData)
+ savedGameData = data.savedGameData;
}
- var gameSelection = getGUIObjectByName("gameSelection");
- getGUIObjectByName("deleteGameButton").enabled = false;
+ var gameSelection = Engine.GetGUIObjectByName("gameSelection");
+ Engine.GetGUIObjectByName("deleteGameButton").enabled = false;
var savedGames = Engine.GetSavedGames();
if (savedGames.length == 0)
{
gameSelection.list = [ "No saved games found" ];
gameSelection.selected = -1;
return;
}
savedGames.sort(sortDecreasingDate);
var gameListIDs = [ game.id for each (game in savedGames) ];
var gameListLabels = [ generateLabel(game.metadata) for each (game in savedGames) ];
g_Descriptions = {};
[ g_Descriptions[game.id] = (game.metadata.description ? game.metadata.description : "") for each (game in savedGames) ];
gameSelection.list = gameListLabels;
gameSelection.list_data = gameListIDs;
gameSelection.selected = -1;
}
function saveGame()
{
- var gameSelection = getGUIObjectByName("gameSelection");
+ var gameSelection = Engine.GetGUIObjectByName("gameSelection");
var gameLabel = gameSelection.list[gameSelection.selected];
var gameID = gameSelection.list_data[gameSelection.selected];
- var desc = getGUIObjectByName("saveGameDesc").caption;
+ var desc = Engine.GetGUIObjectByName("saveGameDesc").caption;
var name = gameID ? gameID : "savegame";
if (gameSelection.selected != -1)
{
// Ask for confirmation
var btCaptions = ["Yes", "No"];
var btCode = [function(){ reallySaveGame(name, desc, false); }, null];
messageBox(500, 200, "\""+gameLabel+"\"\nSaved game will be permanently overwritten, are you sure?", "OVERWRITE SAVE", 0, btCaptions, btCode);
}
else
reallySaveGame(name, desc, true);
}
function reallySaveGame(name, desc, nameIsPrefix)
{
if (nameIsPrefix)
- Engine.SaveGamePrefix(name, desc);
+ Engine.SaveGamePrefix(name, desc, savedGameData);
else
- Engine.SaveGame(name, desc);
+ Engine.SaveGame(name, desc, savedGameData);
closeSave();
}
function closeSave()
{
- Engine.PopGuiPage();
- if (closeCallback)
- closeCallback();
+ Engine.PopGuiPageCB(0);
}
function deleteGame()
{
- var gameSelection = getGUIObjectByName("gameSelection");
+ var gameSelection = Engine.GetGUIObjectByName("gameSelection");
var gameLabel = gameSelection.list[gameSelection.selected];
var gameID = gameSelection.list_data[gameSelection.selected];
// Ask for confirmation
var btCaptions = ["Yes", "No"];
var btCode = [function(){ reallyDeleteGame(gameID); }, null];
messageBox(500, 200, "\""+gameLabel+"\"\nSaved game will be permanently deleted, are you sure?", "DELETE", 0, btCaptions, btCode);
}
function reallyDeleteGame(gameID)
{
if (!Engine.DeleteSavedGame(gameID))
error("Could not delete saved game '"+gameID+"'");
// Run init again to refresh saved game list
init();
}
-// HACK: Engine.SaveGame* expects this function to be defined on the current page
+// HACK: Engine.SaveGame* expects this function to be defined on the current page.
+// That's why we have to pass the data to this page even if we don't need it.
function getSavedGameData()
{
- return gameDataCallback();
+ return savedGameData;
}
Index: ps/trunk/binaries/data/mods/public/gui/session/input.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/session/input.js (revision 14495)
+++ ps/trunk/binaries/data/mods/public/gui/session/input.js (revision 14496)
@@ -1,2234 +1,2234 @@
const SDL_BUTTON_LEFT = 1;
const SDL_BUTTON_MIDDLE = 2;
const SDL_BUTTON_RIGHT = 3;
const SDLK_LEFTBRACKET = 91;
const SDLK_RIGHTBRACKET = 93;
const SDLK_RSHIFT = 303;
const SDLK_LSHIFT = 304;
const SDLK_RCTRL = 305;
const SDLK_LCTRL = 306;
const SDLK_RALT = 307;
const SDLK_LALT = 308;
// TODO: these constants should be defined somewhere else instead, in
// case any other code wants to use them too
const ACTION_NONE = 0;
const ACTION_GARRISON = 1;
const ACTION_REPAIR = 2;
const ACTION_GUARD = 3;
var preSelectedAction = ACTION_NONE;
const INPUT_NORMAL = 0;
const INPUT_SELECTING = 1;
const INPUT_BANDBOXING = 2;
const INPUT_BUILDING_PLACEMENT = 3;
const INPUT_BUILDING_CLICK = 4;
const INPUT_BUILDING_DRAG = 5;
const INPUT_BATCHTRAINING = 6;
const INPUT_PRESELECTEDACTION = 7;
const INPUT_BUILDING_WALL_CLICK = 8;
const INPUT_BUILDING_WALL_PATHING = 9;
const INPUT_MASSTRIBUTING = 10;
var inputState = INPUT_NORMAL;
var placementSupport = new PlacementSupport();
var mouseX = 0;
var mouseY = 0;
var mouseIsOverObject = false;
// Number of pixels the mouse can move before the action is considered a drag
var maxDragDelta = 4;
// Time in milliseconds in which a double click is recognized
const doubleClickTime = 500;
var doubleClickTimer = 0;
var doubleClicked = false;
// Store the previously clicked entity - ensure a double/triple click happens on the same entity
var prevClickedEntity = 0;
// Same double-click behaviour for hotkey presses
const doublePressTime = 500;
var doublePressTimer = 0;
var prevHotkey = 0;
function updateCursorAndTooltip()
{
var cursorSet = false;
var tooltipSet = false;
- var informationTooltip = getGUIObjectByName("informationTooltip");
+ var informationTooltip = Engine.GetGUIObjectByName("informationTooltip");
if (!mouseIsOverObject)
{
var action = determineAction(mouseX, mouseY);
if (inputState == INPUT_NORMAL || inputState == INPUT_PRESELECTEDACTION)
{
if (action)
{
if (action.cursor)
{
Engine.SetCursor(action.cursor);
cursorSet = true;
}
if (action.tooltip)
{
tooltipSet = true;
informationTooltip.caption = action.tooltip;
informationTooltip.hidden = false;
}
}
}
}
if (!cursorSet)
Engine.SetCursor("arrow-default");
if (!tooltipSet)
informationTooltip.hidden = true;
- var placementTooltip = getGUIObjectByName("placementTooltip");
+ var placementTooltip = Engine.GetGUIObjectByName("placementTooltip");
if (placementSupport.tooltipMessage)
{
if (placementSupport.tooltipError)
placementTooltip.sprite = "BackgroundErrorTooltip";
else
placementTooltip.sprite = "BackgroundInformationTooltip";
placementTooltip.caption = placementSupport.tooltipMessage;
placementTooltip.hidden = false;
}
else
{
placementTooltip.caption = "";
placementTooltip.hidden = true;
}
}
function updateBuildingPlacementPreview()
{
// The preview should be recomputed every turn, so that it responds to obstructions/fog/etc moving underneath it, or
// in the case of the wall previews, in response to new tower foundations getting constructed for it to snap to.
// See onSimulationUpdate in session.js.
if (placementSupport.mode === "building")
{
if (placementSupport.template && placementSupport.position)
{
var result = Engine.GuiInterfaceCall("SetBuildingPlacementPreview", {
"template": placementSupport.template,
"x": placementSupport.position.x,
"z": placementSupport.position.z,
"angle": placementSupport.angle,
"actorSeed": placementSupport.actorSeed
});
// Show placement info tooltip if invalid position
placementSupport.tooltipError = !result.success;
placementSupport.tooltipMessage = result.success ? "" : result.message;
if (!result.success)
return false;
if (placementSupport.attack)
{
// building can be placed here, and has an attack
// show the range advantage in the tooltip
var cmd = {x: placementSupport.position.x,
z: placementSupport.position.z,
range: placementSupport.attack.maxRange,
elevationBonus: placementSupport.attack.elevationBonus,
};
var averageRange = Engine.GuiInterfaceCall("GetAverageRangeForBuildings",cmd);
placementSupport.tooltipMessage = "Basic range: "+Math.round(cmd.range/4)+"\nAverage bonus range: "+Math.round((averageRange - cmd.range)/4);
}
return true;
}
}
else if (placementSupport.mode === "wall")
{
if (placementSupport.wallSet && placementSupport.position)
{
// Fetch an updated list of snapping candidate entities
placementSupport.wallSnapEntities = Engine.PickSimilarFriendlyEntities(
placementSupport.wallSet.templates.tower,
placementSupport.wallSnapEntitiesIncludeOffscreen,
true, // require exact template match
true // include foundations
);
return Engine.GuiInterfaceCall("SetWallPlacementPreview", {
"wallSet": placementSupport.wallSet,
"start": placementSupport.position,
"end": placementSupport.wallEndPosition,
"snapEntities": placementSupport.wallSnapEntities, // snapping entities (towers) for starting a wall segment
});
}
}
return false;
}
function findGatherType(gatherer, supply)
{
if (!gatherer || !supply)
return undefined;
if (gatherer[supply.type.generic+"."+supply.type.specific])
return supply.type.specific;
if (gatherer[supply.type.generic])
return supply.type.generic;
return undefined;
}
function getActionInfo(action, target)
{
var simState = GetSimState();
var selection = g_Selection.toList();
// If the selection doesn't exist, no action
var entState = GetEntityState(selection[0]);
if (!entState)
return {"possible": false};
if (!target)
{
if (action == "set-rallypoint")
{
var cursor = "";
var data = {command: "walk"};
if (Engine.HotkeyIsPressed("session.attackmove"))
{
data = {command: "attack-walk"};
cursor = "action-attack-move";
}
return {"possible": true, "data": data, "cursor": cursor};
}
else if (action == "move" || action == "attack-move")
return {"possible": true};
else if (action == "remove-guard")
return {"possible": true};
else
return {"possible": false};
}
if (action == "unset-rallypoint" && selection.indexOf(target) != -1)
{
var haveNonEmptyRallyPoints = selection.some(function(ent) {
var entState = GetEntityState(ent);
return entState && entState.rallyPoint && entState.rallyPoint.position;
});
if (haveNonEmptyRallyPoints)
return {"possible": true};
else
return {"possible": false};
}
// Look at the first targeted entity
// (TODO: maybe we eventually want to look at more, and be more context-sensitive?
// e.g. prefer to attack an enemy unit, even if some friendly units are closer to the mouse)
var targetState = GetExtendedEntityState(target);
var gaiaOwned = (targetState.player == 0);
// Look to see what type of command units going to the rally point should use
if (action == "set-rallypoint")
{
// We assume that all entities are owned by the same player (given par entState of selection[0]).
var playerState = simState.players[entState.player];
var playerOwned = (targetState.player == entState.player);
var allyOwned = playerState.isAlly[targetState.player];
var mutualAllyOwned = playerState.isMutualAlly[targetState.player];
var enemyOwned = playerState.isEnemy[targetState.player];
var tooltip;
// default to walking there (or attack-walking if hotkey pressed)
var data = {command: "walk"};
var cursor = "";
if (Engine.HotkeyIsPressed("session.attackmove"))
{
data = {command: "attack-walk"};
cursor = "action-attack-move";
}
if (targetState.garrisonHolder && (playerOwned || mutualAllyOwned))
{
data.command = "garrison";
data.target = target;
cursor = "action-garrison";
tooltip = "Current garrison: " + targetState.garrisonHolder.garrisonedEntitiesCount
+ "/" + targetState.garrisonHolder.capacity;
if (targetState.garrisonHolder.garrisonedEntitiesCount >= targetState.garrisonHolder.capacity)
tooltip = "[color=\"orange\"]" + tooltip + "[/color]";
}
else if (targetState.resourceSupply)
{
var resourceType = targetState.resourceSupply.type;
if (resourceType.generic == "treasure")
cursor = "action-gather-" + resourceType.generic;
else
cursor = "action-gather-" + resourceType.specific;
data.command = "gather";
data.resourceType = resourceType;
data.resourceTemplate = targetState.template;
}
else if (targetState.foundation && entState.buildEntities)
{
data.command = "build";
data.target = target;
cursor = "action-build";
}
else if (targetState.needsRepair && allyOwned)
{
data.command = "repair";
data.target = target;
cursor = "action-repair";
}
else if (hasClass(entState, "Market") && hasClass(targetState, "Market") && entState.id != targetState.id &&
(!hasClass(entState, "NavalMarket") || hasClass(targetState, "NavalMarket")) && !enemyOwned)
{
// Find a trader (if any) that this building can produce.
var trader;
if (entState.production && entState.production.entities.length)
for (var i = 0; i < entState.production.entities.length; ++i)
if ((trader = GetTemplateData(entState.production.entities[i]).trader))
break;
var traderData = { "firstMarket": entState.id, "secondMarket": targetState.id, "template": trader };
var gain = Engine.GuiInterfaceCall("GetTradingRouteGain", traderData);
if (gain && gain.traderGain)
{
data.command = "trade";
data.target = traderData.secondMarket;
data.source = traderData.firstMarket;
cursor = "action-setup-trade-route";
tooltip = "Right-click to establish a default route for new traders.";
if (trader)
tooltip += "\nGain: " + getTradingTooltip(gain);
else // Foundation or cannot produce traders
tooltip += "\nExpected gain: " + getTradingTooltip(gain);
}
}
// Don't allow the rally point to be set on any of the currently selected entities (used for unset)
// except if the autorallypoint hotkey is pressed and the target can produce entities
if (!Engine.HotkeyIsPressed("session.autorallypoint") || !targetState.production || !targetState.production.entities.length)
{
for (var i = 0; i < selection.length; i++)
if (target === selection[i])
return {"possible": false};
}
return {"possible": true, "data": data, "position": targetState.position, "cursor": cursor, "tooltip": tooltip};
}
// Check if the target entity is a resource, dropsite, foundation, or enemy unit.
// Check if any entities in the selection can gather the requested resource,
// can return to the dropsite, can build the foundation, or can attack the enemy
for each (var entityID in selection)
{
var entState = GetExtendedEntityState(entityID);
if (!entState)
continue;
var playerState = simState.players[entState.player];
var playerOwned = (targetState.player == entState.player);
var allyOwned = playerState.isAlly[targetState.player];
var mutualAllyOwned = playerState.isMutualAlly[targetState.player];
var neutralOwned = playerState.isNeutral[targetState.player];
var enemyOwned = playerState.isEnemy[targetState.player];
// Find the resource type we're carrying, if any
var carriedType = undefined;
if (entState.resourceCarrying && entState.resourceCarrying.length)
carriedType = entState.resourceCarrying[0].type;
switch (action)
{
case "garrison":
if (hasClass(entState, "Unit") && targetState.garrisonHolder && (playerOwned || mutualAllyOwned))
{
var tooltip = "Current garrison: " + targetState.garrisonHolder.garrisonedEntitiesCount
+ "/" + targetState.garrisonHolder.capacity;
var extraCount = 0;
if (entState.garrisonHolder)
extraCount += entState.garrisonHolder.garrisonedEntitiesCount;
if (targetState.garrisonHolder.garrisonedEntitiesCount + extraCount >= targetState.garrisonHolder.capacity)
tooltip = "[color=\"orange\"]" + tooltip + "[/color]";
var allowedClasses = targetState.garrisonHolder.allowedClasses;
for each (var unitClass in entState.identity.classes)
{
if (allowedClasses.indexOf(unitClass) != -1)
return {"possible": true, "tooltip": tooltip};
}
}
break;
case "setup-trade-route":
// If ground or sea trade possible
if (!targetState.foundation && ((entState.trader && hasClass(entState, "Organic") && (playerOwned || allyOwned) && hasClass(targetState, "Market")) ||
(entState.trader && hasClass(entState, "Ship") && (playerOwned || allyOwned) && hasClass(targetState, "NavalMarket"))))
{
var tradingData = {"trader": entState.id, "target": target};
var tradingDetails = Engine.GuiInterfaceCall("GetTradingDetails", tradingData);
var tooltip;
if (tradingDetails === null)
return {"possible": false};
switch (tradingDetails.type)
{
case "is first":
tooltip = "Origin trade market.";
if (tradingDetails.hasBothMarkets)
tooltip += "\nGain: " + getTradingTooltip(tradingDetails.gain);
else
tooltip += "\nRight-click on another market to set it as a destination trade market."
break;
case "is second":
tooltip = "Destination trade market.\nGain: " + getTradingTooltip(tradingDetails.gain);
break;
case "set first":
tooltip = "Right-click to set as origin trade market";
break;
case "set second":
if (tradingDetails.gain.traderGain == 0) // markets too close
return {"possible": false};
tooltip = "Right-click to set as destination trade market.\nGain: " + getTradingTooltip(tradingDetails.gain);
break;
}
return {"possible": true, "tooltip": tooltip};
}
break;
case "heal":
// The check if the target is unhealable is done by targetState.needsHeal
if (entState.healer && hasClass(targetState, "Unit") && targetState.needsHeal && (playerOwned || allyOwned))
{
// Healers can't heal themselves.
if (entState.id == targetState.id)
return {"possible": false};
var unhealableClasses = entState.healer.unhealableClasses;
for each (var unitClass in targetState.identity.classes)
{
if (unhealableClasses.indexOf(unitClass) != -1)
return {"possible": false};
}
var healableClasses = entState.healer.healableClasses;
for each (var unitClass in targetState.identity.classes)
{
if (healableClasses.indexOf(unitClass) != -1)
return {"possible": true};
}
}
break;
case "gather":
if (targetState.resourceSupply)
{
var resource = findGatherType(entState.resourceGatherRates, targetState.resourceSupply);
if (resource)
return {"possible": true, "cursor": "action-gather-" + resource};
}
break;
case "returnresource":
if (targetState.resourceDropsite && playerOwned && carriedType && targetState.resourceDropsite.types.indexOf(carriedType) != -1)
return {"possible": true, "cursor": "action-return-" + carriedType};
break;
case "build":
if (targetState.foundation && entState.buildEntities && playerOwned)
return {"possible": true};
break;
case "repair":
if (entState.buildEntities && targetState.needsRepair && allyOwned)
return {"possible": true};
break;
case "attack":
if (entState.attack && targetState.hitpoints && (enemyOwned || neutralOwned))
return {"possible": Engine.GuiInterfaceCall("CanAttack", {"entity": entState.id, "target": target})};
break;
case "guard":
if (targetState.guard && (playerOwned || mutualAllyOwned))
return {"possible": (entState.unitAI && entState.unitAI.canGuard && !(targetState.unitAI && targetState.unitAI.isGuarding))};
break;
}
}
if (action == "move" || action == "attack-move")
return {"possible": true};
else
return {"possible": false};
}
/**
* Determine the context-sensitive action that should be performed when the mouse is at (x,y)
*/
function determineAction(x, y, fromMinimap)
{
var selection = g_Selection.toList();
// No action if there's no selection
if (!selection.length)
{
preSelectedAction = ACTION_NONE;
return undefined;
}
// If the selection doesn't exist, no action
var entState = GetEntityState(selection[0]);
if (!entState)
return undefined;
// If the selection isn't friendly units, no action
var playerID = Engine.GetPlayerID();
var allOwnedByPlayer = selection.every(function(ent) {
var entState = GetEntityState(ent);
return entState && entState.player == playerID;
});
if (!g_DevSettings.controlAll && !allOwnedByPlayer)
return undefined;
// Work out whether at least part of the selection have UnitAI
var haveUnitAI = selection.some(function(ent) {
var entState = GetEntityState(ent);
return entState && entState.unitAI;
});
// Work out whether at least part the selection have rally points
// while none have UnitAI
var haveRallyPoints = !haveUnitAI && selection.some(function(ent) {
var entState = GetEntityState(ent);
return entState && entState.rallyPoint;
});
var targets = [];
var target = undefined;
if (!fromMinimap)
targets = Engine.PickEntitiesAtPoint(x, y);
if (targets.length)
target = targets[0];
var actionInfo = undefined;
if (preSelectedAction != ACTION_NONE)
{
switch (preSelectedAction)
{
case ACTION_GARRISON:
if ((actionInfo = getActionInfo("garrison", target)).possible)
return {"type": "garrison", "cursor": "action-garrison", "tooltip": actionInfo.tooltip, "target": target};
else
return {"type": "none", "cursor": "action-garrison-disabled", "target": undefined};
break;
case ACTION_REPAIR:
if (getActionInfo("repair", target).possible)
return {"type": "repair", "cursor": "action-repair", "target": target};
else
return {"type": "none", "cursor": "action-repair-disabled", "target": undefined};
break;
case ACTION_GUARD:
if (getActionInfo("guard", target).possible)
return {"type": "guard", "cursor": "action-guard", "target": target};
else
return {"type": "none", "cursor": "action-guard-disabled", "target": undefined};
break;
}
}
else if (Engine.HotkeyIsPressed("session.attack") && getActionInfo("attack", target).possible)
{
return {"type": "attack", "cursor": "action-attack", "target": target};
}
else if (Engine.HotkeyIsPressed("session.garrison") && (actionInfo = getActionInfo("garrison", target)).possible)
{
return {"type": "garrison", "cursor": "action-garrison", "tooltip": actionInfo.tooltip, "target": target};
}
else if (haveUnitAI && Engine.HotkeyIsPressed("session.attackmove") && getActionInfo("attack-move", target).possible)
{
return {"type": "attack-move", "cursor": "action-attack-move"};
}
else if (Engine.HotkeyIsPressed("session.guard") && getActionInfo("guard", target).possible)
{
return {"type": "guard", "cursor": "action-guard", "target": target};
}
else if (Engine.HotkeyIsPressed("session.guard") && getActionInfo("remove-guard", target).possible)
{
var isGuarding = selection.some(function(ent) {
var entState = GetEntityState(ent);
return entState && entState.unitAI && entState.unitAI.isGuarding;
});
if (isGuarding)
return {"type": "remove-guard", "cursor": "action-remove-guard"};
}
else
{
if ((actionInfo = getActionInfo("setup-trade-route", target)).possible)
return {"type": "setup-trade-route", "cursor": "action-setup-trade-route", "tooltip": actionInfo.tooltip, "target": target};
else if ((actionInfo = getActionInfo("gather", target)).possible)
return {"type": "gather", "cursor": actionInfo.cursor, "target": target};
else if ((actionInfo = getActionInfo("returnresource", target)).possible)
return {"type": "returnresource", "cursor": actionInfo.cursor, "target": target};
else if (getActionInfo("build", target).possible)
return {"type": "build", "cursor": "action-build", "target": target};
else if (getActionInfo("repair", target).possible)
return {"type": "build", "cursor": "action-repair", "target": target};
else if (haveRallyPoints && (actionInfo = getActionInfo("set-rallypoint", target)).possible)
return {"type": "set-rallypoint", "cursor": actionInfo.cursor, "data": actionInfo.data, "tooltip": actionInfo.tooltip, "position": actionInfo.position};
else if (getActionInfo("heal", target).possible)
return {"type": "heal", "cursor": "action-heal", "target": target};
else if (getActionInfo("attack", target).possible)
return {"type": "attack", "cursor": "action-attack", "target": target};
else if (haveRallyPoints && getActionInfo("unset-rallypoint", target).possible)
return {"type": "unset-rallypoint", "cursor": "action-unset-rally"};
else if (haveUnitAI && getActionInfo("move", target).possible)
return {"type": "move"};
}
return {"type": "none", "cursor": "", "target": target};
}
var dragStart; // used for remembering mouse coordinates at start of drag operations
function tryPlaceBuilding(queued)
{
if (placementSupport.mode !== "building")
{
error("[tryPlaceBuilding] Called while in '"+placementSupport.mode+"' placement mode instead of 'building'");
return false;
}
// Use the preview to check it's a valid build location
if (!updateBuildingPlacementPreview())
{
// invalid location - don't build it
// TODO: play a sound?
return false;
}
var selection = g_Selection.toList();
// Start the construction
Engine.PostNetworkCommand({
"type": "construct",
"template": placementSupport.template,
"x": placementSupport.position.x,
"z": placementSupport.position.z,
"angle": placementSupport.angle,
"actorSeed": placementSupport.actorSeed,
"entities": selection,
"autorepair": true,
"autocontinue": true,
"queued": queued
});
Engine.GuiInterfaceCall("PlaySound", { "name": "order_repair", "entity": selection[0] });
if (!queued)
placementSupport.Reset();
else
placementSupport.RandomizeActorSeed();
return true;
}
function tryPlaceWall(queued)
{
if (placementSupport.mode !== "wall")
{
error("[tryPlaceWall] Called while in '" + placementSupport.mode + "' placement mode; expected 'wall' mode");
return false;
}
var wallPlacementInfo = updateBuildingPlacementPreview(); // entities making up the wall (wall segments, towers, ...)
if (!(wallPlacementInfo === false || typeof(wallPlacementInfo) === "object"))
{
error("[tryPlaceWall] Unexpected return value from updateBuildingPlacementPreview: '" + uneval(placementInfo) + "'; expected either 'false' or 'object'");
return false;
}
if (!wallPlacementInfo)
return false;
var selection = g_Selection.toList();
var cmd = {
"type": "construct-wall",
"autorepair": true,
"autocontinue": true,
"queued": queued,
"entities": selection,
"wallSet": placementSupport.wallSet,
"pieces": wallPlacementInfo.pieces,
"startSnappedEntity": wallPlacementInfo.startSnappedEnt,
"endSnappedEntity": wallPlacementInfo.endSnappedEnt,
};
// make sure that there's at least one non-tower entity getting built, to prevent silly edge cases where the start and end
// point are too close together for the algorithm to place a wall segment inbetween, and only the towers are being previewed
// (this is somewhat non-ideal and hardcode-ish)
var hasWallSegment = false;
for (var k in cmd.pieces)
{
if (cmd.pieces[k].template != cmd.wallSet.templates.tower) // TODO: hardcode-ish :(
{
hasWallSegment = true;
break;
}
}
if (hasWallSegment)
{
Engine.PostNetworkCommand(cmd);
Engine.GuiInterfaceCall("PlaySound", {"name": "order_repair", "entity": selection[0] });
}
return true;
}
// Limits bandboxed selections to certain types of entities based on priority
function getPreferredEntities(ents)
{
var entStateList = [];
var preferredEnts = [];
// Check if there are units in the selection and get a list of entity states
for each (var ent in ents)
{
var entState = GetEntityState(ent);
if (!entState)
continue;
if (hasClass(entState, "Unit"))
preferredEnts.push(ent);
entStateList.push(entState);
}
// If there are no units, check if there are defensive entities in the selection
if (!preferredEnts.length)
for (var i = 0; i < ents.length; i++)
if (hasClass(entStateList[i], "Defensive"))
preferredEnts.push(ents[i]);
return preferredEnts;
}
// Removes any support units from the passed list of entities
function getMilitaryEntities(ents)
{
var militaryEnts = [];
for each (var ent in ents)
{
var entState = GetEntityState(ent);
if (!hasClass(entState, "Support"))
militaryEnts.push(ent);
}
return militaryEnts;
}
function handleInputBeforeGui(ev, hoveredObject)
{
// Capture mouse position so we can use it for displaying cursors,
// and key states
switch (ev.type)
{
case "mousebuttonup":
case "mousebuttondown":
case "mousemotion":
mouseX = ev.x;
mouseY = ev.y;
break;
}
// Remember whether the mouse is over a GUI object or not
mouseIsOverObject = (hoveredObject != null);
// Close the menu when interacting with the game world
if (!mouseIsOverObject && (ev.type =="mousebuttonup" || ev.type == "mousebuttondown")
&& (ev.button == SDL_BUTTON_LEFT || ev.button == SDL_BUTTON_RIGHT))
closeMenu();
// State-machine processing:
//
// (This is for states which should override the normal GUI processing - events will
// be processed here before being passed on, and propagation will stop if this function
// returns true)
//
// TODO: it'd probably be nice to have a better state-machine system, with guaranteed
// entry/exit functions, since this is a bit broken now
switch (inputState)
{
case INPUT_BANDBOXING:
switch (ev.type)
{
case "mousemotion":
var x0 = dragStart[0];
var y0 = dragStart[1];
var x1 = ev.x;
var y1 = ev.y;
if (x0 > x1) { var t = x0; x0 = x1; x1 = t; }
if (y0 > y1) { var t = y0; y0 = y1; y1 = t; }
- var bandbox = getGUIObjectByName("bandbox");
+ var bandbox = Engine.GetGUIObjectByName("bandbox");
bandbox.size = [x0, y0, x1, y1].join(" ");
bandbox.hidden = false;
// TODO: Should we handle "control all units" here as well?
var ents = Engine.PickFriendlyEntitiesInRect(x0, y0, x1, y1, Engine.GetPlayerID());
g_Selection.setHighlightList(ents);
return false;
case "mousebuttonup":
if (ev.button == SDL_BUTTON_LEFT)
{
var x0 = dragStart[0];
var y0 = dragStart[1];
var x1 = ev.x;
var y1 = ev.y;
if (x0 > x1) { var t = x0; x0 = x1; x1 = t; }
if (y0 > y1) { var t = y0; y0 = y1; y1 = t; }
- var bandbox = getGUIObjectByName("bandbox");
+ var bandbox = Engine.GetGUIObjectByName("bandbox");
bandbox.hidden = true;
// Get list of entities limited to preferred entities
// TODO: Should we handle "control all units" here as well?
var ents = Engine.PickFriendlyEntitiesInRect(x0, y0, x1, y1, Engine.GetPlayerID());
var preferredEntities = getPreferredEntities(ents)
if (preferredEntities.length)
{
ents = preferredEntities;
if (Engine.HotkeyIsPressed("selection.milonly"))
{
var militaryEntities = getMilitaryEntities(ents);
if (militaryEntities.length)
ents = militaryEntities;
}
}
// Remove the bandbox hover highlighting
g_Selection.setHighlightList([]);
// Update the list of selected units
if (Engine.HotkeyIsPressed("selection.add"))
{
g_Selection.addList(ents);
}
else if (Engine.HotkeyIsPressed("selection.remove"))
{
g_Selection.removeList(ents);
}
else
{
g_Selection.reset();
g_Selection.addList(ents);
}
inputState = INPUT_NORMAL;
return true;
}
else if (ev.button == SDL_BUTTON_RIGHT)
{
// Cancel selection
- var bandbox = getGUIObjectByName("bandbox");
+ var bandbox = Engine.GetGUIObjectByName("bandbox");
bandbox.hidden = true;
g_Selection.setHighlightList([]);
inputState = INPUT_NORMAL;
return true;
}
break;
}
break;
case INPUT_BUILDING_CLICK:
switch (ev.type)
{
case "mousemotion":
// If the mouse moved far enough from the original click location,
// then switch to drag-orientation mode
var dragDeltaX = ev.x - dragStart[0];
var dragDeltaY = ev.y - dragStart[1];
var maxDragDelta = 16;
if (Math.abs(dragDeltaX) >= maxDragDelta || Math.abs(dragDeltaY) >= maxDragDelta)
{
inputState = INPUT_BUILDING_DRAG;
return false;
}
break;
case "mousebuttonup":
if (ev.button == SDL_BUTTON_LEFT)
{
// If shift is down, let the player continue placing another of the same building
var queued = Engine.HotkeyIsPressed("session.queue");
if (tryPlaceBuilding(queued))
{
if (queued)
inputState = INPUT_BUILDING_PLACEMENT;
else
inputState = INPUT_NORMAL;
}
else
{
inputState = INPUT_BUILDING_PLACEMENT;
}
return true;
}
break;
case "mousebuttondown":
if (ev.button == SDL_BUTTON_RIGHT)
{
// Cancel building
placementSupport.Reset();
inputState = INPUT_NORMAL;
return true;
}
break;
}
break;
case INPUT_BUILDING_WALL_CLICK:
// User is mid-click in choosing a starting point for building a wall. The build process can still be cancelled at this point
// by right-clicking; releasing the left mouse button will 'register' the starting point and commence endpoint choosing mode.
switch (ev.type)
{
case "mousebuttonup":
if (ev.button === SDL_BUTTON_LEFT)
{
inputState = INPUT_BUILDING_WALL_PATHING;
return true;
}
break;
case "mousebuttondown":
if (ev.button == SDL_BUTTON_RIGHT)
{
// Cancel building
placementSupport.Reset();
updateBuildingPlacementPreview();
inputState = INPUT_NORMAL;
return true;
}
break;
}
break;
case INPUT_BUILDING_WALL_PATHING:
// User has chosen a starting point for constructing the wall, and is now looking to set the endpoint.
// Right-clicking cancels wall building mode, left-clicking sets the endpoint and builds the wall and returns to
// normal input mode. Optionally, shift + left-clicking does not return to normal input, and instead allows the
// user to continue building walls.
switch (ev.type)
{
case "mousemotion":
placementSupport.wallEndPosition = Engine.GetTerrainAtScreenPoint(ev.x, ev.y);
// Update the building placement preview, and by extension, the list of snapping candidate entities for both (!)
// the ending point and the starting point to snap to.
//
// TODO: Note that here, we need to fetch all similar entities, including any offscreen ones, to support the case
// where the snap entity for the starting point has moved offscreen, or has been deleted/destroyed, or was a
// foundation and has been replaced with a completed entity since the user first chose it. Fetching all towers on
// the entire map instead of only the current screen might get expensive fast since walls all have a ton of towers
// in them. Might be useful to query only for entities within a certain range around the starting point and ending
// points.
placementSupport.wallSnapEntitiesIncludeOffscreen = true;
var result = updateBuildingPlacementPreview(); // includes an update of the snap entity candidates
if (result && result.cost)
{
placementSupport.tooltipMessage = getEntityCostTooltip(result);
var neededResources = Engine.GuiInterfaceCall("GetNeededResources", result.cost);
if (neededResources)
placementSupport.tooltipMessage += getNeededResourcesTooltip(neededResources);
}
break;
case "mousebuttondown":
if (ev.button == SDL_BUTTON_LEFT)
{
var queued = Engine.HotkeyIsPressed("session.queue");
if (tryPlaceWall(queued))
{
if (queued)
{
// continue building, just set a new starting position where we left off
placementSupport.position = placementSupport.wallEndPosition;
placementSupport.wallEndPosition = undefined;
inputState = INPUT_BUILDING_WALL_CLICK;
}
else
{
placementSupport.Reset();
inputState = INPUT_NORMAL;
}
}
else
{
placementSupport.tooltipMessage = "Cannot build wall here!";
}
updateBuildingPlacementPreview();
return true;
}
else if (ev.button == SDL_BUTTON_RIGHT)
{
// reset to normal input mode
placementSupport.Reset();
updateBuildingPlacementPreview();
inputState = INPUT_NORMAL;
return true;
}
break;
}
break;
case INPUT_BUILDING_DRAG:
switch (ev.type)
{
case "mousemotion":
var dragDeltaX = ev.x - dragStart[0];
var dragDeltaY = ev.y - dragStart[1];
var maxDragDelta = 16;
if (Math.abs(dragDeltaX) >= maxDragDelta || Math.abs(dragDeltaY) >= maxDragDelta)
{
// Rotate in the direction of the mouse
var target = Engine.GetTerrainAtScreenPoint(ev.x, ev.y);
placementSupport.angle = Math.atan2(target.x - placementSupport.position.x, target.z - placementSupport.position.z);
}
else
{
// If the mouse is near the center, snap back to the default orientation
placementSupport.SetDefaultAngle();
}
var snapData = Engine.GuiInterfaceCall("GetFoundationSnapData", {
"template": placementSupport.template,
"x": placementSupport.position.x,
"z": placementSupport.position.z
});
if (snapData)
{
placementSupport.angle = snapData.angle;
placementSupport.position.x = snapData.x;
placementSupport.position.z = snapData.z;
}
updateBuildingPlacementPreview();
break;
case "mousebuttonup":
if (ev.button == SDL_BUTTON_LEFT)
{
// If shift is down, let the player continue placing another of the same building
var queued = Engine.HotkeyIsPressed("session.queue");
if (tryPlaceBuilding(queued))
{
if (queued)
inputState = INPUT_BUILDING_PLACEMENT;
else
inputState = INPUT_NORMAL;
}
else
{
inputState = INPUT_BUILDING_PLACEMENT;
}
return true;
}
break;
case "mousebuttondown":
if (ev.button == SDL_BUTTON_RIGHT)
{
// Cancel building
placementSupport.Reset();
inputState = INPUT_NORMAL;
return true;
}
break;
}
break;
case INPUT_MASSTRIBUTING:
if (ev.type == "hotkeyup" && ev.hotkey == "session.masstribute")
{
flushTributing();
inputState = INPUT_NORMAL;
}
break;
case INPUT_BATCHTRAINING:
if (ev.type == "hotkeyup" && ev.hotkey == "session.batchtrain")
{
flushTrainingBatch();
inputState = INPUT_NORMAL;
}
break;
}
return false;
}
function handleInputAfterGui(ev)
{
// Handle the time-warp testing features, restricted to single-player
- if (!g_IsNetworked && getGUIObjectByName("devTimeWarp").checked)
+ if (!g_IsNetworked && Engine.GetGUIObjectByName("devTimeWarp").checked)
{
if (ev.type == "hotkeydown" && ev.hotkey == "timewarp.fastforward")
Engine.SetSimRate(20.0);
else if (ev.type == "hotkeyup" && ev.hotkey == "timewarp.fastforward")
Engine.SetSimRate(1.0);
else if (ev.type == "hotkeyup" && ev.hotkey == "timewarp.rewind")
Engine.RewindTimeWarp();
}
if (ev.hotkey == "session.showstatusbars")
{
g_ShowAllStatusBars = (ev.type == "hotkeydown");
recalculateStatusBarDisplay();
}
if (ev.hotkey == "session.highlightguarding")
{
g_ShowGuarding = (ev.type == "hotkeydown");
updateAdditionalHighlight();
}
if (ev.hotkey == "session.highlightguarded")
{
g_ShowGuarded = (ev.type == "hotkeydown");
updateAdditionalHighlight();
}
// State-machine processing:
switch (inputState)
{
case INPUT_NORMAL:
switch (ev.type)
{
case "mousemotion":
// Highlight the first hovered entity (if any)
var ents = Engine.PickEntitiesAtPoint(ev.x, ev.y);
if (ents.length)
g_Selection.setHighlightList([ents[0]]);
else
g_Selection.setHighlightList([]);
return false;
case "mousebuttondown":
if (ev.button == SDL_BUTTON_LEFT)
{
dragStart = [ ev.x, ev.y ];
inputState = INPUT_SELECTING;
return true;
}
else if (ev.button == SDL_BUTTON_RIGHT)
{
var action = determineAction(ev.x, ev.y);
if (!action)
break;
return doAction(action, ev);
}
break;
case "hotkeydown":
if (ev.hotkey.indexOf("selection.group.") == 0)
{
var now = new Date();
if ((now.getTime() - doublePressTimer < doublePressTime) && (ev.hotkey == prevHotkey))
{
if (ev.hotkey.indexOf("selection.group.select.") == 0)
{
var sptr = ev.hotkey.split(".");
performGroup("snap", sptr[3]);
}
}
else
{
var sptr = ev.hotkey.split(".");
performGroup(sptr[2], sptr[3]);
doublePressTimer = now.getTime();
prevHotkey = ev.hotkey;
}
}
break;
}
break;
case INPUT_PRESELECTEDACTION:
switch (ev.type)
{
case "mousemotion":
// Highlight the first hovered entity (if any)
var ents = Engine.PickEntitiesAtPoint(ev.x, ev.y);
if (ents.length)
g_Selection.setHighlightList([ents[0]]);
else
g_Selection.setHighlightList([]);
return false;
case "mousebuttondown":
if (ev.button == SDL_BUTTON_LEFT && preSelectedAction != ACTION_NONE)
{
var action = determineAction(ev.x, ev.y);
if (!action)
break;
preSelectedAction = ACTION_NONE;
inputState = INPUT_NORMAL;
return doAction(action, ev);
}
else if (ev.button == SDL_BUTTON_RIGHT && preSelectedAction != ACTION_NONE)
{
preSelectedAction = ACTION_NONE;
inputState = INPUT_NORMAL;
break;
}
// else
default:
// Slight hack: If selection is empty, reset the input state
if (g_Selection.toList().length == 0)
{
preSelectedAction = ACTION_NONE;
inputState = INPUT_NORMAL;
break;
}
}
break;
case INPUT_SELECTING:
switch (ev.type)
{
case "mousemotion":
// If the mouse moved further than a limit, switch to bandbox mode
var dragDeltaX = ev.x - dragStart[0];
var dragDeltaY = ev.y - dragStart[1];
if (Math.abs(dragDeltaX) >= maxDragDelta || Math.abs(dragDeltaY) >= maxDragDelta)
{
inputState = INPUT_BANDBOXING;
return false;
}
var ents = Engine.PickEntitiesAtPoint(ev.x, ev.y);
g_Selection.setHighlightList(ents);
return false;
case "mousebuttonup":
if (ev.button == SDL_BUTTON_LEFT)
{
var ents = Engine.PickEntitiesAtPoint(ev.x, ev.y);
if (!ents.length)
{
if (!Engine.HotkeyIsPressed("selection.add") && !Engine.HotkeyIsPressed("selection.remove"))
{
g_Selection.reset();
resetIdleUnit();
}
inputState = INPUT_NORMAL;
return true;
}
var selectedEntity = ents[0];
var now = new Date();
// If camera following and we select different unit, stop
if (Engine.GetFollowedEntity() != selectedEntity)
{
Engine.CameraFollow(0);
}
if ((now.getTime() - doubleClickTimer < doubleClickTime) && (selectedEntity == prevClickedEntity))
{
// Double click or triple click has occurred
var showOffscreen = Engine.HotkeyIsPressed("selection.offscreen");
var matchRank = true;
var templateToMatch;
// Check for double click or triple click
if (!doubleClicked)
{
// If double click hasn't already occurred, this is a double click.
// Select similar units regardless of rank
templateToMatch = GetEntityState(selectedEntity).identity.selectionGroupName;
if (templateToMatch)
{
matchRank = false;
}
else
{ // No selection group name defined, so fall back to exact match
templateToMatch = GetEntityState(selectedEntity).template;
}
doubleClicked = true;
// Reset the timer so the user has an extra period 'doubleClickTimer' to do a triple-click
doubleClickTimer = now.getTime();
}
else
{
// Double click has already occurred, so this is a triple click.
// Select units matching exact template name (same rank)
templateToMatch = GetEntityState(selectedEntity).template;
}
// TODO: Should we handle "control all units" here as well?
ents = Engine.PickSimilarFriendlyEntities(templateToMatch, showOffscreen, matchRank, false);
}
else
{
// It's single click right now but it may become double or triple click
doubleClicked = false;
doubleClickTimer = now.getTime();
prevClickedEntity = selectedEntity;
// We only want to include the first picked unit in the selection
ents = [ents[0]];
}
// Update the list of selected units
if (Engine.HotkeyIsPressed("selection.add"))
{
g_Selection.addList(ents);
}
else if (Engine.HotkeyIsPressed("selection.remove"))
{
g_Selection.removeList(ents);
}
else
{
g_Selection.reset();
g_Selection.addList(ents);
}
inputState = INPUT_NORMAL;
return true;
}
break;
}
break;
case INPUT_BUILDING_PLACEMENT:
switch (ev.type)
{
case "mousemotion":
placementSupport.position = Engine.GetTerrainAtScreenPoint(ev.x, ev.y);
if (placementSupport.mode === "wall")
{
// Including only the on-screen towers in the next snap candidate list is sufficient here, since the user is
// still selecting a starting point (which must necessarily be on-screen). (The update of the snap entities
// itself happens in the call to updateBuildingPlacementPreview below).
placementSupport.wallSnapEntitiesIncludeOffscreen = false;
}
else
{
var snapData = Engine.GuiInterfaceCall("GetFoundationSnapData", {
"template": placementSupport.template,
"x": placementSupport.position.x,
"z": placementSupport.position.z,
});
if (snapData)
{
placementSupport.angle = snapData.angle;
placementSupport.position.x = snapData.x;
placementSupport.position.z = snapData.z;
}
}
updateBuildingPlacementPreview(); // includes an update of the snap entity candidates
return false; // continue processing mouse motion
case "mousebuttondown":
if (ev.button == SDL_BUTTON_LEFT)
{
if (placementSupport.mode === "wall")
{
var validPlacement = updateBuildingPlacementPreview();
if (validPlacement !== false)
{
inputState = INPUT_BUILDING_WALL_CLICK;
}
}
else
{
placementSupport.position = Engine.GetTerrainAtScreenPoint(ev.x, ev.y);
dragStart = [ ev.x, ev.y ];
inputState = INPUT_BUILDING_CLICK;
}
return true;
}
else if (ev.button == SDL_BUTTON_RIGHT)
{
// Cancel building
placementSupport.Reset();
inputState = INPUT_NORMAL;
return true;
}
break;
case "hotkeydown":
var rotation_step = Math.PI / 12; // 24 clicks make a full rotation
switch (ev.hotkey)
{
case "session.rotate.cw":
placementSupport.angle += rotation_step;
updateBuildingPlacementPreview();
break;
case "session.rotate.ccw":
placementSupport.angle -= rotation_step;
updateBuildingPlacementPreview();
break;
}
break;
}
break;
}
return false;
}
function doAction(action, ev)
{
var selection = g_Selection.toList();
// If shift is down, add the order to the unit's order queue instead
// of running it immediately
var queued = Engine.HotkeyIsPressed("session.queue");
switch (action.type)
{
case "move":
var target = Engine.GetTerrainAtScreenPoint(ev.x, ev.y);
Engine.PostNetworkCommand({"type": "walk", "entities": selection, "x": target.x, "z": target.z, "queued": queued});
Engine.GuiInterfaceCall("PlaySound", { "name": "order_walk", "entity": selection[0] });
return true;
case "attack-move":
var target = Engine.GetTerrainAtScreenPoint(ev.x, ev.y);
Engine.PostNetworkCommand({"type": "attack-walk", "entities": selection, "x": target.x, "z": target.z, "queued": queued});
Engine.GuiInterfaceCall("PlaySound", { "name": "order_walk", "entity": selection[0] });
return true;
case "attack":
Engine.PostNetworkCommand({"type": "attack", "entities": selection, "target": action.target, "queued": queued});
Engine.GuiInterfaceCall("PlaySound", { "name": "order_attack", "entity": selection[0] });
return true;
case "heal":
Engine.PostNetworkCommand({"type": "heal", "entities": selection, "target": action.target, "queued": queued});
Engine.GuiInterfaceCall("PlaySound", { "name": "order_heal", "entity": selection[0] });
return true;
case "build": // (same command as repair)
case "repair":
Engine.PostNetworkCommand({"type": "repair", "entities": selection, "target": action.target, "autocontinue": true, "queued": queued});
Engine.GuiInterfaceCall("PlaySound", { "name": "order_repair", "entity": selection[0] });
return true;
case "gather":
Engine.PostNetworkCommand({"type": "gather", "entities": selection, "target": action.target, "queued": queued});
Engine.GuiInterfaceCall("PlaySound", { "name": "order_gather", "entity": selection[0] });
return true;
case "returnresource":
Engine.PostNetworkCommand({"type": "returnresource", "entities": selection, "target": action.target, "queued": queued});
Engine.GuiInterfaceCall("PlaySound", { "name": "order_gather", "entity": selection[0] });
return true;
case "setup-trade-route":
Engine.PostNetworkCommand({"type": "setup-trade-route", "entities": selection, "target": action.target});
Engine.GuiInterfaceCall("PlaySound", { "name": "order_trade", "entity": selection[0] });
return true;
case "garrison":
Engine.PostNetworkCommand({"type": "garrison", "entities": selection, "target": action.target, "queued": queued});
Engine.GuiInterfaceCall("PlaySound", { "name": "order_garrison", "entity": selection[0] });
return true;
case "guard":
Engine.PostNetworkCommand({"type": "guard", "entities": selection, "target": action.target, "queued": queued});
Engine.GuiInterfaceCall("PlaySound", { "name": "order_guard", "entity": selection[0] });
return true;
case "remove-guard":
Engine.PostNetworkCommand({"type": "remove-guard", "entities": selection, "target": action.target, "queued": queued});
Engine.GuiInterfaceCall("PlaySound", { "name": "order_guard", "entity": selection[0] });
return true;
case "set-rallypoint":
var pos = undefined;
// if there is a position set in the action then use this so that when setting a
// rally point on an entity it is centered on that entity
if (action.position)
{
pos = action.position;
}
else
{
pos = Engine.GetTerrainAtScreenPoint(ev.x, ev.y);
}
Engine.PostNetworkCommand({"type": "set-rallypoint", "entities": selection, "x": pos.x, "z": pos.z, "data": action.data, "queued": queued});
// Display rally point at the new coordinates, to avoid display lag
Engine.GuiInterfaceCall("DisplayRallyPoint", {
"entities": selection,
"x": pos.x,
"z": pos.z,
"queued": queued
});
return true;
case "unset-rallypoint":
var target = Engine.GetTerrainAtScreenPoint(ev.x, ev.y);
Engine.PostNetworkCommand({"type": "unset-rallypoint", "entities": selection});
// Remove displayed rally point
Engine.GuiInterfaceCall("DisplayRallyPoint", {
"entities": []
});
return true;
case "none":
return true;
default:
error("Invalid action.type "+action.type);
return false;
}
}
function handleMinimapEvent(target)
{
// Partly duplicated from handleInputAfterGui(), but with the input being
// world coordinates instead of screen coordinates.
if (inputState == INPUT_NORMAL)
{
var fromMinimap = true;
var action = determineAction(undefined, undefined, fromMinimap);
if (!action)
return false;
var selection = g_Selection.toList();
var queued = Engine.HotkeyIsPressed("session.queue");
switch (action.type)
{
case "move":
Engine.PostNetworkCommand({"type": "walk", "entities": selection, "x": target.x, "z": target.z, "queued": queued});
Engine.GuiInterfaceCall("PlaySound", { "name": "order_walk", "entity": selection[0] });
return true;
case "attack-move":
Engine.PostNetworkCommand({"type": "attack-walk", "entities": selection, "x": target.x, "z": target.z, "queued": queued});
Engine.GuiInterfaceCall("PlaySound", { "name": "order_walk", "entity": selection[0] });
return true;
case "set-rallypoint":
Engine.PostNetworkCommand({"type": "set-rallypoint", "entities": selection, "x": target.x, "z": target.z, "queued": queued});
// Display rally point at the new coordinates, to avoid display lag
Engine.GuiInterfaceCall("DisplayRallyPoint", {
"entities": selection,
"x": target.x,
"z": target.z,
"queued": queued
});
return true;
default:
error("Invalid action.type "+action.type);
}
}
return false;
}
// Called by GUI when user clicks construction button
// @param buildTemplate Template name of the entity the user wants to build
function startBuildingPlacement(buildTemplate, playerState)
{
if(getEntityLimitAndCount(playerState, buildTemplate)[2] == 0)
return;
// TODO: we should clear any highlight selection rings here. If the mouse was over an entity before going onto the GUI
// to start building a structure, then the highlight selection rings are kept during the construction of the building.
// Gives the impression that somehow the hovered-over entity has something to do with the building you're constructing.
placementSupport.Reset();
// find out if we're building a wall, and change the entity appropriately if so
var templateData = GetTemplateData(buildTemplate);
if (templateData.wallSet)
{
placementSupport.mode = "wall";
placementSupport.wallSet = templateData.wallSet;
inputState = INPUT_BUILDING_PLACEMENT;
}
else
{
placementSupport.mode = "building";
placementSupport.template = buildTemplate;
inputState = INPUT_BUILDING_PLACEMENT;
}
if (templateData.attack &&
templateData.attack.Ranged &&
templateData.attack.Ranged.maxRange)
{
// add attack information to display a good tooltip
placementSupport.attack = templateData.attack.Ranged;
}
}
// Called by GUI when user changes required trading goods
function selectRequiredGoods(data)
{
Engine.PostNetworkCommand({"type": "select-required-goods", "entities": data.entities, "requiredGoods": data.requiredGoods});
}
// Called by GUI when user clicks exchange resources button
function exchangeResources(command)
{
Engine.PostNetworkCommand({"type": "barter", "sell": command.sell, "buy": command.buy, "amount": command.amount});
}
// Camera jumping: when the user presses a hotkey the current camera location is marked.
// When they press another hotkey the camera jumps back to that position. If the camera is already roughly at that location,
// jump back to where it was previously.
var jumpCameraPositions = [], jumpCameraLast;
function jumpCamera(index)
{
var position = jumpCameraPositions[index], distanceThreshold = Engine.ConfigDB_GetValue("user", "camerajump.threshold");
if (position)
{
if (jumpCameraLast &&
Math.abs(Engine.CameraGetX() - position.x) < distanceThreshold &&
Math.abs(Engine.CameraGetZ() - position.z) < distanceThreshold)
Engine.CameraMoveTo(jumpCameraLast.x, jumpCameraLast.z);
else
{
jumpCameraLast = {x: Engine.CameraGetX(), z: Engine.CameraGetZ()};
Engine.CameraMoveTo(position.x, position.z);
}
}
}
function setJumpCamera(index)
{
jumpCameraPositions[index] = {x: Engine.CameraGetX(), z: Engine.CameraGetZ()};
}
// Batch training:
// When the user shift-clicks, we set these variables and switch to INPUT_BATCHTRAINING
// When the user releases shift, or clicks on a different training button, we create the batched units
var batchTrainingEntities;
var batchTrainingType;
var batchTrainingCount;
var batchTrainingEntityAllowedCount;
const batchIncrementSize = 5;
function flushTrainingBatch()
{
var appropriateBuildings = getBuildingsWhichCanTrainEntity(batchTrainingEntities, batchTrainingType);
// If training limits don't allow us to train batchTrainingCount in each appropriate building
if (batchTrainingEntityAllowedCount !== undefined &&
batchTrainingEntityAllowedCount < batchTrainingCount * appropriateBuildings.length)
{
// Train as many full batches as we can
var buildingsCountToTrainFullBatch = Math.floor(batchTrainingEntityAllowedCount / batchTrainingCount);
var buildingsToTrainFullBatch = appropriateBuildings.slice(0, buildingsCountToTrainFullBatch);
Engine.PostNetworkCommand({"type": "train", "entities": buildingsToTrainFullBatch,
"template": batchTrainingType, "count": batchTrainingCount});
// Train remainer in one more building
var remainderToTrain = batchTrainingEntityAllowedCount % batchTrainingCount;
Engine.PostNetworkCommand({"type": "train",
"entities": [ appropriateBuildings[buildingsCountToTrainFullBatch] ],
"template": batchTrainingType, "count": remainderToTrain});
}
else
{
Engine.PostNetworkCommand({"type": "train", "entities": appropriateBuildings,
"template": batchTrainingType, "count": batchTrainingCount});
}
}
function getBuildingsWhichCanTrainEntity(entitiesToCheck, trainEntType)
{
return entitiesToCheck.filter(function(entity) {
var state = GetEntityState(entity);
var canTrain = state && state.production && state.production.entities.length &&
state.production.entities.indexOf(trainEntType) != -1;
return canTrain;
});
}
function getEntityLimitAndCount(playerState, entType)
{
var template = GetTemplateData(entType);
var entCategory = null;
if (template.trainingRestrictions)
entCategory = template.trainingRestrictions.category;
else if (template.buildRestrictions)
entCategory = template.buildRestrictions.category;
var entLimit = undefined;
var entCount = undefined;
var entLimitChangers = undefined;
var canBeAddedCount = undefined;
if (entCategory && playerState.entityLimits[entCategory] != null)
{
entLimit = playerState.entityLimits[entCategory];
entCount = playerState.entityCounts[entCategory];
entLimitChangers = playerState.entityLimitChangers[entCategory];
canBeAddedCount = Math.max(entLimit - entCount, 0);
}
return [entLimit, entCount, canBeAddedCount, entLimitChangers];
}
// Add the unit shown at position to the training queue for all entities in the selection
function addTrainingByPosition(position)
{
var simState = GetSimState();
var playerState = simState.players[Engine.GetPlayerID()];
var selection = g_Selection.toList();
if (!selection.length)
return;
var trainableEnts = getAllTrainableEntitiesFromSelection();
// Check if the position is valid
if (!trainableEnts.length || trainableEnts.length <= position)
return;
var entToTrain = trainableEnts[position];
addTrainingToQueue(selection, entToTrain, playerState);
return;
}
// Called by GUI when user clicks training button
function addTrainingToQueue(selection, trainEntType, playerState)
{
// Create list of buildings which can train trainEntType
var appropriateBuildings = getBuildingsWhichCanTrainEntity(selection, trainEntType);
// Check trainEntType entity limit and count
var [trainEntLimit, trainEntCount, canBeTrainedCount] = getEntityLimitAndCount(playerState, trainEntType)
// Batch training possible if we can train at least 2 units
var batchTrainingPossible = canBeTrainedCount == undefined || canBeTrainedCount > 1;
var decrement = Engine.HotkeyIsPressed("selection.remove");
if (!decrement)
var template = GetTemplateData(trainEntType);
if (Engine.HotkeyIsPressed("session.batchtrain") && batchTrainingPossible)
{
if (inputState == INPUT_BATCHTRAINING)
{
// Check if we are training in the same building(s) as the last batch
var sameEnts = false;
if (batchTrainingEntities.length == selection.length)
{
// NOTE: We just check if the arrays are the same and if the order is the same
// If the order changed, we have a new selection and we should create a new batch.
for (var i = 0; i < batchTrainingEntities.length; ++i)
{
if (!(sameEnts = batchTrainingEntities[i] == selection[i]))
break;
}
}
// If we're already creating a batch of this unit (in the same building(s)), then just extend it
// (if training limits allow)
if (sameEnts && batchTrainingType == trainEntType)
{
if (decrement)
{
batchTrainingCount -= batchIncrementSize;
if (batchTrainingCount <= 0)
inputState = INPUT_NORMAL;
}
else if (canBeTrainedCount == undefined ||
canBeTrainedCount > batchTrainingCount * appropriateBuildings.length)
{
if (Engine.GuiInterfaceCall("GetNeededResources", multiplyEntityCosts(
template, batchTrainingCount + batchIncrementSize)))
return;
batchTrainingCount += batchIncrementSize;
}
batchTrainingEntityAllowedCount = canBeTrainedCount;
return;
}
// Otherwise start a new one
else if (!decrement)
{
flushTrainingBatch();
// fall through to create the new batch
}
}
// Don't start a new batch if decrementing or unable to afford it.
if (decrement || Engine.GuiInterfaceCall("GetNeededResources",
multiplyEntityCosts(template, batchIncrementSize)))
return;
inputState = INPUT_BATCHTRAINING;
batchTrainingEntities = selection;
batchTrainingType = trainEntType;
batchTrainingEntityAllowedCount = canBeTrainedCount;
batchTrainingCount = batchIncrementSize;
}
else
{
// Non-batched - just create a single entity in each building
// (but no more than entity limit allows)
var buildingsForTraining = appropriateBuildings;
if (trainEntLimit)
buildingsForTraining = buildingsForTraining.slice(0, canBeTrainedCount);
Engine.PostNetworkCommand({"type": "train", "template": trainEntType,
"count": 1, "entities": buildingsForTraining});
}
}
// Called by GUI when user clicks research button
function addResearchToQueue(entity, researchType)
{
Engine.PostNetworkCommand({"type": "research", "entity": entity, "template": researchType});
}
// Returns the number of units that will be present in a batch if the user clicks
// the training button with shift down
function getTrainingBatchStatus(playerState, entity, trainEntType, selection)
{
var appropriateBuildings = [entity];
if (selection && selection.indexOf(entity) != -1)
appropriateBuildings = getBuildingsWhichCanTrainEntity(selection, trainEntType);
var nextBatchTrainingCount = 0;
var currentBatchTrainingCount = 0;
if (inputState == INPUT_BATCHTRAINING && batchTrainingEntities.indexOf(entity) != -1 &&
batchTrainingType == trainEntType)
{
nextBatchTrainingCount = batchTrainingCount;
currentBatchTrainingCount = batchTrainingCount;
var canBeTrainedCount = batchTrainingEntityAllowedCount;
}
else
{
var [trainEntLimit, trainEntCount, canBeTrainedCount] =
getEntityLimitAndCount(playerState, trainEntType);
var batchSize = Math.min(canBeTrainedCount, batchIncrementSize);
}
// We need to calculate count after the next increment if it's possible
if (canBeTrainedCount == undefined ||
canBeTrainedCount > nextBatchTrainingCount * appropriateBuildings.length)
nextBatchTrainingCount += batchIncrementSize;
// If training limits don't allow us to train batchTrainingCount in each appropriate building
// train as many full batches as we can and remainer in one more building.
var buildingsCountToTrainFullBatch = appropriateBuildings.length;
var remainderToTrain = 0;
if (canBeTrainedCount !== undefined &&
canBeTrainedCount < nextBatchTrainingCount * appropriateBuildings.length)
{
buildingsCountToTrainFullBatch = Math.floor(canBeTrainedCount / nextBatchTrainingCount);
remainderToTrain = canBeTrainedCount % nextBatchTrainingCount;
}
return [buildingsCountToTrainFullBatch, nextBatchTrainingCount, remainderToTrain, currentBatchTrainingCount];
}
// Called by GUI when user clicks production queue item
function removeFromProductionQueue(entity, id)
{
Engine.PostNetworkCommand({"type": "stop-production", "entity": entity, "id": id});
}
// Called by unit selection buttons
function changePrimarySelectionGroup(templateName, deselectGroup)
{
if (Engine.HotkeyIsPressed("session.deselectgroup") || deselectGroup)
g_Selection.makePrimarySelection(templateName, true);
else
g_Selection.makePrimarySelection(templateName, false);
}
// Performs the specified command (delete, town bell, repair, etc.)
function performCommand(entity, commandName)
{
if (entity)
{
var entState = GetExtendedEntityState(entity);
var playerID = Engine.GetPlayerID();
if (entState.player == playerID || g_DevSettings.controlAll)
{
switch (commandName)
{
case "delete":
var selection = g_Selection.toList();
if (selection.length > 0)
if (!entState.resourceSupply || !entState.resourceSupply.killBeforeGather)
openDeleteDialog(selection);
break;
case "stop":
var selection = g_Selection.toList();
if (selection.length > 0)
stopUnits(selection);
break;
case "garrison":
inputState = INPUT_PRESELECTEDACTION;
preSelectedAction = ACTION_GARRISON;
break;
case "repair":
inputState = INPUT_PRESELECTEDACTION;
preSelectedAction = ACTION_REPAIR;
break;
case "add-guard":
inputState = INPUT_PRESELECTEDACTION;
preSelectedAction = ACTION_GUARD;
break;
case "remove-guard":
removeGuard();
break;
case "unload-all":
unloadAll();
break;
case "focus-rally":
// if the selected building has a rally point set, move the camera to it; otherwise, move to the building itself
// (since that's where units will spawn without a rally point)
var focusTarget = null;
if (entState.rallyPoint && entState.rallyPoint.position)
{
focusTarget = entState.rallyPoint.position;
}
else
{
if (entState.position)
focusTarget = entState.position;
}
if (focusTarget !== null)
Engine.CameraMoveTo(focusTarget.x, focusTarget.z);
break;
case "back-to-work":
backToWork();
break;
case "increase-alert-level":
increaseAlertLevel();
break;
case "alert-end":
endOfAlert();
break;
case "select-trading-goods":
toggleTrade();
break;
default:
break;
}
}
}
}
// Performs the specified formation
function performFormation(entity, formationName)
{
if (entity)
{
var selection = g_Selection.toList();
Engine.PostNetworkCommand({
"type": "formation",
"entities": selection,
"name": formationName
});
}
}
// Performs the specified group
function performGroup(action, groupId)
{
switch (action)
{
case "snap":
case "select":
case "add":
var toSelect = [];
g_Groups.update();
for (var ent in g_Groups.groups[groupId].ents)
toSelect.push(+ent);
if (action != "add")
g_Selection.reset();
g_Selection.addList(toSelect);
if (action == "snap" && toSelect.length)
Engine.CameraFollow(toSelect[0]);
break;
case "save":
var selection = g_Selection.toList();
g_Groups.groups[groupId].reset();
g_Groups.addEntities(groupId, selection);
updateGroups();
break;
}
}
// Performs the specified stance
function performStance(entity, stanceName)
{
if (entity)
{
var selection = g_Selection.toList();
Engine.PostNetworkCommand({
"type": "stance",
"entities": selection,
"name": stanceName
});
}
}
// Lock / Unlock the gate
function lockGate(lock)
{
var selection = g_Selection.toList();
Engine.PostNetworkCommand({
"type": "lock-gate",
"entities": selection,
"lock": lock,
});
}
// Pack / unpack unit(s)
function packUnit(pack)
{
var selection = g_Selection.toList();
Engine.PostNetworkCommand({
"type": "pack",
"entities": selection,
"pack": pack,
"queued": false
});
}
// Cancel un/packing unit(s)
function cancelPackUnit(pack)
{
var selection = g_Selection.toList();
Engine.PostNetworkCommand({
"type": "cancel-pack",
"entities": selection,
"pack": pack,
"queued": false
});
}
// Transform a wall to a gate
function transformWallToGate(template)
{
var selection = g_Selection.toList();
Engine.PostNetworkCommand({
"type": "wall-to-gate",
"entities": selection.filter( function(e) { return getWallGateTemplate(e) == template } ),
"template": template,
});
}
// Gets the gate form (if any) of a given long wall piece
function getWallGateTemplate(entity)
{
// TODO: find the gate template name in a better way
var entState = GetEntityState(entity);
var index;
if (entState && !entState.foundation && hasClass(entState, "LongWall") && (index = entState.template.indexOf("long")) >= 0)
return entState.template.substr(0, index) + "gate";
return undefined;
}
// Set the camera to follow the given unit
function setCameraFollow(entity)
{
// Follow the given entity if it's a unit
if (entity)
{
var entState = GetEntityState(entity);
if (entState && hasClass(entState, "Unit"))
{
Engine.CameraFollow(entity);
return;
}
}
// Otherwise stop following
Engine.CameraFollow(0);
}
var lastIdleUnit = 0;
var currIdleClass = 0;
var lastIdleType = undefined;
function resetIdleUnit()
{
lastIdleUnit = 0;
currIdleClass = 0;
lastIdleType = undefined;
}
function findIdleUnit(classes)
{
var append = Engine.HotkeyIsPressed("selection.add");
var selectall = Engine.HotkeyIsPressed("selection.offscreen");
// Reset the last idle unit, etc., if the selection type has changed.
var type = classes.join();
if (selectall || type != lastIdleType)
resetIdleUnit();
lastIdleType = type;
// If selectall is true, there is no limit and it's necessary to iterate
// over all of the classes, resetting only when the first match is found.
var matched = false;
for (var i = 0; i < classes.length; ++i)
{
var data = { idleClass: classes[currIdleClass], prevUnit: lastIdleUnit, limit: 1 };
if (append)
data.excludeUnits = g_Selection.toList();
if (selectall)
data = { idleClass: classes[currIdleClass] };
// Check if we have new valid entity
var idleUnits = Engine.GuiInterfaceCall("FindIdleUnits", data);
if (idleUnits.length && idleUnits[0] != lastIdleUnit)
{
lastIdleUnit = idleUnits[0];
if (!append && (!selectall || selectall && !matched))
g_Selection.reset()
if (selectall)
g_Selection.addList(idleUnits);
else
{
g_Selection.addList([lastIdleUnit]);
var position = GetEntityState(lastIdleUnit).position;
Engine.CameraMoveTo(position.x, position.z);
return;
}
matched = true;
}
lastIdleUnit = 0;
currIdleClass = (currIdleClass + 1) % classes.length;
}
// TODO: display a message or play a sound to indicate no more idle units, or something
// Reset for next cycle
resetIdleUnit();
}
function stopUnits(entities)
{
Engine.PostNetworkCommand({ "type": "stop", "entities": entities, "queued": false });
}
function unload(garrisonHolder, entities)
{
if (Engine.HotkeyIsPressed("session.unloadtype"))
Engine.PostNetworkCommand({"type": "unload", "entities": entities, "garrisonHolder": garrisonHolder});
else
Engine.PostNetworkCommand({"type": "unload", "entities": [entities[0]], "garrisonHolder": garrisonHolder});
}
function unloadTemplate(template)
{
// Filter out all entities that aren't garrisonable.
var garrisonHolders = g_Selection.toList().filter(function(e) {
var state = GetEntityState(e);
if (state && state.garrisonHolder)
return true;
return false;
});
Engine.PostNetworkCommand({
"type": "unload-template",
"all": Engine.HotkeyIsPressed("session.unloadtype"),
"template": template,
"garrisonHolders": garrisonHolders
});
}
function unloadAll()
{
// Filter out all entities that aren't garrisonable.
var garrisonHolders = g_Selection.toList().filter(function(e) {
var state = GetEntityState(e);
if (state && state.garrisonHolder)
return true;
return false;
});
Engine.PostNetworkCommand({"type": "unload-all", "garrisonHolders": garrisonHolders});
}
function backToWork()
{
// Filter out all entities that can't go back to work.
var workers = g_Selection.toList().filter(function(e) {
var state = GetEntityState(e);
return (state && state.unitAI && state.unitAI.hasWorkOrders);
});
Engine.PostNetworkCommand({"type": "back-to-work", "entities": workers});
}
function removeGuard()
{
// Filter out all entities that are currently guarding/escorting.
var entities = g_Selection.toList().filter(function(e) {
var state = GetEntityState(e);
return (state && state.unitAI && state.unitAI.isGuarding);
});
Engine.PostNetworkCommand({"type": "remove-guard", "entities": entities});
}
function increaseAlertLevel()
{
var entities = g_Selection.toList().filter(function(e) {
var state = GetEntityState(e);
return (state && state.alertRaiser && state.alertRaiser.canIncreaseLevel);
});
Engine.PostNetworkCommand({"type": "increase-alert-level", "entities": entities});
}
function endOfAlert()
{
var entities = g_Selection.toList().filter(function(e) {
var state = GetEntityState(e);
return (state && state.alertRaiser && state.alertRaiser.hasRaisedAlert);
});
Engine.PostNetworkCommand({"type": "alert-end", "entities": entities});
}
function clearSelection()
{
if(inputState==INPUT_BUILDING_PLACEMENT || inputState==INPUT_BUILDING_WALL_PATHING)
{
inputState = INPUT_NORMAL;
placementSupport.Reset();
}
else
g_Selection.reset();
preSelectedAction = ACTION_NONE;
}
Index: ps/trunk/binaries/data/mods/public/gui/session/messages.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/session/messages.js (revision 14495)
+++ ps/trunk/binaries/data/mods/public/gui/session/messages.js (revision 14496)
@@ -1,543 +1,543 @@
// Chat data
const CHAT_TIMEOUT = 30000;
const MAX_NUM_CHAT_LINES = 20;
var chatMessages = [];
var chatTimers = [];
// Notification Data
const NOTIFICATION_TIMEOUT = 10000;
const MAX_NUM_NOTIFICATION_LINES = 3;
var notifications = [];
var notificationsTimers = [];
var cheats = getCheatsData();
function getCheatsData()
{
var cheats = {};
var cheatFileList = getJSONFileList("simulation/data/cheats/");
for each (var fileName in cheatFileList)
{
var currentCheat = parseJSONData("simulation/data/cheats/"+fileName+".json");
if (Object.keys(cheats).indexOf(currentCheat.Name) !== -1)
warn("Cheat name '"+currentCheat.Name+"' is already present");
else
cheats[currentCheat.Name] = currentCheat.Data;
}
return cheats;
}
// Notifications
function handleNotifications()
{
var notification = Engine.GuiInterfaceCall("GetNextNotification");
if (!notification)
return;
// Handle chat notifications specially
if (notification.type == "chat")
{
var guid = findGuidForPlayerID(g_PlayerAssignments, notification.player);
if (guid == undefined)
{
addChatMessage({
"type": "message",
"guid": -1,
"player": notification.player,
"text": notification.message
});
} else {
addChatMessage({
"type": "message",
"guid": findGuidForPlayerID(g_PlayerAssignments, notification.player),
"text": notification.message
});
}
}
else if (notification.type == "defeat")
{
addChatMessage({
"type": "defeat",
"guid": findGuidForPlayerID(g_PlayerAssignments, notification.player),
"player": notification.player
});
// If the diplomacy panel is open refresh it.
if (isDiplomacyOpen)
openDiplomacy();
}
else if (notification.type == "diplomacy")
{
addChatMessage({
"type": "diplomacy",
"player": notification.player,
"player1": notification.player1,
"status": notification.status
});
// If the diplomacy panel is open refresh it.
if (isDiplomacyOpen)
openDiplomacy();
}
else if (notification.type == "quit")
{
// Used for AI testing
exit();
}
else if (notification.type == "tribute")
{
addChatMessage({
"type": "tribute",
"player": notification.player,
"player1": notification.player1,
"amounts": notification.amounts
});
}
else if (notification.type == "attack")
{
if (notification.player == Engine.GetPlayerID())
{
if (Engine.ConfigDB_GetValue("user", "gui.session.attacknotificationmessage") === "true")
{
addChatMessage({
"type": "attack",
"player": notification.player,
"attacker": notification.attacker
});
}
}
}
else
{
// Only display notifications directed to this player
if (notification.player == Engine.GetPlayerID())
{
notifications.push(notification);
notificationsTimers.push(setTimeout(removeOldNotifications, NOTIFICATION_TIMEOUT));
if (notifications.length > MAX_NUM_NOTIFICATION_LINES)
removeOldNotifications();
else
displayNotifications();
}
}
}
function removeOldNotifications()
{
clearTimeout(notificationsTimers[0]); // The timer only needs to be cleared when new notifications bump old notifications off
notificationsTimers.shift();
notifications.shift();
displayNotifications();
}
function displayNotifications()
{
var messages = [];
for each (var n in notifications)
messages.push(n.message);
- getGUIObjectByName("notificationText").caption = messages.join("\n");
+ Engine.GetGUIObjectByName("notificationText").caption = messages.join("\n");
}
function updateTimeNotifications()
{
- getGUIObjectByName("timeNotificationText").caption = Engine.GuiInterfaceCall("GetTimeNotificationText");
+ Engine.GetGUIObjectByName("timeNotificationText").caption = Engine.GuiInterfaceCall("GetTimeNotificationText");
}
// Returns [username, playercolor] for the given player
function getUsernameAndColor(player)
{
// This case is hit for AIs, whose names don't exist in playerAssignments.
var color = g_Players[player].color;
return [
escapeText(g_Players[player].name),
color.r + " " + color.g + " " + color.b,
];
}
// Messages
function handleNetMessage(message)
{
log("Net message: " + uneval(message));
switch (message.type)
{
case "netstatus":
// If we lost connection, further netstatus messages are useless
if (g_Disconnected)
return;
- var obj = getGUIObjectByName("netStatus");
+ var obj = Engine.GetGUIObjectByName("netStatus");
switch (message.status)
{
case "waiting_for_players":
obj.caption = "Waiting for other players to connect...";
obj.hidden = false;
break;
case "join_syncing":
obj.caption = "Synchronising gameplay with other players...";
obj.hidden = false;
break;
case "active":
obj.caption = "";
obj.hidden = true;
break;
case "connected":
obj.caption = "Connected to the server.";
obj.hidden = false;
break;
case "authenticated":
obj.caption = "Connection to the server has been authenticated.";
obj.hidden = false;
break;
case "disconnected":
g_Disconnected = true;
obj.caption = "Connection to the server has been lost.\n\nThe game has ended.";
obj.hidden = false;
break;
default:
error("Unrecognised netstatus type "+message.status);
break;
}
break;
case "players":
// Find and report all leavings
for (var host in g_PlayerAssignments)
{
if (! message.hosts[host])
{
// Tell the user about the disconnection
addChatMessage({ "type": "disconnect", "guid": host });
// Update the cached player data, so we can display the disconnection status
updatePlayerDataRemove(g_Players, host);
}
}
// Find and report all joinings
for (var host in message.hosts)
{
if (! g_PlayerAssignments[host])
{
// Update the cached player data, so we can display the correct name
updatePlayerDataAdd(g_Players, host, message.hosts[host]);
// Tell the user about the connection
addChatMessage({ "type": "connect", "guid": host }, message.hosts);
}
}
g_PlayerAssignments = message.hosts;
if (g_IsController)
{
var players = [ assignment.name for each (assignment in g_PlayerAssignments) ]
Engine.SendChangeStateGame(Object.keys(g_PlayerAssignments).length, players.join(", "));
}
break;
case "chat":
addChatMessage({ "type": "message", "guid": message.guid, "text": message.text });
break;
// To prevent errors, ignore these message types that occur during autostart
case "gamesetup":
case "start":
break;
default:
error("Unrecognised net message type "+message.type);
}
}
function submitChatDirectly(text)
{
if (text.length)
{
if (g_IsNetworked)
Engine.SendNetworkChat(text);
else
addChatMessage({ "type": "message", "guid": "local", "text": text });
}
}
function submitChatInput()
{
- var input = getGUIObjectByName("chatInput");
+ var input = Engine.GetGUIObjectByName("chatInput");
var text = input.caption;
var isCheat = false;
if (text.length)
{
if (g_Players[Engine.GetPlayerID()].cheatsEnabled)
{
for each (var cheat in Object.keys(cheats))
{
// Line must start with the cheat.
if (text.indexOf(cheat) !== 0)
continue;
var number;
if (cheats[cheat].DefaultNumber !== undefined)
{
// Match the first word in the substring.
var match = text.substr(cheat.length).match(/\S+/);
if (match && match[0])
number = Math.floor(match[0]);
if (number <= 0 || isNaN(number))
number = cheats[cheat].DefaultNumber;
}
Engine.PostNetworkCommand({
"type": "cheat",
"action": cheats[cheat].Action,
"number": number,
"text": cheats[cheat].Type,
"selected": g_Selection.toList(),
"templates": cheats[cheat].Templates,
"player": Engine.GetPlayerID()});
isCheat = true;
break;
}
}
if (!isCheat)
{
- if (getGUIObjectByName("toggleTeamChat").checked)
+ if (Engine.GetGUIObjectByName("toggleTeamChat").checked)
text = "/team " + text;
if (g_IsNetworked)
Engine.SendNetworkChat(text);
else
addChatMessage({ "type": "message", "guid": "local", "text": text });
}
input.caption = ""; // Clear chat input
}
input.blur(); // Remove focus
toggleChatWindow();
}
function addChatMessage(msg, playerAssignments)
{
// Default to global assignments, but allow overriding for when reporting
// new players joining
if (!playerAssignments)
playerAssignments = g_PlayerAssignments;
var playerColor, username;
// No prefix by default. May be set by parseChatCommands().
msg.prefix = "";
if (playerAssignments[msg.guid])
{
var n = playerAssignments[msg.guid].player;
playerColor = g_Players[n].color.r + " " + g_Players[n].color.g + " " + g_Players[n].color.b;
username = escapeText(playerAssignments[msg.guid].name);
// Parse in-line commands in regular messages.
if (msg.type == "message")
parseChatCommands(msg, playerAssignments);
}
else if (msg.type == "defeat" && msg.player)
{
[username, playerColor] = getUsernameAndColor(msg.player);
}
else if (msg.type == "message")
{
[username, playerColor] = getUsernameAndColor(msg.player);
parseChatCommands(msg, playerAssignments);
}
else
{
playerColor = "255 255 255";
username = "Unknown player";
}
var message = escapeText(msg.text);
var formatted;
switch (msg.type)
{
case "connect":
formatted = "[color=\"" + playerColor + "\"]" + username + "[/color] has joined the game.";
break;
case "disconnect":
formatted = "[color=\"" + playerColor + "\"]" + username + "[/color] has left the game.";
break;
case "defeat":
// In singleplayer, the local player is "You". "You has" is incorrect.
var verb = (!g_IsNetworked && msg.player == Engine.GetPlayerID()) ? "have" : "has";
formatted = "[color=\"" + playerColor + "\"]" + username + "[/color] " + verb + " been defeated.";
break;
case "diplomacy":
var status = (msg.status == "ally" ? "allied" : (msg.status == "enemy" ? "at war" : "neutral"));
if (msg.player == Engine.GetPlayerID())
{
[username, playerColor] = getUsernameAndColor(msg.player1);
formatted = "You are now "+status+" with [color=\"" + playerColor + "\"]"+username + "[/color].";
}
else if (msg.player1 == Engine.GetPlayerID())
{
[username, playerColor] = getUsernameAndColor(msg.player);
formatted = "[color=\"" + playerColor + "\"]" + username + "[/color] is now " + status + " with you."
}
else // No need for other players to know of this.
return;
break;
case "tribute":
if (msg.player != Engine.GetPlayerID())
return;
[username, playerColor] = getUsernameAndColor(msg.player1);
// Format the amounts to proper English: 200 food, 100 wood, and 300 metal; 100 food; 400 wood and 200 stone
var amounts = Object.keys(msg.amounts)
.filter(function (type) { return msg.amounts[type] > 0; })
.map(function (type) { return msg.amounts[type] + " " + type; });
if (amounts.length > 1)
{
var lastAmount = amounts.pop();
amounts = amounts.join(", ") + " and " + lastAmount;
}
formatted = "[color=\"" + playerColor + "\"]" + username + "[/color] has sent you " + amounts + ".";
break;
case "attack":
if (msg.player != Engine.GetPlayerID())
return;
[username, playerColor] = getUsernameAndColor(msg.attacker);
formatted = "You have been attacked by [color=\"" + playerColor + "\"]" + username + "[/color]!";
break;
case "message":
// May have been hidden by the 'team' command.
if (msg.hide)
return;
if (msg.action)
{
Engine.Console_Write(msg.prefix + "* " + username + " " + message);
formatted = msg.prefix + "* [color=\"" + playerColor + "\"]" + username + "[/color] " + message;
}
else
{
Engine.Console_Write(msg.prefix + "<" + username + "> " + message);
formatted = msg.prefix + "<[color=\"" + playerColor + "\"]" + username + "[/color]> " + message;
}
break;
default:
error("Invalid chat message '" + uneval(msg) + "'");
return;
}
chatMessages.push(formatted);
chatTimers.push(setTimeout(removeOldChatMessages, CHAT_TIMEOUT));
if (chatMessages.length > MAX_NUM_CHAT_LINES)
removeOldChatMessages();
else
- getGUIObjectByName("chatText").caption = chatMessages.join("\n");
+ Engine.GetGUIObjectByName("chatText").caption = chatMessages.join("\n");
}
function removeOldChatMessages()
{
clearTimeout(chatTimers[0]); // The timer only needs to be cleared when new messages bump old messages off
chatTimers.shift();
chatMessages.shift();
- getGUIObjectByName("chatText").caption = chatMessages.join("\n");
+ Engine.GetGUIObjectByName("chatText").caption = chatMessages.join("\n");
}
// Parses chat messages for commands.
function parseChatCommands(msg, playerAssignments)
{
// Only interested in messages that start with '/'.
if (!msg.text || msg.text[0] != '/')
return;
var sender;
if (playerAssignments[msg.guid])
sender = playerAssignments[msg.guid].player;
else
sender = msg.player;
var recurse = false;
var split = msg.text.split(/\s/);
// Parse commands embedded in the message.
switch (split[0])
{
case "/all":
// Resets values that 'team' or 'enemy' may have set.
msg.prefix = "";
msg.hide = false;
recurse = true;
break;
case "/team":
// Check if we are in a team.
if (g_Players[Engine.GetPlayerID()] && g_Players[Engine.GetPlayerID()].team != -1)
{
if (g_Players[Engine.GetPlayerID()].team != g_Players[sender].team)
msg.hide = true;
else
msg.prefix = "(Team) ";
}
else
msg.hide = true;
recurse = true;
break;
case "/enemy":
// Check if we are in a team.
if (g_Players[Engine.GetPlayerID()] && g_Players[Engine.GetPlayerID()].team != -1)
{
if (g_Players[Engine.GetPlayerID()].team == g_Players[sender].team && sender != Engine.GetPlayerID())
msg.hide = true;
else
msg.prefix = "(Enemy) ";
}
recurse = true;
break;
case "/me":
msg.action = true;
break;
case "/msg":
var trimmed = msg.text.substr(split[0].length + 1);
var matched = "";
// Reject names which don't match or are a superset of the intended name.
for each (var player in playerAssignments)
if (trimmed.indexOf(player.name + " ") == 0 && player.name.length > matched.length)
matched = player.name;
// If the local player's name was the longest one matched, show the message.
var playerName = g_Players[Engine.GetPlayerID()].name;
if (matched.length && (matched == playerName || sender == Engine.GetPlayerID()))
{
msg.prefix = "(Private) ";
msg.text = trimmed.substr(matched.length + 1);
msg.hide = false; // Might override team message hiding.
return;
}
else
msg.hide = true;
break;
default:
return;
}
msg.text = msg.text.substr(split[0].length + 1);
// Hide the message if parsing commands left it empty.
if (!msg.text.length)
msg.hide = true;
// Attempt to parse more commands if the current command allows it.
if (recurse)
parseChatCommands(msg, playerAssignments);
}
Index: ps/trunk/binaries/data/mods/public/gui/session/session.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/session/session.js (revision 14495)
+++ ps/trunk/binaries/data/mods/public/gui/session/session.js (revision 14496)
@@ -1,894 +1,894 @@
// Network Mode
var g_IsNetworked = false;
// Is this user in control of game settings (i.e. is a network server, or offline player)
var g_IsController;
// Match ID for tracking
var g_MatchID;
// Cache the basic player data (name, civ, color)
var g_Players = [];
// Cache the useful civ data
var g_CivData = {};
var g_GameSpeeds = {};
var g_CurrentSpeed;
var g_PlayerAssignments = { "local": { "name": "You", "player": 1 } };
// Cache dev-mode settings that are frequently or widely used
var g_DevSettings = {
controlAll: false
};
// Whether status bars should be shown for all of the player's units.
var g_ShowAllStatusBars = false;
// Indicate when one of the current player's training queues is blocked
// (this is used to support population counter blinking)
var g_IsTrainingBlocked = false;
// Cache simulation state (updated on every simulation update)
var g_SimState;
// Cache EntityStates
var g_EntityStates = {}; // {id:entState}
// Whether the player has lost/won and reached the end of their game
var g_GameEnded = false;
var g_Disconnected = false; // Lost connection to server
// Holds player states from the last tick
var g_CachedLastStates = "";
// Colors to flash when pop limit reached
const DEFAULT_POPULATION_COLOR = "white";
const POPULATION_ALERT_COLOR = "orange";
// List of additional entities to highlight
var g_ShowGuarding = false;
var g_ShowGuarded = false;
var g_AdditionalHighlight = [];
function GetSimState()
{
if (!g_SimState)
g_SimState = Engine.GuiInterfaceCall("GetSimulationState");
return g_SimState;
}
function GetEntityState(entId)
{
if (!(entId in g_EntityStates))
{
var entState = Engine.GuiInterfaceCall("GetEntityState", entId);
if (entState)
entState.extended = false;
g_EntityStates[entId] = entState;
}
return g_EntityStates[entId];
}
function GetExtendedEntityState(entId)
{
if (entId in g_EntityStates)
var entState = g_EntityStates[entId];
else
var entState = Engine.GuiInterfaceCall("GetEntityState", entId);
if (!entState || entState.extended)
return entState;
var extension = Engine.GuiInterfaceCall("GetExtendedEntityState", entId);
for (var prop in extension)
entState[prop] = extension[prop];
entState.extended = true;
g_EntityStates[entId] = entState;
return entState;
}
// Cache TemplateData
var g_TemplateData = {}; // {id:template}
function GetTemplateData(templateName)
{
if (!(templateName in g_TemplateData))
{
var template = Engine.GuiInterfaceCall("GetTemplateData", templateName);
g_TemplateData[templateName] = template;
}
return g_TemplateData[templateName];
}
// Cache TechnologyData
var g_TechnologyData = {}; // {id:template}
function GetTechnologyData(technologyName)
{
if (!(technologyName in g_TechnologyData))
{
var template = Engine.GuiInterfaceCall("GetTechnologyData", technologyName);
g_TechnologyData[technologyName] = template;
}
return g_TechnologyData[technologyName];
}
// Init
function init(initData, hotloadData)
{
if (initData)
{
g_IsNetworked = initData.isNetworked; // Set network mode
g_IsController = initData.isController; // Set controller mode
g_PlayerAssignments = initData.playerAssignments;
g_MatchID = initData.attribs.matchID;
// Cache the player data
// (This may be updated at runtime by handleNetMessage)
g_Players = getPlayerData(g_PlayerAssignments);
if (initData.savedGUIData)
restoreSavedGameData(initData.savedGUIData);
- getGUIObjectByName("gameSpeedButton").hidden = g_IsNetworked;
+ Engine.GetGUIObjectByName("gameSpeedButton").hidden = g_IsNetworked;
}
else // Needed for autostart loading option
{
g_Players = getPlayerData(null);
}
// Cache civ data
g_CivData = loadCivData();
g_CivData["gaia"] = { "Code": "gaia", "Name": "Gaia" };
g_GameSpeeds = initGameSpeeds();
g_CurrentSpeed = Engine.GetSimRate();
- var gameSpeed = getGUIObjectByName("gameSpeed");
+ var gameSpeed = Engine.GetGUIObjectByName("gameSpeed");
gameSpeed.list = g_GameSpeeds.names;
gameSpeed.list_data = g_GameSpeeds.speeds;
var idx = g_GameSpeeds.speeds.indexOf(g_CurrentSpeed);
gameSpeed.selected = idx != -1 ? idx : g_GameSpeeds["default"];
gameSpeed.onSelectionChange = function() { changeGameSpeed(+this.list_data[this.selected]); }
- getGUIObjectByName("civIcon").sprite = "stretched:" + g_CivData[g_Players[Engine.GetPlayerID()].civ].Emblem;
- getGUIObjectByName("civIcon").tooltip = g_CivData[g_Players[Engine.GetPlayerID()].civ].Name;
+ Engine.GetGUIObjectByName("civIcon").sprite = "stretched:" + g_CivData[g_Players[Engine.GetPlayerID()].civ].Emblem;
+ Engine.GetGUIObjectByName("civIcon").tooltip = g_CivData[g_Players[Engine.GetPlayerID()].civ].Name;
initMenuPosition(); // set initial position
// Populate player selection dropdown
var playerNames = [];
var playerIDs = [];
for (var player in g_Players)
{
playerNames.push(g_Players[player].name);
playerIDs.push(player);
}
- var viewPlayerDropdown = getGUIObjectByName("viewPlayer");
+ var viewPlayerDropdown = Engine.GetGUIObjectByName("viewPlayer");
viewPlayerDropdown.list = playerNames;
viewPlayerDropdown.list_data = playerIDs;
viewPlayerDropdown.selected = Engine.GetPlayerID();
// If in Atlas editor, disable the exit button
if (Engine.IsAtlasRunning())
- getGUIObjectByName("menuExitButton").enabled = false;
+ Engine.GetGUIObjectByName("menuExitButton").enabled = false;
if (hotloadData)
{
g_Selection.selected = hotloadData.selection;
}
else
{
// Starting for the first time:
var civMusic = g_CivData[g_Players[Engine.GetPlayerID()].civ].Music;
initMusic();
global.music.storeTracks(civMusic);
global.music.setState(global.music.states.PEACE);
playRandomAmbient("temperate");
}
if (Engine.ConfigDB_GetValue("user", "gui.session.timeelapsedcounter") === "true")
- getGUIObjectByName("timeElapsedCounter").hidden = false;
+ Engine.GetGUIObjectByName("timeElapsedCounter").hidden = false;
onSimulationUpdate();
// Report the performance after 5 seconds (when we're still near
// the initial camera view) and a minute (when the profiler will
// have settled down if framerates as very low), to give some
// extremely rough indications of performance
setTimeout(function() { reportPerformance(5); }, 5000);
setTimeout(function() { reportPerformance(60); }, 60000);
}
function selectViewPlayer(playerID)
{
Engine.SetPlayerID(playerID);
if (playerID != 0) {
- getGUIObjectByName("civIcon").sprite = "stretched:" + g_CivData[g_Players[playerID].civ].Emblem;
- getGUIObjectByName("civIcon").tooltip = g_CivData[g_Players[playerID].civ].Name;
+ Engine.GetGUIObjectByName("civIcon").sprite = "stretched:" + g_CivData[g_Players[playerID].civ].Emblem;
+ Engine.GetGUIObjectByName("civIcon").tooltip = g_CivData[g_Players[playerID].civ].Name;
}
}
function reportPerformance(time)
{
var settings = Engine.GetMapSettings();
var data = {
time: time,
map: settings.Name,
seed: settings.Seed, // only defined for random maps
size: settings.Size, // only defined for random maps
profiler: Engine.GetProfilerState()
};
Engine.SubmitUserReport("profile", 3, JSON.stringify(data));
}
function resignGame()
{
var simState = GetSimState();
// Players can't resign if they've already won or lost.
if (simState.players[Engine.GetPlayerID()].state != "active" || g_Disconnected)
return;
// Tell other players that we have given up and been defeated
Engine.PostNetworkCommand({
"type": "defeat-player",
"playerId": Engine.GetPlayerID()
});
global.music.setState(global.music.states.DEFEAT);
resumeGame();
}
function leaveGame()
{
var extendedSimState = Engine.GuiInterfaceCall("GetExtendedSimulationState");
var playerState = extendedSimState.players[Engine.GetPlayerID()];
var mapSettings = Engine.GetMapSettings();
var gameResult;
if (g_Disconnected)
{
gameResult = "You have been disconnected."
}
else if (playerState.state == "won")
{
gameResult = "You have won the battle!";
}
else if (playerState.state == "defeated")
{
gameResult = "You have been defeated...";
}
else // "active"
{
gameResult = "You have abandoned the game.";
// Tell other players that we have given up and been defeated
Engine.PostNetworkCommand({
"type": "defeat-player",
"playerId": Engine.GetPlayerID()
});
global.music.setState(global.music.states.DEFEAT);
}
stopAmbient();
- endGame();
+ Engine.EndGame();
if (g_IsController && Engine.HasXmppClient())
Engine.SendUnregisterGame();
Engine.SwitchGuiPage("page_summary.xml", {
"gameResult" : gameResult,
"timeElapsed" : extendedSimState.timeElapsed,
"playerStates": extendedSimState.players,
"players": g_Players,
"mapSettings": mapSettings
});
}
// Return some data that we'll use when hotloading this file after changes
function getHotloadData()
{
return { selection: g_Selection.selected };
}
// Return some data that will be stored in saved game files
function getSavedGameData()
{
var data = {};
data.playerAssignments = g_PlayerAssignments;
data.groups = g_Groups.groups;
// TODO: any other gui state?
return data;
}
function restoreSavedGameData(data)
{
// Clear selection when loading a game
g_Selection.reset();
// Restore control groups
for (var groupNumber in data.groups)
{
g_Groups.groups[groupNumber].groups = data.groups[groupNumber].groups;
g_Groups.groups[groupNumber].ents = data.groups[groupNumber].ents;
}
updateGroups();
}
var lastTickTime = new Date;
/**
* Called every frame.
*/
function onTick()
{
var now = new Date;
var tickLength = new Date - lastTickTime;
lastTickTime = now;
checkPlayerState();
while (true)
{
var message = Engine.PollNetworkClient();
if (!message)
break;
handleNetMessage(message);
}
updateCursorAndTooltip();
// If the selection changed, we need to regenerate the sim display (the display depends on both the
// simulation state and the current selection).
if (g_Selection.dirty)
{
g_Selection.dirty = false;
onSimulationUpdate();
// Display rally points for selected buildings
Engine.GuiInterfaceCall("DisplayRallyPoint", { "entities": g_Selection.toList() });
}
// Run timers
updateTimers();
// Animate menu
updateMenuPosition(tickLength);
// When training is blocked, flash population (alternates colour every 500msec)
if (g_IsTrainingBlocked && (Date.now() % 1000) < 500)
- getGUIObjectByName("resourcePop").textcolor = POPULATION_ALERT_COLOR;
+ Engine.GetGUIObjectByName("resourcePop").textcolor = POPULATION_ALERT_COLOR;
else
- getGUIObjectByName("resourcePop").textcolor = DEFAULT_POPULATION_COLOR;
+ Engine.GetGUIObjectByName("resourcePop").textcolor = DEFAULT_POPULATION_COLOR;
// Clear renamed entities list
Engine.GuiInterfaceCall("ClearRenamedEntities");
}
function checkPlayerState()
{
// Once the game ends, we're done here.
if (g_GameEnded)
return;
// Send a game report for each player in this game.
var m_simState = GetSimState();
var playerState = m_simState.players[Engine.GetPlayerID()];
var tempStates = "";
for each (var player in m_simState.players) {tempStates += player.state + ",";}
if (g_CachedLastStates != tempStates)
{
g_CachedLastStates = tempStates;
reportGame(Engine.GuiInterfaceCall("GetExtendedSimulationState"));
}
// If the local player hasn't finished playing, we return here to avoid the victory/defeat messages.
if (playerState.state == "active")
return;
// We can't resign once the game is over.
- getGUIObjectByName("menuResignButton").enabled = false;
+ Engine.GetGUIObjectByName("menuResignButton").enabled = false;
// Make sure nothing is open to avoid stacking.
closeMenu();
closeOpenDialogs();
// Make sure this doesn't run again.
g_GameEnded = true;
if (Engine.IsAtlasRunning())
{
// If we're in Atlas, we can't leave the game
var btCaptions = ["OK"];
var btCode = [null];
var message = "Press OK to continue";
}
else
{
var btCaptions = ["Yes", "No"];
var btCode = [leaveGame, null];
var message = "Do you want to quit?";
}
if (playerState.state == "defeated")
{
global.music.setState(global.music.states.DEFEAT);
messageBox(400, 200, message, "DEFEATED!", 0, btCaptions, btCode);
}
else if (playerState.state == "won")
{
global.music.setState(global.music.states.VICTORY);
// TODO: Reveal map directly instead of this silly proxy.
- if (!getGUIObjectByName("devCommandsRevealMap").checked)
- getGUIObjectByName("devCommandsRevealMap").checked = true;
+ if (!Engine.GetGUIObjectByName("devCommandsRevealMap").checked)
+ Engine.GetGUIObjectByName("devCommandsRevealMap").checked = true;
messageBox(400, 200, message, "VICTORIOUS!", 0, btCaptions, btCode);
}
}
function changeGameSpeed(speed)
{
// For non-networked games only
if (!g_IsNetworked)
{
Engine.SetSimRate(speed);
g_CurrentSpeed = speed;
}
}
/**
* Recomputes GUI state that depends on simulation state or selection state. Called directly every simulation
* update (see session.xml), or from onTick when the selection has changed.
*/
function onSimulationUpdate()
{
g_EntityStates = {};
g_TemplateData = {};
g_TechnologyData = {};
g_SimState = Engine.GuiInterfaceCall("GetSimulationState");
// If we're called during init when the game is first loading, there will be no simulation yet, so do nothing
if (!g_SimState)
return;
handleNotifications();
if (g_ShowAllStatusBars)
recalculateStatusBarDisplay();
if (g_ShowGuarding || g_ShowGuarded)
updateAdditionalHighlight();
updateHero();
updateGroups();
updateDebug();
updatePlayerDisplay();
updateSelectionDetails();
updateResearchDisplay();
updateBuildingPlacementPreview();
updateTimeElapsedCounter();
updateTimeNotifications();
// Update music state on basis of battle state.
var battleState = Engine.GuiInterfaceCall("GetBattleState", Engine.GetPlayerID());
if (battleState)
global.music.setState(global.music.states[battleState]);
}
function updateHero()
{
var simState = GetSimState();
var playerState = simState.players[Engine.GetPlayerID()];
- var heroButton = getGUIObjectByName("unitHeroButton");
+ var heroButton = Engine.GetGUIObjectByName("unitHeroButton");
if (!playerState || playerState.heroes.length <= 0)
{
heroButton.hidden = true;
return;
}
- var heroImage = getGUIObjectByName("unitHeroImage");
+ var heroImage = Engine.GetGUIObjectByName("unitHeroImage");
var heroState = GetExtendedEntityState(playerState.heroes[0]);
var template = GetTemplateData(heroState.template);
heroImage.sprite = "stretched:session/portraits/" + template.icon;
var hero = playerState.heroes[0];
heroButton.onpress = function()
{
if (!Engine.HotkeyIsPressed("selection.add"))
g_Selection.reset();
g_Selection.addList([hero]);
};
heroButton.ondoublepress = function() { selectAndMoveTo(getEntityOrHolder(hero)); };
heroButton.hidden = false;
// Setup tooltip
var tooltip = "[font=\"serif-bold-16\"]" + template.name.specific + "[/font]";
tooltip += "\n[font=\"serif-bold-13\"]Health:[/font] " + heroState.hitpoints + "/" + heroState.maxHitpoints;
tooltip += "\n[font=\"serif-bold-13\"]" + (heroState.attack ? heroState.attack.type + " " : "")
+ "Attack:[/font] " + damageTypeDetails(heroState.attack);
// Show max attack range if ranged attack, also convert to tiles (4m per tile)
if (heroState.attack && heroState.attack.type == "Ranged")
tooltip += ", [font=\"serif-bold-13\"]Range:[/font] " + Math.round(heroState.attack.maxRange/4);
tooltip += "\n[font=\"serif-bold-13\"]Armor:[/font] " + damageTypeDetails(heroState.armour);
tooltip += "\n" + template.tooltip;
heroButton.tooltip = tooltip;
};
function updateGroups()
{
var guiName = "Group";
g_Groups.update();
for (var i = 0; i < 10; i++)
{
- var button = getGUIObjectByName("unit"+guiName+"Button["+i+"]");
- var label = getGUIObjectByName("unit"+guiName+"Label["+i+"]").caption = i;
+ var button = Engine.GetGUIObjectByName("unit"+guiName+"Button["+i+"]");
+ var label = Engine.GetGUIObjectByName("unit"+guiName+"Label["+i+"]").caption = i;
if (g_Groups.groups[i].getTotalCount() == 0)
button.hidden = true;
else
button.hidden = false;
button.onpress = (function(i) { return function() { performGroup((Engine.HotkeyIsPressed("selection.add") ? "add" : "select"), i); } })(i);
button.ondoublepress = (function(i) { return function() { performGroup("snap", i); } })(i);
}
var numButtons = i;
var rowLength = 1;
var numRows = Math.ceil(numButtons / rowLength);
- var buttonSideLength = getGUIObjectByName("unit"+guiName+"Button[0]").size.bottom;
+ var buttonSideLength = Engine.GetGUIObjectByName("unit"+guiName+"Button[0]").size.bottom;
var buttonSpacer = buttonSideLength+1;
for (var i = 0; i < numRows; i++)
layoutButtonRow(i, guiName, buttonSideLength, buttonSpacer, rowLength*i, rowLength*(i+1) );
}
function updateDebug()
{
var simState = GetSimState();
- var debug = getGUIObjectByName("debug");
+ var debug = Engine.GetGUIObjectByName("debug");
- if (getGUIObjectByName("devDisplayState").checked)
+ if (Engine.GetGUIObjectByName("devDisplayState").checked)
{
debug.hidden = false;
}
else
{
debug.hidden = true;
return;
}
var conciseSimState = deepcopy(simState);
conciseSimState.players = "<<>>";
var text = "simulation: " + uneval(conciseSimState);
var selection = g_Selection.toList();
if (selection.length)
{
var entState = GetExtendedEntityState(selection[0]);
if (entState)
{
var template = GetTemplateData(entState.template);
text += "\n\nentity: {\n";
for (var k in entState)
text += " "+k+":"+uneval(entState[k])+"\n";
text += "}\n\ntemplate: " + uneval(template);
}
}
debug.caption = text;
}
function updatePlayerDisplay()
{
var simState = GetSimState();
var playerState = simState.players[Engine.GetPlayerID()];
if (!playerState)
return;
- getGUIObjectByName("resourceFood").caption = playerState.resourceCounts.food;
- getGUIObjectByName("resourceWood").caption = playerState.resourceCounts.wood;
- getGUIObjectByName("resourceStone").caption = playerState.resourceCounts.stone;
- getGUIObjectByName("resourceMetal").caption = playerState.resourceCounts.metal;
- getGUIObjectByName("resourcePop").caption = playerState.popCount + "/" + playerState.popLimit;
+ Engine.GetGUIObjectByName("resourceFood").caption = playerState.resourceCounts.food;
+ Engine.GetGUIObjectByName("resourceWood").caption = playerState.resourceCounts.wood;
+ Engine.GetGUIObjectByName("resourceStone").caption = playerState.resourceCounts.stone;
+ Engine.GetGUIObjectByName("resourceMetal").caption = playerState.resourceCounts.metal;
+ Engine.GetGUIObjectByName("resourcePop").caption = playerState.popCount + "/" + playerState.popLimit;
g_IsTrainingBlocked = playerState.trainingBlocked;
}
function selectAndMoveTo(ent)
{
var entState = GetEntityState(ent);
if (!entState || !entState.position)
return;
g_Selection.reset();
g_Selection.addList([ent]);
var position = entState.position;
Engine.CameraMoveTo(position.x, position.z);
}
function updateResearchDisplay()
{
var researchStarted = Engine.GuiInterfaceCall("GetStartedResearch", Engine.GetPlayerID());
if (!researchStarted)
return;
// Set up initial positioning.
- var buttonSideLength = getGUIObjectByName("researchStartedButton[0]").size.right;
+ var buttonSideLength = Engine.GetGUIObjectByName("researchStartedButton[0]").size.right;
for (var i = 0; i < 10; ++i)
{
- var button = getGUIObjectByName("researchStartedButton[" + i + "]");
+ var button = Engine.GetGUIObjectByName("researchStartedButton[" + i + "]");
var size = button.size;
size.top = (4 + buttonSideLength) * i;
size.bottom = size.top + buttonSideLength;
button.size = size;
}
var numButtons = 0;
for (var tech in researchStarted)
{
// Show at most 10 in-progress techs.
if (numButtons >= 10)
break;
var template = GetTechnologyData(tech);
- var button = getGUIObjectByName("researchStartedButton[" + numButtons + "]");
+ var button = Engine.GetGUIObjectByName("researchStartedButton[" + numButtons + "]");
button.hidden = false;
button.tooltip = getEntityNames(template);
button.onpress = (function(e) { return function() { selectAndMoveTo(e) } })(researchStarted[tech].researcher);
var icon = "stretched:session/portraits/" + template.icon;
- getGUIObjectByName("researchStartedIcon[" + numButtons + "]").sprite = icon;
+ Engine.GetGUIObjectByName("researchStartedIcon[" + numButtons + "]").sprite = icon;
// Scale the progress indicator.
- var size = getGUIObjectByName("researchStartedProgressSlider[" + numButtons + "]").size;
+ var size = Engine.GetGUIObjectByName("researchStartedProgressSlider[" + numButtons + "]").size;
// Buttons are assumed to be square, so left/right offsets can be used for top/bottom.
size.top = size.left + Math.round(researchStarted[tech].progress * (size.right - size.left));
- getGUIObjectByName("researchStartedProgressSlider[" + numButtons + "]").size = size;
+ Engine.GetGUIObjectByName("researchStartedProgressSlider[" + numButtons + "]").size = size;
++numButtons;
}
// Hide unused buttons.
for (var i = numButtons; i < 10; ++i)
- getGUIObjectByName("researchStartedButton[" + i + "]").hidden = true;
+ Engine.GetGUIObjectByName("researchStartedButton[" + i + "]").hidden = true;
}
function updateTimeElapsedCounter()
{
var simState = GetSimState();
var speed = g_CurrentSpeed != 1.0 ? " (" + g_CurrentSpeed + "x)" : "";
- var timeElapsedCounter = getGUIObjectByName("timeElapsedCounter");
+ var timeElapsedCounter = Engine.GetGUIObjectByName("timeElapsedCounter");
timeElapsedCounter.caption = timeToString(simState.timeElapsed) + speed;
}
// Toggles the display of status bars for all of the player's entities.
function recalculateStatusBarDisplay()
{
if (g_ShowAllStatusBars)
var entities = Engine.PickFriendlyEntitiesOnScreen(Engine.GetPlayerID());
else
{
var selected = g_Selection.toList();
for each (var ent in g_Selection.highlighted)
selected.push(ent);
// Remove selected entities from the 'all entities' array, to avoid disabling their status bars.
var entities = Engine.GuiInterfaceCall("GetPlayerEntities").filter(
function(idx) { return (selected.indexOf(idx) == -1); }
);
}
Engine.GuiInterfaceCall("SetStatusBars", { "entities": entities, "enabled": g_ShowAllStatusBars });
}
// Update the additional list of entities to be highlighted.
function updateAdditionalHighlight()
{
var entsAdd = []; // list of entities units to be highlighted
var entsRemove = [];
var highlighted = g_Selection.toList();
for each (var ent in g_Selection.highlighted)
highlighted.push(ent);
if (g_ShowGuarding)
{
// flag the guarding entities to add in this additional highlight
for each (var sel in g_Selection.selected)
{
var state = GetEntityState(sel);
if (!state.guard || !state.guard.entities.length)
continue;
for each (var ent in state.guard.entities)
if (highlighted.indexOf(ent) == -1 && entsAdd.indexOf(ent) == -1)
entsAdd.push(ent);
}
}
if (g_ShowGuarded)
{
// flag the guarded entities to add in this additional highlight
for each (var sel in g_Selection.selected)
{
var state = GetEntityState(sel);
if (!state.unitAI || !state.unitAI.isGuarding)
continue;
var ent = state.unitAI.isGuarding;
if (highlighted.indexOf(ent) == -1 && entsAdd.indexOf(ent) == -1)
entsAdd.push(ent);
}
}
// flag the entities to remove (from the previously added) from this additional highlight
for each (var ent in g_AdditionalHighlight)
if (highlighted.indexOf(ent) == -1 && entsAdd.indexOf(ent) == -1 && entsRemove.indexOf(ent) == -1)
entsRemove.push(ent);
_setHighlight(entsAdd , HIGHLIGHTED_ALPHA, true );
_setHighlight(entsRemove, 0 , false);
g_AdditionalHighlight = entsAdd;
}
// Temporarily adding this here
const AMBIENT_TEMPERATE = "temperate";
var currentAmbient;
function playRandomAmbient(type)
{
switch (type)
{
case AMBIENT_TEMPERATE:
// Seem to need the underscore at the end of "temperate" to avoid crash
// (Might be caused by trying to randomly load day_temperate.xml)
// currentAmbient = newRandomSound("ambient", "temperate_", "dayscape");
const AMBIENT = "audio/ambient/dayscape/day_temperate_gen_03.ogg";
Engine.PlayAmbientSound( AMBIENT, true );
break;
default:
Engine.Console_Write("Unrecognized ambient type: " + type);
break;
}
}
// Temporarily adding this here
function stopAmbient()
{
if (currentAmbient)
{
currentAmbient.free();
currentAmbient = null;
}
}
// Send a report on the game status to the lobby
function reportGame(extendedSimState)
{
if (!Engine.HasXmppClient())
return;
// Resources gathered and used
var playerFoodGatheredString = "";
var playerWoodGatheredString = "";
var playerStoneGatheredString = "";
var playerMetalGatheredString = "";
var playerFoodUsedString = "";
var playerWoodUsedString = "";
var playerStoneUsedString = "";
var playerMetalUsedString = "";
// Resources exchanged
var playerFoodBoughtString = "";
var playerWoodBoughtString = "";
var playerStoneBoughtString = "";
var playerMetalBoughtString = "";
var playerFoodSoldString = "";
var playerWoodSoldString = "";
var playerStoneSoldString = "";
var playerMetalSoldString = "";
var playerTradeIncomeString = "";
// Unit Stats
var playerUnitsLostString = "";
var playerUnitsTrainedString = "";
var playerEnemyUnitsKilledString = "";
// Building stats
var playerBuildingsConstructedString = "";
var playerBuildingsLostString = "";
var playerEnemyBuildingsDestroyedString = "";
var playerCivCentersBuiltString = "";
var playerEnemyCivCentersDestroyedString = "";
// Tribute
var playerTributeSentString = "";
var playerTributeReceivedString = "";
// Various
var mapName = Engine.GetMapSettings().Name;
var playerStatesString = "";
var playerCivsString = "";
var playerPercentMapExploredString = "";
var playerTreasuresCollectedString = "";
// Serialize the statistics for each player into a comma-separated list.
for each (var player in extendedSimState.players)
{
playerStatesString += player.state + ",";
playerCivsString += player.civ + ",";
playerFoodGatheredString += player.statistics.resourcesGathered.food + ",";
playerWoodGatheredString += player.statistics.resourcesGathered.wood + ",";
playerStoneGatheredString += player.statistics.resourcesGathered.stone + ",";
playerMetalGatheredString += player.statistics.resourcesGathered.metal + ",";
playerFoodUsedString += player.statistics.resourcesUsed.food + ",";
playerWoodUsedString += player.statistics.resourcesUsed.wood + ",";
playerStoneUsedString += player.statistics.resourcesUsed.stone + ",";
playerMetalUsedString += player.statistics.resourcesUsed.metal + ",";
playerUnitsLostString += player.statistics.unitsLost + ",";
playerUnitsTrainedString += player.statistics.unitsTrained + ",";
playerEnemyUnitsKilledString += player.statistics.enemyUnitsKilled + ",";
playerBuildingsConstructedString += player.statistics.buildingsConstructed + ",";
playerBuildingsLostString += player.statistics.buildingsLost + ",";
playerEnemyBuildingsDestroyedString += player.statistics.enemyBuildingsDestroyed + ",";
playerFoodBoughtString += player.statistics.resourcesBought.food + ",";
playerWoodBoughtString += player.statistics.resourcesBought.wood + ",";
playerStoneBoughtString += player.statistics.resourcesBought.stone + ",";
playerMetalBoughtString += player.statistics.resourcesBought.metal + ",";
playerFoodSoldString += player.statistics.resourcesSold.food + ",";
playerWoodSoldString += player.statistics.resourcesSold.wood + ",";
playerStoneSoldString += player.statistics.resourcesSold.stone + ",";
playerMetalSoldString += player.statistics.resourcesSold.metal + ",";
playerTributeSentString += player.statistics.tributesSent + ",";
playerTributeReceivedString += player.statistics.tributesReceived + ",";
playerPercentMapExploredString += player.statistics.precentMapExplored = ",";
playerCivCentersBuiltString += player.statistics.civCentresBuilt + ",";
playerEnemyCivCentersDestroyedString += player.statistics.enemyCivCentresDestroyed + ",";
playerTreasuresCollectedString += player.statistics.treasuresCollected + ",";
playerTradeIncomeString += player.statistics.tradeIncome + ",";
}
// Send the report with serialized data
Engine.SendGameReport({
"timeElapsed" : extendedSimState.timeElapsed,
"playerStates" : playerStatesString,
"playerID": Engine.GetPlayerID(),
"matchID": g_MatchID,
"civs" : playerCivsString,
"mapName" : mapName,
"foodGathered": playerFoodGatheredString,
"woodGathered": playerWoodGatheredString,
"stoneGathered": playerStoneGatheredString,
"metalGathered": playerMetalGatheredString,
"foodUsed": playerFoodUsedString,
"woodUsed": playerWoodUsedString,
"stoneUsed": playerStoneUsedString,
"metalUsed": playerMetalUsedString,
"unitsLost": playerUnitsLostString,
"unitsTrained": playerUnitsTrainedString,
"enemyUnitsKilled": playerEnemyUnitsKilledString,
"buildingsLost": playerBuildingsLostString,
"buildingsConstructed": playerBuildingsConstructedString,
"enemyBuildingsDestroyed": playerEnemyBuildingsDestroyedString,
"foodBought": playerFoodBoughtString,
"woodBought": playerWoodBoughtString,
"stoneBought": playerStoneBoughtString,
"metalBought": playerMetalBoughtString,
"foodSold": playerFoodSoldString,
"woodSold": playerWoodSoldString,
"stoneSold": playerStoneSoldString,
"metalSold": playerMetalSoldString,
"tributeSent": playerTributeSentString,
"tributeReceived": playerTributeReceivedString,
"precentMapExplored": playerPercentMapExploredString,
"civCentersBuilt": playerCivCentersBuiltString,
"enemyCivCentersDestroyed": playerEnemyCivCentersDestroyedString,
"treasuresCollected": playerTreasuresCollectedString,
"tradeIncome": playerTradeIncomeString
});
}
Index: ps/trunk/binaries/data/mods/public/gui/session/unit_commands.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/session/unit_commands.js (revision 14495)
+++ ps/trunk/binaries/data/mods/public/gui/session/unit_commands.js (revision 14496)
@@ -1,1325 +1,1325 @@
// Panel types
const SELECTION = "Selection";
const QUEUE = "Queue";
const GARRISON = "Garrison";
const FORMATION = "Formation";
const TRAINING = "Training";
const RESEARCH = "Research";
const CONSTRUCTION = "Construction";
const TRADING = "Trading";
const COMMAND = "Command";
const STANCE = "Stance";
const GATE = "Gate";
const PACK = "Pack";
// Constants
const COMMANDS_PANEL_WIDTH = 228;
const UNIT_PANEL_BASE = -52; // QUEUE: The offset above the main panel (will often be negative)
const UNIT_PANEL_HEIGHT = 44; // QUEUE: The height needed for a row of buttons
// Trading constants
const TRADING_RESOURCES = ["food", "wood", "stone", "metal"];
// Barter constants
const BARTER_RESOURCE_AMOUNT_TO_SELL = 100;
const BARTER_BUNCH_MULTIPLIER = 5;
const BARTER_RESOURCES = ["food", "wood", "stone", "metal"];
const BARTER_ACTIONS = ["Sell", "Buy"];
// Gate constants
const GATE_ACTIONS = ["Lock", "Unlock"];
// The number of currently visible buttons (used to optimise showing/hiding)
var g_unitPanelButtons = {"Selection": 0, "Queue": 0, "Formation": 0, "Garrison": 0, "Training": 0, "Research": 0, "Barter": 0, "Trading": 0, "Construction": 0, "Command": 0, "Stance": 0, "Gate": 0, "Pack": 0};
// Unit panels are panels with row(s) of buttons
var g_unitPanels = ["Selection", "Queue", "Formation", "Garrison", "Training", "Barter", "Trading", "Construction", "Research", "Stance", "Command", "Gate", "Pack"];
// Indexes of resources to sell and buy on barter panel
var g_barterSell = 0;
// Lay out a row of centered buttons (does not work inside a loop like the other function)
function layoutButtonRowCentered(rowNumber, guiName, startIndex, endIndex, width)
{
- var buttonSideLength = getGUIObjectByName("unit"+guiName+"Button[0]").size.bottom;
+ var buttonSideLength = Engine.GetGUIObjectByName("unit"+guiName+"Button[0]").size.bottom;
var buttonSpacer = buttonSideLength+1;
var colNumber = 0;
// Collect buttons
var buttons = [];
var icons = [];
for (var i = startIndex; i < endIndex; i++)
{
- var button = getGUIObjectByName("unit"+guiName+"Button["+i+"]");
- var icon = getGUIObjectByName("unit"+guiName+"Icon["+i+"]");
+ var button = Engine.GetGUIObjectByName("unit"+guiName+"Button["+i+"]");
+ var icon = Engine.GetGUIObjectByName("unit"+guiName+"Icon["+i+"]");
if (button)
{
buttons.push(button);
icons.push(icon);
}
}
// Location of middle button
var middleIndex = Math.ceil(buttons.length/2);
// Determine whether even or odd number of buttons
var center = (buttons.length/2 == Math.ceil(buttons.length/2))? Math.ceil(width/2) : Math.ceil(width/2+buttonSpacer/2);
// Left Side
for (var i = middleIndex-1; i >= 0; i--)
{
if (buttons[i])
{
var icon = icons[i];
var size = buttons[i].size;
size.left = center - buttonSpacer*colNumber - buttonSideLength;
size.right = center - buttonSpacer*colNumber;
size.top = buttonSpacer*rowNumber;
size.bottom = buttonSpacer*rowNumber + buttonSideLength;
buttons[i].size = size;
colNumber++;
}
}
// Right Side
center += 1; // add spacing to center buttons
colNumber = 0; // reset to 0
for (var i = middleIndex; i < buttons.length; i++)
{
if (buttons[i])
{
var icon = icons[i];
var size = buttons[i].size;
size.left = center + buttonSpacer*colNumber;
size.right = center + buttonSpacer*colNumber + buttonSideLength;
size.top = buttonSpacer*rowNumber;
size.bottom = buttonSpacer*rowNumber + buttonSideLength;
buttons[i].size = size;
colNumber++;
}
}
}
// Lay out button rows
function layoutButtonRow(rowNumber, guiName, buttonSideWidth, buttonSpacer, startIndex, endIndex)
{
layoutRow("Button", rowNumber, guiName, buttonSideWidth, buttonSpacer, buttonSideWidth, buttonSpacer, startIndex, endIndex);
}
// Lay out rows
function layoutRow(objectName, rowNumber, guiName, objectSideWidth, objectSpacerWidth, objectSideHeight, objectSpacerHeight, startIndex, endIndex)
{
var colNumber = 0;
for (var i = startIndex; i < endIndex; i++)
{
- var button = getGUIObjectByName("unit"+guiName+objectName+"["+i+"]");
+ var button = Engine.GetGUIObjectByName("unit"+guiName+objectName+"["+i+"]");
if (button)
{
var size = button.size;
size.left = objectSpacerWidth*colNumber;
size.right = objectSpacerWidth*colNumber + objectSideWidth;
size.top = objectSpacerHeight*rowNumber;
size.bottom = objectSpacerHeight*rowNumber + objectSideHeight;
button.size = size;
colNumber++;
}
}
}
// Set the visibility of the object
function setOverlay(object, value)
{
object.hidden = !value;
}
/**
* Format entity count/limit message for the tooltip
*/
function formatLimitString(trainEntLimit, trainEntCount, trainEntLimitChangers)
{
if (trainEntLimit == undefined)
return "";
var text = "\n\nCurrent Count: " + trainEntCount + ", Limit: " + trainEntLimit + ".";
if (trainEntCount >= trainEntLimit)
text = "[color=\"red\"]" + text + "[/color]";
for (var c in trainEntLimitChangers)
{
if (trainEntLimitChangers[c] > 0)
text += "\n" + c + " enlarges the limit with " + trainEntLimitChangers[c] + ".";
else if (trainEntLimitChangers[c] < 0)
text += "\n" + c + " lessens the limit with " + (-trainEntLimitChangers[c]) + ".";
}
return text;
}
/**
* Format batch training string for the tooltip
* Examples:
* buildingsCountToTrainFullBatch = 1, fullBatchSize = 5, remainderBatch = 0:
* "Shift-click to train 5"
* buildingsCountToTrainFullBatch = 2, fullBatchSize = 5, remainderBatch = 0:
* "Shift-click to train 10 (2*5)"
* buildingsCountToTrainFullBatch = 1, fullBatchSize = 15, remainderBatch = 12:
* "Shift-click to train 27 (15 + 12)"
*/
function formatBatchTrainingString(buildingsCountToTrainFullBatch, fullBatchSize, remainderBatch)
{
var totalBatchTrainingCount = buildingsCountToTrainFullBatch * fullBatchSize + remainderBatch;
// Don't show the batch training tooltip if either units of this type can't be trained at all
// or only one unit can be trained
if (totalBatchTrainingCount < 2)
return "";
var batchTrainingString = "";
var fullBatchesString = "";
if (buildingsCountToTrainFullBatch > 0)
{
if (buildingsCountToTrainFullBatch > 1)
fullBatchesString += buildingsCountToTrainFullBatch + "*";
fullBatchesString += fullBatchSize;
}
var remainderBatchString = remainderBatch > 0 ? remainderBatch : "";
var batchDetailsString = "";
// We need to display the batch details part if there is either more than
// one building with full batch or one building with the full batch and
// another with a partial batch
if (buildingsCountToTrainFullBatch > 1 ||
(buildingsCountToTrainFullBatch == 1 && remainderBatch > 0))
{
batchDetailsString += " (" + fullBatchesString;
if (remainderBatchString != "")
batchDetailsString += " + " + remainderBatchString;
batchDetailsString += ")";
}
return "\n[font=\"serif-bold-13\"]Shift-click[/font][font=\"serif-13\"] to train "
+ totalBatchTrainingCount + batchDetailsString + ".[/font]";
}
/**
* Helper function for updateUnitCommands; sets up "unit panels" (i.e. panels with rows of icons) for the currently selected
* unit.
*
* @param guiName Short identifier string of this panel; see constants defined at the top of this file.
* @param usedPanels Output object; usedPanels[guiName] will be set to 1 to indicate that this panel was used during this
* run of updateUnitCommands and should not be hidden. TODO: why is this done this way instead of having
* updateUnitCommands keep track of this?
* @param unitEntState Entity state of the (first) selected unit.
* @param items Panel-specific data to construct the icons with.
* @param callback Callback function to argument to execute when an item's icon gets clicked. Takes a single 'item' argument.
*/
function setupUnitPanel(guiName, usedPanels, unitEntState, playerState, items, callback)
{
usedPanels[guiName] = 1;
var numberOfItems = items.length;
var selection = g_Selection.toList();
var garrisonGroups = new EntityGroups();
// Determine how many buttons there should be
switch (guiName)
{
case SELECTION:
if (numberOfItems > 16)
numberOfItems = 16;
break;
case QUEUE:
if (numberOfItems > 16)
numberOfItems = 16;
break;
case GARRISON:
if (numberOfItems > 12)
numberOfItems = 12;
break;
case STANCE:
if (numberOfItems > 5)
numberOfItems = 5;
break;
case FORMATION:
if (numberOfItems > 16)
numberOfItems = 16;
break;
case TRAINING:
if (numberOfItems > 24)
numberOfItems = 24;
break;
case RESEARCH:
if (numberOfItems > 8)
numberOfItems = 8;
break;
case CONSTRUCTION:
if (numberOfItems > 24)
numberOfItems = 24;
break;
case COMMAND:
if (numberOfItems > 6)
numberOfItems = 6;
break;
case GATE:
if(numberOfItems > 8)
numberOfItems = 8;
break;
case PACK:
if(numberOfItems > 8)
numberOfItems = 8;
break;
default:
break;
}
switch (guiName)
{
case GARRISON:
case COMMAND:
// Common code for garrison and 'unload all' button counts.
for (var i = 0; i < selection.length; ++i)
{
var state = GetEntityState(selection[i]);
if (state.garrisonHolder)
garrisonGroups.add(state.garrisonHolder.entities)
}
break;
default:
break;
}
var rowLength = 8;
if (guiName == SELECTION)
rowLength = 4;
else if (guiName == FORMATION || guiName == GARRISON || guiName == COMMAND)
rowLength = 4;
// Make buttons
var i;
for (i = 0; i < numberOfItems; i++)
{
var item = items[i];
// If a tech has been researched it leaves an empty slot
if (guiName == RESEARCH && !item)
{
- getGUIObjectByName("unit"+guiName+"Button["+i+"]").hidden = true;
+ Engine.GetGUIObjectByName("unit"+guiName+"Button["+i+"]").hidden = true;
// We also remove the paired tech and the pair symbol
- getGUIObjectByName("unit"+guiName+"Button["+(i+rowLength)+"]").hidden = true;
- getGUIObjectByName("unit"+guiName+"Pair["+i+"]").hidden = true;
+ Engine.GetGUIObjectByName("unit"+guiName+"Button["+(i+rowLength)+"]").hidden = true;
+ Engine.GetGUIObjectByName("unit"+guiName+"Pair["+i+"]").hidden = true;
continue;
}
// Get the entity type and load the template for that type if necessary
var entType;
var template;
var entType1;
var template1;
switch (guiName)
{
case QUEUE:
// The queue can hold both technologies and units so we need to use the correct code for
// loading the templates
if (item.unitTemplate)
{
entType = item.unitTemplate;
template = GetTemplateData(entType);
}
else if (item.technologyTemplate)
{
entType = item.technologyTemplate;
template = GetTechnologyData(entType);
}
if (!template)
continue; // ignore attempts to use invalid templates (an error should have been
// reported already)
break;
case RESEARCH:
if (item.pair)
{
entType1 = item.top;
template1 = GetTechnologyData(entType1);
if (!template1)
continue; // ignore attempts to use invalid templates (an error should have been
// reported already)
entType = item.bottom;
}
else
{
entType = item;
}
template = GetTechnologyData(entType);
if (!template)
continue; // ignore attempts to use invalid templates (an error should have been
// reported already)
break;
case SELECTION:
case GARRISON:
case TRAINING:
case CONSTRUCTION:
entType = item;
template = GetTemplateData(entType);
if (!template)
continue; // ignore attempts to use invalid templates (an error should have been
// reported already)
break;
}
switch (guiName)
{
case SELECTION:
var name = getEntityNames(template);
var tooltip = name;
var count = g_Selection.groups.getCount(item);
- getGUIObjectByName("unit"+guiName+"Count["+i+"]").caption = (count > 1 ? count : "");
+ Engine.GetGUIObjectByName("unit"+guiName+"Count["+i+"]").caption = (count > 1 ? count : "");
break;
case QUEUE:
var tooltip = getEntityNames(template);
if (item.neededSlots)
tooltip += "\n[color=\"red\"]Insufficient population capacity:\n[/color]"+getCostComponentDisplayName("population")+" "+item.neededSlots;
var progress = Math.round(item.progress*100) + "%";
- getGUIObjectByName("unit"+guiName+"Count["+i+"]").caption = (item.count > 1 ? item.count : "");
+ Engine.GetGUIObjectByName("unit"+guiName+"Count["+i+"]").caption = (item.count > 1 ? item.count : "");
if (i == 0)
{
- getGUIObjectByName("queueProgress").caption = (item.progress ? progress : "");
- var size = getGUIObjectByName("unit"+guiName+"ProgressSlider["+i+"]").size;
+ Engine.GetGUIObjectByName("queueProgress").caption = (item.progress ? progress : "");
+ var size = Engine.GetGUIObjectByName("unit"+guiName+"ProgressSlider["+i+"]").size;
// Buttons are assumed to be square, so left/right offsets can be used for top/bottom.
size.top = size.left + Math.round(item.progress * (size.right - size.left));
- getGUIObjectByName("unit"+guiName+"ProgressSlider["+i+"]").size = size;
+ Engine.GetGUIObjectByName("unit"+guiName+"ProgressSlider["+i+"]").size = size;
}
break;
case GARRISON:
var name = getEntityNames(template);
var tooltip = "Unload " + name + "\nSingle-click to unload 1. Shift-click to unload all of this type.";
var count = garrisonGroups.getCount(item);
- getGUIObjectByName("unit"+guiName+"Count["+i+"]").caption = (count > 1 ? count : "");
+ Engine.GetGUIObjectByName("unit"+guiName+"Count["+i+"]").caption = (count > 1 ? count : "");
break;
case GATE:
var tooltip = item.tooltip;
if (item.template)
{
var template = GetTemplateData(item.template);
var wallCount = g_Selection.toList().reduce(function (count, ent) {
var state = GetEntityState(ent);
if (hasClass(state, "LongWall") && !state.gate)
count++;
return count;
}, 0);
tooltip += "\n" + getEntityCostTooltip(template, wallCount);
- var affordableMask = getGUIObjectByName("unitGateUnaffordable["+i+"]");
+ var affordableMask = Engine.GetGUIObjectByName("unitGateUnaffordable["+i+"]");
affordableMask.hidden = true;
var neededResources = Engine.GuiInterfaceCall("GetNeededResources", multiplyEntityCosts(template, wallCount));
if (neededResources)
{
affordableMask.hidden = false;
tooltip += getNeededResourcesTooltip(neededResources);
}
}
break;
case PACK:
var tooltip = item.tooltip;
break;
case STANCE:
case FORMATION:
var tooltip = toTitleCase(item);
break;
case TRAINING:
var tooltip = getEntityNamesFormatted(template);
var key = Engine.ConfigDB_GetValue("user", "hotkey.session.queueunit." + (i + 1));
if (key)
tooltip = "[color=\"255 251 131\"][font=\"serif-bold-16\"][" + key + "][/font][/color] " + tooltip;
if (template.tooltip)
tooltip += "\n[font=\"serif-13\"]" + template.tooltip + "[/font]";
var [buildingsCountToTrainFullBatch, fullBatchSize, remainderBatch] =
getTrainingBatchStatus(playerState, unitEntState.id, entType, selection);
if (Engine.HotkeyIsPressed("session.batchtrain"))
trainNum = buildingsCountToTrainFullBatch * fullBatchSize + remainderBatch;
tooltip += "\n" + getEntityCostTooltip(template, trainNum, unitEntState.id);
var [trainEntLimit, trainEntCount, canBeAddedCount, trainEntLimitChangers] =
getEntityLimitAndCount(playerState, entType);
tooltip += formatLimitString(trainEntLimit, trainEntCount, trainEntLimitChangers);
tooltip += "[color=\"255 251 131\"]" + formatBatchTrainingString(buildingsCountToTrainFullBatch, fullBatchSize, remainderBatch) + "[/color]";
break;
case RESEARCH:
var tooltip = getEntityNamesFormatted(template);
if (template.tooltip)
tooltip += "\n[font=\"serif-13\"]" + template.tooltip + "[/font]";
tooltip += "\n" + getEntityCostTooltip(template);
if (item.pair)
{
var tooltip1 = getEntityNamesFormatted(template1);
if (template1.tooltip)
tooltip1 += "\n[font=\"serif-13\"]" + template1.tooltip + "[/font]";
tooltip1 += "\n" + getEntityCostTooltip(template1);
}
break;
case CONSTRUCTION:
var tooltip = getEntityNamesFormatted(template);
if (template.tooltip)
tooltip += "\n[font=\"serif-13\"]" + template.tooltip + "[/font]";
tooltip += "\n" + getEntityCostTooltip(template);
tooltip += getPopulationBonusTooltip(template);
var [entLimit, entCount, canBeAddedCount, entLimitChangers] =
getEntityLimitAndCount(playerState, entType);
tooltip += formatLimitString(entLimit, entCount, entLimitChangers);
break;
case COMMAND:
// here, "item" is an object with properties .name (command name), .tooltip and .icon (relative to session/icons/single)
if (item.name == "unload-all")
{
var count = garrisonGroups.getTotalCount();
- getGUIObjectByName("unit"+guiName+"Count["+i+"]").caption = (count > 0 ? count : "");
+ Engine.GetGUIObjectByName("unit"+guiName+"Count["+i+"]").caption = (count > 0 ? count : "");
}
else
{
- getGUIObjectByName("unit"+guiName+"Count["+i+"]").caption = "";
+ Engine.GetGUIObjectByName("unit"+guiName+"Count["+i+"]").caption = "";
}
tooltip = (item.tooltip ? item.tooltip : toTitleCase(item.name));
break;
default:
break;
}
// Button
- var button = getGUIObjectByName("unit"+guiName+"Button["+i+"]");
- var button1 = getGUIObjectByName("unit"+guiName+"Button["+(i+rowLength)+"]");
- var affordableMask = getGUIObjectByName("unit"+guiName+"Unaffordable["+i+"]");
- var affordableMask1 = getGUIObjectByName("unit"+guiName+"Unaffordable["+(i+rowLength)+"]");
- var icon = getGUIObjectByName("unit"+guiName+"Icon["+i+"]");
- var guiSelection = getGUIObjectByName("unit"+guiName+"Selection["+i+"]");
- var pair = getGUIObjectByName("unit"+guiName+"Pair["+i+"]");
+ var button = Engine.GetGUIObjectByName("unit"+guiName+"Button["+i+"]");
+ var button1 = Engine.GetGUIObjectByName("unit"+guiName+"Button["+(i+rowLength)+"]");
+ var affordableMask = Engine.GetGUIObjectByName("unit"+guiName+"Unaffordable["+i+"]");
+ var affordableMask1 = Engine.GetGUIObjectByName("unit"+guiName+"Unaffordable["+(i+rowLength)+"]");
+ var icon = Engine.GetGUIObjectByName("unit"+guiName+"Icon["+i+"]");
+ var guiSelection = Engine.GetGUIObjectByName("unit"+guiName+"Selection["+i+"]");
+ var pair = Engine.GetGUIObjectByName("unit"+guiName+"Pair["+i+"]");
button.hidden = false;
button.tooltip = tooltip;
// Button Function (need nested functions to get the closure right)
// Items can have a callback element that overrides the normal caller-supplied callback function.
button.onpress = (function(e){ return function() { e.callback ? e.callback(e) : callback(e) } })(item);
if(guiName == SELECTION)
{
button.onpressright = (function(e){return function() {callback(e, true) } })(item);
button.onpress = (function(e){ return function() {callback(e, false) } })(item);
}
if (guiName == RESEARCH)
{
if (item.pair)
{
button.onpress = (function(e){ return function() { callback(e) } })(item.bottom);
- var icon1 = getGUIObjectByName("unit"+guiName+"Icon["+(i+rowLength)+"]");
+ var icon1 = Engine.GetGUIObjectByName("unit"+guiName+"Icon["+(i+rowLength)+"]");
button1.hidden = false;
button1.tooltip = tooltip1;
button1.onpress = (function(e){ return function() { callback(e) } })(item.top);
// when we hover over a pair, the other one gets a red cross over it to show it won't be available any more.
- var unchosenIcon = getGUIObjectByName("unit"+guiName+"UnchosenIcon["+i+"]");
- var unchosenIcon1 = getGUIObjectByName("unit"+guiName+"UnchosenIcon["+(i+rowLength)+"]");
+ var unchosenIcon = Engine.GetGUIObjectByName("unit"+guiName+"UnchosenIcon["+i+"]");
+ var unchosenIcon1 = Engine.GetGUIObjectByName("unit"+guiName+"UnchosenIcon["+(i+rowLength)+"]");
button1.onmouseenter = (function(e){ return function() { setOverlay(e, true) } })(unchosenIcon);
button1.onmouseleave = (function(e){ return function() { setOverlay(e, false) } })(unchosenIcon);
button.onmouseenter = (function(e){ return function() { setOverlay(e, true) } })(unchosenIcon1);
button.onmouseleave = (function(e){ return function() { setOverlay(e, false) } })(unchosenIcon1);
pair.hidden = false;
}
else
{
// Hide the overlay.
- var unchosenIcon = getGUIObjectByName("unit"+guiName+"UnchosenIcon["+i+"]");
+ var unchosenIcon = Engine.GetGUIObjectByName("unit"+guiName+"UnchosenIcon["+i+"]");
unchosenIcon.hidden = true;
}
}
// Get icon image
if (guiName == FORMATION)
{
var formationOk = canMoveSelectionIntoFormation(item);
var grayscale = "";
button.enabled = formationOk;
if (!formationOk)
{
grayscale = "grayscale:";
// Display a meaningful tooltip why the formation is disabled
var requirements = Engine.GuiInterfaceCall("GetFormationRequirements", {
"formationName": item
});
button.tooltip += " (disabled)";
if (requirements.count > 1)
button.tooltip += "\n" + requirements.count + " units required";
if (requirements.classesRequired)
{
button.tooltip += "\nOnly units of type";
for each (var classRequired in requirements.classesRequired)
{
button.tooltip += " " + classRequired;
}
button.tooltip += " allowed.";
}
}
var formationSelected = Engine.GuiInterfaceCall("IsFormationSelected", {
"ents": g_Selection.toList(),
"formationName": item
});
guiSelection.hidden = !formationSelected;
icon.sprite = "stretched:"+grayscale+"session/icons/formations/"+item.replace(/\s+/,'').toLowerCase()+".png";
}
else if (guiName == STANCE)
{
var stanceSelected = Engine.GuiInterfaceCall("IsStanceSelected", {
"ents": g_Selection.toList(),
"stance": item
});
guiSelection.hidden = !stanceSelected;
icon.sprite = "stretched:session/icons/stances/"+item+".png";
}
else if (guiName == COMMAND)
{
icon.sprite = "stretched:session/icons/" + item.icon;
}
else if (guiName == GATE)
{
var gateIcon;
// If already a gate, show locking actions
if (item.gate)
{
gateIcon = "icons/lock_" + GATE_ACTIONS[item.locked ? 0 : 1].toLowerCase() + "ed.png";
guiSelection.hidden = item.gate.locked === undefined ? false : item.gate.locked != item.locked;
}
// otherwise show gate upgrade icon
else
{
template = GetTemplateData(item.template);
gateIcon = template.icon ? "portraits/" + template.icon : "icons/gate_closed.png";
guiSelection.hidden = true;
}
icon.sprite = "stretched:session/" + gateIcon;
}
else if (guiName == PACK)
{
if (item.packing)
{
icon.sprite = "stretched:session/icons/cancel.png";
}
else
{
if (item.packed)
icon.sprite = "stretched:session/icons/unpack.png";
else
icon.sprite = "stretched:session/icons/pack.png";
}
}
else if (template.icon)
{
var grayscale = "";
button.enabled = true;
if (affordableMask)
affordableMask.hidden = true; // actually used for the red "lack of resource" overlay, and darkening if unavailable. Sort of a hack.
// In case this is an icon that would require tech checking, make sure we have the requirements.
if (guiName != SELECTION && guiName != GARRISON && guiName != QUEUE && template.requiredTechnology && !Engine.GuiInterfaceCall("IsTechnologyResearched", template.requiredTechnology))
{
button.enabled = false;
var techName = getEntityNames(GetTechnologyData(template.requiredTechnology));
button.tooltip += "\nRequires " + techName;
grayscale = "grayscale:";
affordableMask.hidden = false;
affordableMask.sprite = "colour: 0 0 0 127";
}
if (guiName == RESEARCH && !Engine.GuiInterfaceCall("CheckTechnologyRequirements", entType))
{
button.enabled = false;
button.tooltip += "\n" + GetTechnologyData(entType).requirementsTooltip;
grayscale = "grayscale:";
affordableMask.hidden = false;
affordableMask.sprite = "colour: 0 0 0 127";
}
if ((guiName == CONSTRUCTION || guiName == TRAINING) && canBeAddedCount == 0)
{
grayscale = "grayscale:";
affordableMask.hidden = false;
affordableMask.sprite = "colour: 0 0 0 127";
}
if (guiName == GARRISON)
{
var ents = garrisonGroups.getEntsByName(item);
var entplayer = GetEntityState(ents[0]).player;
button.sprite = "colour: " + g_Players[entplayer].color.r + " " + g_Players[entplayer].color.g + " " + g_Players[entplayer].color.b;
var player = Engine.GetPlayerID();
if(player != unitEntState.player && !g_DevSettings.controlAll)
{
if (entplayer != player)
{
button.enabled = false;
grayscale = "grayscale:";
}
}
}
icon.sprite = "stretched:" + grayscale + "session/portraits/" + template.icon;
if (guiName == RESEARCH)
{
// Check resource requirements
var neededResources = Engine.GuiInterfaceCall("GetNeededResources", template.cost);
if (neededResources)
{
if (button.enabled !== false)
{
button.enabled = false;
affordableMask.hidden = false;
var totalCost = 0;
for each (var resource in neededResources)
totalCost += resource;
var alpha = 50 + totalCost/10;
alpha = alpha > 125 ? 125 : alpha;
affordableMask.sprite = "colour: 255 0 0 " + (alpha);
}
button.tooltip += getNeededResourcesTooltip(neededResources);
}
if (item.pair)
{
grayscale = "";
button1.enabled = true;
affordableMask1.hidden = true;
if (!Engine.GuiInterfaceCall("CheckTechnologyRequirements", entType1))
{
button1.enabled = false;
button1.tooltip += "\n" + GetTechnologyData(entType1).requirementsTooltip;
grayscale = "grayscale:";
affordableMask1.hidden = false;
affordableMask1.sprite = "colour: 0 0 0 127";
}
icon1.sprite = "stretched:" + grayscale + "session/portraits/" +template1.icon;
// Check resource requirements for second button
neededResources = Engine.GuiInterfaceCall("GetNeededResources", template1.cost);
if (neededResources)
{
if (button1.enabled !== false)
{
button1.enabled = false;
affordableMask1.hidden = false;
var totalCost = 0;
for each (var resource in neededResources)
totalCost += resource;
var alpha = 50 + totalCost/10;
alpha = alpha > 125 ? 125 : alpha;
affordableMask1.sprite = "colour: 255 0 0 " + (alpha);
}
button1.tooltip += getNeededResourcesTooltip(neededResources);
}
}
else
{
pair.hidden = true;
button1.hidden = true;
affordableMask1.hidden = true;
}
}
else if (guiName == CONSTRUCTION || guiName == TRAINING)
{
var totalCosts = {};
var trainNum = 1;
var button_disableable = true;
if (guiName == TRAINING)
{
if (Engine.HotkeyIsPressed("session.batchtrain"))
{
var [buildingsCountToTrainFullBatch, fullBatchSize, remainderBatch, batchTrainingCount] =
getTrainingBatchStatus(playerState, unitEntState.id, entType, selection);
trainNum = buildingsCountToTrainFullBatch * fullBatchSize + remainderBatch;
button_disableable = !Engine.HotkeyIsPressed("selection.remove");
}
- getGUIObjectByName("unit"+guiName+"Count["+i+"]").caption = (batchTrainingCount > 0) ? batchTrainingCount : "";
+ Engine.GetGUIObjectByName("unit"+guiName+"Count["+i+"]").caption = (batchTrainingCount > 0) ? batchTrainingCount : "";
}
// Walls have no cost defined.
if (template.cost !== undefined)
totalCosts = multiplyEntityCosts(template, trainNum);
var neededResources = Engine.GuiInterfaceCall("GetNeededResources", totalCosts);
if (neededResources)
{
if (button.enabled !== false)
{
button.enabled = (button_disableable ? false : true);
// Don't display the red overlay if we can't even train/build it
if (canBeAddedCount != 0)
{
affordableMask.hidden = false;
var totalCost = 0;
for each (var resource in neededResources)
totalCost += resource;
var alpha = 50 + totalCost/10;
alpha = alpha > 125 ? 125 : alpha;
affordableMask.sprite = "colour: 255 0 0 " + (alpha);
}
}
button.tooltip += getNeededResourcesTooltip(neededResources);
}
}
}
else
{
// TODO: we should require all entities to have icons, so this case never occurs
icon.sprite = "bkFillBlack";
}
}
// Position the visible buttons (TODO: if there's lots, maybe they should be squeezed together to fit)
var numButtons = i;
var numRows = Math.ceil(numButtons / rowLength);
- var buttonSideLength = getGUIObjectByName("unit"+guiName+"Button[0]").size.bottom;
+ var buttonSideLength = Engine.GetGUIObjectByName("unit"+guiName+"Button[0]").size.bottom;
// We sort pairs upside down, so get the size from the topmost button.
if (guiName == RESEARCH)
- buttonSideLength = getGUIObjectByName("unit"+guiName+"Button["+(rowLength*numRows)+"]").size.bottom;
+ buttonSideLength = Engine.GetGUIObjectByName("unit"+guiName+"Button["+(rowLength*numRows)+"]").size.bottom;
var buttonSpacer = buttonSideLength+1;
// Layout buttons
if (guiName == COMMAND)
{
layoutButtonRowCentered(0, guiName, 0, numButtons, COMMANDS_PANEL_WIDTH);
}
else if (guiName == RESEARCH)
{
// We support pairs so we need to add a row
numRows++;
// Layout rows from bottom to top
for (var i = 0, j = numRows; i < numRows; i++, j--)
{
layoutButtonRow(i, guiName, buttonSideLength, buttonSpacer, rowLength*(j-1), rowLength*j);
}
}
else
{
for (var i = 0; i < numRows; i++)
layoutButtonRow(i, guiName, buttonSideLength, buttonSpacer, rowLength*i, rowLength*(i+1) );
}
// Layout pair icons
if (guiName == RESEARCH)
{
- var pairSize = getGUIObjectByName("unit"+guiName+"Pair[0]").size;
+ var pairSize = Engine.GetGUIObjectByName("unit"+guiName+"Pair[0]").size;
var pairSideWidth = pairSize.right;
var pairSideHeight = pairSize.bottom;
var pairSpacerHeight = pairSideHeight + 1;
var pairSpacerWidth = pairSideWidth + 1;
layoutRow("Pair", 0, guiName, pairSideWidth, pairSpacerWidth, pairSideHeight, pairSpacerHeight, 0, rowLength);
}
// Resize Queue panel if needed
if (guiName == QUEUE) // or garrison
{
- var panel = getGUIObjectByName("unitQueuePanel");
+ var panel = Engine.GetGUIObjectByName("unitQueuePanel");
var size = panel.size;
size.top = (UNIT_PANEL_BASE - ((numRows-1)*UNIT_PANEL_HEIGHT));
panel.size = size;
}
// Hide any buttons we're no longer using
for (var i = numButtons; i < g_unitPanelButtons[guiName]; ++i)
- getGUIObjectByName("unit"+guiName+"Button["+i+"]").hidden = true;
+ Engine.GetGUIObjectByName("unit"+guiName+"Button["+i+"]").hidden = true;
// Hide unused pair buttons and symbols
if (guiName == RESEARCH)
{
for (var i = numButtons; i < g_unitPanelButtons[guiName]; ++i)
{
- getGUIObjectByName("unit"+guiName+"Button["+(i+rowLength)+"]").hidden = true;
- getGUIObjectByName("unit"+guiName+"Pair["+i+"]").hidden = true;
+ Engine.GetGUIObjectByName("unit"+guiName+"Button["+(i+rowLength)+"]").hidden = true;
+ Engine.GetGUIObjectByName("unit"+guiName+"Pair["+i+"]").hidden = true;
}
}
g_unitPanelButtons[guiName] = numButtons;
}
// Sets up "unit trading panel" - special case for setupUnitPanel
function setupUnitTradingPanel(usedPanels, unitEntState, selection)
{
usedPanels[TRADING] = 1;
var requiredGoods = unitEntState.trader.requiredGoods;
for (var i = 0; i < TRADING_RESOURCES.length; i++)
{
var resource = TRADING_RESOURCES[i];
- var button = getGUIObjectByName("unitTradingButton["+i+"]");
+ var button = Engine.GetGUIObjectByName("unitTradingButton["+i+"]");
button.size = (i * 46) + " 0 " + ((i + 1) * 46) + " 46";
if (resource == requiredGoods)
var selectRequiredGoodsData = { "entities": selection, "requiredGoods": undefined };
else
var selectRequiredGoodsData = { "entities": selection, "requiredGoods": resource };
button.onpress = (function(e){ return function() { selectRequiredGoods(e); } })(selectRequiredGoodsData);
button.enabled = true;
button.tooltip = "Set/unset " + resource + " as forced trading goods.";
- var icon = getGUIObjectByName("unitTradingIcon["+i+"]");
- var selected = getGUIObjectByName("unitTradingSelection["+i+"]");
+ var icon = Engine.GetGUIObjectByName("unitTradingIcon["+i+"]");
+ var selected = Engine.GetGUIObjectByName("unitTradingSelection["+i+"]");
selected.hidden = !(resource == requiredGoods);
var grayscale = (resource != requiredGoods) ? "grayscale:" : "";
icon.sprite = "stretched:"+grayscale+"session/icons/resources/" + resource + ".png";
}
}
// Sets up "unit barter panel" - special case for setupUnitPanel
function setupUnitBarterPanel(unitEntState, playerState)
{
// Amount of player's resource to exchange
var amountToSell = BARTER_RESOURCE_AMOUNT_TO_SELL;
if (Engine.HotkeyIsPressed("session.massbarter"))
amountToSell *= BARTER_BUNCH_MULTIPLIER;
// One pass for each resource
for (var i = 0; i < BARTER_RESOURCES.length; i++)
{
var resource = BARTER_RESOURCES[i];
// One pass for 'sell' row and another for 'buy'
for (var j = 0; j < 2; j++)
{
var action = BARTER_ACTIONS[j];
if (j == 0)
{
// Display the selection overlay
- var selection = getGUIObjectByName("unitBarter" + action + "Selection["+i+"]");
+ var selection = Engine.GetGUIObjectByName("unitBarter" + action + "Selection["+i+"]");
selection.hidden = !(i == g_barterSell);
}
// We gray out the not selected icons in 'sell' row
var grayscale = (j == 0 && i != g_barterSell) ? "grayscale:" : "";
- var icon = getGUIObjectByName("unitBarter" + action + "Icon["+i+"]");
+ var icon = Engine.GetGUIObjectByName("unitBarter" + action + "Icon["+i+"]");
- var button = getGUIObjectByName("unitBarter" + action + "Button["+i+"]");
+ var button = Engine.GetGUIObjectByName("unitBarter" + action + "Button["+i+"]");
button.size = (i * 46) + " 0 " + ((i + 1) * 46) + " 46";
var amountToBuy;
// We don't display a button in 'buy' row if the same resource is selected in 'sell' row
if (j == 1 && i == g_barterSell)
{
button.hidden = true;
}
else
{
button.hidden = false;
button.tooltip = action + " " + resource;
icon.sprite = "stretched:"+grayscale+"session/icons/resources/" + resource + ".png";
var sellPrice = unitEntState.barterMarket.prices["sell"][BARTER_RESOURCES[g_barterSell]];
var buyPrice = unitEntState.barterMarket.prices["buy"][resource];
amountToBuy = "+" + Math.round(sellPrice / buyPrice * amountToSell);
}
var amount;
if (j == 0)
{
button.onpress = (function(i){ return function() { g_barterSell = i; } })(i);
if (i == g_barterSell)
{
amount = "-" + amountToSell;
var neededRes = {};
neededRes[resource] = amountToSell;
var neededResources = Engine.GuiInterfaceCall("GetNeededResources", neededRes);
var hidden = neededResources ? false : true;
for (var ii = 0; ii < BARTER_RESOURCES.length; ii++)
{
- var affordableMask = getGUIObjectByName("unitBarterBuyUnaffordable["+ii+"]");
+ var affordableMask = Engine.GetGUIObjectByName("unitBarterBuyUnaffordable["+ii+"]");
affordableMask.hidden = hidden;
}
}
else
amount = "";
}
else
{
var exchangeResourcesParameters = { "sell": BARTER_RESOURCES[g_barterSell], "buy": BARTER_RESOURCES[i], "amount": amountToSell };
button.onpress = (function(exchangeResourcesParameters){ return function() { exchangeResources(exchangeResourcesParameters); } })(exchangeResourcesParameters);
amount = amountToBuy;
}
- getGUIObjectByName("unitBarter" + action + "Amount["+i+"]").caption = amount;
+ Engine.GetGUIObjectByName("unitBarter" + action + "Amount["+i+"]").caption = amount;
}
}
}
/**
* Updates the right hand side "Unit Commands" panel. Runs in the main session loop via updateSelectionDetails().
* Delegates to setupUnitPanel to set up individual subpanels, appropriately activated depending on the selected
* unit's state.
*
* @param entState Entity state of the (first) selected unit.
* @param supplementalDetailsPanel Reference to the "supplementalSelectionDetails" GUI Object
* @param commandsPanel Reference to the "commandsPanel" GUI Object
* @param selection Array of currently selected entity IDs.
*/
function updateUnitCommands(entState, supplementalDetailsPanel, commandsPanel, selection)
{
// Panels that are active
var usedPanels = {};
// If the selection is friendly units, add the command panels
var player = Engine.GetPlayerID();
// Get player state to check some constraints
// e.g. presence of a hero or build limits
var simState = GetSimState();
var playerState = simState.players[player];
if (entState.player == player || g_DevSettings.controlAll)
{
if (selection.length > 1)
setupUnitPanel(SELECTION, usedPanels, entState, playerState, g_Selection.groups.getTemplateNames(),
function (entType, rightPressed) { changePrimarySelectionGroup(entType, rightPressed); } );
var commands = getEntityCommandsList(entState);
if (commands.length)
setupUnitPanel(COMMAND, usedPanels, entState, playerState, commands,
function (item) { performCommand(entState.id, item.name); } );
if (entState.garrisonHolder)
{
var groups = new EntityGroups();
for (var i in selection)
{
var state = GetEntityState(selection[i]);
if (state.garrisonHolder)
groups.add(state.garrisonHolder.entities)
}
setupUnitPanel(GARRISON, usedPanels, entState, playerState, groups.getTemplateNames(),
function (item) { unloadTemplate(item); } );
}
var formations = Engine.GuiInterfaceCall("GetAvailableFormations");
if (hasClass(entState, "Unit") && !hasClass(entState, "Animal") && !entState.garrisonHolder && formations.length)
{
setupUnitPanel(FORMATION, usedPanels, entState, playerState, formations,
function (item) { performFormation(entState.id, item); } );
}
// TODO: probably should load the stance list from a data file,
// and/or vary depending on what units are selected
var stances = ["violent", "aggressive", "passive", "defensive", "standground"];
if (hasClass(entState, "Unit") && !hasClass(entState, "Animal") && stances.length)
{
setupUnitPanel(STANCE, usedPanels, entState, playerState, stances,
function (item) { performStance(entState.id, item); } );
}
- getGUIObjectByName("unitBarterPanel").hidden = !entState.barterMarket;
+ Engine.GetGUIObjectByName("unitBarterPanel").hidden = !entState.barterMarket;
if (entState.barterMarket)
{
usedPanels["Barter"] = 1;
setupUnitBarterPanel(entState, playerState);
}
var buildableEnts = getAllBuildableEntitiesFromSelection();
var trainableEnts = getAllTrainableEntitiesFromSelection();
// Whether the GUI's right panel has been filled.
var rightUsed = true;
// The first selected entity's type has priority.
if (entState.buildEntities)
setupUnitPanel(CONSTRUCTION, usedPanels, entState, playerState, buildableEnts,
function (trainEntType) { startBuildingPlacement(trainEntType, playerState); } );
else if (entState.production && entState.production.entities)
setupUnitPanel(TRAINING, usedPanels, entState, playerState, trainableEnts,
function (trainEntType) { addTrainingToQueue(selection, trainEntType, playerState); } );
// else if (entState.trader)
// setupUnitTradingPanel(usedPanels, entState, selection);
else if (!entState.foundation && entState.gate || hasClass(entState, "LongWall"))
{
// Allow long wall pieces to be converted to gates
var longWallTypes = {};
var walls = [];
var gates = [];
for (var i in selection)
{
var state = GetEntityState(selection[i]);
if (hasClass(state, "LongWall") && !state.gate && !longWallTypes[state.template])
{
var gateTemplate = getWallGateTemplate(state.id);
if (gateTemplate)
{
var wallName = GetTemplateData(state.template).name.generic;
var gateName = GetTemplateData(gateTemplate).name.generic;
walls.push({
"tooltip": "Convert " + wallName + " to " + gateName,
"template": gateTemplate,
"callback": function (item) { transformWallToGate(item.template); }
});
}
// We only need one entity per type.
longWallTypes[state.template] = true;
}
else if (state.gate && !gates.length)
for (var j = 0; j < GATE_ACTIONS.length; ++j)
gates.push({
"gate": state.gate,
"tooltip": GATE_ACTIONS[j] + " gate",
"locked": j == 0,
"callback": function (item) { lockGate(item.locked); }
});
// Show both 'locked' and 'unlocked' as active if the selected gates have both lock states.
else if (state.gate && state.gate.locked != gates[0].gate.locked)
for (var j = 0; j < gates.length; ++j)
delete gates[j].gate.locked;
}
// Place wall conversion options after gate lock/unlock icons.
var items = gates.concat(walls);
if (items.length)
setupUnitPanel(GATE, usedPanels, entState, playerState, items);
else
rightUsed = false;
}
else if (entState.pack)
{
var items = [];
var packButton = false;
var unpackButton = false;
var packCancelButton = false;
var unpackCancelButton = false;
for (var i in selection)
{
// Find un/packable entities
var state = GetEntityState(selection[i]);
if (state.pack)
{
if (state.pack.progress == 0)
{
if (!state.pack.packed)
packButton = true;
else if (state.pack.packed)
unpackButton = true;
}
else
{
// Already un/packing - show cancel button
if (!state.pack.packed)
packCancelButton = true;
else if (state.pack.packed)
unpackCancelButton = true;
}
}
}
if (packButton)
items.push({ "packing": false, "packed": false, "tooltip": "Pack", "callback": function() { packUnit(true); } });
if (unpackButton)
items.push({ "packing": false, "packed": true, "tooltip": "Unpack", "callback": function() { packUnit(false); } });
if (packCancelButton)
items.push({ "packing": true, "packed": false, "tooltip": "Cancel packing", "callback": function() { cancelPackUnit(true); } });
if (unpackCancelButton)
items.push({ "packing": true, "packed": true, "tooltip": "Cancel unpacking", "callback": function() { cancelPackUnit(false); } });
if (items.length)
setupUnitPanel(PACK, usedPanels, entState, playerState, items);
else
rightUsed = false;
}
else
rightUsed = false;
if (!rightUsed)
{
// The right pane is empty. Fill the pane with a sane type.
// Prefer buildables for units and trainables for structures.
if (buildableEnts.length && (hasClass(entState, "Unit") || !trainableEnts.length))
setupUnitPanel(CONSTRUCTION, usedPanels, entState, playerState, buildableEnts,
function (trainEntType) { startBuildingPlacement(trainEntType, playerState); });
else if (trainableEnts.length)
setupUnitPanel(TRAINING, usedPanels, entState, playerState, trainableEnts,
function (trainEntType) { addTrainingToQueue(selection, trainEntType, playerState); } );
}
// Show technologies if the active panel has at most one row of icons.
if (entState.production && entState.production.technologies.length)
{
var activepane = usedPanels[CONSTRUCTION] ? buildableEnts.length : trainableEnts.length;
if (selection.length == 1 || activepane <= 8)
setupUnitPanel(RESEARCH, usedPanels, entState, playerState, entState.production.technologies,
function (researchType) { addResearchToQueue(entState.id, researchType); } );
}
if (entState.production && entState.production.queue.length)
setupUnitPanel(QUEUE, usedPanels, entState, playerState, entState.production.queue,
function (item) { removeFromProductionQueue(entState.id, item.id); } );
supplementalDetailsPanel.hidden = false;
commandsPanel.hidden = false;
}
else if (playerState.isMutualAlly[entState.player]) // owned by allied player
{
if (entState.garrisonHolder)
{
var groups = new EntityGroups();
for (var i in selection)
{
var state = GetEntityState(selection[i]);
if (state.garrisonHolder)
groups.add(state.garrisonHolder.entities)
}
setupUnitPanel(GARRISON, usedPanels, entState, playerState, groups.getTemplateNames(),
function (item) { unloadTemplate(item); } );
supplementalDetailsPanel.hidden = false;
}
else
{
supplementalDetailsPanel.hidden = true;
}
commandsPanel.hidden = true;
}
else // owned by another player
{
supplementalDetailsPanel.hidden = true;
commandsPanel.hidden = true;
}
// Hides / unhides Unit Panels (panels should be grouped by type, not by order, but we will leave that for another time)
var offset = 0;
for each (var panelName in g_unitPanels)
{
- var panel = getGUIObjectByName("unit" + panelName + "Panel");
+ var panel = Engine.GetGUIObjectByName("unit" + panelName + "Panel");
if (usedPanels[panelName])
panel.hidden = false;
else
panel.hidden = true;
}
}
// Force hide commands panels
function hideUnitCommands()
{
for each (var panelName in g_unitPanels)
- getGUIObjectByName("unit" + panelName + "Panel").hidden = true;
+ Engine.GetGUIObjectByName("unit" + panelName + "Panel").hidden = true;
}
// Get all of the available entities which can be trained by the selected entities
function getAllTrainableEntities(selection)
{
var trainableEnts = [];
var state;
// Get all buildable and trainable entities
for (var i in selection)
{
if ((state = GetEntityState(selection[i])) && state.production && state.production.entities.length)
trainableEnts = trainableEnts.concat(state.production.entities);
}
// Remove duplicates
removeDupes(trainableEnts);
return trainableEnts;
}
function getAllTrainableEntitiesFromSelection()
{
if (!g_allTrainableEntities)
g_allTrainableEntities = getAllTrainableEntities(g_Selection.toList());
return g_allTrainableEntities;
}
// Get all of the available entities which can be built by the selected entities
function getAllBuildableEntities(selection)
{
var buildableEnts = [];
var state;
// Get all buildable entities
for (var i in selection)
{
if ((state = GetEntityState(selection[i])) && state.buildEntities && state.buildEntities.length)
buildableEnts = buildableEnts.concat(state.buildEntities);
}
// Remove duplicates
removeDupes(buildableEnts);
return buildableEnts;
}
function getAllBuildableEntitiesFromSelection()
{
if (!g_allBuildableEntities)
g_allBuildableEntities = getAllBuildableEntities(g_Selection.toList());
return g_allBuildableEntities;
}
// Check if the selection can move into formation, and cache the result
function canMoveSelectionIntoFormation(formationName)
{
if (!(formationName in g_canMoveIntoFormation))
{
g_canMoveIntoFormation[formationName] = Engine.GuiInterfaceCall("CanMoveEntsIntoFormation", {
"ents": g_Selection.toList(),
"formationName": formationName
});
}
return g_canMoveIntoFormation[formationName];
}
Index: ps/trunk/binaries/data/mods/public/gui/splashscreen/splashscreen.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/splashscreen/splashscreen.xml (revision 14495)
+++ ps/trunk/binaries/data/mods/public/gui/splashscreen/splashscreen.xml (revision 14496)
@@ -1,40 +1,40 @@
Welcome to 0 A.D. !Show this message in the future
OK
Known issues (web)
Index: ps/trunk/source/graphics/MapGenerator.cpp
===================================================================
--- ps/trunk/source/graphics/MapGenerator.cpp (revision 14495)
+++ ps/trunk/source/graphics/MapGenerator.cpp (revision 14496)
@@ -1,284 +1,283 @@
/* Copyright (C) 2012 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see .
*/
#include "precompiled.h"
#include "MapGenerator.h"
#include "lib/timer.h"
#include "ps/CLogger.h"
// TODO: what's a good default? perhaps based on map size
#define RMS_RUNTIME_SIZE 96 * 1024 * 1024
CMapGeneratorWorker::CMapGeneratorWorker()
{
// If something happens before we initialize, that's a failure
m_Progress = -1;
}
CMapGeneratorWorker::~CMapGeneratorWorker()
{
// Wait for thread to end
pthread_join(m_WorkerThread, NULL);
// The StructuredClone destructor references a JSContext created by our
// ScriptInterface, so explicitly clean it up before ScriptInterface
m_MapData.reset();
// Cleanup ScriptInterface
delete m_ScriptInterface;
}
void CMapGeneratorWorker::Initialize(const VfsPath& scriptFile, const std::string& settings)
{
CScopeLock lock(m_WorkerMutex);
// Set progress to positive value
m_Progress = 1;
m_ScriptPath = scriptFile;
m_Settings = settings;
// Launch the worker thread
int ret = pthread_create(&m_WorkerThread, NULL, &RunThread, this);
ENSURE(ret == 0);
}
void* CMapGeneratorWorker::RunThread(void *data)
{
debug_SetThreadName("MapGenerator");
g_Profiler2.RegisterCurrentThread("MapGenerator");
CMapGeneratorWorker* self = static_cast(data);
self->m_ScriptInterface = new ScriptInterface("RMS", "MapGenerator", ScriptInterface::CreateRuntime(RMS_RUNTIME_SIZE));
// Run map generation scripts
if ((!self->Run()) || (self->m_Progress > 0))
{
// Don't leave progress in an unknown state, if generator failed, set it to -1
CScopeLock lock(self->m_WorkerMutex);
self->m_Progress = -1;
}
// At this point the random map scripts are done running, so the thread has no further purpose
// and can die. The data will be stored in m_MapData already if successful, or m_Progress
// will contain an error value on failure.
return NULL;
}
bool CMapGeneratorWorker::Run()
{
m_ScriptInterface->SetCallbackData(static_cast (this));
// Replace RNG with a seeded deterministic function
m_ScriptInterface->ReplaceNondeterministicRNG(m_MapGenRNG);
m_ScriptInterface->LoadGlobalScripts();
// Functions for RMS
m_ScriptInterface->RegisterFunction("LoadLibrary");
m_ScriptInterface->RegisterFunction("ExportMap");
m_ScriptInterface->RegisterFunction("SetProgress");
m_ScriptInterface->RegisterFunction("MaybeGC");
m_ScriptInterface->RegisterFunction, CMapGeneratorWorker::GetCivData>("GetCivData");
// Parse settings
CScriptValRooted settingsVal = m_ScriptInterface->ParseJSON(m_Settings);
if (settingsVal.undefined())
{
LOGERROR(L"CMapGeneratorWorker::Run: Failed to parse settings");
return false;
}
// Init RNG seed
uint32_t seed;
if (!m_ScriptInterface->GetProperty(settingsVal.get(), "Seed", seed))
{ // No seed specified
LOGWARNING(L"CMapGeneratorWorker::Run: No seed value specified - using 0");
seed = 0;
}
m_MapGenRNG.seed((int32_t)seed);
// Copy settings to global variable
if (!m_ScriptInterface->SetProperty(m_ScriptInterface->GetGlobalObject(), "g_MapSettings", settingsVal))
{
LOGERROR(L"CMapGeneratorWorker::Run: Failed to define g_MapSettings");
return false;
}
// Load RMS
LOGMESSAGE(L"Loading RMS '%ls'", m_ScriptPath.string().c_str());
if (!m_ScriptInterface->LoadGlobalScriptFile(m_ScriptPath))
{
LOGERROR(L"CMapGeneratorWorker::Run: Failed to load RMS '%ls'", m_ScriptPath.string().c_str());
return false;
}
return true;
}
int CMapGeneratorWorker::GetProgress()
{
CScopeLock lock(m_WorkerMutex);
return m_Progress;
}
shared_ptr CMapGeneratorWorker::GetResults()
{
CScopeLock lock(m_WorkerMutex);
return m_MapData;
}
-bool CMapGeneratorWorker::LoadLibrary(void* cbdata, std::wstring name)
+bool CMapGeneratorWorker::LoadLibrary(ScriptInterface::CxPrivate* pCxPrivate, std::wstring name)
{
- CMapGeneratorWorker* self = static_cast(cbdata);
-
+ CMapGeneratorWorker* self = static_cast(pCxPrivate->pCBData);
return self->LoadScripts(name);
}
-void CMapGeneratorWorker::ExportMap(void* cbdata, CScriptValRooted data)
+void CMapGeneratorWorker::ExportMap(ScriptInterface::CxPrivate* pCxPrivate, CScriptValRooted data)
{
- CMapGeneratorWorker* self = static_cast(cbdata);
+ CMapGeneratorWorker* self = static_cast(pCxPrivate->pCBData);
// Copy results
CScopeLock lock(self->m_WorkerMutex);
self->m_MapData = self->m_ScriptInterface->WriteStructuredClone(data.get());
self->m_Progress = 0;
}
-void CMapGeneratorWorker::SetProgress(void* cbdata, int progress)
+void CMapGeneratorWorker::SetProgress(ScriptInterface::CxPrivate* pCxPrivate, int progress)
{
- CMapGeneratorWorker* self = static_cast(cbdata);
+ CMapGeneratorWorker* self = static_cast(pCxPrivate->pCBData);
// Copy data
CScopeLock lock(self->m_WorkerMutex);
self->m_Progress = progress;
}
-void CMapGeneratorWorker::MaybeGC(void* cbdata)
+void CMapGeneratorWorker::MaybeGC(ScriptInterface::CxPrivate* pCxPrivate)
{
- CMapGeneratorWorker* self = static_cast(cbdata);
+ CMapGeneratorWorker* self = static_cast(pCxPrivate->pCBData);
self->m_ScriptInterface->MaybeGC();
}
-std::vector CMapGeneratorWorker::GetCivData(void* UNUSED(cbdata))
+std::vector CMapGeneratorWorker::GetCivData(ScriptInterface::CxPrivate* UNUSED(pCxPrivate))
{
VfsPath path(L"civs/");
VfsPaths pathnames;
std::vector data;
// Load all JSON files in civs directory
Status ret = vfs::GetPathnames(g_VFS, path, L"*.json", pathnames);
if (ret == INFO::OK)
{
for (VfsPaths::iterator it = pathnames.begin(); it != pathnames.end(); ++it)
{
// Load JSON file
CVFSFile file;
PSRETURN ret = file.Load(g_VFS, *it);
if (ret != PSRETURN_OK)
{
LOGERROR(L"CMapGeneratorWorker::GetCivData: Failed to load file '%ls': %hs", path.string().c_str(), GetErrorString(ret));
}
else
{
data.push_back(file.DecodeUTF8()); // assume it's UTF-8
}
}
}
else
{
// Some error reading directory
wchar_t error[200];
LOGERROR(L"CMapGeneratorWorker::GetCivData: Error reading directory '%ls': %ls", path.string().c_str(), StatusDescription(ret, error, ARRAY_SIZE(error)));
}
return data;
}
bool CMapGeneratorWorker::LoadScripts(const std::wstring& libraryName)
{
// Ignore libraries that are already loaded
if (m_LoadedLibraries.find(libraryName) != m_LoadedLibraries.end())
{
return true;
}
// Mark this as loaded, to prevent it recursively loading itself
m_LoadedLibraries.insert(libraryName);
VfsPath path = L"maps/random/" + libraryName + L"/";
VfsPaths pathnames;
// Load all scripts in mapgen directory
Status ret = vfs::GetPathnames(g_VFS, path, L"*.js", pathnames);
if (ret == INFO::OK)
{
for (VfsPaths::iterator it = pathnames.begin(); it != pathnames.end(); ++it)
{
LOGMESSAGE(L"Loading map generator script '%ls'", it->string().c_str());
if (!m_ScriptInterface->LoadGlobalScriptFile(*it))
{
LOGERROR(L"CMapGeneratorWorker::LoadScripts: Failed to load script '%ls'", it->string().c_str());
return false;
}
}
}
else
{
// Some error reading directory
wchar_t error[200];
LOGERROR(L"CMapGeneratorWorker::LoadScripts: Error reading scripts in directory '%ls': %ls", path.string().c_str(), StatusDescription(ret, error, ARRAY_SIZE(error)));
return false;
}
return true;
}
//////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////
CMapGenerator::CMapGenerator() : m_Worker(new CMapGeneratorWorker())
{
}
CMapGenerator::~CMapGenerator()
{
delete m_Worker;
}
void CMapGenerator::GenerateMap(const VfsPath& scriptFile, const std::string& settings)
{
m_Worker->Initialize(scriptFile, settings);
}
int CMapGenerator::GetProgress()
{
return m_Worker->GetProgress();
}
shared_ptr CMapGenerator::GetResults()
{
return m_Worker->GetResults();
}
Index: ps/trunk/source/graphics/scripting/JSInterface_GameView.h
===================================================================
--- ps/trunk/source/graphics/scripting/JSInterface_GameView.h (revision 14495)
+++ ps/trunk/source/graphics/scripting/JSInterface_GameView.h (revision 14496)
@@ -1,41 +1,41 @@
/* Copyright (C) 2013 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see .
*/
#ifndef INCLUDED_JSINTERFACE_GAMEVIEW
#define INCLUDED_JSINTERFACE_GAMEVIEW
#include "ps/CStr.h"
-class ScriptInterface;
+#include "scriptinterface/ScriptInterface.h"
#define DECLARE_BOOLEAN_SCRIPT_SETTING(NAME) \
- bool Get##NAME##Enabled(void* cbdata); \
- void Set##NAME##Enabled(void* cbdata, bool Enabled);
+ bool Get##NAME##Enabled(ScriptInterface::CxPrivate* pCxPrivate); \
+ void Set##NAME##Enabled(ScriptInterface::CxPrivate* pCxPrivate, bool Enabled);
namespace JSI_GameView
{
void RegisterScriptFunctions(ScriptInterface& ScriptInterface);
DECLARE_BOOLEAN_SCRIPT_SETTING(Culling);
DECLARE_BOOLEAN_SCRIPT_SETTING(LockCullCamera);
DECLARE_BOOLEAN_SCRIPT_SETTING(ConstrainCamera);
}
#undef DECLARE_BOOLEAN_SCRIPT_SETTING
#endif
Index: ps/trunk/source/gui/CGUI.h
===================================================================
--- ps/trunk/source/gui/CGUI.h (revision 14495)
+++ ps/trunk/source/gui/CGUI.h (revision 14496)
@@ -1,685 +1,673 @@
/* Copyright (C) 2013 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see .
*/
/*
CGUI
--Overview--
This is the top class of the whole GUI, all objects
and settings are stored within this class.
--More info--
Check GUI.h
*/
#ifndef INCLUDED_CGUI
#define INCLUDED_CGUI
//--------------------------------------------------------
// Includes / Compiler directives
//--------------------------------------------------------
// NOTE: GUI.h included at the bottom of this file (has to be after CGUI class
// definition)
#include "GUITooltip.h"
#include "GUIbase.h"
+#include "scriptinterface/ScriptInterface.h"
#include "ps/Overlay.h" // CPos and CRect
#include "lib/input.h"
#include "ps/XML/Xeromyces.h"
#include
//--------------------------------------------------------
// Macros
//--------------------------------------------------------
//--------------------------------------------------------
// Types
//--------------------------------------------------------
//--------------------------------------------------------
// Error declarations
//--------------------------------------------------------
ERROR_TYPE(GUI, JSOpenFailed);
//--------------------------------------------------------
// Declarations
//--------------------------------------------------------
/**
* Contains a list of values for new defaults to objects.
*/
struct SGUIStyle
{
// A list of defaults for
std::map m_SettingsDefaults;
};
struct JSObject; // The GUI stores a JSObject*, so needs to know that JSObject exists
class IGUIObject;
class CGUISpriteInstance;
struct SGUIText;
struct CColor;
struct SGUIText;
struct SGUIIcon;
class CGUIString;
class CGUISprite;
struct SGUIImageEffects;
struct SGUIScrollBarStyle;
class GUITooltip;
/**
* The main object that represents a whole GUI page.
*
* No interfacial functions throws.
*/
class CGUI
{
NONCOPYABLE(CGUI);
friend class IGUIObject;
friend class IGUIScrollBarOwner;
friend class CInternalCGUIAccessorBase;
private:
// Private typedefs
typedef IGUIObject *(*ConstructObjectFunction)();
public:
- CGUI();
+ CGUI(const shared_ptr& runtime);
~CGUI();
/**
- * Initializes GUI script classes
- */
- static void ScriptingInit();
-
- /**
* Initializes the GUI, needs to be called before the GUI is used
*/
void Initialize();
/**
* Performs processing that should happen every frame
* (including sending the "Tick" event to scripts)
*/
void TickObjects();
/**
* Sends a specified script event to every object
*
* @param EventName String representation of event name
*/
void SendEventToAll(const CStr& EventName);
/**
* Displays the whole GUI
*/
void Draw();
/**
* Draw GUI Sprite
*
* @param Sprite Object referring to the sprite (which also caches
* calculations for faster rendering)
* @param CellID Number of the icon cell to use. (Ignored if this sprite doesn't
* have any images with "cell-size")
* @param Z Drawing order, depth value
* @param Rect Position and Size
* @param Clipping The sprite shouldn't be drawn outside this rectangle
*/
void DrawSprite(const CGUISpriteInstance& Sprite, int CellID, const float &Z,
const CRect &Rect, const CRect &Clipping=CRect());
/**
* Draw a SGUIText object
*
* @param Text Text object.
* @param DefaultColor Color used if no tag applied.
* @param pos position
* @param z z value.
* @param clipping
*/
void DrawText(SGUIText &Text, const CColor &DefaultColor,
const CPos &pos, const float &z, const CRect &clipping);
/**
* Clean up, call this to clean up all memory allocated
* within the GUI.
*/
void Destroy();
/**
* The replacement of Process(), handles an SDL_Event_
*
* @param ev SDL Event, like mouse/keyboard input
*/
InReaction HandleEvent(const SDL_Event_* ev);
/**
* Load a GUI XML file into the GUI.
*
* VERY IMPORTANT! All \-files must be read before
* everything else!
*
* @param Filename Name of file
* @param Paths Set of paths; all XML and JS files loaded will be added to this
*/
void LoadXmlFile(const VfsPath& Filename, boost::unordered_set& Paths);
/**
* Checks if object exists and return true or false accordingly
*
* @param Name String name of object
* @return true if object exists
*/
bool ObjectExists(const CStr& Name) const;
/**
* Returns the GUI object with the desired name, or NULL
* if no match is found,
*
* @param Name String name of object
* @return Matching object, or NULL
*/
IGUIObject* FindObjectByName(const CStr& Name) const;
/**
* Returns the GUI object under the mouse, or NULL if none.
*/
IGUIObject* FindObjectUnderMouse() const;
/**
* The GUI needs to have all object types inputted and
* their constructors. Also it needs to associate a type
* by a string name of the type.
*
* To add a type:
* @code
* AddObjectType("button", &CButton::ConstructObject);
* @endcode
*
* @param str Reference name of object type
* @param pFunc Pointer of function ConstuctObject() in the object
*
* @see CGUI#ConstructObject()
*/
void AddObjectType(const CStr& str, ConstructObjectFunction pFunc) { m_ObjectTypes[str] = pFunc; }
/**
* Update Resolution, should be called every time the resolution
* of the OpenGL screen has been changed, this is because it needs
* to re-cache all its actual sizes
*
* Needs no input since screen resolution is global.
*
* @see IGUIObject#UpdateCachedSize()
*/
void UpdateResolution();
/**
* Generate a SGUIText object from the inputted string.
* The function will break down the string and its
* tags to calculate exactly which rendering queries
* will be sent to the Renderer. Also, horizontal alignment
* is taken into acount in this method but NOT vertical alignment.
*
* Done through the CGUI since it can communicate with
*
* @param Text Text to generate SGUIText object from
* @param Font Default font, notice both Default color and default font
* can be changed by tags.
* @param Width Width, 0 if no word-wrapping.
* @param BufferZone space between text and edge, and space between text and images.
* @param pObject Optional parameter for error output. Used *only* if error parsing fails,
* and we need to be able to output which object the error occured in to aid the user.
*/
SGUIText GenerateText(const CGUIString &Text, const CStrW& Font,
const float &Width, const float &BufferZone,
const IGUIObject *pObject=NULL);
- /**
- * Returns the JSObject* associated with the GUI
- *
- * @return The relevant JS object
- */
- JSObject* GetScriptObject() { return m_ScriptObject; }
/**
* Check if an icon exists
*/
bool IconExists(const CStr& str) const { return (m_Icons.count(str) != 0); }
/**
* Get Icon (a copy, can never be changed)
*/
SGUIIcon GetIcon(const CStr& str) const { return m_Icons.find(str)->second; }
/**
* Get pre-defined color (if it exists)
* Returns false if it fails.
*/
bool GetPreDefinedColor(const CStr& name, CColor &Output);
+
+ shared_ptr GetScriptInterface() { return m_ScriptInterface; };
+ jsval GetGlobalObject() { return m_ScriptInterface->GetGlobalObject(); };
private:
/**
* Updates the object pointers, needs to be called each
* time an object has been added or removed.
*
* This function is atomic, meaning if it throws anything, it will
* have seen it through that nothing was ultimately changed.
*
* @throws PSERROR_GUI that is thrown from IGUIObject::AddToPointersMap().
*/
void UpdateObjects();
/**
* Adds an object to the GUI's object database
* Private, since you can only add objects through
* XML files. Why? Because it enables the GUI to
* be much more encapsulated and safe.
*
* @throws Rethrows PSERROR_GUI from IGUIObject::AddChild().
*/
void AddObject(IGUIObject* pObject);
/**
* You input the name of the object type, and let's
* say you input "button", then it will construct a
* CGUIObjet* as a CButton.
*
* @param str Name of object type
* @return Newly constructed IGUIObject (but constructed as a subclass)
*/
IGUIObject *ConstructObject(const CStr& str);
/**
* Get Focused Object.
*/
IGUIObject *GetFocusedObject() { return m_FocusedObject; }
public:
/**
* Change focus to new object.
* Will send LOST_FOCUS/GOT_FOCUS messages as appropriate.
* pObject can be NULL to remove all focus.
*/
void SetFocusedObject(IGUIObject* pObject);
private:
//--------------------------------------------------------
/** @name XML Reading Xeromyces specific subroutines
*
* These does not throw!
* Because when reading in XML files, it won't be fatal
* if an error occurs, perhaps one particular object
* fails, but it'll still continue reading in the next.
* All Error are reported with ReportParseError
*/
//--------------------------------------------------------
/*
Xeromyces_* functions tree
(ReadRootObjects)
|
+-