1
0
forked from 0ad/0ad

Internationalization of the public mod (JavaScript and GUI XML side)

It includes a marvelous language selection menu developed by Yves.

It also includes the introduction of a sprintf implementation for
JavaScript, as well as
translation helper functions that use JavaScript-side translation
caching.

This patch includes code by Yves, sanderd17, leper and Gallaecio.

Ref #67

This was SVN commit r14954.
This commit is contained in:
Adrián Chaves 2014-04-20 20:51:48 +00:00
parent d6db5a466d
commit e05c8263c5
62 changed files with 2608 additions and 1116 deletions

View File

@ -0,0 +1,154 @@
var g_translations = {};
var g_pluralTranslations = {};
var g_translationsWithContext = {};
var g_pluralTranslationsWithContext = {};
// Translates the specified English message into the current language.
//
// This function relies on the g_translations cache when possible. You should use this function instead of
// Engine.Translate() whenever you can to minimize the number of C++ calls and string conversions involved.
function translate(message)
{
var translation = g_translations[message];
if (!translation)
return g_translations[message] = Engine.Translate(message);
return translation;
}
// Translates the specified English message into the current language for the specified number.
//
// This function relies on the g_pluralTranslations cache when possible. You should use this function instead of
// Engine.TranslatePlural() whenever you can to minimize the number of C++ calls and string conversions involved.
function translatePlural(singularMessage, pluralMessage, number)
{
var translation = g_pluralTranslations[singularMessage];
if (!translation)
g_pluralTranslations[singularMessage] = {};
var pluralTranslation = g_pluralTranslations[singularMessage][number];
if (!pluralTranslation)
return g_pluralTranslations[singularMessage][number] = Engine.TranslatePlural(singularMessage, pluralMessage, number);
return pluralTranslation;
}
// Translates the specified English message into the current language for the specified context.
//
// This function relies on the g_translationsWithContext cache when possible. You should use this function instead of
// Engine.TranslateWithContext() whenever you can to minimize the number of C++ calls and string conversions involved.
function translateWithContext(context, message)
{
var translationContext = g_translationsWithContext[context];
if (!translationContext)
g_translationsWithContext[context] = {}
var translationWithContext = g_translationsWithContext[context][message];
if (!translationWithContext)
return g_translationsWithContext[context][message] = Engine.TranslateWithContext(context, message);
return translationWithContext;
}
// Translates the specified English message into the current language for the specified context and number.
//
// This function relies on the g_pluralTranslationsWithContext cache when possible. You should use this function instead of
// Engine.TranslatePluralWithContext() whenever you can to minimize the number of C++ calls and string conversions involved.
function translatePluralWithContext(context, singularMessage, pluralMessage, number)
{
var translationContext = g_pluralTranslationsWithContext[context];
if (!translationContext)
g_pluralTranslationsWithContext[context] = {};
var translationWithContext = g_pluralTranslationsWithContext[context][singularMessage];
if (!translationWithContext)
g_pluralTranslationsWithContext[context][singularMessage] = {};
var pluralTranslationWithContext = g_pluralTranslationsWithContext[context][singularMessage][number];
if (!pluralTranslationWithContext)
return g_pluralTranslationsWithContext[context][singularMessage][number] = Engine.TranslatePluralWithContext(context, singularMessage, pluralMessage, number);
return pluralTranslationWithContext;
}
/**
* Translates any string value in the specified JavaScript object
* that is associated with a key included in the specified keys array.
*
* it accepts an object in the form of
*
* {
* translatedString1: "my first message",
* unTranslatedString1: "some English string",
* ignoredObject: {
* translatedString2: "my second message",
* unTranslatedString2: "some English string"
* },
* translatedObject1: {
* message: "my third singular message",
* context: "message context",
* },
* translatedObject2: {
* list: ["list", "of", "strings"],
* context: "message context",
* },
* }
*
* Together with a keys list to translate the strings and objects
* ["translatedString1", "translatedString2", "translatedObject1",
* "translatedObject2"]
*
* The result will be (f.e. in Dutch)
* {
* translatedString1: "mijn eerste bericht",
* unTranslatedString1: "some English string",
* ignoredObject: {
* translatedString2: "mijn tweede bericht",
* unTranslatedString2: "some English string"
* },
* translatedObject1: "mijn derde bericht",
* translatedObject2: "lijst, van, teksten",
* }
*
* So you see that the keys array can also contain lower-level keys,
* And that you can include objects in the keys array to translate
* them with a context, or to join a list of translations.
*/
function translateObjectKeys(object, keys) {
for (var property in object)
{
if (keys.indexOf(property) > -1)
{
if (typeof object[property] == "string")
object[property] = translate(object[property]);
else if (object[property] instanceof Object)
{
// the translation function
var trans = translate;
if (object[property].context)
trans = function(msg) { return translateWithContext(object[property].context, msg);};
if (object[property].message)
object[property] = trans(object[property].message);
else if (object[property].list)
{
var translatedList = object[property].list.map(trans);
object[property] = translatedList.join(translateWithContext("enumeration", ", "));
}
}
}
else if (object[property] instanceof Object)
translateObjectKeys(object[property], keys);
}
}
function markForTranslation(message) {
return message;
}
function markForTranslationWithContext(context, message) {
return message;
}

View File

@ -0,0 +1,183 @@
/**
sprintf() for JavaScript 0.7-beta1
http://www.diveintojavascript.com/projects/javascript-sprintf
Copyright (c) Alexandru Marasteanu <alexaholic [at) gmail (dot] com>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of sprintf() for JavaScript nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL Alexandru Marasteanu BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Changelog:
2010.09.06 - 0.7-beta1
- features: vsprintf, support for named placeholders
- enhancements: format cache, reduced global namespace pollution
2010.05.22 - 0.6:
- reverted to 0.4 and fixed the bug regarding the sign of the number 0
Note:
Thanks to Raphael Pigulla <raph (at] n3rd [dot) org> (http://www.n3rd.org/)
who warned me about a bug in 0.5, I discovered that the last update was
a regress. I appologize for that.
2010.05.09 - 0.5:
- bug fix: 0 is now preceeded with a + sign
- bug fix: the sign was not at the right position on padded results (Kamal Abdali)
- switched from GPL to BSD license
2007.10.21 - 0.4:
- unit test and patch (David Baird)
2007.09.17 - 0.3:
- bug fix: no longer throws exception on empty paramenters (Hans Pufal)
2007.09.11 - 0.2:
- feature: added argument swapping
2007.04.03 - 0.1:
- initial release
**/
var sprintf = (function() {
function get_type(variable) {
return Object.prototype.toString.call(variable).slice(8, -1).toLowerCase();
}
function str_repeat(input, multiplier) {
for (var output = []; multiplier > 0; output[--multiplier] = input) {/* do nothing */}
return output.join('');
}
var str_format = function() {
if (!str_format.cache.hasOwnProperty(arguments[0])) {
str_format.cache[arguments[0]] = str_format.parse(arguments[0]);
}
return str_format.format.call(null, str_format.cache[arguments[0]], arguments);
};
str_format.format = function(parse_tree, argv) {
var cursor = 1, tree_length = parse_tree.length, node_type = '', arg, output = [], i, k, match, pad, pad_character, pad_length;
for (i = 0; i < tree_length; i++) {
node_type = get_type(parse_tree[i]);
if (node_type === 'string') {
output.push(parse_tree[i]);
}
else if (node_type === 'array') {
match = parse_tree[i]; // convenience purposes only
if (match[2]) { // keyword argument
arg = argv[cursor];
for (k = 0; k < match[2].length; k++) {
if (!arg.hasOwnProperty(match[2][k])) {
throw(sprintf(Engine.Translate('[sprintf] property "%s" does not exist'), match[2][k]));
}
arg = arg[match[2][k]];
}
}
else if (match[1]) { // positional argument (explicit)
arg = argv[match[1]];
}
else { // positional argument (implicit)
arg = argv[cursor++];
}
if (/[^s]/.test(match[8]) && (get_type(arg) != 'number')) {
throw(sprintf(Engine.Translate('[sprintf] expecting number but found %s'), get_type(arg)));
}
switch (match[8]) {
case 'b': arg = arg.toString(2); break;
case 'c': arg = String.fromCharCode(arg); break;
case 'd': arg = parseInt(arg, 10); break;
case 'e': arg = match[7] ? arg.toExponential(match[7]) : arg.toExponential(); break;
case 'f': arg = match[7] ? parseFloat(arg).toFixed(match[7]) : parseFloat(arg); break;
case 'o': arg = arg.toString(8); break;
case 's': arg = ((arg = String(arg)) && match[7] ? arg.substring(0, match[7]) : arg); break;
case 'u': arg = Math.abs(arg); break;
case 'x': arg = arg.toString(16); break;
case 'X': arg = arg.toString(16).toUpperCase(); break;
}
arg = (/[def]/.test(match[8]) && match[3] && arg >= 0 ? '+'+ arg : arg);
pad_character = match[4] ? match[4] == '0' ? '0' : match[4].charAt(1) : ' ';
pad_length = match[6] - String(arg).length;
pad = match[6] ? str_repeat(pad_character, pad_length) : '';
output.push(match[5] ? arg + pad : pad + arg);
}
}
return output.join('');
};
str_format.cache = {};
str_format.parse = function(fmt) {
var _fmt = fmt, match = [], parse_tree = [], arg_names = 0;
while (_fmt) {
if ((match = /^[^\x25]+/.exec(_fmt)) !== null) {
parse_tree.push(match[0]);
}
else if ((match = /^\x25{2}/.exec(_fmt)) !== null) {
parse_tree.push('%');
}
else if ((match = /^\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-fosuxX])/.exec(_fmt)) !== null) {
if (match[2]) {
arg_names |= 1;
var field_list = [], replacement_field = match[2], field_match = [];
if ((field_match = /^([a-z_][a-z_\d]*)/i.exec(replacement_field)) !== null) {
field_list.push(field_match[1]);
while ((replacement_field = replacement_field.substring(field_match[0].length)) !== '') {
if ((field_match = /^\.([a-z_][a-z_\d]*)/i.exec(replacement_field)) !== null) {
field_list.push(field_match[1]);
}
else if ((field_match = /^\[(\d+)\]/.exec(replacement_field)) !== null) {
field_list.push(field_match[1]);
}
else {
throw(Engine.Translate('[sprintf] huh?'));
}
}
}
else {
throw(Engine.Translate('[sprintf] huh?'));
}
match[2] = field_list;
}
else {
arg_names |= 2;
}
if (arg_names === 3) {
throw(Engine.Translate('[sprintf] mixing positional and named placeholders is not (yet) supported'));
}
parse_tree.push(match);
}
else {
throw(sprintf(Engine.Translate('[sprintf] No placeholder found in the ‘%(formatString)s’ format string. Maybe you used an incorrect syntax for your placeholder?'), { formatString: _fmt } ));
}
_fmt = _fmt.substring(match[0].length);
}
return parse_tree;
};
return str_format;
})();
var vsprintf = function(fmt, argv) {
argv.unshift(fmt);
return sprintf.apply(null, argv);
};

View File

@ -4,12 +4,14 @@ var g_PlayerSlot;
function init(settings)
{
g_PlayerSlot = settings.playerSlot;
translateObjectKeys(settings.ais, ["name", "description"]);
g_AIs = [
{id: "", data: {name: "None", description: "AI will be disabled for this player."}}
{id: "", data: {name: translateWithContext("ai", "None"), description: translate("AI will be disabled for this player.")}}
].concat(settings.ais);
var aiSelection = Engine.GetGUIObjectByName("aiSelection");
aiSelection.list = [ ai.data.name for each (ai in g_AIs) ];
aiSelection.list = [ translate(ai.data.name) for each (ai in g_AIs) ];
var selected = 0;
for (var i = 0; i < g_AIs.length; ++i)
@ -23,7 +25,7 @@ function init(settings)
aiSelection.selected = selected;
var aiDiff = Engine.GetGUIObjectByName("aiDifficulty");
aiDiff.list = [ "Sandbox", "Easy", "Medium", "Hard", "Very Hard" ];
aiDiff.list = [translate("Sandbox"), translate("Easy"), translate("Medium"), translate("Hard"), translate("Very Hard")];
aiDiff.selected = settings.difficulty;
}

View File

@ -9,11 +9,13 @@
<object type="image" style="ModernDialog" size="50%-200 40%-140 50%+200 40%+180">
<object style="TitleText" type="text" size="50%-128 -16 50%+128 16">AI Configuration</object>
<object style="TitleText" type="text" size="50%-128 -16 50%+128 16">
<translatableAttribute id="caption">AI Configuration</translatableAttribute>
</object>
<object size="50%-128 30 50%+128 80">
<object type="text" style="ModernRightLabelText" size="-10 0 90 50%">
AI Player
<translatableAttribute id="caption">AI Player</translatableAttribute>
</object>
<object name="aiSelection" type="dropdown" style="ModernDropDown" size="50%-24 0 50%+136 28">
@ -21,7 +23,7 @@
</object>
<object type="text" style="ModernRightLabelText" size="-10 35 90 50%+35">
AI Difficulty
<translatableAttribute id="caption">AI Difficulty</translatableAttribute>
</object>
<object name="aiDifficulty" type="dropdown" style="ModernDropDown" size="50%-24 35 50%+136 63">
@ -37,12 +39,12 @@
</object>
<object type="button" style="StoneButton" size="50%-144 100%-60 50%-16 100%-32">
OK
<translatableAttribute id="caption">OK</translatableAttribute>
<action on="Press">returnAI();</action>
</object>
<object type="button" style="StoneButton" size="50%+16 100%-60 50%+144 100%-32">
Cancel
<translatableAttribute id="caption">Cancel</translatableAttribute>
<action on="Press">Engine.PopGuiPage();</action>
</object>
</object>

View File

@ -80,37 +80,41 @@ function heading(string, size)
// Called when user selects civ from dropdown
function selectCiv(code)
{
var escapeChars = function(str)
{
return str.replace(/\[/g, "&#91;").replace(/\]/g, "&#93;").replace(/"/g, "&#34;");
};
var civInfo = g_CivData[code];
if(!civInfo)
error("Error loading civ data for \""+code+"\"");
error(sprintf("Error loading civ data for \"%(code)s\"", { code: code }));
// Update civ gameplay display
Engine.GetGUIObjectByName("civGameplayHeading").caption = heading(civInfo.Name+" Gameplay", 16);
Engine.GetGUIObjectByName("civGameplayHeading").caption = heading(sprintf(translate("%(civilization)s Gameplay"), { civilization: civInfo.Name }), 16);
// Bonuses
var bonusCaption = heading("Civilization Bonus"+(civInfo.CivBonuses.length == 1 ? "" : "es"), 12) + '\n';
var bonusCaption = heading(translatePlural("Civilization Bonus", "Civilization Bonuses", civInfo.CivBonuses.length), 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]';
+ escapeChars(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';
bonusCaption += heading(translatePlural("Team Bonus", "Team Bonuses", civInfo.TeamBonuses.length), 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]';
+ escapeChars(civInfo.TeamBonuses[i].History) + '" tooltip_style="civInfoTooltip"]\n ' + civInfo.TeamBonuses[i].Description + '\n[/color]';
}
Engine.GetGUIObjectByName("civBonuses").caption = bonusCaption;
// Special techs / buildings
var techCaption = heading("Special Technologies", 12) + '\n';
var techCaption = heading(translate("Special Technologies"), 12) + '\n';
for(var i = 0; i < civInfo.Factions.length; ++i)
{
@ -118,23 +122,23 @@ function selectCiv(code)
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]';
+ escapeChars(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';
techCaption += heading(translatePlural("Special Building", "Special Buildings", civInfo.Structures.length), 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';
+ escapeChars(civInfo.Structures[i].History) + '" tooltip_style="civInfoTooltip"]\n';
}
Engine.GetGUIObjectByName("civTechs").caption = techCaption;
// Heroes
var heroCaption = heading("Heroes", 12) + '\n';
var heroCaption = heading(translate("Heroes"), 12) + '\n';
for(var i = 0; i < civInfo.Factions.length; ++i)
{
@ -142,7 +146,7 @@ function selectCiv(code)
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';
+ escapeChars(faction.Heroes[j].History) + '" tooltip_style="civInfoTooltip"]\n';
}
heroCaption += '\n';
}
@ -151,6 +155,6 @@ function selectCiv(code)
// Update civ history display
Engine.GetGUIObjectByName("civHistoryHeading").caption = heading("History of the " + civInfo.Name, 16);
Engine.GetGUIObjectByName("civHistoryHeading").caption = heading(sprintf(translate("History of the %(civilization)s"), { civilization: civInfo.Name }), 16);
Engine.GetGUIObjectByName("civHistoryText").caption = civInfo.History;
}

View File

@ -10,9 +10,11 @@
<!-- Add a translucent black background to fade out the menu page -->
<object type="image" z="0" sprite="bkTranslucent"/>
<object type="image" style="ModernDialog" size="50%-466 50%-316 50%+466 50%+316">
<object type="image" style="ModernDialog" size="50%-500 50%-368 50%+500 50%+370">
<object style="TitleText" type="text" size="50%-128 -16 50%+128 16">Civilizations</object>
<object style="TitleText" type="text" size="50%-128 -16 50%+128 16">
<translatableAttribute id="caption">Civilizations</translatableAttribute>
</object>
<!-- Civ selection -->
<object size="25 10 100% 30">
@ -22,8 +24,8 @@
font="serif-bold-20"
textcolor="white"
text_align="left"
size="50%-320 10 50%-96 48">
Civilization Selection
size="50%-420 10 50%-96 48">
<translatableAttribute id="caption">Civilization Selection</translatableAttribute>
</object>
<object name="civSelection" type="dropdown" style="ModernDropDown" size="50%-96 10 50%+96 40">
@ -112,7 +114,8 @@
type="button"
style="StoneButton"
size="100%-164 100%-52 100%-24 100%-24"
>Close
>
<translatableAttribute id="caption">Close</translatableAttribute>
<action on="Press">
<![CDATA[
Engine.PopGuiPage();

View File

@ -14,6 +14,7 @@ function loadCivData()
for each (var filename in civFiles)
{ // Parse data if valid file
var data = parseJSONData(filename);
translateObjectKeys(data, ["Name", "Description", "History", "Special"]);
civData[data.Code] = data;
}

View File

@ -70,5 +70,5 @@ function messageBox (mbWidth, mbHeight, mbMessage, mbTitle, mbMode, mbButtonCapt
function updateFPS()
{
Engine.GetGUIObjectByName("fpsCounter").caption = "FPS: " + Engine.GetFPS();
Engine.GetGUIObjectByName("fpsCounter").caption = sprintf(translate("FPS: %(fps)s"), { fps: Engine.GetFPS() });
}

View File

@ -64,7 +64,7 @@ function parseJSONData(pathname)
var rawData = Engine.ReadFile(pathname);
if (!rawData)
{
error("Failed to read file: "+pathname);
error(sprintf("Failed to read file: %(path)s", { path: pathname }));
}
else
{
@ -73,13 +73,11 @@ function parseJSONData(pathname)
// 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)");
error(sprintf("Failed to parse JSON data in: %(path)s (check for valid JSON data)", { path: pathname }));
}
catch(err)
{
error(err.toString()+": parsing JSON data in "+pathname);
error(sprintf("%(error)s: parsing JSON data in %(path)s", { error: err.toString(), path: pathname }));
}
}
@ -141,7 +139,7 @@ function parseJSONFromDataFile(filename)
var path = "simulation/data/"+filename;
var rawData = Engine.ReadFile(path);
if (!rawData)
error("Failed to read file: "+path);
error(sprintf("Failed to read file: %(path)s", { path: path }));
try
{
@ -152,7 +150,7 @@ function parseJSONFromDataFile(filename)
}
catch(err)
{
error(err.toString()+": parsing JSON data in "+path);
error(sprintf("%(error)s: parsing JSON data in %(path)s", { error: err.toString(), path: path }));
}
return undefined;
@ -169,7 +167,10 @@ function initPlayerDefaults()
if (!data || !data.PlayerData)
error("Failed to parse player defaults in player_defaults.json (check for valid JSON data)");
else
{
translateObjectKeys(data.PlayerData, ["Name"])
defaults = data.PlayerData;
}
return defaults;
}
@ -191,6 +192,7 @@ function initMapSizes()
error("Failed to parse map sizes in map_sizes.json (check for valid JSON data)");
else
{
translateObjectKeys(data, ["Name", "LongName"]);
for (var i = 0; i < data.Sizes.length; ++i)
{
sizes.shortNames.push(data.Sizes[i].Name);
@ -221,6 +223,7 @@ function initGameSpeeds()
error("Failed to parse game speeds in game_speeds.json (check for valid JSON data)");
else
{
translateObjectKeys(data, ["Name"]);
for (var i = 0; i < data.Speeds.length; ++i)
{
gameSpeeds.names.push(data.Speeds[i].Name);
@ -250,16 +253,17 @@ function iColorToString(color)
// ====================================================================
/**
* Convert time in milliseconds to hh:mm:ss string representation.
* 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);
if (time < 1000 * 60 * 60)
var format = translate("mm:ss");
else
var format = translate("HH:mm:ss");
return Engine.FormatMillisecondsIntoDateString(time, format);
}
// ====================================================================

View File

@ -19,7 +19,7 @@ function cancelOnError(msg)
width: 500,
height: 200,
message: '[font="serif-bold-18"]' + msg + '[/font]',
title: "Loading Aborted",
title: translate("Loading Aborted"),
mode: 2
});
}

View File

@ -11,7 +11,7 @@
function removeItem (objectName, pos)
{
if (Engine.GetGUIObjectByName (objectName) == null)
Engine.Console_Write ("removeItem(): " + objectName + " not found.");
Engine.Console_Write (sprintf(translate("%(functionName)s: %(object)s not found."), { functionName: "removeItem()", object: objectName }));
var list = Engine.GetGUIObjectByName (objectName).list;
var selected = Engine.GetGUIObjectByName (objectName).selected;
@ -41,7 +41,7 @@ function removeItem (objectName, pos)
function addItem (objectName, pos, value)
{
if (Engine.GetGUIObjectByName (objectName) == null)
Engine.Console_Write ("addItem(): " + objectName + " not found.");
Engine.Console_Write (sprintf(translate("%(functionName)s: %(object)s not found."), { functionName: "addItem()", object: objectName }));
var list = Engine.GetGUIObjectByName (objectName).list;
var selected = Engine.GetGUIObjectByName (objectName).selected;
@ -66,7 +66,7 @@ function addItem (objectName, pos, value)
function pushItem (objectName, value)
{
if (Engine.GetGUIObjectByName (objectName) == null)
Engine.Console_Write ("pushItem(): " + objectName + " not found.");
Engine.Console_Write (sprintf(translate("%(functionName)s: %(object)s not found."), { functionName: "pushItem()", object: objectName }));
var list = Engine.GetGUIObjectByName (objectName).list;
list.push (value);
@ -81,7 +81,7 @@ function pushItem (objectName, value)
function popItem (objectName)
{
if (Engine.GetGUIObjectByName (objectName) == null)
Engine.Console_Write ("popItem(): " + objectName + " not found.");
Engine.Console_Write (sprintf(translate("%(functionName)s: %(object)s not found."), { functionName: "popItem()", object: objectName }));
var selected = Engine.GetGUIObjectByName (objectName).selected;
removeItem(objectName, getNumItems(objectName)-1);
@ -98,7 +98,7 @@ function popItem (objectName)
function getNumItems (objectName)
{
if (Engine.GetGUIObjectByName (objectName) == null)
Engine.Console_Write ("getNumItems(): " + objectName + " not found.");
Engine.Console_Write (sprintf(translate("%(functionName)s: %(object)s not found."), { functionName: "getNumItems()", object: objectName }));
var list = Engine.GetGUIObjectByName(objectName).list;
return list.length;
@ -110,7 +110,7 @@ function getNumItems (objectName)
function getItemValue (objectName, pos)
{
if (Engine.GetGUIObjectByName (objectName) == null)
Engine.Console_Write ("getItemValue(): " + objectName + " not found.");
Engine.Console_Write (sprintf(translate("%(functionName)s: %(object)s not found."), { functionName: "getItemValue()", object: objectName }));
var list = Engine.GetGUIObjectByName(objectName).list;
return list[pos];
@ -122,7 +122,7 @@ function getItemValue (objectName, pos)
function getCurrItemValue (objectName)
{
if (Engine.GetGUIObjectByName (objectName) == null)
Engine.Console_Write ("getCurrItemValue(): " + objectName + " not found.");
Engine.Console_Write (sprintf(translate("%(functionName)s: %(object)s not found."), { functionName: "getCurrItemValue()", object: objectName }));
if (Engine.GetGUIObjectByName(objectName).selected == -1)
return "";
@ -136,8 +136,9 @@ function getCurrItemValue (objectName)
// already in the list).
function setCurrItemValue (objectName, string)
{
if (Engine.GetGUIObjectByName(objectName) == null) {
Engine.Console_Write ("setCurrItemValue(): " + objectName + " not found.");
if (Engine.GetGUIObjectByName(objectName) == null)
{
Engine.Console_Write (sprintf(translate("%(functionName)s: %(object)s not found."), { functionName: "setCurrItemValue()", object: objectName }));
return -1;
}
@ -157,7 +158,7 @@ function setCurrItemValue (objectName, string)
}
// Return -2 if failed to find value in list.
Engine.Console_Write ("Requested string '" + string + "' not found in " + objectName + "'s list.");
Engine.Console_Write (sprintf(translate("Requested string '%(string)s' not found in %(object)s's list."), { string: string, object: objectName }));
return -2;
}

View File

@ -3,29 +3,21 @@ function sortDecreasingDate(a, b)
return b.metadata.time - a.metadata.time;
}
function twoDigits(n)
{
return n < 10 ? "0" + n : n;
}
function generateLabel(metadata, engineInfo)
{
var t = new Date(metadata.time*1000);
var date = t.getFullYear()+"-"+twoDigits(1+t.getMonth())+"-"+twoDigits(t.getDate());
var time = twoDigits(t.getHours())+":"+twoDigits(t.getMinutes())+":"+twoDigits(t.getSeconds());
var label = "["+date+" "+time+"] ";
var dateTimeString = Engine.FormatMillisecondsIntoDateString(metadata.time*1000, translate("yyyy-MM-dd HH:mm:ss"));
var dateString = sprintf(translate("[%(date)s]"), { date: dateTimeString });
if (engineInfo)
{
if (!hasSameVersion(metadata, engineInfo))
label = "[color=\"red\"]" + label + "[/color]";
dateString = "[color=\"red\"]" + dateString + "[/color]";
else if (!hasSameMods(metadata, engineInfo))
label = "[color=\"orange\"]" + label + "[/color]";
dateString = "[color=\"orange\"]" + dateString + "[/color]";
}
label += metadata.initAttributes.map.replace("maps/","")
+ (metadata.description ? " - "+metadata.description : "");
return label;
if (metadata.description)
return sprintf(translate("%(dateString)s %(map)s - %(description)s"), { dateString: dateString, map: metadata.initAttributes.map, description: metadata.description });
else
return sprintf(translate("%(dateString)s %(map)s"), { dateString: dateString, map: metadata.initAttributes.map });
}
/**

View File

@ -53,7 +53,7 @@ function newRandomSound(soundType, soundSubType, soundPrePath)
var soundArray = Engine.BuildDirEntList(randomSoundPath, "*" + soundSubType + "*", false);
if (soundArray.length == 0)
{
Engine.Console_Write ("Failed to find sounds matching '*"+soundSubType+"*'");
Engine.Console_Write (sprintf("Failed to find sounds matching '*%(subtype)s*'", { soundSubType: subtype }));
return undefined;
}
// Get a random number within the sound's range.
@ -75,7 +75,7 @@ function newRandomSound(soundType, soundSubType, soundPrePath)
return new AmbientSound(randomSoundPath);
break;
case "effect":
Engine.Console_Write("am loading effect '*"+randomSoundPath+"*'");
Engine.Console_Write(sprintf("am loading effect '*%(path)s*'", { path: randomSoundPath }));
break;
default:
break;

View File

@ -99,7 +99,7 @@ Music.prototype.updateState = function()
break;
default:
warn("Music.updateState(): Unknown music state: " + this.currentState);
warn(sprintf("%(functionName)s: Unknown music state: %(state)s", { functionName: "Music.updateState()", state: this.currentState }));
break;
}
}
@ -122,7 +122,7 @@ Music.prototype.storeTracks = function(civMusic)
if (type === undefined)
{
warn("Music.storeTracks(): Unrecognized music type: " + music.Type);
warn(sprintf("%(functionName)s: Unrecognized music type: %(musicType)s", { functionName: "Music.storeTracks()", musicType: music.Type }));
continue;
}

View File

@ -3,11 +3,11 @@ function getDisconnectReason(id)
// Must be kept in sync with source/network/NetHost.h
switch (id)
{
case 0: return "Unknown reason";
case 1: return "Unexpected shutdown";
case 2: return "Incorrect network protocol version";
case 3: return "Game has already started";
default: return "[Invalid value "+id+"]";
case 0: return translate("Unknown reason");
case 1: return translate("Unexpected shutdown");
case 2: return translate("Incorrect network protocol version");
case 3: return translate("Game has already started");
default: return sprintf(translate("[Invalid value %(id)s]"), { id: id });
}
}
@ -16,6 +16,6 @@ function reportDisconnect(reason)
var reasontext = getDisconnectReason(reason);
messageBox(400, 200,
"Lost connection to the server.\n\nReason: " + reasontext + ".",
"Disconnected", 2);
translate("Lost connection to the server.") + "\n\n" + sprintf(translate("Reason: %(reason)s."), { reason: reasontext }),
translate("Disconnected"), 2);
}

View File

@ -62,7 +62,7 @@ function updateTimers()
t[1]();
} catch (e) {
var stack = e.stack.trimRight().replace(/^/mg, ' '); // indent the stack trace
error("Error in timer: "+e+"\n"+stack+"\n");
error(sprintf("Error in timer: %(error)s", { error: e })+"\n"+stack+"\n");
}
delete g_Timers[id];
}

View File

@ -4,13 +4,13 @@ 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_TEXT = [translate("Conquest"), translate("Wonder"), translateWithContext("victory", "None")];
const VICTORY_DATA = ["conquest", "wonder", "endless"];
const VICTORY_DEFAULTIDX = 0;
const POPULATION_CAP = ["50", "100", "150", "200", "250", "300", "Unlimited"];
const POPULATION_CAP = ["50", "100", "150", "200", "250", "300", translate("Unlimited")];
const POPULATION_CAP_DATA = [50, 100, 150, 200, 250, 300, 10000];
const POPULATION_CAP_DEFAULTIDX = 5;
const STARTING_RESOURCES = ["Very Low", "Low", "Medium", "High", "Very High", "Deathmatch"];
const STARTING_RESOURCES = [translate("Very Low"), translate("Low"), translate("Medium"), translate("High"), translate("Very High"), translate("Deathmatch")];
const STARTING_RESOURCES_DATA = [100, 300, 500, 1000, 3000, 50000];
const STARTING_RESOURCES_DEFAULTIDX = 1;
// Max number of players for any map
@ -53,9 +53,10 @@ 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.";
var g_NavalWarning = "\n\n" + sprintf(
translate("%(warning)s The AI does not support naval maps and may cause severe performance issues. Naval maps are recommended to be played with human opponents only."),
{ warning: "[font=\"serif-bold-12\"][color=\"orange\"]" + translate("Warning:") + "[/color][/font]" }
);
// 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
@ -81,7 +82,7 @@ function init(attribs)
g_IsController = false;
break;
default:
error("Unexpected 'type' in gamesetup init: "+attribs.type);
error(sprintf("Unexpected 'type' in gamesetup init: %(unexpectedType)s", { unexpectedType: attribs.type }));
}
if (attribs.serverName)
@ -91,11 +92,11 @@ function init(attribs)
var cancelButton = Engine.GetGUIObjectByName("cancelGame");
if(!Engine.HasXmppClient())
{
cancelButton.tooltip = "Return to the main menu."
cancelButton.tooltip = translate("Return to the main menu.");
}
else
{
cancelButton.tooltip = "Return to the lobby."
cancelButton.tooltip = translate("Return to the lobby.");
}
}
@ -125,19 +126,20 @@ function initMain()
// Init map types
var mapTypes = Engine.GetGUIObjectByName("mapTypeSelection");
mapTypes.list = ["Skirmish","Random","Scenario"];
mapTypes.list = [translateWithContext("map", "Skirmish"), translateWithContext("map", "Random"), translate("Scenario")];
mapTypes.list_data = ["skirmish","random","scenario"];
// Setup map filters - will appear in order they are added
addFilter("Default", function(settings) { return settings && (settings.Keywords === undefined || !keywordTestOR(settings.Keywords, ["naval", "demo", "hidden"])); });
addFilter("Naval Maps", function(settings) { return settings && settings.Keywords !== undefined && keywordTestAND(settings.Keywords, ["naval"]); });
addFilter("Demo Maps", function(settings) { return settings && settings.Keywords !== undefined && keywordTestAND(settings.Keywords, ["demo"]); });
addFilter("All Maps", function(settings) { return true; });
addFilter("default", translate("Default"), function(settings) { return settings && (settings.Keywords === undefined || !keywordTestOR(settings.Keywords, ["naval", "demo", "hidden"])); });
addFilter("naval", translate("Naval Maps"), function(settings) { return settings && settings.Keywords !== undefined && keywordTestAND(settings.Keywords, ["naval"]); });
addFilter("demo", translate("Demo Maps"), function(settings) { return settings && settings.Keywords !== undefined && keywordTestAND(settings.Keywords, ["demo"]); });
addFilter("all", translate("All Maps"), function(settings) { return true; });
// Populate map filters dropdown
var mapFilters = Engine.GetGUIObjectByName("mapFilterSelection");
mapFilters.list = getFilters();
g_GameAttributes.mapFilter = "Default";
mapFilters.list = getFilterNames();
mapFilters.list_data = getFilterIds();
g_GameAttributes.mapFilter = "default";
// Setup controls for host only
if (g_IsController)
@ -341,7 +343,7 @@ function initMain()
// Populate team dropdowns
var team = Engine.GetGUIObjectByName("playerTeam["+i+"]");
team.list = ["None", "1", "2", "3", "4"];
team.list = [translateWithContext("team", "None"), "1", "2", "3", "4"];
team.list_data = [-1, 0, 1, 2, 3];
team.selected = 0;
@ -513,7 +515,7 @@ function initCivNameList()
var civListCodes = [ civ.code for each (civ in civList) ];
// Add random civ to beginning of list
civListNames.unshift("[color=\"orange\"]Random");
civListNames.unshift("[color=\"orange\"]" + translateWithContext("civilization", "Random"));
civListCodes.unshift("random");
// Update the dropdowns
@ -546,7 +548,7 @@ function initMapNameList()
break;
default:
error("initMapNameList: Unexpected map type '"+g_GameAttributes.mapType+"'");
error(sprintf("initMapNameList: Unexpected map type '%(mapType)s'", { mapType: g_GameAttributes.mapType }));
return;
}
@ -564,7 +566,7 @@ function initMapNameList()
// Alphabetically sort the list, ignoring case
mapList.sort(sortNameIgnoreCase);
if (g_GameAttributes.mapType == "random")
mapList.unshift({ "name": "[color=\"orange\"]Random[/color]", "file": "random" });
mapList.unshift({ "name": "[color=\"orange\"]" + translateWithContext("map", "Random") + "[/color]", "file": "random" });
var mapListNames = [ map.name for each (map in mapList) ];
var mapListFiles = [ map.file for each (map in mapList) ];
@ -595,17 +597,21 @@ function loadMapData(name)
case "scenario":
case "skirmish":
g_MapData[name] = Engine.LoadMapSettings(name);
translateObjectKeys(g_MapData[name], ["Name", "Description"]);
break;
case "random":
if (name == "random")
g_MapData[name] = {settings : {"Name" : "Random", "Description" : "Randomly selects a map from the list"}};
g_MapData[name] = { settings: { "Name": translateWithContext("map", "Random"), "Description": translate("Randomly selects a map from the list") } };
else
{
g_MapData[name] = parseJSONData(name+".json");
translateObjectKeys(g_MapData[name], ["Name", "Description"]);
}
break;
default:
error("loadMapData: Unexpected map type '"+g_GameAttributes.mapType+"'");
error(sprintf("loadMapData: Unexpected map type '%(mapType)s'", { mapType: g_GameAttributes.mapType }));
return undefined;
}
}
@ -705,7 +711,7 @@ function selectNumPlayers(num)
if (g_IsNetworked)
Engine.AssignNetworkPlayer(player, "");
else
g_PlayerAssignments = { "local": { "name": "You", "player": 1, "civ": "", "team": -1} };
g_PlayerAssignments = { "local": { "name": translate("You"), "player": 1, "civ": "", "team": -1} };
}
}
@ -759,7 +765,7 @@ function selectMapType(type)
break;
default:
error("selectMapType: Unexpected map type '"+g_GameAttributes.mapType+"'");
error(sprintf("selectMapType: Unexpected map type '%(mapType)s'", { mapType: g_GameAttributes.mapType }));
return;
}
@ -768,7 +774,7 @@ function selectMapType(type)
updateGameAttributes();
}
function selectMapFilter(filterName)
function selectMapFilter(id)
{
// Avoid recursion
if (g_IsInGuiUpdate)
@ -778,7 +784,7 @@ function selectMapFilter(filterName)
if (g_IsNetworked && !g_IsController)
return;
g_GameAttributes.mapFilter = filterName;
g_GameAttributes.mapFilter = id;
initMapNameList();
@ -806,7 +812,7 @@ function selectMap(name)
// Copy any new settings
g_GameAttributes.map = name;
g_GameAttributes.script = mapSettings.Script;
if (mapData !== "Random")
if (g_GameAttributes.map !== "random")
for (var prop in mapSettings)
g_GameAttributes.settings[prop] = mapSettings[prop];
@ -822,7 +828,7 @@ function selectMap(name)
// Reset player assignments on map change
if (!g_IsNetworked)
{ // Slot 1
g_PlayerAssignments = { "local": { "name": "You", "player": 1, "civ": "", "team": -1} };
g_PlayerAssignments = { "local": { "name": translate("You"), "player": 1, "civ": "", "team": -1} };
}
else
{
@ -897,8 +903,9 @@ function launchGame()
usedName++;
// Assign civ specific names to AI players
chosenName = translate(chosenName);
if (usedName)
g_GameAttributes.settings.PlayerData[i].Name = chosenName + " " + romanNumbers[usedName+1];
g_GameAttributes.settings.PlayerData[i].Name = sprintf(translate("%(playerName)s %(romanNumber)s"), { playerName: chosenName, romanNumber: romanNumbers[usedName+1]});
else
g_GameAttributes.settings.PlayerData[i].Name = chosenName;
}
@ -947,7 +954,9 @@ function onGameAttributesChange()
// Update some controls for clients
if (!g_IsController)
{
Engine.GetGUIObjectByName("mapFilterText").caption = g_GameAttributes.mapFilter;
var mapFilderSelection = Engine.GetGUIObjectByName("mapFilterText");
var mapFilterId = mapFilderSelection.list_data.indexOf(g_GameAttributes.mapFilter);
Engine.GetGUIObjectByName("mapFilterText").caption = mapFilderSelection.list[mapFilterId];
var mapTypeSelection = Engine.GetGUIObjectByName("mapTypeSelection");
var idx = mapTypeSelection.list_data.indexOf(g_GameAttributes.mapType);
Engine.GetGUIObjectByName("mapTypeText").caption = mapTypeSelection.list[idx];
@ -1012,7 +1021,7 @@ function onGameAttributesChange()
switch (g_GameAttributes.mapType)
{
case "random":
mapSizeDesc.hidden = false;
mapSizeDesc.hidden = false;
if (g_IsController)
{
//Host
@ -1035,16 +1044,16 @@ function onGameAttributesChange()
populationCapText.hidden = true;
startingResourcesText.hidden = true;
mapSizeText.caption = "Map size:";
mapSizeText.caption = translate("Map size:");
mapSize.selected = sizeIdx;
revealMapText.caption = "Reveal map:";
exploreMapText.caption = "Explore map:";
revealMapText.caption = translate("Reveal map:");
exploreMapText.caption = translate("Explored map:");
revealMap.checked = (mapSettings.RevealMap ? true : false);
exploreMap.checked = (mapSettings.ExploreMap ? true : false);
victoryConditionText.caption = "Victory condition:";
victoryConditionText.caption = translate("Victory condition:");
victoryCondition.selected = victoryIdx;
lockTeamsText.caption = "Teams locked:";
lockTeamsText.caption = translate("Teams locked:");
lockTeams.checked = (mapSettings.LockTeams ? true : false);
}
else
@ -1063,16 +1072,16 @@ function onGameAttributesChange()
numPlayersText.caption = numPlayers;
mapSizeText.caption = g_MapSizes.names[sizeIdx];
revealMapText.caption = (mapSettings.RevealMap ? "Yes" : "No");
exploreMapText.caption = (mapSettings.ExloreMap ? "Yes" : "No");
revealMapText.caption = (mapSettings.RevealMap ? translate("Yes") : translate("No"));
exploreMapText.caption = (mapSettings.ExporeMap ? translate("Yes") : translate("No"));
victoryConditionText.caption = VICTORY_TEXT[victoryIdx];
lockTeamsText.caption = (mapSettings.LockTeams ? "Yes" : "No");
lockTeamsText.caption = (mapSettings.LockTeams ? translate("Yes") : translate("No"));
}
break;
case "skirmish":
mapSizeText.caption = "Default";
mapSizeText.caption = translate("Default");
numPlayersText.caption = numPlayers;
numPlayersSelection.hidden = true;
mapSize.hidden = true;
@ -1096,14 +1105,14 @@ function onGameAttributesChange()
populationCapText.hidden = true;
startingResourcesText.hidden = true;
revealMapText.caption = "Reveal map:";
exploreMapText.caption = "Explore map:";
revealMapText.caption = translate("Reveal map:");
exploreMapText.caption = translate("Explore map:");
revealMap.checked = (mapSettings.RevealMap ? true : false);
exploreMap.checked = (mapSettings.ExploreMap ? true : false);
victoryConditionText.caption = "Victory condition:";
victoryConditionText.caption = translate("Victory condition:");
victoryCondition.selected = victoryIdx;
lockTeamsText.caption = "Teams locked:";
lockTeamsText.caption = translate("Teams locked:");
lockTeams.checked = (mapSettings.LockTeams ? true : false);
}
else
@ -1119,10 +1128,10 @@ function onGameAttributesChange()
startingResources.hidden = true;
startingResourcesText.hidden = false;
revealMapText.caption = (mapSettings.RevealMap ? "Yes" : "No");
exploreMapText.caption = (mapSettings.ExploreMap ? "Yes" : "No");
revealMapText.caption = (mapSettings.RevealMap ? translate("Yes") : translate("No"));
exploreMapText.caption = (mapSettings.ExploreMap ? translate("Yes") : translate("No"));
victoryConditionText.caption = VICTORY_TEXT[victoryIdx];
lockTeamsText.caption = (mapSettings.LockTeams ? "Yes" : "No");
lockTeamsText.caption = (mapSettings.LockTeams ? translate("Yes") : translate("No"));
}
break;
@ -1149,17 +1158,17 @@ function onGameAttributesChange()
startingResourcesText.hidden = false;
numPlayersText.caption = numPlayers;
mapSizeText.caption = "Default";
revealMapText.caption = (mapSettings.RevealMap ? "Yes" : "No");
exploreMapText.caption = (mapSettings.ExploreMap ? "Yes" : "No");
mapSizeText.caption = translate("Default");
revealMapText.caption = (mapSettings.RevealMap ? translate("Yes") : translate("No"));
exploreMapText.caption = (mapSettings.ExploreMap ? translate("Yes") : translate("No"));
victoryConditionText.caption = VICTORY_TEXT[victoryIdx];
lockTeamsText.caption = (mapSettings.LockTeams ? "Yes" : "No");
lockTeamsText.caption = (mapSettings.LockTeams ? translate("Yes") : translate("No"));
Engine.GetGUIObjectByName("populationCap").selected = POPULATION_CAP_DEFAULTIDX;
break;
default:
error("onGameAttributesChange: Unexpected map type '"+g_GameAttributes.mapType+"'");
error(sprintf("onGameAttributesChange: Unexpected map type '%(mapType)s'", { mapType: g_GameAttributes.mapType }));
return;
}
@ -1167,13 +1176,13 @@ function onGameAttributesChange()
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.";
var description = mapSettings.Description || translate("Sorry, no description available.");
if (g_GameAttributes.mapFilter == "Naval Maps")
if (g_GameAttributes.mapFilter == "naval")
description += g_NavalWarning;
// Describe the number of players
var playerString = numPlayers + " " + (numPlayers == 1 ? "player" : "players") + ". ";
var playerString = sprintf(translatePlural("%(number)s player. %(description)s", "%(number)s players. %(description)s", numPlayers), { number: numPlayers, description: description });
for (var i = 0; i < MAX_PLAYERS; ++i)
{
@ -1212,7 +1221,7 @@ function onGameAttributesChange()
pTeam.hidden = true;
// Set text values
if (civ == "random")
pCivText.caption = "[color=\"orange\"]Random";
pCivText.caption = "[color=\"orange\"]" + translateWithContext("civilization", "Random");
else
pCivText.caption = g_CivData[civ].Name;
pTeamText.caption = (team !== undefined && team >= 0) ? team+1 : "-";
@ -1229,7 +1238,7 @@ function onGameAttributesChange()
}
}
Engine.GetGUIObjectByName("mapInfoDescription").caption = playerString + description;
Engine.GetGUIObjectByName("mapInfoDescription").caption = playerString;
g_IsInGuiUpdate = false;
@ -1310,12 +1319,12 @@ function updatePlayerList()
}
// 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);
hostNameList.push("[color=\"70 150 70 255\"]" + sprintf(translate("AI: %(ai)s"), { ai: translate(ai.data.name) }));
hostGuidList.push("ai:" + ai.id);
}
noAssignment = hostNameList.length;
hostNameList.push("[color=\"140 140 140 255\"]Unassigned");
hostNameList.push("[color=\"140 140 140 255\"]" + translate("Unassigned"));
hostGuidList.push("");
for (var i = 0; i < MAX_PLAYERS; ++i)
@ -1342,7 +1351,7 @@ function updatePlayerList()
if (aiId in aiAssignments)
selection = aiAssignments[aiId];
else
warn("AI \""+aiId+"\" not present. Defaulting to unassigned.");
warn(sprintf("AI \"%(id)s\" not present. Defaulting to unassigned.", { id: aiId }));
}
if (!selection)
@ -1507,19 +1516,23 @@ function addChatMessage(msg)
switch (msg.type)
{
case "connect":
formatted = '[font="serif-bold-13"][color="'+ color +'"]' + username + '[/color][/font] [color="gold"]has joined[/color]';
var formattedUsername = '[font="serif-bold-13"][color="'+ color +'"]' + username + '[/color][/font][color="gold"]'
formatted = '[color="gold"]' + sprintf(translate("%(username)s has joined"), { username: formattedUsername });
break;
case "disconnect":
formatted = '[font="serif-bold-13"][color="'+ color +'"]' + username + '[/color][/font] [color="gold"]has left[/color]';
var formattedUsername = '[font="serif-bold-13"][color="'+ color +'"]' + username + '[/color][/font][color="gold"]'
formatted = '[color="gold"]' + sprintf(translate("%(username)s has left"), { username: formattedUsername });
break;
case "message":
formatted = '[font="serif-bold-13"]<[color="'+ color +'"]' + username + '[/color]>[/font] ' + message;
var formattedUsername = '[color="'+ color +'"]' + username + '[/color]'
var formattedUsernamePrefix = '[font="serif-bold-13"]' + sprintf(translate("<%(username)s>"), { username: formattedUsername }) + '[/font]'
formatted = sprintf(translate("%(username)s %(message)s"), { username: formattedUsernamePrefix, message: message });
break;
default:
error("Invalid chat message '" + uneval(msg) + "'");
error(sprintf("Invalid chat message '%(message)s'", { message: uneval(msg) }));
return;
}
@ -1538,11 +1551,12 @@ function toggleMoreOptions()
// Basic map filters API
// Add a new map list filter
function addFilter(name, filterFunc)
function addFilter(id, name, filterFunc)
{
if (filterFunc instanceof Object)
{ // Basic validity test
var newFilter = {};
newFilter.id = id;
newFilter.name = name;
newFilter.filter = filterFunc;
@ -1550,12 +1564,22 @@ function addFilter(name, filterFunc)
}
else
{
error("Invalid map filter: "+name);
error(sprintf("Invalid map filter: %(name)s", { name: name }));
}
}
// Get array of map filter IDs
function getFilterIds()
{
var filters = [];
for (var i = 0; i < g_MapFilters.length; ++i)
filters.push(g_MapFilters[i].id);
return filters;
}
// Get array of map filter names
function getFilters()
function getFilterNames()
{
var filters = [];
for (var i = 0; i < g_MapFilters.length; ++i)
@ -1565,13 +1589,13 @@ function getFilters()
}
// Test map filter on given map settings object
function testFilter(name, mapSettings)
function testFilter(id, mapSettings)
{
for (var i = 0; i < g_MapFilters.length; ++i)
if (g_MapFilters[i].name == name)
if (g_MapFilters[i].id == id)
return g_MapFilters[i].filter(mapSettings);
error("Invalid map filter: "+name);
error(sprintf("Invalid map filter: %(id)s", { id: id }));
return false;
}

View File

@ -12,17 +12,17 @@
<object type="image" style="ModernWindow" size="0 0 100% 100%">
<object style="TitleText" type="text" size="50%-128 4 50%+128 36">
Match Setup
<translatableAttribute id="caption">Match Setup</translatableAttribute>
</object>
<object type="image" style="ModernDialog" size="50%-190 50%-80 50%+190 50%+80" name="loadingWindow">
<object type="text" style="TitleText" size="50%-128 0%-16 50%+128 16">
Loading
<translatableAttribute id="caption">Loading</translatableAttribute>
</object>
<object type="text" style="ModernLabelText">
Loading map data. Please wait...
<translatableAttribute id="caption">Loading map data. Please wait...</translatableAttribute>
</object>
</object>
@ -38,7 +38,9 @@
<!-- Number of Players-->
<object size="0 0 150 28">
<object size="0 0 100% 100%" type="text" style="ModernRightLabelText">Number of players:</object>
<object size="0 0 100% 100%" type="text" style="ModernRightLabelText">
<translatableAttribute id="caption">Number of players:</translatableAttribute>
</object>
</object>
<!-- Number of Players-->
@ -48,8 +50,8 @@
type="dropdown"
style="ModernDropDown"
size="0 0 100% 28"
tooltip_style="onscreenToolTip"
tooltip="Select number of players.">
tooltip_style="onscreenToolTip">
<translatableAttribute id="tooltip">Select number of players.</translatableAttribute>
<action on="SelectionChange">selectNumPlayers(this.list_data[this.selected]);</action>
</object>
</object>
@ -59,37 +61,53 @@
<!-- Player assignments -->
<object size="24 59 100%-440 358" type="image" sprite="ModernDarkBoxGold" name="playerAssignmentsPannel">
<object size="0 6 100% 30">
<object name="playerNameHeading" type="text" style="ModernLabelText" size="0 0 20% 100%">Player Name</object>
<object name="playerPlacementHeading" type="text" style="ModernLabelText" size="20%+5 0 50% 100%">Player Placement</object>
<object name="playerCivHeading" type="text" style="ModernLabelText" size="50%+65 0 85% 100%">Civilization</object>
<object name="playerNameHeading" type="text" style="ModernLabelText" size="0 0 20% 100%">
<translatableAttribute id="caption">Player Name</translatableAttribute>
</object>
<object name="playerPlacementHeading" type="text" style="ModernLabelText" size="20%+5 0 50% 100%">
<translatableAttribute id="caption">Player Placement</translatableAttribute>
</object>
<object name="playerCivHeading" type="text" style="ModernLabelText" size="50%+65 0 85% 100%">
<translatableAttribute id="caption">Civilization</translatableAttribute>
</object>
<object name="civInfoButton"
type="button"
sprite="iconInfoGold"
sprite_over="iconInfoWhite"
size="82%-8 0 82%+8 16"
tooltip_style="onscreenToolTip"
tooltip="View civilization info"
>
<translatableAttribute id="tooltip">View civilization info</translatableAttribute>
<action on="Press"><![CDATA[
Engine.PushGuiPage("page_civinfo.xml");
]]></action>
</object>
<object name="playerTeamHeading" type="text" style="ModernLabelText" size="85%+5 0 100%-5 100%">Team</object>
<object name="playerTeamHeading" type="text" style="ModernLabelText" size="85%+5 0 100%-5 100%">
<translatableAttribute id="caption">Team</translatableAttribute>
</object>
</object>
<object size="1 36 100%-1 100%">
<repeat count="8">
<object name="playerBox[n]" size="0 0 100% 32" hidden="true">
<object name="playerColour[n]" type="image" size="0 0 100% 100%"/>
<object name="playerName[n]" type="text" style="ModernLabelText" size="0 2 20% 30"/>
<object name="playerAssignment[n]" type="dropdown" style="ModernDropDown" size="20%+5 2 50% 30" tooltip_style="onscreenToolTip" tooltip="Select player."/>
<object name="playerAssignment[n]" type="dropdown" style="ModernDropDown" size="20%+5 2 50% 30" tooltip_style="onscreenToolTip">
<translatableAttribute id="tooltip">Select player.</translatableAttribute>
</object>
<object name="playerConfig[n]" type="button" style="StoneButton" size="50%+5 6 50%+60 26"
tooltip_style="onscreenToolTip"
tooltip="Configure AI settings."
font="serif-bold-stroke-12"
>Settings</object>
<object name="playerCiv[n]" type="dropdown" style="ModernDropDown" size="50%+65 2 85% 30" tooltip_style="onscreenToolTip" tooltip="Select player's civilization."/>
>
<translatableAttribute id="caption">Settings</translatableAttribute>
<translatableAttribute id="tooltip">Configure AI settings.</translatableAttribute>
</object>
<object name="playerCiv[n]" type="dropdown" style="ModernDropDown" size="50%+65 2 85% 30" tooltip_style="onscreenToolTip">
<translatableAttribute id="tooltip">Select player's civilization.</translatableAttribute>
</object>
<object name="playerCivText[n]" type="text" style="ModernLabelText" size="50%+65 0 85% 30"/>
<object name="playerTeam[n]" type="dropdown" style="ModernDropDown" size="85%+5 2 100%-5 30" tooltip_style="onscreenToolTip" tooltip="Select player's team."/>
<object name="playerTeam[n]" type="dropdown" style="ModernDropDown" size="85%+5 2 100%-5 30" tooltip_style="onscreenToolTip">
<translatableAttribute id="tooltip">Select player's team.</translatableAttribute>
</object>
<object name="playerTeamText[n]" type="text" style="ModernLabelText" size="85%+5 0 100%-5 100%"/>
</object>
</repeat>
@ -100,10 +118,18 @@
<object size="100%-425 363 100%-325 455" name="mapTypeSelectionTooltip">
<object type="text" style="ModernRightLabelText" size="0 0 100% 30">Match Type:</object>
<object type="text" style="ModernRightLabelText" size="0 32 100% 62">Map Filter:</object>
<object type="text" style="ModernRightLabelText" size="0 64 100% 94">Select Map:</object>
<object name="mapSizeDesc" type="text" style="ModernRightLabelText" size="0 96 100% 126">Map Size:</object>
<object type="text" style="ModernRightLabelText" size="0 0 100% 30">
<translatableAttribute id="caption">Match Type:</translatableAttribute>
</object>
<object type="text" style="ModernRightLabelText" size="0 32 100% 62">
<translatableAttribute id="caption">Map Filter:</translatableAttribute>
</object>
<object type="text" style="ModernRightLabelText" size="0 64 100% 94">
<translatableAttribute id="caption">Select Map:</translatableAttribute>
</object>
<object name="mapSizeDesc" type="text" style="ModernRightLabelText" size="0 96 100% 126">
<translatableAttribute id="caption">Map Size:</translatableAttribute>
</object>
</object>
<object size="100%-327 363 100%-25 423" name="mapFilterSelectionTooltip">
@ -117,8 +143,8 @@
type="dropdown"
style="ModernDropDown"
size="100%-315 363 100%-25 391"
tooltip_style="onscreenToolTip"
tooltip="Select a map type.">
tooltip_style="onscreenToolTip">
<translatableAttribute id="tooltip">Select a map type.</translatableAttribute>
<action on="SelectionChange">selectMapType(this.list_data[this.selected]);</action>
</object>
@ -126,9 +152,9 @@
type="dropdown"
style="ModernDropDown"
size="100%-315 395 100%-25 423"
tooltip_style="onscreenToolTip"
tooltip="Select a map filter.">
<action on="SelectionChange">selectMapFilter(this.list[this.selected]);</action>
tooltip_style="onscreenToolTip">
<translatableAttribute id="tooltip">Select a map filter.</translatableAttribute>
<action on="SelectionChange">selectMapFilter(this.list_data[this.selected]);</action>
</object>
<object size="100%-315 427 100%-25 455" name="mapSelectionPannel">
@ -137,13 +163,15 @@
style="ModernDropDown"
type="dropdown"
size="0 0 100% 100%"
tooltip_style="onscreenToolTip"
tooltip="Select a map to play on.">
tooltip_style="onscreenToolTip">
<translatableAttribute id="tooltip">Select a map to play on.</translatableAttribute>
<action on="SelectionChange">selectMap(this.list_data[this.selected]);</action>
</object>
</object>
<object name="mapSize" size="100%-315 459 100%-25 487" type="dropdown" style="ModernDropDown" hidden="true" tooltip_style="onscreenToolTip" tooltip="Select map size. (Larger sizes may reduce performance.)"/>
<object name="mapSize" size="100%-325 459 100%-25 487" type="dropdown" style="ModernDropDown" hidden="true" tooltip_style="onscreenToolTip">
<translatableAttribute id="tooltip">Select map size. (Larger sizes may reduce performance.)</translatableAttribute>
</object>
<!-- Map Preview -->
<object type="image" sprite="ModernDarkBoxGold" name="gamePreviewBox" size="100%-426 57 100%-24 359">
@ -167,7 +195,7 @@
</object>
<object size="100%-72 100%-24 100%-4 100%-4" type="button" style="StoneButton">
Send
<translatableAttribute id="caption">Send</translatableAttribute>
<action on="Press">submitChatInput();</action>
</object>
</object>
@ -180,7 +208,9 @@
sprite="BackgroundTranslucent"
hidden="true"
size="100%-700 100%-56 100%-312 100%-24"
>[Tooltip text]</object>
>
<translatableAttribute id="caption">[Tooltip text]</translatableAttribute>
</object>
<!-- Start Button -->
<object
@ -189,22 +219,22 @@
style="StoneButton"
size="100%-308 100%-52 100%-168 100%-24"
tooltip_style="onscreenToolTip"
tooltip="Start a new game with the current settings."
enabled="false"
>
Start game!
<translatableAttribute id="caption">Start game!</translatableAttribute>
<translatableAttribute id="tooltip">Start a new game with the current settings.</translatableAttribute>
<action on="Press">launchGame();</action>
</object>
<!-- Cancel Button -->
<object
name="cancelGame"
caption="Back"
type="button"
style="StoneButton"
size="100%-164 100%-52 100%-24 100%-24"
tooltip_style="onscreenToolTip"
>
<translatableAttribute id="caption">Back</translatableAttribute>
<action on="Press">
<![CDATA[
cancelSetup();
@ -225,9 +255,9 @@
style="StoneButton"
size="100%-120 0 100% 28"
tooltip_style="onscreenToolTip"
tooltip="See more game options"
>
More Options
<translatableAttribute id="caption">More Options</translatableAttribute>
<translatableAttribute id="tooltip">See more game options</translatableAttribute>
<action on="Press">toggleMoreOptions();</action>
</object>
@ -300,9 +330,9 @@
style="StoneButton"
size="50%-70 310 50%+70 336"
tooltip_style="onscreenToolTip"
tooltip="Close more game options window"
>
OK
<translatableAttribute id="caption">OK</translatableAttribute>
<translatableAttribute id="tooltip">Close more game options window</translatableAttribute>
<action on="Press">toggleMoreOptions();</action>
</object>
<!-- End More Options -->

View File

@ -31,13 +31,13 @@ function init(attribs)
{
Engine.GetGUIObjectByName("hostServerNameWrapper").hidden = false;
Engine.GetGUIObjectByName("hostPlayerName").caption = attribs.name;
Engine.GetGUIObjectByName("hostServerName").caption = attribs.name + "'s game";
Engine.GetGUIObjectByName("hostServerName").caption = sprintf(translate("%(name)s's game"), { name: attribs.name });
}
else
Engine.GetGUIObjectByName("hostPlayerNameWrapper").hidden = false;
break;
default:
error("Unrecognised multiplayer game type : " + attribs.multiplayerGameType);
error(sprintf("Unrecognised multiplayer game type: %(gameType)s", { gameType: multiplayerGameType }));
break;
}
}
@ -57,7 +57,7 @@ function startConnectionStatus(type)
g_GameType = type;
g_IsConnecting = true;
g_IsRejoining = false;
Engine.GetGUIObjectByName("connectionStatus").caption = "Connecting to server...";
Engine.GetGUIObjectByName("connectionStatus").caption = translate("Connecting to server...");
}
function onTick()
@ -76,7 +76,7 @@ function pollAndHandleNetworkClient()
if (!message)
break;
log("Net message: "+uneval(message));
log(sprintf(translate("Net message: %(message)s"), { 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
@ -94,7 +94,7 @@ function pollAndHandleNetworkClient()
return;
default:
error("Unrecognised netstatus type "+message.status);
error(sprintf("Unrecognised netstatus type %(netType)s", { netType: message.status }));
break;
}
break;
@ -120,7 +120,7 @@ function pollAndHandleNetworkClient()
break;
default:
error("Unrecognised net message type "+message.type);
error(sprintf("Unrecognised net message type %(messageType)s", { messageType: message.type }));
}
}
else
@ -133,13 +133,13 @@ function pollAndHandleNetworkClient()
switch (message.status)
{
case "connected":
Engine.GetGUIObjectByName("connectionStatus").caption = "Registering with server...";
Engine.GetGUIObjectByName("connectionStatus").caption = translate("Registering with server...");
break;
case "authenticated":
if (message.rejoining)
{
Engine.GetGUIObjectByName("connectionStatus").caption = "Game has already started - rejoining...";
Engine.GetGUIObjectByName("connectionStatus").caption = translate("Game has already started, rejoining...");
g_IsRejoining = true;
return; // we'll process the game setup messages in the next tick
}
@ -155,12 +155,12 @@ function pollAndHandleNetworkClient()
return;
default:
error("Unrecognised netstatus type "+message.status);
error(sprintf("Unrecognised netstatus type %(netType)s", { netType: message.status }));
break;
}
break;
default:
error("Unrecognised net message type "+message.type);
error(sprintf("Unrecognised net message type %(messageType)s", { messageType: message.type }));
break;
}
}
@ -182,7 +182,7 @@ function startHost(playername, servername)
{
if (g.name === servername)
{
Engine.GetGUIObjectByName("hostFeedback").caption = "Game name already in use.";
Engine.GetGUIObjectByName("hostFeedback").caption = translate("Game name already in use.");
return false;
}
}
@ -198,7 +198,7 @@ function startHost(playername, servername)
{
cancelSetup();
messageBox(400, 200,
"Cannot host game: " + e.message + ".",
sprintf("Cannot host game: %(message)s.", { message: e.message }),
"Error", 2);
return false;
}
@ -224,7 +224,7 @@ function startJoin(playername, ip)
{
cancelSetup();
messageBox(400, 200,
"Cannot join game: " + e.message + ".",
sprintf("Cannot join game: %(message)s.", { message: e.message }),
"Error", 2);
return false;
}
@ -235,3 +235,8 @@ function startJoin(playername, ip)
Engine.LobbySetPlayerPresence("playing");
return true;
}
function getDefaultGameName()
{
return sprintf(translate("%(playername)s's game"), { playername: Engine.ConfigDB_GetValue("user", "playername")});
}

View File

@ -16,17 +16,17 @@
</action>
<object style="TitleText" type="text" size="50%-128 0%-16 50%+128 16">
Multiplayer
<translatableAttribute id="caption">Multiplayer</translatableAttribute>
</object>
<object name="pageJoin" size="0 32 100% 100%" hidden="true">
<object type="text" style="ModernLabelText" size="0 0 400 30">
Joining an existing game.
<translatableAttribute id="caption">Joining an existing game.</translatableAttribute>
</object>
<object type="text" size="0 40 200 70" style="ModernRightLabelText">
Player name:
<translatableAttribute id="caption">Player name:</translatableAttribute>
</object>
<object name="joinPlayerName" type="input" size="210 40 100%-32 64" style="ModernInput">
@ -36,7 +36,7 @@
</object>
<object type="text" size="0 80 200 110" style="ModernRightLabelText">
Server Hostname or IP:
<translatableAttribute id="caption">Server Hostname or IP:</translatableAttribute>
</object>
<object name="joinServer" type="input" size="210 80 100%-32 104" style="ModernInput">
@ -46,7 +46,7 @@
</object>3 100%-33 103 100%-3
<object hotkey="confirm" type="button" size="50%-144 100%-60 50%-16 100%-32" style="StoneButton">
Continue
<translatableAttribute id="caption">Continue</translatableAttribute>
<action on="Press">
var joinPlayerName = Engine.GetGUIObjectByName("joinPlayerName").caption;
var joinServer = Engine.GetGUIObjectByName("joinServer").caption;
@ -64,12 +64,12 @@
<object name="pageHost" size="0 32 100% 100%" hidden="true">
<object type="text" style="ModernLabelText" size="0 0 400 30">
Set up your server to host.
<translatableAttribute id="caption">Set up your server to host.</translatableAttribute>
</object>
<object name="hostPlayerNameWrapper" hidden="true">
<object type="text" size="0 40 200 70" style="ModernRightLabelText">
Player name:
<translatableAttribute id="caption">Player name:</translatableAttribute>
</object>
<object name="hostPlayerName" type="input" size="210 40 100%-32 64" style="ModernInput">
@ -81,18 +81,18 @@
<object name="hostServerNameWrapper" hidden="true">
<object type="text" size="0 80 200 110" style="ModernRightLabelText">
Server name:
<translatableAttribute id="caption">Server name:</translatableAttribute>
</object>
<object name="hostServerName" type="input" size="210 80 100%-32 104" style="ModernInput">
<action on="Load">
this.caption = Engine.ConfigDB_GetValue("user", "playername") + "'s game";
this.caption = getDefaultGameName();
</action>
</object>
</object>
<object type="button" size="50%-144 100%-60 50%-16 100%-32" style="StoneButton">
Continue
<translatableAttribute id="caption">Continue</translatableAttribute>
<action on="Press">
var hostPlayerName = Engine.GetGUIObjectByName("hostPlayerName").caption;
Engine.ConfigDB_CreateValue("user", "playername", hostPlayerName);
@ -106,13 +106,13 @@
<object name="hostFeedback" type="text" style="ModernLabelText" size="32 150 100%-32 180" textcolor="red" />
<object type="button" style="StoneButton" size="50%+16 100%-60 50%+144 100%-32">
Cancel
<translatableAttribute id="caption">Cancel</translatableAttribute>
<action on="Press">cancelSetup();</action>
</object>
<object name="pageConnecting" hidden="true">
<object name="connectionStatus" type="text" style="ModernLabelText" size="0 100 100% 120">
[Connection status]
<translatableAttribute id="caption">[Connection status]</translatableAttribute>
</object>
</object>

View File

@ -15,7 +15,7 @@ function init(data)
{
// Set tip text
var tipTextFilePath = tipTextLoadingArray[getRandom (0, tipTextLoadingArray.length-1)];
var tipText = Engine.ReadFile(tipTextFilePath);
var tipText = Engine.TranslateLines(Engine.ReadFile(tipTextFilePath));
if (tipText)
{
@ -48,15 +48,15 @@ function init(data)
{
case "skirmish":
case "scenario":
loadingMapName.caption = "Loading \"" + mapName + "\"";
loadingMapName.caption = sprintf(translate("Loading \"%(map)s\""), {map: mapName});
break;
case "random":
loadingMapName.caption = "Generating \"" + mapName + "\"";
loadingMapName.caption = sprintf(translate("Generating \"%(map)s\""), {map: mapName});
break;
default:
error("Unknown map type: " + data.attribs.mapType);
error(sprintf("Unknown map type: %(mapType)s", { mapType: data.attribs.mapType }));
}
}
@ -65,7 +65,7 @@ function init(data)
// Pick a random quote of the day (each line is a separate tip).
var quoteArray = Engine.ReadFileLines("gui/text/quotes.txt");
Engine.GetGUIObjectByName("quoteText").caption = quoteArray[getRandom(0, quoteArray.length-1)];
Engine.GetGUIObjectByName("quoteText").caption = translate(quoteArray[getRandom(0, quoteArray.length-1)]);
}
// ====================================================================

View File

@ -44,7 +44,9 @@
<!-- LOADING SCREEN QUOTE (needs increased z value to overcome the transparent area of the tip image above it -->
<object size="50%-448 50%+230 50%+448 100%-16" z="20">
<object name="quoteTitleText" size="0 0 100% 30" type="text" style="LoadingTitleText">Quote of the Day:</object>
<object name="quoteTitleText" size="0 0 100% 30" type="text" style="LoadingTitleText">
<translatableAttribute id="caption">Quote of the Day:</translatableAttribute>
</object>
<object name="quoteText" size="0 30 100% 100%" type="text" style="LoadingText"></object>
</object>
</object>

View File

@ -22,7 +22,7 @@ function init(attribs)
g_Name = Engine.LobbyGetNick();
g_mapSizes = initMapSizes();
g_mapSizes.shortNames.splice(0, 0, "Any");
g_mapSizes.shortNames.splice(0, 0, translateWithContext("map size", "Any"));
g_mapSizes.tiles.splice(0, 0, "");
var mapSizeFilter = Engine.GetGUIObjectByName("mapSizeFilter");
@ -30,11 +30,11 @@ function init(attribs)
mapSizeFilter.list_data = g_mapSizes.tiles;
var playersNumberFilter = Engine.GetGUIObjectByName("playersNumberFilter");
playersNumberFilter.list = ["Any",2,3,4,5,6,7,8];
playersNumberFilter.list = [translateWithContext("player number", "Any"),2,3,4,5,6,7,8];
playersNumberFilter.list_data = ["",2,3,4,5,6,7,8];
var mapTypeFilter = Engine.GetGUIObjectByName("mapTypeFilter");
mapTypeFilter.list = ["Any", "Skirmish", "Random", "Scenario"];
mapTypeFilter.list = [translateWithContext("map", "Any"), translateWithContext("map", "Skirmish"), translateWithContext("map", "Random"), translate("Scenario")];
mapTypeFilter.list_data = ["", "skirmish", "random", "scenario"];
Engine.LobbySetPlayerPresence("available");
@ -296,30 +296,31 @@ function updateGameList()
function formatPlayerListEntry(nickname, presence, rating, role)
{
// Set colors based on player status
var color, status;
var color;
var status;
switch (presence)
{
case "playing":
color = "125 0 0";
status = "Busy";
status = translate("Busy");
break;
case "gone":
case "away":
color = "229 76 13";
status = "Away";
status = translate("Away");
break;
case "available":
color = "0 125 0";
status = "Online";
status = translate("Online");
break;
case "offline":
color = "0 0 0";
status = "Offline";
status = translate("Offline");
break;
default:
warn("Unknown presence '" + presence + "'");
warn(sprintf("Unknown presence '%(presence)s'", { presence: presence }));
color = "178 178 178";
status = "Unknown";
status = translateWithContext("lobby presence", "Unknown");
break;
}
// Center the unrated symbol.
@ -334,6 +335,7 @@ function formatPlayerListEntry(nickname, presence, rating, role)
// Give moderators special formatting.
if (role == "moderator")
formattedName = formattedName; //TODO
// Push this player's name and status onto the list
return [formattedName, formattedStatus, formattedRating];
}
@ -358,14 +360,14 @@ function updateGameSelection()
// Load map data
if (g_GameList[g].mapType == "random" && g_GameList[g].mapName == "random")
mapData = {"settings": {"Description": "A randomly selected map."}};
mapData = {"settings": {"Description": translate("A randomly selected map.")}};
else if (g_GameList[g].mapType == "random" && Engine.FileExists(g_GameList[g].mapName + ".json"))
mapData = parseJSONData(g_GameList[g].mapName + ".json");
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 locally.");
warn(sprintf("Map '%(mapName)s' not found locally.", { mapName: g_GameList[g].mapName }));
// Show the game info panel and join button.
Engine.GetGUIObjectByName("gameInfo").hidden = false;
@ -383,7 +385,7 @@ function updateGameSelection()
if (mapData && mapData.settings.Description)
var mapDescription = mapData.settings.Description;
else
var mapDescription = "Sorry, no description available.";
var mapDescription = translate("Sorry, no description available.");
// Display map preview if it exists, otherwise display a placeholder.
if (mapData && mapData.settings.Preview)
@ -411,7 +413,7 @@ function joinSelectedGame()
// Check if it looks like an ip address
if (sip.split('.').length != 4)
{
addChatMessage({ "from": "system", "text": "This game's address '" + sip + "' does not appear to be valid." });
addChatMessage({ "from": "system", "text": sprintf(translate("This game's address '%(ip)s' does not appear to be valid."), { ip: sip }) });
return;
}
@ -433,14 +435,6 @@ function hostGame()
// Utils
////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Add a leading zero to single-digit numbers.
*/
function twoDigits(n)
{
return n < 10 ? "0" + n : n;
}
function stripColorCodes(input)
{
return input.replace(/\[(\w+)[^w]*?](.*?)\[\/\1]/g, '$2');
@ -500,7 +494,7 @@ function onTick()
nickList.push(nick);
ratingList.push(String(rating));
Engine.SendGetRatingList();
addChatMessage({ "text": "/special " + nick + " has joined.", "key": g_specialKey });
addChatMessage({ "text": "/special " + sprintf(translate("%(nick)s has joined."), { nick: nick }), "key": g_specialKey });
break;
case "leave":
if (nickIndex == -1) // Left, but not present (TODO: warn about this?)
@ -509,21 +503,21 @@ function onTick()
presenceList.splice(nickIndex, 1);
nickList.splice(nickIndex, 1);
ratingList.splice(nickIndex, 1);
addChatMessage({ "text": "/special " + nick + " has left.", "key": g_specialKey });
addChatMessage({ "text": "/special " + sprintf(translate("%(nick)s has left."), { nick: nick }), "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 });
addChatMessage({ "from": "system", "text": sprintf(translate("Invalid nickname: %(nick)s"), { nick: message.data })});
break;
}
var [name, status, rating] = formatPlayerListEntry(message.data, presence, stripColorCodes(ratingList[nickIndex])); // 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 });
addChatMessage({ "text": "/special " + sprintf(translate("%(oldnick)s is now known as %(newnick)s."), { oldnick: nick, newnick: message.data }), "key": g_specialKey });
Engine.SendGetRatingList();
break;
case "presence":
@ -538,7 +532,7 @@ function onTick()
updateSubject(message.text);
break;
default:
warn("Unknown message.level '" + message.level + "'");
warn(sprintf("Unknown message.level '%(msglvl)s'", { msglvl: message.level }));
break;
}
// Push new data to GUI
@ -588,7 +582,7 @@ function onTick()
}
break;
default:
error("Unrecognised message type "+message.type);
error(sprintf("Unrecognised message type %(msgtype)s", { msgtype: message.type }));
}
}
}
@ -677,7 +671,7 @@ function handleSpecialCommand(text)
case "me":
return false;
default:
addChatMessage({ "from":"system", "text":"We're sorry, the '" + cmd + "' command is not supported."});
addChatMessage({ "from":"system", "text": sprintf(translate("We're sorry, the '%(cmd)s' command is not supported."), { cmd: cmd})});
}
return true;
}
@ -749,15 +743,6 @@ function ircFormat(text, from, color, key)
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] == '/')
{
@ -765,19 +750,61 @@ function ircFormat(text, from, color, key)
switch (command)
{
case "me":
return formatted + '[font="serif-bold-13"]* ' + coloredFrom + '[/font] ' + message;
// Translation: IRC message prefix when the sender uses the /me command.
var senderString = '[font="serif-bold-13"]' + sprintf(translate("* %(sender)s"), { sender: coloredFrom }) + '[/font]';
// Translation: IRC message issued using the ‘/me’ command.
var formattedMessage = sprintf(translate("%(sender)s %(action)s"), { sender: senderString, action: message });
break;
case "say":
return formatted + '[font="serif-bold-13"]<' + coloredFrom + '>[/font] ' + message;
// Translation: IRC message prefix.
var senderString = '[font="serif-bold-13"]' + sprintf(translate("<%(sender)s>"), { sender: coloredFrom }) + '[/font]';
// Translation: IRC message.
var formattedMessage = sprintf(translate("%(sender)s %(message)s"), { sender: senderString, message: message });
break
case "special":
if (key === g_specialKey)
return formatted + '[font="serif-bold-13"] == ' + message + '[/font]';
// Translation: IRC system message.
var formattedMessage = '[font="serif-bold-13"]' + sprintf(translate("== %(message)s"), { message: message }) + '[/font]';
else
{
// Translation: IRC message prefix.
var senderString = '[font="serif-bold-13"]' + sprintf(translate("<%(sender)s>"), { sender: coloredFrom }) + '[/font]';
// Translation: IRC message.
var formattedMessage = sprintf(translate("%(sender)s %(message)s"), { sender: senderString, message: message });
}
break;
default:
// This should never happen.
return "";
var formattedMessage = "";
}
}
return formatted + '[font="serif-bold-13"]<' + coloredFrom + '>[/font] ' + text;
else
{
// Translation: IRC message prefix.
var senderString = '[font="serif-bold-13"]' + sprintf(translate("<%(sender)s>"), { sender: coloredFrom }) + '[/font]';
// Translation: IRC message.
var formattedMessage = sprintf(translate("%(sender)s %(message)s"), { sender: senderString, message: text });
}
// Build time header if enabled
if (g_timestamp)
{
// Time for optional time header
var time = new Date(Date.now());
// Translation: Time as shown in the multiplayer lobby (when you enable it in the options page).
// For a list of symbols that you can use, see:
// https://sites.google.com/site/icuprojectuserguide/formatparse/datetime?pli=1#TOC-Date-Field-Symbol-Table
var timeString = Engine.FormatMillisecondsIntoDateString(time.getTime(), translate("HH:mm"));
// Translation: Time prefix as shown in the multiplayer lobby (when you enable it in the options page).
var timePrefixString = '[font="serif-bold-13"]' + sprintf(translate("[%(time)s]"), { time: timeString }) + '[/font]';
// Translation: IRC message format when there is a time prefix.
return sprintf(translate("%(time)s %(message)s"), { time: timePrefixString, message: formattedMessage });
}
else
return formattedMessage;
}
/**
@ -824,7 +851,7 @@ function isSpam(text, from)
{
g_spamMonitor[from][2] = time;
if (from == g_Name)
addChatMessage({ "from": "system", "text": "Please do not spam. You have been blocked for thirty seconds." });
addChatMessage({ "from": "system", "text": translate("Please do not spam. You have been blocked for thirty seconds.") });
return true;
}
// Return false if everything is clear.

View File

@ -11,7 +11,7 @@
<object type="image" style="ModernWindow" size="0 0 100% 100%" name="lobbyWindow">
<object style="TitleText" type="text" size="50%-128 0%+4 50%+128 36">
Multiplayer Lobby
<translatableAttribute id="caption">Multiplayer Lobby</translatableAttribute>
</object>
<action on="Tick">
@ -21,15 +21,21 @@
<!-- Left panel: Player list. -->
<object name="leftPanel" size="20 30 20% 100%-50">
<object name="playersBox" style="ModernList" type="olist" size="0 0 100% 100%" font="serif-bold-stroke-13">
<def id="status" heading="Status" width="27%"/>
<def id="name" heading="Name" width="50%"/>
<def id="rating" heading="Rating" width="23%"/>
<def id="status" width="27%">
<translatableAttribute id="heading">Status</translatableAttribute>
</def>
<def id="name" width="50%">
<translatableAttribute id="heading">Name</translatableAttribute>
</def>
<def id="rating" width="23%">
<translatableAttribute id="heading">Rating</translatableAttribute>
</def>
</object>
</object>
<object name="leftButtonPanel" size="20 100%-45 20% 100%-20">
<object type="button" style="StoneButton" size="0 0 100% 100%">
Leaderboard
<translatableAttribute id="caption">Leaderboard</translatableAttribute>
<action on="Press">Engine.GetGUIObjectByName("leaderboard").hidden = false;Engine.GetGUIObjectByName("leaderboardFade").hidden = false;</action>
</object>
</object>
@ -54,7 +60,9 @@
<!-- Map Type -->
<object size="5 240 50% 265" type="image" sprite="ModernItemBackShadeLeft">
<object size="0 0 100%-10 100%" type="text" style="ModernLabelText" text_align="right">Map Type:</object>
<object size="0 0 100%-10 100%" type="text" style="ModernLabelText" text_align="right">
<translatableAttribute id="caption">Map Type:</translatableAttribute>
</object>
</object>
<object size="50% 240 100%-5 265" type="image" sprite="ModernItemBackShadeRight">
<object name="sgMapType" size="0 0 100% 100%" type="text" style="ModernLabelText" text_align="left"/>
@ -64,7 +72,9 @@
<!-- Map Size -->
<object size="5 265 50% 290" type="image" sprite="ModernItemBackShadeLeft">
<object size="0 0 100%-10 100%" type="text" style="ModernLabelText" text_align="right">Map Size:</object>
<object size="0 0 100%-10 100%" type="text" style="ModernLabelText" text_align="right">
<translatableAttribute id="caption">Map Size:</translatableAttribute>
</object>
</object>
<object size="50% 265 100%-5 290" type="image" sprite="ModernItemBackShadeRight">
<object name="sgMapSize" size="0 0 100% 100%" type="text" style="ModernLabelText" text_align="left"/>
@ -79,7 +89,9 @@
<object type="image" sprite="ModernDarkBoxWhite" size="3% 76% 97% 99%">
<!-- Number of Players -->
<object size="32% 3% 57% 12%" type="text" style="ModernLabelText" text_align="left">Players:</object>
<object size="32% 3% 57% 12%" type="text" style="ModernLabelText" text_align="left">
<translatableAttribute id="caption">Players:</translatableAttribute>
</object>
<object name="sgNbPlayers" size="52% 3% 62% 12%" type="text" style="ModernLabelText" text_align="left"/>
<!-- Player Names -->
@ -87,20 +99,20 @@
</object>
</object>
<object name="joinGameButton" type="button" style="StoneButton" size="0 100%-85 100% 100%-60" hidden="true">
Join Game
<translatableAttribute id="caption">Join Game</translatableAttribute>
<action on="Press">
joinSelectedGame();
</action>
</object>
<object name="hostButton" type="button" style="StoneButton" size="0 100%-55 100% 100%-30">
Host Game
<translatableAttribute id="caption">Host Game</translatableAttribute>
<action on="Press">
hostGame();
</action>
</object>
<object type="button" style="StoneButton" size="0 100%-25 100% 100%">
Main Menu
<translatableAttribute id="caption">Main Menu</translatableAttribute>
<action on="Press">
lobbyStop();
Engine.SwitchGuiPage("page_pregame.xml");
@ -112,12 +124,22 @@
<object name="middlePanel" size="20%+5 5% 100%-255 97.2%">
<object name="gamesBox" style="ModernList" type="olist" size="0 25 100% 48%">
<action on="SelectionChange">updateGameSelection();</action>
<def id="name" heading="Name" color="0 60 0" width="20%"/>
<def id="name" color="0 60 0" width="20%">
<translatableAttribute id="heading">Name</translatableAttribute>
</def>
<!--<def id="ip" heading="IP" color="0 128 128" width="170"/>-->
<def id="mapName" heading="Map Name" color="128 128 128" width="25%"/>
<def id="mapSize" heading="Map Size" color="128 128 128" width="25%"/>
<def id="mapType" heading="Map Type" color="0 128 128" width="20%"/>
<def id="nPlayers" heading="Players" color="0 128 128" width="10%"/>
<def id="mapName" color="128 128 128" width="25%">
<translatableAttribute id="heading">Map Name</translatableAttribute>
</def>
<def id="mapSize" color="128 128 128" width="25%">
<translatableAttribute id="heading">Map Size</translatableAttribute>
</def>
<def id="mapType" color="0 128 128" width="20%">
<translatableAttribute id="heading">Map Type</translatableAttribute>
</def>
<def id="nPlayers" color="0 128 128" width="10%">
<translatableAttribute id="heading">Players</translatableAttribute>
</def>
</object>
<object name="filterPanel" size="0 0 100% 24">
@ -145,7 +167,9 @@
<action on="SelectionChange">applyFilters();</action>
</object>
<object type="text" size="22 0 35% 100%" text_align="left" textcolor="white">Show full games</object>
<object type="text" size="22 0 35% 100%" text_align="left" textcolor="white">
<translatableAttribute id="caption">Show full games</translatableAttribute>
</object>
<object name="showFullFilter"
type="checkbox"
checked="true"
@ -169,21 +193,29 @@
<!-- Add a translucent black background to fade out the menu page -->
<object hidden="true" name="leaderboardFade" type="image" z="100" sprite="ModernFade"/>
<object hidden="true" name="leaderboard" type="image" style="ModernDialog" size="50%-224 50%-160 50%+224 50%+160" z="101">
<object style="TitleText" type="text" size="50%-128 0%-16 50%+128 16">Leaderboard</object>
<object style="TitleText" type="text" size="50%-128 0%-16 50%+128 16">
<translatableAttribute id="caption">Leaderboard</translatableAttribute>
</object>
<object name="leaderboardBox"
style="ModernList"
type="olist"
size="19 19 100%-19 100%-58">
<def id="rank" heading="Rank" color="255 255 255" width="15%"/>
<def id="rating" heading="Rating" color="255 255 255" width="20%"/>
<def id="name" heading="Name" color="255 255 255" width="65%"/>
<def id="rank" color="255 255 255" width="15%">
<translatableAttribute id="heading">Rank</translatableAttribute>
</def>
<def id="rating" color="255 255 255" width="20%">
<translatableAttribute id="heading">Rating</translatableAttribute>
</def>
<def id="name" color="255 255 255" width="65%">
<translatableAttribute id="heading">Name</translatableAttribute>
</def>
</object>
<object type="button" style="StoneButton" size="50%+5 100%-45 50%+133 100%-17">
Back
<translatableAttribute id="caption">Back</translatableAttribute>
<action on="Press">Engine.GetGUIObjectByName("leaderboard").hidden = true;Engine.GetGUIObjectByName("leaderboardFade").hidden = true;</action>
</object>
<object type="button" style="StoneButton" size="50%-133 100%-45 50%-5 100%-17">
Update
<translatableAttribute id="caption">Update</translatableAttribute>
<action on="Press">Engine.SendGetBoardList();</action>
</object>
</object>

View File

@ -32,7 +32,7 @@ function lobbyStart()
var room = Engine.ConfigDB_GetValue("user", "lobby.room");
var history = Number(Engine.ConfigDB_GetValue("user", "lobby.history"));
feedback.caption = "Connecting....";
feedback.caption = translate("Connecting...");
// If they enter a different password, re-encrypt.
if (password != g_EncrytedPassword.substring(0, 10))
g_EncrytedPassword = Engine.EncryptPassword(password, username);
@ -58,14 +58,14 @@ function lobbyStartRegister()
// Check the passwords match.
if (password != passwordAgain)
{
feedback.caption = "Passwords do not match";
feedback.caption = translate("Passwords do not match");
Engine.GetGUIObjectByName("connectPassword").caption = "";
Engine.GetGUIObjectByName("registerPasswordAgain").caption = "";
switchRegister();
return;
}
feedback.caption = "Registering...";
feedback.caption = translate("Registering...");
g_EncrytedPassword = Engine.EncryptPassword(password, account);
Engine.StartRegisterXmppClient(account, g_EncrytedPassword);
g_LobbyIsConnecting = true;
@ -106,20 +106,21 @@ function onTick()
connectButton.enabled = false;
registerButton.enabled = false;
if (!username && !password)
feedback.caption = "Please enter existing login or desired registration credentials.";
feedback.caption = translate("Please enter existing login or desired registration credentials.");
}
// Check they are using a valid account name.
else if (username != sanitizedName)
{
feedback.caption = "Usernames can't contain [, ], unicode, whitespace, or commas.";
feedback.caption = translate("Usernames can't contain [, ], 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 == "Usernames can't contain [, ], unicode, whitespace, or commas." ||
feedback.caption == "Please enter existing login or desired registration credentials.")
// TODO Do this without comparing the caption
if (feedback.caption == translate("Usernames can't contain [, ], unicode, whitespace, or commas.") ||
feedback.caption == translate("Please enter existing login or desired registration credentials."))
feedback.caption = "";
connectButton.enabled = true;
registerButton.enabled = true;

View File

@ -16,20 +16,20 @@
<object name="pageConnecting" hidden="true">
<object name="connectionStatus" type="text" text_align="center" size="0 100 100% 120">
[Connection status]
<translatableAttribute id="caption">[Connection status]</translatableAttribute>
</object>
</object>
<object style="TitleText" type="text" size="50%-128 0%-16 50%+128 16">
Multiplayer Lobby
<translatableAttribute id="caption">Multiplayer Lobby</translatableAttribute>
</object>
<object name="pageConnect" size="0 32 100% 100%">
<object type="text" size="0 0 400 30" style="ModernLabelText" text_align="center">
Connect to the game lobby
<translatableAttribute id="caption">Connect to the game lobby</translatableAttribute>
</object>
<object name="connectUsernameLabel" type="text" size="50 40 125 70" style="ModernLabelText" text_align="right">
Login:
<translatableAttribute id="caption">Login:</translatableAttribute>
</object>
<object name="connectUsername" type="input" size="135 40 100%-50 64" style="ModernInput">
<action on="Load">
@ -37,7 +37,7 @@
</action>
</object>
<object name="connectPasswordLabel" type="text" size="50 80 125 110" style="ModernLabelText" text_align="right">
Password:
<translatableAttribute id="caption">Password:</translatableAttribute>
</object>
<object name="connectPassword" type="input" size="135 80 100%-50 104" style="ModernInput" mask="true" mask_char="*">
<action on="Load">
@ -51,10 +51,10 @@
</object>
<object name="pageRegister" size="0 32 100% 100%" hidden="true">
<object type="text" style="ModernLabelText" size="0 0 400 30" text_align="center">
Registration
<translatableAttribute id="caption">Registration</translatableAttribute>
</object>
<object type="text" size="50 40 170 70" style="ModernLabelText" text_align="right">
Password again:
<translatableAttribute id="caption">Password again:</translatableAttribute>
</object>
<object name="registerPasswordAgain" type="input" size="180 40 100%-50 64" style="ModernInput" mask="true" mask_char="*">
<action on="Press">
@ -64,7 +64,7 @@
</object>
<object name="feedback" type="text" size="50 150 100%-50 190" style="ModernLabelText" textcolor="red" text_align="center"/>
<object type="button" size="18 100%-45 126 100%-17" style="StoneButton">
Cancel
<translatableAttribute id="caption">Cancel</translatableAttribute>
<action on="Press">
if (Engine.GetGUIObjectByName("pageRegister").hidden)
{
@ -76,7 +76,7 @@
</action>
</object>
<object name="register" type="button" size="136 100%-45 244 100%-17" style="StoneButton">
Register
<translatableAttribute id="caption">Register</translatableAttribute>
<action on="Press">
if (Engine.GetGUIObjectByName("pageRegister").hidden)
{
@ -87,7 +87,7 @@
</action>
</object>
<object name="connect" type="button" size="254 100%-45 100%-18 100%-17" style="StoneButton">
Connect
<translatableAttribute id="caption">Connect</translatableAttribute>
<action on="Press">
lobbyStart();
</action>

View File

@ -0,0 +1,65 @@
function init()
{
var languageList = Engine.GetGUIObjectByName("languageList");
languageList.list = Engine.GetSupportedLocaleDisplayNames();
languageList.list_data = Engine.GetSupportedLocaleBaseNames();
var currentLocale = Engine.GetCurrentLocale();
var currentLocaleBaseName = Engine.GetLocaleBaseName(currentLocale);
var currentLocaleLanguage = Engine.GetLocaleLanguage(currentLocale);
if (languageList.list_data.indexOf(currentLocaleBaseName) != -1)
languageList.selected = languageList.list_data.indexOf(currentLocaleBaseName);
else if (languageList.list_data.indexOf(currentLocaleLanguage) != -1)
languageList.selected = languageList.list_data.indexOf(currentLocaleLanguage);
var localeText = Engine.GetGUIObjectByName("localeText");
localeText.caption = currentLocale;
}
function cancelSetup()
{
Engine.PopGuiPage();
}
function applySelectedLocale()
{
var localeText = Engine.GetGUIObjectByName("localeText");
if(!Engine.SaveLocale(localeText.caption))
{
warn("Selected locale could not be saved in the configuration!");
return;
}
Engine.ReevaluateCurrentLocaleAndReload();
Engine.SwitchGuiPage("page_pregame.xml");
}
function languageSelectionChanged()
{
var languageList = Engine.GetGUIObjectByName("languageList");
var locale = languageList.list_data[languageList.selected];
if(!Engine.ValidateLocale(locale))
warn("Selected locale is not valid! This is not expected, please report the issue.");
var localeText = Engine.GetGUIObjectByName("localeText");
localeText.caption = locale;
}
function openAdvancedMenu()
{
var localeText = Engine.GetGUIObjectByName("localeText");
Engine.PushGuiPage("page_locale_advanced.xml", { "callback": "applyFromAdvancedMenu", "locale": localeText.caption } );
}
function applyFromAdvancedMenu(locale)
{
var languageList = Engine.GetGUIObjectByName("languageList");
var localeBaseName = Engine.GetLocaleBaseName(locale);
var localeLanguage = Engine.GetLocaleLanguage(locale);
if (languageList.list_data.indexOf(localeBaseName) != -1)
languageList.selected = languageList.list_data.indexOf(localeBaseName);
else if (languageList.list_data.indexOf(localeLanguage) != -1)
languageList.selected = languageList.list_data.indexOf(localeLanguage);
var localeText = Engine.GetGUIObjectByName("localeText");
localeText.caption = locale;
}

View File

@ -0,0 +1,47 @@
<?xml version="1.0" encoding="utf-8"?>
<objects>
<script file="gui/common/functions_global_object.js"/>
<script file="gui/locale/locale.js"/>
<!-- Add a translucent black background to fade out the menu page -->
<object type="image" z="0" sprite="bkTranslucent"/>
<object type="image" style="ModernDialog" size="50%-190 50%-100 50%+190 50%+100">
<object size="15 0 100%-15 100%">
<object style="TitleText" type="text" size="50%-128 0%-16 50%+128 16">
<translatableAttribute id="caption">Language</translatableAttribute>
</object>
<object type="text" size="5 50 40% 75" style="ModernLeftLabelText">
<translatableAttribute id="caption">Language:</translatableAttribute>
</object>
<object name="languageList"
type="dropdown"
style="ModernDropDown"
size="40%+10 50 100% 75">
<action on="SelectionChange">languageSelectionChanged();</action>
</object>
<object type="text" size="5 80 40% 105" style="ModernLeftLabelText">
<translatableAttribute id="caption">Locale:</translatableAttribute>
</object>
<object name="localeText" type="text" size="40%+10 80 100% 105" textcolor="white" />
<object type="button" size="0 100%-60 33% 100%-32" style="StoneButton">
<translatableAttribute id="caption">Accept</translatableAttribute>
<action on="Press">applySelectedLocale();</action>
</object>
<object type="button" size="33%+5 100%-60 66% 100%-32" style="StoneButton">
<translatableAttribute id="caption">Advanced</translatableAttribute>
<action on="Press">openAdvancedMenu();</action>
</object>
<object type="button" style="StoneButton" size="66%+5 100%-60 100% 100%-32">
<translatableAttribute id="caption">Cancel</translatableAttribute>
<action on="Press">cancelSetup();</action>
</object>
</object>
</object>
</objects>

View File

@ -0,0 +1,115 @@
function init(initData)
{
var languageList = Engine.GetGUIObjectByName("languageList");
var countryList = Engine.GetGUIObjectByName("countryList");
var resultingLocaleText = Engine.GetGUIObjectByName("resultingLocale");
var scriptInput = Engine.GetGUIObjectByName("scriptInput");
// get languageList data. Only list languages for which we have a dictionary.
var languageListData = [];
var languageListTmp = Engine.GetSupportedLocaleBaseNames();
var currentLocaleLanguage = Engine.GetLocaleLanguage(initData.locale);
for (var i=0; i<languageListTmp.length; ++i)
{
var lang = Engine.GetLocaleLanguage(languageListTmp[i]);
if (lang != "" && languageListData.indexOf(lang) == -1)
languageListData.push(lang);
}
// get countryList data (we get all countries and not only the ones we have dictionaries for)
var countryListData = [];
countryListData.push(translateWithContext("localeCountry", "None"));
var countryListTmp = Engine.GetAllLocales();
var currentLocaleCountry = Engine.GetLocaleCountry(initData.locale);
for (var i=0; i<countryListTmp.length; ++i)
{
var country = Engine.GetLocaleCountry(countryListTmp[i]);
if (country != "" && countryListData.indexOf(country) == -1)
countryListData.push(country);
}
// fill the languageList
languageList.list = languageListData;
languageList.list_data = languageListData;
if (languageList.list_data.indexOf(currentLocaleLanguage) != -1)
languageList.selected = languageList.list_data.indexOf(currentLocaleLanguage);
// fill the country list
countryList.list = countryListData;
countryList.list_data = countryListData;
if (currentLocaleCountry != "")
countryList.selected = countryList.list_data.indexOf(currentLocaleCountry);
else
countryList.selected = 0;
// fill the script
scriptInput.caption = Engine.GetLocaleScript(initData.locale);
}
// TODO: an onChanged event for input boxes would be useful and would allow us to avoid a tick event here.
function onTick()
{
updateResultingLocale();
}
function cancelSetup()
{
Engine.PopGuiPage();
}
function updateResultingLocale()
{
var languageList = Engine.GetGUIObjectByName("languageList");
var countryList = Engine.GetGUIObjectByName("countryList");
var resultingLocaleText = Engine.GetGUIObjectByName("resultingLocale");
var scriptInput = Engine.GetGUIObjectByName("scriptInput");
var variantInput = Engine.GetGUIObjectByName("variantInput");
var dictionaryFile = Engine.GetGUIObjectByName("dictionaryFile");
var resultingLocaleTmp = "";
var resultingLocaleTmp = languageList.list_data[languageList.selected];
if (scriptInput.caption != "")
resultingLocaleTmp = resultingLocaleTmp + "_" + scriptInput.caption;
if (countryList.selected != -1 && countryList.list_data[countryList.selected] != translateWithContext("localeCountry", "None"))
resultingLocaleTmp = resultingLocaleTmp + "_" + countryList.list_data[countryList.selected];
if (Engine.ValidateLocale(resultingLocaleTmp))
{
resultingLocaleText.caption = resultingLocaleTmp;
var dictionaryFileList = Engine.GetDictionariesForDictLocale(Engine.GetDictionaryLocale(resultingLocaleTmp));
var dictionaryFileString = "";
dictionaryFileList.forEach( function (entry) { dictionaryFileString = dictionaryFileString + entry + "\n"; });
dictionaryFile.caption = dictionaryFileString;
}
else
{
resultingLocaleText.caption = translate("<invalid>");
dictionaryFile.caption = "";
}
}
function autoDetectLocale()
{
var languageList = Engine.GetGUIObjectByName("languageList");
var countryList = Engine.GetGUIObjectByName("countryList");
var scriptInput = Engine.GetGUIObjectByName("scriptInput");
var variantInput = Engine.GetGUIObjectByName("variantInput");
var dictionaryFile = Engine.GetGUIObjectByName("dictionaryFile");
variantInput.caption = "";
dictionaryFile.caption = "";
var locale = Engine.GetDictionaryLocale("");
languageList.selected = languageList.list_data.indexOf(Engine.GetLocaleLanguage(locale));
countryList.selected = countryList.selected = countryList.list_data.indexOf(Engine.GetLocaleCountry(locale));
scriptInput.caption = Engine.GetLocaleScript(locale);
}
function applySelectedLocale()
{
var resultingLocaleText = Engine.GetGUIObjectByName("resultingLocale");
Engine.PopGuiPageCB(resultingLocaleText.caption);
}

View File

@ -0,0 +1,81 @@
<?xml version="1.0" encoding="utf-8"?>
<objects>
<script file="gui/common/functions_global_object.js"/>
<script file="gui/locale/locale_advanced.js"/>
<!-- Add a translucent black background to fade out the menu page -->
<object type="image" z="0" sprite="bkTranslucent"/>
<object type="image" style="ModernDialog" size="50%-220 50%-200 50%+220 50%+200">
<action on="Tick">onTick();</action>
<object size="15 0 100%-15 100%">
<object style="TitleText" type="text" size="50%-150 0%-16 50%+150 16">
<translatableAttribute id="caption">Language</translatableAttribute>
</object>
<object type="text" size="5 50 40% 75" style="ModernLeftLabelText">
<translatableAttribute id="caption">Language:</translatableAttribute>
</object>
<object name="languageList"
type="dropdown"
style="ModernDropDown"
size="40%+10 50 100% 75">
</object>
<object type="text" size="5 80 40% 105" style="ModernLeftLabelText">
<translatableAttribute id="caption">Country:</translatableAttribute>
</object>
<object name="countryList"
type="dropdown"
style="ModernDropDown"
size="40%+10 80 100% 105">
</object>
<object type="text" size="5 110 40% 135" style="ModernLeftLabelText">
<translatableAttribute id="caption">Script:</translatableAttribute>
</object>
<object name="scriptInput" size="40%+10 110 100% 135" type="input" style="ModernInput">
<translatableAttribute id="tooltip">Optional four-letter script code part following the language code (as listed in ISO 15924)</translatableAttribute>
</object>
<object type="text" size="5 140 40% 165" style="ModernLeftLabelText">
<translatableAttribute id="caption">Variant (unused):</translatableAttribute>
</object>
<object name="variantInput" size="40%+10 140 100% 165" type="input" style="ModernInput">
<translatableAttribute id="tooltip">Not implemented yet.</translatableAttribute>
</object>
<object type="text" size="5 170 40% 195" style="ModernLeftLabelText">
<translatableAttribute id="caption">Keywords (unused):</translatableAttribute>
</object>
<object name="keywordsInput" size="40%+10 170 100% 195" type="input" style="ModernInput">
<translatableAttribute id="tooltip">Not implemented yet.</translatableAttribute>
</object>
<object type="text" size="5 230 40% 255" style="ModernLeftLabelText">
<translatableAttribute id="caption">Resulting locale:</translatableAttribute>
</object>
<object name="resultingLocale" type="text" size="40%+10 230 100% 255" textcolor="white" />
<object type="text" size="5 260 40% 285" style="ModernLeftLabelText">
<translatableAttribute id="caption">Dictionary files used:</translatableAttribute>
</object>
<object name="dictionaryFile" type="text" size="40%+10 260 100% 345" textcolor="white" />
<object type="button" size="0 100%-60 33% 100%-32" style="StoneButton">
<translatableAttribute id="caption">Accept</translatableAttribute>
<action on="Press">applySelectedLocale();</action>
</object>
<object type="button" size="33%+5 100%-60 66% 100%-32" style="StoneButton">
<translatableAttribute id="caption">Auto detect</translatableAttribute>
<action on="Press">autoDetectLocale();</action>
</object>
<object type="button" style="StoneButton" size="66%+5 100%-60 100% 100%-32">
<translatableAttribute id="caption">Cancel</translatableAttribute>
<action on="Press">cancelSetup();</action>
</object>
</object>
</object>
</objects>

View File

@ -2,7 +2,7 @@ var hasCallback = false;
function init(data)
{
Engine.GetGUIObjectByName("mainText").caption = Engine.ReadFile("gui/manual/" + data.page + ".txt");
Engine.GetGUIObjectByName("mainText").caption = Engine.TranslateLines(Engine.ReadFile("gui/manual/" + data.page + ".txt"));
if (data.callback)
hasCallback = true;
}

View File

@ -8,21 +8,24 @@
<object type="image" z="0" style="TranslucentPanel"/>
<object type="image" style="ModernDialog" size="50%-466 50%-316 50%+466 50%+316">
<object type="text" style="TitleText" size="50%-128 0%-16 50%+128 16">Manual</object>
<object type="text" style="TitleText" size="50%-128 0%-16 50%+128 16">
<translatableAttribute id="caption">Manual</translatableAttribute>
</object>
<object type="image" sprite="ModernFade" size="20 20 100%-20 100%-58">
<object name="mainText" type="text" style="textPanel"/>
</object>
<object type="button" style="StoneButton" size="100%-308 100%-52 100%-168 100%-24">
Online Manual
<object type="button" style="StoneButton" size="100%-308 100%-52 100%-168 100%-24">
<translatableAttribute id="caption">Online Manual</translatableAttribute>
<action on="Press"><![CDATA[
var url = "http://trac.wildfiregames.com/wiki/0adManual";
Engine.OpenURL(url);
messageBox(450, 200, "Opening "+url+"\n in default web browser. Please wait...", "Opening page", 2);
]]></action>
]]></action>
</object>
<object type="button" style="StoneButton" tooltip_style="snToolTip" size="100%-164 100%-52 100%-24 100%-24">
Close
<translatableAttribute id="caption">Close</translatableAttribute>
<action on="Press"><![CDATA[closeManual();]]></action>
</object>
</object>

View File

@ -0,0 +1,100 @@
function init(data)
{
var mbMainObj = Engine.GetGUIObjectByName("mbMain");
var mbTitleObj = Engine.GetGUIObjectByName("mbTitleBar");
var mbTextObj = Engine.GetGUIObjectByName("mbText");
var mbButton1Obj = Engine.GetGUIObjectByName("mbButton1");
var mbButton2Obj = Engine.GetGUIObjectByName("mbButton2");
var mbButton3Obj = Engine.GetGUIObjectByName("mbButton3");
// Calculate size
var mbLRDiff = data.width / 2; // Message box left/right difference from 50% of screen
var mbUDDiff = data.height / 2; // Message box up/down difference from 50% of screen
var mbSizeString = "50%-" + mbLRDiff + " 50%-" + mbUDDiff + " 50%+" + mbLRDiff + " 50%+" + mbUDDiff;
mbMainObj.size = mbSizeString;
// Texts
mbTitleObj.caption = data.title;
mbTextObj.caption = data.message;
if (data.font)
mbTextObj.font = data.font;
// Message box modes
// There is a number of standard modes, and if none of these is used (mbMode == 0), the button captions will be
// taken from the array mbButtonCaptions; there currently is a maximum of three buttons.
switch (data.mode)
{
case 1:
// Simple Yes/No question box
data.buttonCaptions = [translate("Yes"), translate("No")];
break;
case 2:
// Okay-only box
data.buttonCaptions = [translate("OK")];
break;
case 3:
// Retry/Abort/Ignore box (will we ever need this?!)
data.buttonCaptions = [translate("Retry"), translate("Ignore"), translate("Abort")];
default:
break;
}
// Buttons
var codes = data.buttonCode;
if (data.buttonCaptions.length >= 1)
{
mbButton1Obj.caption = data.buttonCaptions[0];
mbButton1Obj.onPress = function ()
{
if (data.callback)
Engine.PopGuiPageCB(0);
else
Engine.PopGuiPage();
};
mbButton1Obj.hidden = false;
}
if (data.buttonCaptions.length >= 2)
{
mbButton2Obj.caption = data.buttonCaptions[1];
mbButton2Obj.onPress = function ()
{
if (data.callback)
Engine.PopGuiPageCB(1);
else
Engine.PopGuiPage();
};
mbButton2Obj.hidden = false;
}
if (data.buttonCaptions.length >= 3)
{
mbButton3Obj.caption = data.buttonCaptions[2];
mbButton3Obj.onPress = function ()
{
if (data.callback)
Engine.PopGuiPageCB(2);
else
Engine.PopGuiPage();
};
mbButton3Obj.hidden = false;
}
switch (data.buttonCaptions.length)
{
case 1:
mbButton1Obj.size = "50%-64 100%-76 50%+64 100%-48";
break;
case 2:
mbButton1Obj.size = "50%-144 100%-76 50%-16 100%-48";
mbButton2Obj.size = "50%+16 100%-76 50%+144 100%-48";
break;
case 3:
mbButton1Obj.size = "10% 100%-76 30% 100%-48";
mbButton2Obj.size = "40% 100%-76 60% 100%-48";
mbButton3Obj.size = "70% 100%-76 90% 100%-48";
break;
}
}

View File

@ -1,108 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<objects>
<script><![CDATA[
function init(data)
{
var mbMainObj = Engine.GetGUIObjectByName("mbMain");
var mbTitleObj = Engine.GetGUIObjectByName("mbTitleBar");
var mbTextObj = Engine.GetGUIObjectByName("mbText");
var mbButton1Obj = Engine.GetGUIObjectByName("mbButton1");
var mbButton2Obj = Engine.GetGUIObjectByName("mbButton2");
var mbButton3Obj = Engine.GetGUIObjectByName("mbButton3");
// Calculate size
var mbLRDiff = data.width / 2; // Message box left/right difference from 50% of screen
var mbUDDiff = data.height / 2; // Message box up/down difference from 50% of screen
var mbSizeString = "50%-" + mbLRDiff + " 50%-" + mbUDDiff + " 50%+" + mbLRDiff + " 50%+" + mbUDDiff;
mbMainObj.size = mbSizeString;
// Texts
mbTitleObj.caption = data.title;
mbTextObj.caption = data.message;
if (data.font)
mbTextObj.font = data.font;
// Message box modes
// There is a number of standard modes, and if none of these is used (mbMode == 0), the button captions will be
// taken from the array mbButtonCaptions; there currently is a maximum of three buttons.
switch (data.mode)
{
case 1:
// Simple Yes/No question box
data.buttonCaptions = ["Yes", "No"];
break;
case 2:
// Okay-only box
data.buttonCaptions = ["OK"];
break;
case 3:
// Retry/Abort/Ignore box (will we ever need this?!)
data.buttonCaptions = ["Retry", "Ignore", "Abort"];
default:
break;
}
// Buttons
var codes = data.buttonCode;
if (data.buttonCaptions.length >= 1)
{
mbButton1Obj.caption = data.buttonCaptions[0];
mbButton1Obj.onPress = function ()
{
if (data.callback)
Engine.PopGuiPageCB(0);
else
Engine.PopGuiPage();
};
mbButton1Obj.hidden = false;
}
if (data.buttonCaptions.length >= 2)
{
mbButton2Obj.caption = data.buttonCaptions[1];
mbButton2Obj.onPress = function ()
{
if (data.callback)
Engine.PopGuiPageCB(1);
else
Engine.PopGuiPage();
};
mbButton2Obj.hidden = false;
}
if (data.buttonCaptions.length >= 3)
{
mbButton3Obj.caption = data.buttonCaptions[2];
mbButton3Obj.onPress = function ()
{
if (data.callback)
Engine.PopGuiPageCB(2);
else
Engine.PopGuiPage();
};
mbButton3Obj.hidden = false;
}
switch (data.buttonCaptions.length)
{
case 1:
mbButton1Obj.size = "50%-64 100%-76 50%+64 100%-48";
break;
case 2:
mbButton1Obj.size = "50%-144 100%-76 50%-16 100%-48";
mbButton2Obj.size = "50%+16 100%-76 50%+144 100%-48";
break;
case 3:
mbButton1Obj.size = "10% 100%-76 30% 100%-48";
mbButton2Obj.size = "40% 100%-76 60% 100%-48";
mbButton3Obj.size = "70% 100%-76 90% 100%-48";
break;
}
}
]]></script>
<script file="gui/msgbox/msgbox.js"/>
<object hotkey="leave">
<action on="Press">Engine.PopGuiPage();</action>

View File

@ -5,37 +5,37 @@
var options = {
"generalSetting":
[
["Windowed Mode", "Start 0 A.D. in windowed mode", {"config":"windowed"}, "boolean"],
["Background Pause", "Pause single player games when window loses focus", {"config":"pauseonfocusloss"}, "boolean"],
[translate("Windowed Mode"), translate("Start 0 A.D. in windowed mode"), {"config":"windowed"}, "boolean"],
[translate("Background Pause"), translate("Pause single player games when window loses focus"), {"config":"pauseonfocusloss"}, "boolean"],
],
"graphicsSetting":
[
["Prefer GLSL", "Use OpenGL 2.0 shaders (recommended)", {"renderer":"PreferGLSL"}, "boolean"],
["Shadows", "Enable shadows", {"renderer":"Shadows"}, "boolean"],
["Particles", "Enable particles", {"renderer":"Particles"}, "boolean"],
["Show Sky", "Render Sky", {"renderer":"ShowSky"}, "boolean"],
["Unit Silhouettes", "Show outlines of units behind buildings", {"renderer":"Silhouettes"}, "boolean"],
["Shadow Flitering", "Smooth shadows", {"renderer":"ShadowPCF"}, "boolean"],
["HQ Waviness", "Use real normals for ocean-wave rendering, instead of applying them as a flat texture", {"renderer":"WaterNormal"}, "boolean"],
["Real Water Depth", "Use actual water depth in rendering calculations", {"renderer":"WaterRealDepth"}, "boolean"],
["Water Reflections", "Allow water to reflect a mirror image", {"renderer":"WaterReflection"}, "boolean"],
["Water Refraction", "Use a real water refraction map and not transparency", {"renderer":"WaterRefraction"}, "boolean"],
["Shore Foam", "Show foam on water near shore depending on water waviness", {"renderer":"WaterFoam"}, "boolean"],
["Shore Waves", "Show breaking waves on water near shore (Requires HQ Waviness)", {"renderer":"WaterCoastalWaves"}, "boolean"],
["Water Shadows", "Cast shadows on water", {"renderer":"WaterShadow"}, "boolean"],
[translate("Prefer GLSL"), translate("Use OpenGL 2.0 shaders (recommended)"), {"renderer":"PreferGLSL"}, "boolean"],
[translate("Shadows"), translate("Enable shadows"), {"renderer":"Shadows"}, "boolean"],
[translate("Particles"), translate("Enable particles"), {"renderer":"Particles"}, "boolean"],
[translate("Show Sky"), translate("Render Sky"), {"renderer":"ShowSky"}, "boolean"],
[translate("Unit Silhouettes"), translate("Show outlines of units behind buildings"), {"renderer":"Silhouettes"}, "boolean"],
[translate("Shadow Filtering"), translate("Smooth shadows"), {"renderer":"ShadowPCF"}, "boolean"],
[translate("HQ Waviness"), translate("Use real normals for ocean-wave rendering, instead of applying them as a flat texture"), {"renderer":"WaterNormal"}, "boolean"],
[translate("Real Water Depth"), translate("Use actual water depth in rendering calculations"), {"renderer":"WaterRealDepth"}, "boolean"],
[translate("Water Reflections"), translate("Allow water to reflect a mirror image"), {"renderer":"WaterReflection"}, "boolean"],
[translate("Water Refraction"), translate("Use a real water refraction map and not transparency"), {"renderer":"WaterRefraction"}, "boolean"],
[translate("Shore Foam"), translate("Show foam on water near shore depending on water waviness"), {"renderer":"WaterFoam"}, "boolean"],
[translate("Shore Waves"), translate("Show breaking waves on water near shore (Requires HQ Waviness)"), {"renderer":"WaterCoastalWaves"}, "boolean"],
[translate("Water Shadows"), translate("Cast shadows on water"), {"renderer":"WaterShadow"}, "boolean"],
],
"soundSetting":
[
["Master Gain", "Master audio gain", {"config":"sound.mastergain", "function":"Engine.SetMasterGain(Number(this.caption));"}, "number"],
["Music Gain", "In game music gain", {"config":"sound.musicgain", "function":"Engine.SetMusicGain(Number(this.caption));"}, "number"],
["Ambient Gain", "In game ambient sound gain", {"config":"sound.ambientgain", "function":"Engine.SetMusicGain(Number(this.caption));"}, "number"],
["Action Gain", "In game unit action sound gain", {"config":"sound.actiongain", "function":"Engine.SetMusicGain(Number(this.caption));"}, "number"],
["UI Gain", "UI sound gain", {"config":"sound.uigain", "function":"Engine.SetMusicGain(Number(this.caption));"}, "number"],
[translate("Master Gain"), translate("Master audio gain"), {"config":"sound.mastergain", "function":"Engine.SetMasterGain(Number(this.caption));"}, "number"],
[translate("Music Gain"), translate("In game music gain"), {"config":"sound.musicgain", "function":"Engine.SetMusicGain(Number(this.caption));"}, "number"],
[translate("Ambient Gain"), translate("In game ambient sound gain"), {"config":"sound.ambientgain", "function":"Engine.SetMusicGain(Number(this.caption));"}, "number"],
[translate("Action Gain"), translate("In game unit action sound gain"), {"config":"sound.actiongain", "function":"Engine.SetMusicGain(Number(this.caption));"}, "number"],
[translate("UI Gain"), translate("UI sound gain"), {"config":"sound.uigain", "function":"Engine.SetMusicGain(Number(this.caption));"}, "number"],
],
"lobbySetting":
[
["Chat Backlog", "Number of backlogged messages to load when joining the lobby", {"config":"lobby.history"}, "number"],
["Chat Timestamp", "Show time that messages are posted in the lobby chat", {"config":"lobby.chattimestamp"}, "boolean"],
[translate("Chat Backlog"), translate("Number of backlogged messages to load when joining the lobby"), {"config":"lobby.history"}, "number"],
[translate("Chat Timestamp"), translate("Show time that messages are posted in the lobby chat"), {"config":"lobby.chattimestamp"}, "boolean"],
],
};

View File

@ -14,9 +14,13 @@
<!-- Settings Window -->
<object name="options" type="image" style="ModernDialog" size="50%-466 50%-316 50%+466 50%+316">
<object style="TitleText" type="text" size="50%-128 0%-16 50%+128 16">Game Options</object>
<object style="TitleText" type="text" size="50%-128 0%-16 50%+128 16">
<translatableAttribute id="caption">Game Options</translatableAttribute>
</object>
<object name="GeneralSettings" type="image" sprite="ModernDarkBoxGold" size="16 16 312 100%-16">
<object style="TitleText" type="text" size="0 5 100% 25">General</object>
<object style="TitleText" type="text" size="0 5 100% 25">
<translatableAttribute id="caption">General</translatableAttribute>
</object>
<repeat count="23">
<object name="generalSetting[n]" size="0 25 100% 50" hidden="true">
<object name="generalSettingLabel[n]" size="0 0 65% 100%" type="text" style="ModernLabelText" text_align="right"/>
@ -26,7 +30,9 @@
</repeat>
</object>
<object name="GraphicsSettings" type="image" sprite="ModernDarkBoxGold" size="320 16 612 100%-56">
<object style="TitleText" type="text" size="0 5 100% 25">Graphics Settings</object>
<object style="TitleText" type="text" size="0 5 100% 25">
<translatableAttribute id="caption">Graphics Settings</translatableAttribute>
</object>
<repeat count="21">
<object name="graphicsSetting[n]" size="0 25 100% 50" hidden="true">
<object name="graphicsSettingLabel[n]" size="0 0 65% 100%" type="text" style="ModernLabelText" text_align="right"/>
@ -36,7 +42,9 @@
</repeat>
</object>
<object name="SoundSettings" type="image" sprite="ModernDarkBoxGold" size="620 16 916 50%-4">
<object style="TitleText" type="text" size="0 5 100% 25">Sound Settings</object>
<object style="TitleText" type="text" size="0 5 100% 25">
<translatableAttribute id="caption">Sound Settings</translatableAttribute>
</object>
<repeat count="10">
<object name="soundSetting[n]" size="0 25 100% 50" hidden="true">
<object name="soundSettingLabel[n]" size="0 0 65% 100%" type="text" style="ModernLabelText" text_align="right"/>
@ -46,7 +54,9 @@
</repeat>
</object>
<object name="LobbySettings" type="image" sprite="ModernDarkBoxGold" size="620 50%+4 916 100%-16">
<object style="TitleText" type="text" size="0 5 100% 25">Lobby Settings</object>
<object style="TitleText" type="text" size="0 5 100% 25">
<translatableAttribute id="caption">Lobby Settings</translatableAttribute>
</object>
<repeat count="10">
<object name="lobbySetting[n]" size="0 25 100% 50" hidden="true">
<object name="lobbySettingLabel[n]" size="0 0 65% 100%" type="text" style="ModernLabelText" text_align="right"/>
@ -56,11 +66,11 @@
</repeat>
</object>
<object type="button" style="StoneButton" size="320 100%-48 50%-4 100%-16">
Save
<translatableAttribute id="caption">Save</translatableAttribute>
<action on="Press">Engine.ConfigDB_WriteFile("user", "config/user.cfg");Engine.PopGuiPage();</action>
</object>
<object type="button" style="StoneButton" size="50%+4 100%-48 612 100%-16">
Cancel
<translatableAttribute id="caption">Cancel</translatableAttribute>
<action on="Press">Engine.PopGuiPage();</action>
</object>
</object>

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<page>
<include>common/modern/styles.xml</include>
<include>common/modern/sprites.xml</include>
<include>common/modern/setup.xml</include>
<include>common/setup.xml</include>
<include>common/styles.xml</include>
<include>common/sprite1.xml</include>
<include>common/common_sprites.xml</include>
<include>common/common_styles.xml</include>
<include>locale/locale.xml</include>
<include>common/global.xml</include>
</page>

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<page>
<include>common/modern/styles.xml</include>
<include>common/modern/sprites.xml</include>
<include>common/modern/setup.xml</include>
<include>common/setup.xml</include>
<include>common/styles.xml</include>
<include>common/sprite1.xml</include>
<include>common/common_sprites.xml</include>
<include>common/common_styles.xml</include>
<include>locale/locale_advanced.xml</include>
<include>common/global.xml</include>
</page>

View File

@ -103,34 +103,34 @@ function formatUserReportStatus(status)
var d = status.split(/:/, 3);
if (d[0] == "disabled")
return "disabled";
return translate("disabled");
if (d[0] == "connecting")
return "connecting to server";
return translate("connecting to server");
if (d[0] == "sending")
{
var done = d[1];
return "uploading (" + Math.floor(100*done) + "%)";
return sprintf(translate("uploading (%f%%)"), Math.floor(100*done));
}
if (d[0] == "completed")
{
var httpCode = d[1];
if (httpCode == 200)
return "upload succeeded";
return translate("upload succeeded");
else
return "upload failed (" + httpCode + ")";
return sprintf(translate("upload failed (%(errorCode)s)"), { errorCode: httpCode });
}
if (d[0] == "failed")
{
var errCode = d[1];
var errMessage = d[2];
return "upload failed (" + errMessage + ")";
return sprintf(translate("upload failed (%(errorMessage)s)"), { errorMessage: errMessage });
}
return "unknown";
return translate("unknown");
}
var lastTickTime = new Date;
@ -162,7 +162,6 @@ function onTick()
Engine.PushGuiPage("page_splashscreen.xml", { "page": "splashscreen", callback : "SplashScreenClosedCallback" } );
else
ShowRenderPathMessage();
}
}
@ -173,10 +172,17 @@ function ShowRenderPathMessage()
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!",
"[font=\"serif-bold-16\"]" +
sprintf(translate("%(startWarning)sWarning:%(endWarning)s 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."), { startWarning: "[color=\"200 20 20\"]", endWarning: "[/color]"}) +
"\n\n" +
// Translation: This is the second paragraph of a warning. The
// warning explains that the user is using “non-shader“ graphics,
// and that in the future this will not be supported by the game, so
// the user will need a better graphics card.
translate("Please press \"Read More\" for more information or \"OK\" to continue."),
translate("WARNING!"),
0,
["Ok", "Read More"],
[translate("OK"), translate("Read More")],
[ null, function() { Engine.OpenURL("http://www.wildfiregames.com/forum/index.php?showtopic=16734"); } ]
);
}
@ -275,6 +281,11 @@ function blendSubmenuIntoMain(topPosition, bottomPosition)
bottomSprite.size = "100%-2 " + (bottomPosition) + " 100% 100%";
}
function getBuildString()
{
return sprintf(translate("Build: %(buildDate)s (%(revision)s)"), { buildDate: Engine.GetBuildTimestamp(0), revision: Engine.GetBuildTimestamp(2) });
}
/*
* FUNCTIONS BELOW DO NOT WORK YET
*/
@ -330,3 +341,26 @@ function blendSubmenuIntoMain(topPosition, bottomPosition)
// guiUnHide ("pg");
// }
//}
function exitGamePressed()
{
closeMenu();
var btCaptions = [translate("Yes"), translate("No")];
var btCode = [Engine.Exit, null];
messageBox(400, 200, translate("Are you sure you want to quit 0 A.D.?"), translate("Confirmation"), 0, btCaptions, btCode);
}
function pressedScenarioEditorButton()
{
closeMenu();
// Start Atlas
if (Engine.AtlasIsAvailable())
Engine.RestartInAtlas();
else
messageBox(400, 200, translate("The scenario editor is not available or failed to load."), translate("Error"), 2);
}
function getLobbyDisabledByBuild()
{
return translate("Launch the multiplayer lobby. [DISABLED BY BUILD]");
}

View File

@ -91,16 +91,20 @@
<object
type="text"
style="userReportText"
>[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.
>
<attribute id="caption">
<keep>[font="serif-bold-16"]</keep>
<translate>Help improve 0 A.D.!</translate>
<keep>[/font]\n\n</keep>
<translate>You can automatically send us anonymous feedback that will help us fix bugs, and improve performance and compatibility.</translate>
</attribute>
</object>
<object type="button" style="StoneButton" size="8 100%-36 146 100%-8">
Enable feedback
<translatableAttribute id="caption">Enable feedback</translatableAttribute>
<action on="Press">EnableUserReport(true);</action>
</object>
<object type="button" style="StoneButton" size="100%-146 100%-36 100%-8 100%-8">
Technical details
<translatableAttribute id="caption">Technical details</translatableAttribute>
<action on="Press">Engine.PushGuiPage("page_manual.xml", { "page": "userreport" });</action>
</object>
</object>
@ -115,18 +119,23 @@ You can automatically send us anonymous feedback that will help us fix bugs, and
name="userReportEnabledText"
type="text"
style="userReportText"
>[font="serif-bold-16"]Thank you for helping improve 0 A.D.![/font]
Anonymous feedback is currently enabled.
Status: $status.
</object>
>
<attribute id="caption">
<keep>[font="serif-bold-16"]</keep>
<translate>Thank you for helping improve 0 A.D.!</translate>
<keep>[/font]\n\n</keep>
<translate>Anonymous feedback is currently enabled.</translate>
<keep>\n</keep>
<translate>Status: $status.</translate>
</attribute>
</object>
<object type="button" style="StoneButton" size="8 100%-36 146 100%-8">
Disable feedback
<translatableAttribute id="caption">Disable feedback</translatableAttribute>
<action on="Press">EnableUserReport(false);</action>
</object>
<object type="button" style="StoneButton" size="100%-146 100%-36 100%-8 100%-8">
Technical details
<translatableAttribute id="caption">Technical details</translatableAttribute>
<action on="Press">Engine.PushGuiPage("page_manual.xml", { "page": "userreport" });</action>
</object>
</object>
@ -157,7 +166,6 @@ Status: $status.
type="image"
size="0 4 100%-4 100%-4"
tooltip_style="pgToolTip"
tooltip="The 0 A.D. Game Manual."
hidden="true"
>
<object name="subMenuSinglePlayerButton"
@ -165,9 +173,9 @@ Status: $status.
style="StoneButtonFancy"
size="0 0 100% 28"
tooltip_style="pgToolTip"
tooltip="Click here to start a new single player game."
>
Matches
<translatableAttribute id="caption">Matches</translatableAttribute>
<translatableAttribute id="tooltip">Click here to start a new single player game.</translatableAttribute>
<action on="Press">
Engine.SwitchGuiPage("page_gamesetup.xml", { type: "offline" });
</action>
@ -178,10 +186,10 @@ Status: $status.
style="StoneButtonFancy"
size="0 32 100% 60"
tooltip_style="pgToolTip"
tooltip="Relive history through historical military campaigns. [NOT YET IMPLEMENTED]"
enabled="false"
>
Campaigns
<translatableAttribute id="caption">Campaigns</translatableAttribute>
<translatableAttribute id="tooltip">Relive history through historical military campaigns. [NOT YET IMPLEMENTED]</translatableAttribute>
<action on="Press">
closeMenu();
<![CDATA[
@ -196,9 +204,9 @@ Status: $status.
style="StoneButtonFancy"
size="0 64 100% 92"
tooltip_style="pgToolTip"
tooltip="Click here to load a saved game."
>
Load Game
<translatableAttribute id="caption">Load Game</translatableAttribute>
<translatableAttribute id="tooltip">Click here to load a saved game.</translatableAttribute>
<action on="Press">
closeMenu();
Engine.PushGuiPage("page_loadgame.xml", { type: "offline" });
@ -212,7 +220,6 @@ Status: $status.
type="image"
size="0 4 100%-4 100%-4"
tooltip_style="pgToolTip"
tooltip="The 0 A.D. Game Manual"
hidden="true"
>
<object name="subMenuMultiplayerJoinButton"
@ -220,9 +227,9 @@ Status: $status.
style="StoneButtonFancy"
size="0 0 100% 28"
tooltip_style="pgToolTip"
tooltip="Joining an existing multiplayer game."
>
Join Game
<translatableAttribute id="caption">Join Game</translatableAttribute>
<translatableAttribute id="tooltip">Joining an existing multiplayer game.</translatableAttribute>
<action on="Press">
closeMenu();
// Open Multiplayer connection window with join option.
@ -235,9 +242,9 @@ Status: $status.
style="StoneButtonFancy"
size="0 32 100% 60"
tooltip_style="pgToolTip"
tooltip="Host a multiplayer game.\n\nRequires UDP port 20595 to be open."
>
Host Game
<translatableAttribute id="caption">Host Game</translatableAttribute>
<translatableAttribute id="tooltip">Host a multiplayer game.\n\nRequires UDP port 20595 to be open.</translatableAttribute>
<action on="Press">
closeMenu();
// Open Multiplayer connection window with host option.
@ -250,9 +257,9 @@ Status: $status.
style="StoneButtonFancy"
size="0 64 100% 92"
tooltip_style="pgToolTip"
tooltip="Launch the multiplayer lobby."
>
Game Lobby
<translatableAttribute id="caption">Game Lobby</translatableAttribute>
<translatableAttribute id="tooltip">Launch the multiplayer lobby.</translatableAttribute>
<action on="Press">
closeMenu();
// Open Multiplayer game lobby.
@ -262,7 +269,7 @@ Status: $status.
if (!Engine.StartXmppClient)
{
this.enabled = false;
this.tooltip = "Launch the multiplayer lobby. [DISABLED BY BUILD]";
this.tooltip = getLobbyDisabledByBuild();
}
</action>
</object>
@ -273,7 +280,6 @@ Status: $status.
type="image"
size="0 4 100%-4 100%-4"
tooltip_style="pgToolTip"
tooltip="The 0 A.D. Game Manual"
hidden="true"
>
<object name="submenuOptionsButton"
@ -281,10 +287,10 @@ Status: $status.
type="button"
size="0 0 100% 28"
tooltip_style="pgToolTip"
tooltip="Adjust game settings."
enabled="true"
>
Options
<translatableAttribute id="caption">Options</translatableAttribute>
<translatableAttribute id="tooltip">Adjust game settings.</translatableAttribute>
<action on="Press">
closeMenu();
<![CDATA[
@ -294,25 +300,34 @@ Status: $status.
</action>
</object>
<object name="submenuEditorButton"
<object name="submenuLocaleButton"
style="StoneButtonFancy"
type="button"
size="0 32 100% 60"
tooltip_style="pgToolTip"
tooltip="Open the Atlas Scenario Editor in a new window. You can run this more reliably by starting the game with the command-line argument &quot;-editor&quot;."
>
Scenario Editor
<translatableAttribute id="caption">Language</translatableAttribute>
<translatableAttribute id="tooltip">Choose the language of the game.</translatableAttribute>
<action on="Press">
closeMenu();
<![CDATA[
// Start Atlas
if (Engine.AtlasIsAvailable())
Engine.RestartInAtlas();
else
messageBox(400, 200, "The scenario editor is not available or failed to load.", "Error", 2);
closeMenu();
Engine.PushGuiPage("page_locale.xml");
]]>
</action>
</object>
<object name="submenuEditorButton"
style="StoneButtonFancy"
type="button"
size="0 64 100% 92"
tooltip_style="pgToolTip"
>
<translatableAttribute id="caption">Scenario Editor</translatableAttribute>
<translatableAttribute id="tooltip">Open the Atlas Scenario Editor in a new window. You can run this more reliably by starting the game with the command-line argument &quot;-editor&quot;.</translatableAttribute>
<action on="Press">
pressedScenarioEditorButton();
</action>
</object>
</object>
</object><!-- end of submenu -->
@ -360,9 +375,9 @@ Status: $status.
style="StoneButtonFancy"
size="4 4 100%-4 32"
tooltip_style="pgToolTip"
tooltip="The 0 A.D. Game Manual"
>
Learn To Play
<translatableAttribute id="caption">Learn To Play</translatableAttribute>
<translatableAttribute id="tooltip">The 0 A.D. Game Manual</translatableAttribute>
<action on="Press">
closeMenu();
<![CDATA[
@ -377,9 +392,9 @@ Status: $status.
type="button"
size="4 36 100%-4 64"
tooltip_style="pgToolTip"
tooltip="Challenge the computer player to a single player match."
>
Single Player
<translatableAttribute id="caption">Single Player</translatableAttribute>
<translatableAttribute id="tooltip">Challenge the computer player to a single player match.</translatableAttribute>
<action on="Press">
closeMenu();
openMenu("submenuSinglePlayer", (this.parent.size.top+this.size.top), (this.size.bottom-this.size.top), 3);
@ -392,9 +407,9 @@ Status: $status.
type="button"
size="4 68 100%-4 96"
tooltip_style="pgToolTip"
tooltip="Fight against one or more human players in a multiplayer game."
>
Multiplayer
<translatableAttribute id="caption">Multiplayer</translatableAttribute>
<translatableAttribute id="tooltip">Fight against one or more human players in a multiplayer game.</translatableAttribute>
<action on="Press">
closeMenu();
openMenu("submenuMultiplayer", (this.parent.size.top+this.size.top), (this.size.bottom-this.size.top), 3);
@ -407,12 +422,12 @@ Status: $status.
type="button"
size="4 100 100%-4 128"
tooltip_style="pgToolTip"
tooltip="Game options and scenario design tools."
>
Tools <![CDATA[&]]> Options
<translatableAttribute id="caption">Tools &amp; Options</translatableAttribute>
<translatableAttribute id="tooltip">Game options and scenario design tools.</translatableAttribute>
<action on="Press">
closeMenu();
openMenu("submenuToolsAndOptions", (this.parent.size.top+this.size.top), (this.size.bottom-this.size.top), 2);
openMenu("submenuToolsAndOptions", (this.parent.size.top+this.size.top), (this.size.bottom-this.size.top), 3);
</action>
</object>
@ -422,9 +437,9 @@ Status: $status.
type="button"
size="4 132 100%-4 160"
tooltip_style="pgToolTip"
tooltip="Learn about the many civilizations featured in 0 A.D."
>
History
<translatableAttribute id="caption">History</translatableAttribute>
<translatableAttribute id="tooltip">Learn about the many civilizations featured in 0 A.D.</translatableAttribute>
<action on="Press">
closeMenu();
<![CDATA[
@ -439,17 +454,10 @@ Status: $status.
style="StoneButtonFancy"
size="4 164 100%-4 192"
tooltip_style="pgToolTip"
tooltip="Exit Game"
>
Exit
<action on="Press">
closeMenu();
<![CDATA[
var btCaptions = ["Yes", "No"];
var btCode = [Engine.Exit, null]
messageBox(400, 200, "Are you sure you want to quit 0 A.D.?", "Confirmation", 0, btCaptions, btCode);
]]>
</action>
<translatableAttribute id="caption">Exit</translatableAttribute>
<translatableAttribute id="tooltip">Exit Game</translatableAttribute>
<action on="Press">exitGamePressed();</action>
</object>
</object>
@ -467,21 +475,25 @@ Status: $status.
size="8 8 100%-8 100%-36"
ghost="true"
>
[font="serif-bold-16"]Alpha XV: Osiris<!-- IMPORTANT: remember to update session/session.xml in sync with this -->[/font]
WARNING: This is an early development version of the game. Many features have not been added yet.
Get involved at: play0ad.com
<!-- IMPORTANT: remember to update session/session.xml in sync with this: -->
<attribute id="caption">
<keep>[font="serif-bold-16"]</keep>
<translate>Alpha XV: Osiris</translate>
<keep>[/font]\n\n</keep>
<translate>WARNING: This is an early development version of the game. Many features have not been added yet.</translate>
<keep>\n\n</keep>
<translate>Get involved at: play0ad.com</translate>
</attribute>
</object>
<!-- CONTACT METHODS -->
<object type="button"
style="StoneButton"
tooltip_style="pgToolTip"
tooltip="Click to open play0ad.com in your web browser."
size="8 100%-72 50%-4 100%-44"
>
Website
<translatableAttribute id="caption">Website</translatableAttribute>
<translatableAttribute id="tooltip">Click to open play0ad.com in your web browser.</translatableAttribute>
<action on="Press"><![CDATA[
var url = "http://play0ad.com/";
Engine.OpenURL(url);
@ -492,10 +504,10 @@ Get involved at: play0ad.com
<object type="button"
style="StoneButton"
tooltip_style="pgToolTip"
tooltip="Click to open the 0 A.D. IRC chat in your browser. (#0ad on webchat.quakenet.org)"
size="50%+4 100%-72 100%-8 100%-44"
>
Chat
<translatableAttribute id="caption">Chat</translatableAttribute>
<translatableAttribute id="tooltip">Click to open the 0 A.D. IRC chat in your browser. (#0ad on webchat.quakenet.org)</translatableAttribute>
<action on="Press"><![CDATA[
var url = "http://webchat.quakenet.org/?channels=0ad";
Engine.OpenURL(url);
@ -506,10 +518,10 @@ Get involved at: play0ad.com
<object type="button"
style="StoneButton"
tooltip_style="pgToolTip"
tooltip="Click to visit 0 A.D. Trac to report a bug, crash, or error"
size="8 100%-36 100%-8 100%-8"
>
Report a Bug
<translatableAttribute id="caption">Report a Bug</translatableAttribute>
<translatableAttribute id="tooltip">Click to visit 0 A.D. Trac to report a bug, crash, or error</translatableAttribute>
<action on="Press"><![CDATA[
var url = "http://trac.wildfiregames.com/wiki/ReportingErrors/";
Engine.OpenURL(url);
@ -538,7 +550,9 @@ Get involved at: play0ad.com
style="MediumTitleText"
ghost="true"
size="50%-128 32 50%+128 48"
>WILDFIRE GAMES</object>
>
<translatableAttribute id="caption">WILDFIRE GAMES</translatableAttribute>
</object>
</object>
<!-- VERSION -->
@ -548,9 +562,9 @@ Get involved at: play0ad.com
ghost="true"
size="50%-128 100%-36 50%+128 100%"
>
<action on="Load"><![CDATA[
this.caption = "Build: " + Engine.BuildTime(0) + " - " + Engine.BuildTime(2);
]]></action>
<action on="Load">
this.caption = getBuildString();
</action>
</object>
</object>
</object>

View File

@ -7,7 +7,7 @@ function init()
var savedGames = Engine.GetSavedGames();
if (savedGames.length == 0)
{
gameSelection.list = [ "No saved games found" ];
gameSelection.list = [translate("No saved games found")];
gameSelection.selected = 0;
Engine.GetGUIObjectByName("loadGameButton").enabled = false;
Engine.GetGUIObjectByName("deleteGameButton").enabled = false;
@ -41,29 +41,35 @@ function loadGame()
if (!hasSameVersion(metadata, engineInfo) || !hasSameMods(metadata, engineInfo))
{
// version not compatible ... ask for confirmation
var btCaptions = ["Yes", "No"];
var btCaptions = [translate("Yes"), translate("No")];
var btCode = [function(){ reallyLoadGame(gameId); }, init];
var message = "This saved game may not be compatible:";
var message = translate("This saved game may not be compatible:");
if (!hasSameVersion(metadata, engineInfo))
message += "\nIt needs 0AD version " + metadata.version_major
+ " while you are running version " + engineInfo.version_major + ".";
message += "\n" + sprintf(translate("It needs 0 A.D. version %(requiredVersion)s, while you are running version %(currentVersion)s."), {
requiredVersion: metadata.version_major,
currentVersion: engineInfo.version_major
});
if (!hasSameMods(metadata, engineInfo))
{
if (!metadata.mods) // only for backwards compatibility with previous saved games
metadata.mods = [];
if (metadata.mods.length == 0)
message += "\nIt does not need any mod"
+ " while you are running with \"" + engineInfo.mods.join() + "\".";
message += "\n" + sprintf(translate("It does not need any mod while you are running with \"%(currentMod)s\"."), {
currentMod: engineInfo.mods.join()
});
else if (engineInfo.mods.length == 0)
message += "\nIt needs the mod \"" + metadata.mods.join() + "\""
+ " while you are running without mod.";
message += "\n" + sprintf(translate("It needs the mod \"%(requiredMod)s\" while you are running without a mod."), {
requiredMod: metadata.mods.join()
});
else
message += "\nIt needs the mod \"" + metadata.mods.join() + "\""
+ " while you are running with \"" + engineInfo.mods.join() + "\".";
message += "\n" + sprintf(translate("It needs the mod \"%(requiredMod)s\" while you are running with \"%(currentMod)s\"."), {
requiredMod: metadata.mods.join(),
currentMod: engineInfo.mods.join()
});
}
message += "\nDo you still want to proceed ?";
messageBox(500, 250, message, "Warning", 0, btCaptions, btCode);
message += "\n" + translate("Do you still want to proceed?");
messageBox(500, 250, message, translate("Warning"), 0, btCaptions, btCode);
}
else
reallyLoadGame(gameId);
@ -76,7 +82,7 @@ function reallyLoadGame(gameId)
{
// Probably the file wasn't found
// Show error and refresh saved game list
error("Could not load saved game '"+gameId+"'");
error(sprintf("Could not load saved game '%(id)s'", { id: gameId }));
init();
}
else
@ -97,15 +103,15 @@ function deleteGame()
var gameID = gameSelection.list_data[gameSelection.selected];
// Ask for confirmation
var btCaptions = ["Yes", "No"];
var btCaptions = [translate("Yes"), translate("No")];
var btCode = [function(){ reallyDeleteGame(gameID); }, null];
messageBox(500, 200, "\""+gameLabel+"\"\nSaved game will be permanently deleted, are you sure?", "DELETE", 0, btCaptions, btCode);
messageBox(500, 200, sprintf(translate("\"%(label)s\""), { label: gameLabel }) + "\n" + translate("Saved game will be permanently deleted, are you sure?"), translate("DELETE"), 0, btCaptions, btCode);
}
function reallyDeleteGame(gameID)
{
if (!Engine.DeleteSavedGame(gameID))
error("Could not delete saved game '"+gameID+"'");
error(sprintf("Could not delete saved game '%(id)s'", { id: gameID }));
// Run init again to refresh saved game list
init();

View File

@ -12,7 +12,7 @@
<object type="image" style="ModernDialog" size="50%-300 50%-200 50%+300 50%+200">
<object type="text" style="TitleText" size="50%-128 0%-16 50%+128 16">
Load Game
<translatableAttribute id="caption">Load Game</translatableAttribute>
</object>
<object name="gameSelection"
@ -22,17 +22,17 @@
</object>
<object name="loadGameButton" type="button" size="0%+25 100%-60 33%+10 100%-32" style="StoneButton">
Load
<translatableAttribute id="caption">Load</translatableAttribute>
<action on="Press">loadGame();</action>
</object>
<object name="deleteGameButton" type="button" size="33%+20 100%-60 66%-15 100%-32" style="StoneButton">
Delete
<translatableAttribute id="caption">Delete</translatableAttribute>
<action on="Press">deleteGame();</action>
</object>
<object type="button" style="StoneButton" size="66%-5 100%-60 100%-25 100%-32">
Cancel
<translatableAttribute id="caption">Cancel</translatableAttribute>
<action on="Press">Engine.PopGuiPage();</action>
</object>

View File

@ -27,7 +27,7 @@ function init(data)
var savedGames = Engine.GetSavedGames();
if (savedGames.length == 0)
{
gameSelection.list = [ "No saved games found" ];
gameSelection.list = [translate("No saved games found")];
gameSelection.selected = -1;
return;
}
@ -56,9 +56,9 @@ function saveGame()
if (gameSelection.selected != -1)
{
// Ask for confirmation
var btCaptions = ["Yes", "No"];
var btCaptions = [translate("Yes"), translate("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);
messageBox(500, 200, sprintf(translate("\"%(label)s\""), { label: gameLabel }) + "\n" + translate("Saved game will be permanently overwritten, are you sure?"), translate("OVERWRITE SAVE"), 0, btCaptions, btCode);
}
else
reallySaveGame(name, desc, true);
@ -86,15 +86,15 @@ function deleteGame()
var gameID = gameSelection.list_data[gameSelection.selected];
// Ask for confirmation
var btCaptions = ["Yes", "No"];
var btCaptions = [translate("Yes"), translate("No")];
var btCode = [function(){ reallyDeleteGame(gameID); }, null];
messageBox(500, 200, "\""+gameLabel+"\"\nSaved game will be permanently deleted, are you sure?", "DELETE", 0, btCaptions, btCode);
messageBox(500, 200, sprintf(translate("\"%(label)s\""), { label: gameLabel }) + "\n" + translate("Saved game will be permanently deleted, are you sure?"), translate("DELETE"), 0, btCaptions, btCode);
}
function reallyDeleteGame(gameID)
{
if (!Engine.DeleteSavedGame(gameID))
error("Could not delete saved game '"+gameID+"'");
error(sprintf("Could not delete saved game '%(id)s'", { id: gameID }));
// Run init again to refresh saved game list
init();

View File

@ -12,7 +12,7 @@
<object type="image" style="StoneDialog" size="50%-300 50%-200 50%+300 50%+200">
<object type="image" z="0" sprite="BackgroundTranslucent"/>
<object type="text" style="TitleText" size="50%-128 0%-16 50%+128 16">
Save Game
<translatableAttribute id="caption">Save Game</translatableAttribute>
</object>
<object name="gameSelection"
@ -25,24 +25,26 @@
</action>
</object>
<object size="24 100%-124 100%-24 100%-100" name="descLabel" type="text" style="LeftLabelText">Description:</object>
<object size="24 100%-124 100%-24 100%-100" name="descLabel" type="text" style="LeftLabelText">
<translatableAttribute id="caption">Description:</translatableAttribute>
</object>
<object name="saveGameDesc" size="24 100%-96 100%-24 100%-72" type="input" style="StoneInput">
<action on="Press">saveGame();</action>
</object>
<object name="saveButton" type="button" size="0%+25 100%-60 33%+10 100%-32" style="StoneButton">
Save
<translatableAttribute id="caption">Save</translatableAttribute>
<action on="Press">saveGame();</action>
</object>
<object name="deleteGameButton" type="button" size="33%+20 100%-60 66%-15 100%-32" style="StoneButton">
Delete
<translatableAttribute id="caption">Delete</translatableAttribute>
<action on="Press">deleteGame();</action>
</object>
<object type="button" style="StoneButton" size="66%-5 100%-60 100%-25 100%-32">
Cancel
<translatableAttribute id="caption">Cancel</translatableAttribute>
<action on="Press">closeSave(true);</action>
</object>

View File

@ -123,10 +123,18 @@ function updateBuildingPlacementPreview()
// Show placement info tooltip if invalid position
placementSupport.tooltipError = !result.success;
placementSupport.tooltipMessage = result.success ? "" : result.message;
placementSupport.tooltipMessage = "";
if (!result.success)
{
if (result.message && result.parameters)
{
// translate the message parameters
translateObjectKeys(result.parameters, Object.keys(result.parameters), true)
placementSupport.tooltipMessage = sprintf(translate(result.message), result.parameters);
}
return false;
}
if (placementSupport.attack)
{
@ -138,7 +146,7 @@ function updateBuildingPlacementPreview()
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);
placementSupport.tooltipMessage = sprintf(translate("Basic range: %(range)s"), { range: Math.round(cmd.range/4) }) + "\n" + sprintf(translate("Average bonus range: %(range)s"), { range: Math.round((averageRange - cmd.range)/4) });
}
return true;
}
@ -253,8 +261,10 @@ function getActionInfo(action, target)
data.command = "garrison";
data.target = target;
cursor = "action-garrison";
tooltip = "Current garrison: " + targetState.garrisonHolder.garrisonedEntitiesCount
+ "/" + targetState.garrisonHolder.capacity;
tooltip = sprintf(translate("Current garrison: %(garrisoned)s/%(capacity)s"), {
garrisoned: targetState.garrisonHolder.garrisonedEntitiesCount,
capacity: targetState.garrisonHolder.capacity
});
if (targetState.garrisonHolder.garrisonedEntitiesCount >= targetState.garrisonHolder.capacity)
tooltip = "[color=\"orange\"]" + tooltip + "[/color]";
}
@ -293,11 +303,11 @@ function getActionInfo(action, target)
data.target = traderData.secondMarket;
data.source = traderData.firstMarket;
cursor = "action-setup-trade-route";
tooltip = "Right-click to establish a default route for new traders.";
tooltip = translate("Right-click to establish a default route for new traders.");
if (trader)
tooltip += "\nGain: " + getTradingTooltip(gain);
tooltip += "\n" + sprintf(translate("Gain: %(gain)s"), { gain: getTradingTooltip(gain) });
else // Foundation or cannot produce traders
tooltip += "\nExpected gain: " + getTradingTooltip(gain);
tooltip += "\n" + sprintf(translate("Expected gain: %(gain)s"), { gain: getTradingTooltip(gain) });
}
}
else if (targetState.needsRepair && allyOwned)
@ -344,8 +354,10 @@ function getActionInfo(action, target)
case "garrison":
if (hasClass(entState, "Unit") && targetState.garrisonHolder && (playerOwned || mutualAllyOwned))
{
var tooltip = "Current garrison: " + targetState.garrisonHolder.garrisonedEntitiesCount
+ "/" + targetState.garrisonHolder.capacity;
var tooltip = sprintf(translate("Current garrison: %(garrisoned)s/%(capacity)s"), {
garrisoned: targetState.garrisonHolder.garrisonedEntitiesCount,
capacity: targetState.garrisonHolder.capacity
});
var extraCount = 0;
if (entState.garrisonHolder)
extraCount += entState.garrisonHolder.garrisonedEntitiesCount;
@ -372,22 +384,28 @@ function getActionInfo(action, target)
switch (tradingDetails.type)
{
case "is first":
tooltip = "Origin trade market.";
tooltip = translate("Origin trade market.");
if (tradingDetails.hasBothMarkets)
tooltip += "\nGain: " + getTradingTooltip(tradingDetails.gain);
tooltip += "\n" + sprintf(translate("Gain: %(gain)s"), {
gain: getTradingTooltip(tradingDetails.gain)
});
else
tooltip += "\nRight-click on another market to set it as a destination trade market."
tooltip += "\n" + translate("Right-click on another market to set it as a destination trade market.")
break;
case "is second":
tooltip = "Destination trade market.\nGain: " + getTradingTooltip(tradingDetails.gain);
tooltip = translate("Destination trade market.") + "\n" + sprintf(translate("Gain: %(gain)s"), {
gain: getTradingTooltip(tradingDetails.gain)
});
break;
case "set first":
tooltip = "Right-click to set as origin trade market";
tooltip = translate("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);
tooltip = translate("Right-click to set as destination trade market.") + "\n" + sprintf(translate("Gain: %(gain)s"), {
gain: getTradingTooltip(tradingDetails.gain)
});
break;
}
return {"possible": true, "tooltip": tooltip};
@ -585,7 +603,10 @@ function tryPlaceBuilding(queued)
{
if (placementSupport.mode !== "building")
{
error("[tryPlaceBuilding] Called while in '"+placementSupport.mode+"' placement mode instead of 'building'");
error(sprintf("[%(functionName)s] Called while in '%(mode)s' placement mode instead of 'building'", {
functionName: "tryPlaceBuilding",
mode: placementSupport.mode
}));
return false;
}
@ -626,14 +647,21 @@ function tryPlaceWall(queued)
{
if (placementSupport.mode !== "wall")
{
error("[tryPlaceWall] Called while in '" + placementSupport.mode + "' placement mode; expected 'wall' mode");
error(sprintf("[%(functionName)s] Called while in '%(mode)s' placement mode; expected 'wall' mode", {
functionName: "tryPlaceWall",
mode: placementSupport.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'");
error(sprintf("[%(functionName)s] Unexpected return value from %(function2Name)s: '%(value)s'; expected either 'false' or 'object'", {
functionName: "tryPlaceWall",
function2Name: "updateBuildingPlacementPreview",
value: uneval(placementInfo)
}));
return false;
}
@ -965,7 +993,7 @@ function handleInputBeforeGui(ev, hoveredObject)
}
else
{
placementSupport.tooltipMessage = "Cannot build wall here!";
placementSupport.tooltipMessage = translate("Cannot build wall here!");
}
updateBuildingPlacementPreview();
@ -2245,3 +2273,4 @@ function clearSelection()
g_Selection.reset();
preSelectedAction = ACTION_NONE;
}

View File

@ -136,9 +136,9 @@ function resignMenuButton()
closeMenu();
closeOpenDialogs();
pauseGame();
var btCaptions = ["Yes", "No"];
var btCaptions = [translate("Yes"), translate("No")];
var btCode = [resignGame, resumeGame];
messageBox(400, 200, "Are you sure you want to resign?", "Confirmation", 0, btCaptions, btCode);
messageBox(400, 200, translate("Are you sure you want to resign?"), translate("Confirmation"), 0, btCaptions, btCode);
}
function exitMenuButton()
@ -149,27 +149,27 @@ function exitMenuButton()
if (g_IsNetworked && g_IsController)
{
var btCode = [leaveGame, resumeGame];
var message = "Are you sure you want to quit? Leaving will disconnect all other players.";
var message = translate("Are you sure you want to quit? Leaving will disconnect all other players.");
}
else if (g_IsNetworked && !g_GameEnded && !g_IsObserver)
{
var btCode = [networkReturnQuestion, resumeGame];
var message = "Are you sure you want to quit?";
var message = translate("Are you sure you want to quit?");
}
else
{
var btCode = [leaveGame, resumeGame];
var message = "Are you sure you want to quit?";
var message = translate("Are you sure you want to quit?");
}
messageBox(400, 200, message, "Confirmation", 0, ["Yes", "No"], btCode);
messageBox(400, 200, message, translate("Confirmation"), 0, [translate("Yes"), translate("No")], btCode);
}
function networkReturnQuestion()
{
var btCaptions = ["I resign", "I will return"];
var btCaptions = [translate("I resign"), translate("I will return")];
var btCode = [leaveGame, leaveGame];
var btArgs = [false, true];
messageBox(400, 200, "Do you want to resign or will you return soon?", "Confirmation", 0, btCaptions, btCode, btArgs);
messageBox(400, 200, translate("Do you want to resign or will you return soon?"), translate("Confirmation"), 0, btCaptions, btCode, btArgs);
}
function openDeleteDialog(selection)
@ -182,10 +182,10 @@ function openDeleteDialog(selection)
Engine.PostNetworkCommand({"type": "delete-entities", "entities": selectionArg});
};
var btCaptions = ["Yes", "No"];
var btCaptions = [translate("Yes"), translate("No")];
var btCode = [deleteSelectedEntities, resumeGame];
messageBox(400, 200, "Destroy everything currently selected?", "Delete", 0, btCaptions, btCode, [selection, null]);
messageBox(400, 200, translate("Destroy everything currently selected?"), translate("Delete"), 0, btCaptions, btCode, [selection, null]);
}
// Menu functions
@ -289,10 +289,10 @@ function openDiplomacy()
Engine.GetGUIObjectByName("diplomacyPlayerName["+(i-1)+"]").caption = "[color=\"" + playerColor + "\"]" + players[i].name + "[/color]";
Engine.GetGUIObjectByName("diplomacyPlayerCiv["+(i-1)+"]").caption = g_CivData[players[i].civ].Name;
Engine.GetGUIObjectByName("diplomacyPlayerTeam["+(i-1)+"]").caption = (players[i].team < 0) ? "None" : players[i].team+1;
Engine.GetGUIObjectByName("diplomacyPlayerTeam["+(i-1)+"]").caption = (players[i].team < 0) ? translateWithContext("team", "None") : players[i].team+1;
if (i != we)
Engine.GetGUIObjectByName("diplomacyPlayerTheirs["+(i-1)+"]").caption = (players[i].isAlly[we] ? "Ally" : (players[i].isNeutral[we] ? "Neutral" : "Enemy"));
Engine.GetGUIObjectByName("diplomacyPlayerTheirs["+(i-1)+"]").caption = (players[i].isAlly[we] ? translate("Ally") : (players[i].isNeutral[we] ? translate("Neutral") : translate("Enemy")));
// Don't display the options for ourself, or if we or the other player aren't active anymore
if (i == we || players[we].state != "active" || players[i].state != "active")
@ -353,25 +353,24 @@ function openDiplomacy()
if (setting == "ally")
{
if (players[we].isAlly[i])
button.caption = "x";
button.caption = translate("x");
else
button.caption = "";
}
else if (setting == "neutral")
{
if (players[we].isNeutral[i])
button.caption = "x";
button.caption = translate("x");
else
button.caption = "";
}
else // "enemy"
{
if (players[we].isEnemy[i])
button.caption = "x";
button.caption = translate("x");
else
button.caption = "";
}
button.onpress = (function(e){ return function() { setDiplomacy(e) } })({"player": i, "to": setting});
button.hidden = false;
}
@ -392,7 +391,7 @@ function toggleDiplomacy()
closeDiplomacy();
else
openDiplomacy();
};
}
function openTrade()
{
@ -478,41 +477,126 @@ function openTrade()
var traderNumber = Engine.GuiInterfaceCall("GetTraderNumber");
var caption = "";
var comma = "";
if (traderNumber.landTrader.total == 0)
caption += "0";
caption = translate("There are no land traders.");
else
{
var inactive = traderNumber.landTrader.total - traderNumber.landTrader.trading - traderNumber.landTrader.garrisoned;
var inactiveString = "";
if (inactive > 0)
inactiveString = "[color=\"orange\"]" + sprintf(translatePlural("%(numberOfLandTraders)s inactive", "%(numberOfLandTraders)s inactive", inactive), { numberOfLandTraders: inactive }) + "[/color]";
if (traderNumber.landTrader.trading > 0)
{
caption += traderNumber.landTrader.trading + " trading"
comma = ", ";
var openingTradingString = sprintf(translatePlural("There is %(numberTrading)s land trader trading", "There are %(numberTrading)s land traders trading", traderNumber.landTrader.trading), { numberTrading: traderNumber.landTrader.trading });
if (traderNumber.landTrader.garrisoned > 0)
{
var garrisonedString = sprintf(translatePlural("%(numberGarrisoned)s garrisoned on a trading merchant ship", "%(numberGarrisoned)s garrisoned on a trading merchant ship", traderNumber.landTrader.garrisoned), { numberGarrisoned: traderNumber.landTrader.garrisoned });
if (inactive > 0)
{
caption = sprintf(translate("%(openingTradingString)s, %(garrisonedString)s, and %(inactiveString)s."), {
openingTradingString: openingTradingString,
garrisonedString: garrisonedString,
inactiveString: inactiveString
});
}
else
{
caption = sprintf(translate("%(openingTradingString)s, and %(garrisonedString)s."), {
openingTradingString: openingTradingString,
garrisonedString: garrisonedString
});
}
}
else
{
if (inactive > 0)
{
caption = sprintf(translate("%(openingTradingString)s, and %(inactiveString)s."), {
openingTradingString: openingTradingString,
inactiveString: inactiveString
});
}
else
{
caption = sprintf(translate("%(openingTradingString)s."), {
openingTradingString: openingTradingString,
});
}
}
}
if (traderNumber.landTrader.garrisoned > 0)
else
{
caption += comma + traderNumber.landTrader.garrisoned + " garrisoned inside ships";
comma = ", ";
if (traderNumber.landTrader.garrisoned > 0)
{
var openingGarrisonedString = sprintf(translatePlural("There is %(numberGarrisoned)s land trader garrisoned on a trading merchant ship", "There are %(numberGarrisoned)s land traders garrisoned on a trading merchant ship", traderNumber.landTrader.garrisoned), { numberGarrisoned: traderNumber.landTrader.garrisoned });
if (inactive > 0)
{
caption = sprintf(translate("%(openingGarrisonedString)s, and %(inactiveString)s."), {
openingGarrisonedString: openingGarrisonedString,
inactiveString: inactiveString
});
}
else
{
caption = sprintf(translate("%(openingGarrisonedString)s."), {
openingGarrisonedString: openingGarrisonedString
});
}
}
else
{
if (inactive > 0)
{
inactiveString = "[color=\"orange\"]" + sprintf(translatePlural("%(numberOfLandTraders)s land trader inactive", "%(numberOfLandTraders)s land traders inactive", inactive), { numberOfLandTraders: inactive }) + "[/color]";
caption = sprintf(translatePlural("There is %(inactiveString)s.", "There are %(inactiveString)s.", inactive), {
inactiveString: inactiveString
});
}
// The “else” here is already handled by “if (traderNumber.landTrader.total == 0)” above.
}
}
var inactive = traderNumber.landTrader.total - traderNumber.landTrader.trading - traderNumber.landTrader.garrisoned;
if (inactive > 0)
caption += comma + "[color=\"orange\"]" + inactive + " inactive[/color]";
}
Engine.GetGUIObjectByName("landTraders").caption = caption;
caption = "";
comma = "";
if (traderNumber.shipTrader.total == 0)
caption += "0";
caption = translate("There are no merchant ships.");
else
{
var inactive = traderNumber.shipTrader.total - traderNumber.shipTrader.trading;
var inactiveString = "";
if (inactive > 0)
inactiveString = "[color=\"orange\"]" + sprintf(translatePlural("%(numberOfShipTraders)s inactive", "%(numberOfShipTraders)s inactive", inactive), { numberOfShipTraders: inactive }) + "[/color]";
if (traderNumber.shipTrader.trading > 0)
{
caption += traderNumber.shipTrader.trading + " trading"
comma = ", ";
var openingTradingString = sprintf(translatePlural("There is %(numberTrading)s merchant ship trading", "There are %(numberTrading)s merchant ships trading", traderNumber.shipTrader.trading), { numberTrading: traderNumber.shipTrader.trading });
if (inactive > 0)
{
caption = sprintf(translate("%(openingTradingString)s, and %(inactiveString)s."), {
openingTradingString: openingTradingString,
inactiveString: inactiveString
});
}
else
{
caption = sprintf(translate("%(openingTradingString)s."), {
openingTradingString: openingTradingString,
});
}
}
else
{
if (inactive > 0)
{
inactiveString = "[color=\"orange\"]" + sprintf(translatePlural("%(numberOfShipTraders)s merchant ship inactive", "%(numberOfShipTraders)s merchant ships inactive", inactive), { numberOfShipTraders: inactive }) + "[/color]";
caption = sprintf(translatePlural("There is %(inactiveString)s.", "There are %(inactiveString)s.", inactive), {
inactiveString: inactiveString
});
}
// The “else” here is already handled by “if (traderNumber.shipTrader.total == 0)” above.
}
var inactive = traderNumber.shipTrader.total - traderNumber.shipTrader.trading;
if (inactive > 0)
caption += comma + "[color=\"orange\"]" + inactive + " inactive[/color]";
}
Engine.GetGUIObjectByName("shipTraders").caption = caption;
@ -531,7 +615,7 @@ function toggleTrade()
closeTrade();
else
openTrade();
};
}
function toggleGameSpeed()
{
@ -594,8 +678,10 @@ function toggleDeveloperOverlay()
return;
var devCommands = Engine.GetGUIObjectByName("devCommands");
var text = devCommands.hidden ? "opened." : "closed.";
submitChatDirectly("The Developer Overlay was " + text);
if (devCommands.hidden)
submitChatDirectly(translate("The Developer Overlay was opened."));
else
submitChatDirectly(translate("The Developer Overlay was closed."));
// Update the options dialog
Engine.GetGUIObjectByName("developerOverlayCheckbox").checked = devCommands.hidden;
devCommands.hidden = !devCommands.hidden;
@ -613,6 +699,10 @@ function closeOpenDialogs()
function formatTributeTooltip(player, resource, amount)
{
var playerColor = player.color.r + " " + player.color.g + " " + player.color.b;
return "Tribute " + amount + " " + resource + " to [color=\"" + playerColor + "\"]" + player.name +
"[/color]. Shift-click to tribute " + (amount < 500 ? 500 : amount + 500) + ".";
return sprintf(translate("Tribute %(resourceAmount)s %(resourceType)s to %(playerName)s. Shift-click to tribute %(greaterAmount)s."), {
resourceAmount: amount,
resourceType: resource,
playerName: "[color=\"" + playerColor + "\"]" + player.name + "[/color]",
greaterAmount: (amount < 500 ? 500 : amount + 500)
});
}

View File

@ -19,7 +19,7 @@ function getCheatsData()
{
var currentCheat = parseJSONData("simulation/data/cheats/"+fileName+".json");
if (Object.keys(cheats).indexOf(currentCheat.Name) !== -1)
warn("Cheat name '"+currentCheat.Name+"' is already present");
warn(sprintf("Cheat name '%(name)s' is already present", { name: currentCheat.Name }));
else
cheats[currentCheat.Name] = currentCheat.Data;
}
@ -142,13 +142,34 @@ function displayNotifications()
{
var messages = [];
for each (var n in notifications)
messages.push(n.message);
{
var parameters = n.parameters || {};
if (n.translateParameters && n.translateParameters.length)
translateObjectKeys(parameters, n.translateParameters);
var message = n.message;
if (n.translateMessage)
message = translate(message);
messages.push(sprintf(message, parameters));
}
Engine.GetGUIObjectByName("notificationText").caption = messages.join("\n");
}
function updateTimeNotifications()
{
Engine.GetGUIObjectByName("timeNotificationText").caption = Engine.GuiInterfaceCall("GetTimeNotificationText");
var notifications = Engine.GuiInterfaceCall("GetTimeNotifications");
var notificationText = "";
for (var n of notifications)
{
var message = n.message;
if (n.translateMessage)
message = translate(message);
var parameters = n.parameters || {};
if (n.translateParameters && n.translateParameters.length)
translateObjectKeys(parameters, n.translateParameters);
parameters.time = timeToString(n.time);
notificationText += sprintf(message, parameters) + "\n";
}
Engine.GetGUIObjectByName("timeNotificationText").caption = notificationText;
}
// Returns [username, playercolor] for the given player
@ -165,7 +186,7 @@ function getUsernameAndColor(player)
// Messages
function handleNetMessage(message)
{
log("Net message: " + uneval(message));
log(sprintf(translate("Net message: %(message)s"), { message: uneval(message) }));
switch (message.type)
{
@ -178,11 +199,11 @@ function handleNetMessage(message)
switch (message.status)
{
case "waiting_for_players":
obj.caption = "Waiting for other players to connect...";
obj.caption = translate("Waiting for other players to connect...");
obj.hidden = false;
break;
case "join_syncing":
obj.caption = "Synchronising gameplay with other players...";
obj.caption = translate("Synchronising gameplay with other players...");
obj.hidden = false;
break;
case "active":
@ -190,20 +211,20 @@ function handleNetMessage(message)
obj.hidden = true;
break;
case "connected":
obj.caption = "Connected to the server.";
obj.caption = translate("Connected to the server.");
obj.hidden = false;
break;
case "authenticated":
obj.caption = "Connection to the server has been authenticated.";
obj.caption = translate("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.caption = translate("Connection to the server has been lost.") + "\n\n" + translate("The game has ended.");
obj.hidden = false;
break;
default:
error("Unrecognised netstatus type "+message.status);
error(sprintf("Unrecognised netstatus type %(netType)s", { netType: message.status }));
break;
}
break;
@ -255,7 +276,7 @@ function handleNetMessage(message)
break;
default:
error("Unrecognised net message type "+message.type);
error(sprintf("Unrecognised net message type %(messageType)s", { messageType: message.type }));
}
}
@ -351,8 +372,8 @@ function addChatMessage(msg, playerAssignments)
var playerColor, username;
// No prefix by default. May be set by parseChatCommands().
msg.prefix = "";
// No context by default. May be set by parseChatCommands().
msg.context = "";
if (playerAssignments[msg.guid])
{
@ -379,37 +400,47 @@ function addChatMessage(msg, playerAssignments)
else
{
playerColor = "255 255 255";
username = "Unknown player";
username = translate("Unknown player");
}
var message = escapeText(msg.text);
var formatted;
switch (msg.type)
{
case "connect":
formatted = "[color=\"" + playerColor + "\"]" + username + "[/color] has joined the game.";
formatted = sprintf(translate("%(player)s has joined the game."), { player: "[color=\"" + playerColor + "\"]" + username + "[/color]" });
break;
case "disconnect":
formatted = "[color=\"" + playerColor + "\"]" + username + "[/color] has left the game.";
formatted = sprintf(translate("%(player)s has left the game."), { player: "[color=\"" + playerColor + "\"]" + username + "[/color]" });
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.";
if (!g_IsNetworked && msg.player == Engine.GetPlayerID())
formatted = translate("You have been defeated.");
else
formatted = sprintf(translate("%(player)s has been defeated."), { player: "[color=\"" + playerColor + "\"]" + username + "[/color]" });
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].";
if (msg.status == "ally")
formatted = sprintf(translate("You are now allied with %(player)s."), { player: "[color=\"" + playerColor + "\"]" + username + "[/color]" });
else if (msg.status == "enemy")
formatted = sprintf(translate("You are now at war with %(player)s."), { player: "[color=\"" + playerColor + "\"]" + username + "[/color]" });
else // (msg.status == "neutral")
formatted = sprintf(translate("You are now neutral with %(player)s."), { player: "[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."
if (msg.status == "ally")
formatted = sprintf(translate("%(player)s is now allied with you."), { player: "[color=\"" + playerColor + "\"]" + username + "[/color]" });
else if (msg.status == "enemy")
formatted = sprintf(translate("%(player)s is now at war with you."), { player: "[color=\"" + playerColor + "\"]" + username + "[/color]" });
else // (msg.status == "neutral")
formatted = sprintf(translate("%(player)s is now neutral with you."), { player: "[color=\"" + playerColor + "\"]" + username + "[/color]" });
}
else // No need for other players to know of this.
return;
@ -428,36 +459,84 @@ function addChatMessage(msg, playerAssignments)
if (amounts.length > 1)
{
var lastAmount = amounts.pop();
amounts = amounts.join(", ") + " and " + lastAmount;
amounts = sprintf(translate("%(previousAmounts)s and %(lastAmount)s"), {
previousAmounts: amounts.join(translate(", ")),
lastAmount: lastAmount
});
}
formatted = "[color=\"" + playerColor + "\"]" + username + "[/color] has sent you " + amounts + ".";
formatted = sprintf(translate("%(player)s has sent you %(amounts)s."), {
player: "[color=\"" + playerColor + "\"]" + username + "[/color]",
amounts: 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]!";
formatted = sprintf(translate("You have been attacked by %(attacker)s!"), { attacker: "[color=\"" + playerColor + "\"]" + username + "[/color]" });
break;
case "message":
// May have been hidden by the 'team' command.
if (msg.hide)
return;
var message = escapeText(msg.text);
if (msg.action)
{
Engine.Console_Write(msg.prefix + "* " + username + " " + message);
formatted = msg.prefix + "* [color=\"" + playerColor + "\"]" + username + "[/color] " + message;
if (msg.context !== "")
{
Engine.Console_Write(sprintf(translate("(%(context)s) * %(user)s %(message)s"), {
context: msg.context,
user: username,
message: message
}));
formatted = sprintf(translate("(%(context)s) * %(user)s %(message)s"), {
context: msg.context,
user: "[color=\"" + playerColor + "\"]" + username + "[/color]",
message: message
});
}
else
{
Engine.Console_Write(sprintf(translate("* %(user)s %(message)s"), {
user: username,
message: message
}));
formatted = sprintf(translate("* %(user)s %(message)s"), {
user: "[color=\"" + playerColor + "\"]" + username + "[/color]",
message: message
});
}
}
else
{
Engine.Console_Write(msg.prefix + "<" + username + "> " + message);
formatted = msg.prefix + "<[color=\"" + playerColor + "\"]" + username + "[/color]> " + message;
var userTag = sprintf(translate("<%(user)s>"), { user: username })
var formattedUserTag = sprintf(translate("<%(user)s>"), { user: "[color=\"" + playerColor + "\"]" + username + "[/color]" })
if (msg.context !== "")
{
Engine.Console_Write(sprintf(translate("(%(context)s) %(userTag)s %(message)s"), {
context: msg.context,
userTag: userTag,
message: message
}));
formatted = sprintf(translate("(%(context)s) %(userTag)s %(message)s"), {
context: msg.context,
userTag: formattedUserTag,
message: message
});
}
else
{
Engine.Console_Write(sprintf(translate("%(userTag)s %(message)s"), { userTag: userTag, message: message}));
formatted = sprintf(translate("%(userTag)s %(message)s"), { userTag: formattedUserTag, message: message});
}
}
break;
default:
error("Invalid chat message '" + uneval(msg) + "'");
error(sprintf("Invalid chat message '%(message)s'", { message: uneval(msg) }));
return;
}
@ -499,7 +578,7 @@ function parseChatCommands(msg, playerAssignments)
{
case "/all":
// Resets values that 'team' or 'enemy' may have set.
msg.prefix = "";
msg.context = "";
msg.hide = false;
recurse = true;
break;
@ -510,7 +589,7 @@ function parseChatCommands(msg, playerAssignments)
if (g_Players[Engine.GetPlayerID()].team != g_Players[sender].team)
msg.hide = true;
else
msg.prefix = "(Team) ";
msg.context = translate("Team");
}
else
msg.hide = true;
@ -523,7 +602,7 @@ function parseChatCommands(msg, playerAssignments)
if (g_Players[Engine.GetPlayerID()].team == g_Players[sender].team && sender != Engine.GetPlayerID())
msg.hide = true;
else
msg.prefix = "(Enemy) ";
msg.context = translate("Enemy");
}
recurse = true;
break;
@ -543,7 +622,7 @@ function parseChatCommands(msg, playerAssignments)
var playerName = g_Players[Engine.GetPlayerID()].name;
if (matched.length && (matched == playerName || sender == Engine.GetPlayerID()))
{
msg.prefix = "(Private) ";
msg.context = translate("Private");
msg.text = trimmed.substr(matched.length + 1);
msg.hide = false; // Might override team message hiding.
return;

View File

@ -10,6 +10,40 @@ function layoutSelectionMultiple()
Engine.GetGUIObjectByName("detailsAreaSingle").hidden = true;
}
function getLocalizedResourceName(resourceCode)
{
switch(resourceCode)
{
case "food": return translate("Food");
case "meat": return translate("Meat");
case "metal": return translate("Metal");
case "ore": return translate("Ore");
case "rock": return translate("Rock");
case "ruins": return translate("Ruins");
case "stone": return translate("Stone");
case "treasure": return translate("Treasure");
case "tree": return translate("Tree");
case "wood": return translate("Wood");
case "fruit": return translate("Fruit");
case "grain": return translate("Grain");
case "fish": return translate("Fish");
default:
warn(sprintf("Internationalization: Unexpected resource type found with code ‘%(resource)s’. This resource type must be internationalized.", { resource: resourceCode }));
return resourceCode; // It should never get here.
}
}
function getResourceTypeDisplayName(resourceType)
{
var resourceCode = resourceType["generic"];
var displayName = "";
if (resourceCode == "treasure")
displayName = getLocalizedResourceName(resourceType["specific"]);
else
displayName = getLocalizedResourceName(resourceCode);
return displayName;
}
// Fills out information that most entities have
function displaySingle(entState, template)
{
@ -18,7 +52,7 @@ function displaySingle(entState, template)
var genericName = template.name.generic != template.name.specific ? template.name.generic : "";
// If packed, add that to the generic name (reduces template clutter)
if (genericName && template.pack && template.pack.state == "packed")
genericName += " -- Packed";
genericName = sprintf(translate("%(genericName)s — Packed"), { genericName: genericName });
var playerState = g_Players[entState.player];
var civName = g_CivData[playerState.civ].Name;
@ -30,13 +64,13 @@ function displaySingle(entState, template)
// Indicate disconnected players by prefixing their name
if (g_Players[entState.player].offline)
{
playerName = "[OFFLINE] " + playerName;
playerName = sprintf(translate("[OFFLINE] %(player)s"), { player: playerName });
}
// Rank
if (entState.identity && entState.identity.rank && entState.identity.classes)
{
Engine.GetGUIObjectByName("rankIcon").tooltip = entState.identity.rank + " Rank";
Engine.GetGUIObjectByName("rankIcon").tooltip = sprintf(translate("%(rank)s Rank"), { rank: entState.identity.rank });
Engine.GetGUIObjectByName("rankIcon").sprite = getRankIconSprite(entState);
Engine.GetGUIObjectByName("rankIcon").hidden = false;
}
@ -54,8 +88,10 @@ function displaySingle(entState, template)
healthSize.rright = 100*Math.max(0, Math.min(1, entState.hitpoints / entState.maxHitpoints));
unitHealthBar.size = healthSize;
var hitpoints = Math.ceil(entState.hitpoints) + " / " + entState.maxHitpoints;
Engine.GetGUIObjectByName("healthStats").caption = hitpoints;
Engine.GetGUIObjectByName("healthStats").caption = sprintf(translate("%(hitpoints)s / %(maxHitpoints)s"), {
hitpoints: Math.ceil(entState.hitpoints),
maxHitpoints: entState.maxHitpoints
});
Engine.GetGUIObjectByName("healthSection").hidden = false;
}
else
@ -66,13 +102,9 @@ function displaySingle(entState, template)
// TODO: Stamina
var player = Engine.GetPlayerID();
if (entState.stamina && (entState.player == player || g_DevSettings.controlAll))
{
Engine.GetGUIObjectByName("staminaSection").hidden = false;
}
else
{
Engine.GetGUIObjectByName("staminaSection").hidden = true;
}
// Experience
if (entState.promotion)
@ -82,10 +114,17 @@ function displaySingle(entState, template)
experienceSize.rtop = 100 - (100 * Math.max(0, Math.min(1, 1.0 * +entState.promotion.curr / +entState.promotion.req)));
experienceBar.size = experienceSize;
var experience = "[font=\"serif-bold-13\"]Experience: [/font]" + Math.floor(entState.promotion.curr);
if (entState.promotion.curr < entState.promotion.req)
experience += " / " + entState.promotion.req;
Engine.GetGUIObjectByName("experience").tooltip = experience;
Engine.GetGUIObjectByName("experience").tooltip = sprintf(translate("%(experience)s %(current)s / %(required)s"), {
experience: "[font=\"serif-bold-13\"]" + translate("Experience:") + "[/font]",
current: Math.floor(entState.promotion.curr),
required: entState.promotion.req
});
else
Engine.GetGUIObjectByName("experience").tooltip = sprintf(translate("%(experience)s %(current)s"), {
experience: "[font=\"serif-bold-13\"]" + translate("Experience:") + "[/font]",
current: Math.floor(entState.promotion.curr)
});
Engine.GetGUIObjectByName("experience").hidden = false;
}
else
@ -96,11 +135,9 @@ function displaySingle(entState, template)
// Resource stats
if (entState.resourceSupply)
{
var resources = entState.resourceSupply.isInfinite ? "\u221E" : // Infinity symbol
Math.ceil(+entState.resourceSupply.amount) + " / " + entState.resourceSupply.max;
var resourceType = entState.resourceSupply.type["generic"];
if (resourceType == "treasure")
resourceType = entState.resourceSupply.type["specific"];
var resources = entState.resourceSupply.isInfinite ? translate("∞") : // Infinity symbol
sprintf(translate("%(amount)s / %(max)s"), { amount: Math.ceil(+entState.resourceSupply.amount), max: entState.resourceSupply.max });
var resourceType = getResourceTypeDisplayName(entState.resourceSupply.type);
var unitResourceBar = Engine.GetGUIObjectByName("resourceBar");
var resourceSize = unitResourceBar.size;
@ -108,7 +145,7 @@ function displaySingle(entState, template)
resourceSize.rright = entState.resourceSupply.isInfinite ? 100 :
100 * Math.max(0, Math.min(1, +entState.resourceSupply.amount / +entState.resourceSupply.max));
unitResourceBar.size = resourceSize;
Engine.GetGUIObjectByName("resourceLabel").caption = toTitleCase(resourceType) + ":";
Engine.GetGUIObjectByName("resourceLabel").caption = sprintf(translate("%(resource)s:"), { resource: resourceType });
Engine.GetGUIObjectByName("resourceStats").caption = resources;
if (entState.hitpoints)
@ -132,7 +169,7 @@ function displaySingle(entState, template)
Engine.GetGUIObjectByName("resourceCarryingIcon").hidden = false;
Engine.GetGUIObjectByName("resourceCarryingText").hidden = false;
Engine.GetGUIObjectByName("resourceCarryingIcon").sprite = "stretched:session/icons/resources/"+carried.type+".png";
Engine.GetGUIObjectByName("resourceCarryingText").caption = carried.amount + " / " + carried.max;
Engine.GetGUIObjectByName("resourceCarryingText").caption = sprintf(translate("%(amount)s / %(max)s"), { amount: carried.amount, max: carried.max });
Engine.GetGUIObjectByName("resourceCarryingIcon").tooltip = "";
}
// Use the same indicators for traders
@ -147,7 +184,7 @@ function displaySingle(entState, template)
if (entState.trader.goods.amount.market2Gain)
totalGain += entState.trader.goods.amount.market2Gain;
Engine.GetGUIObjectByName("resourceCarryingText").caption = totalGain;
Engine.GetGUIObjectByName("resourceCarryingIcon").tooltip = "Gain: " + getTradingTooltip(entState.trader.goods.amount);
Engine.GetGUIObjectByName("resourceCarryingIcon").tooltip = sprintf(translate("Gain: %(amount)s"), { amount: getTradingTooltip(entState.trader.goods.amount) });
}
// And for number of workers
else if (entState.foundation)
@ -156,15 +193,15 @@ function displaySingle(entState, template)
Engine.GetGUIObjectByName("resourceCarryingText").hidden = false;
Engine.GetGUIObjectByName("resourceCarryingIcon").sprite = "stretched:session/icons/repair.png";
Engine.GetGUIObjectByName("resourceCarryingText").caption = entState.foundation.numBuilders + " ";
Engine.GetGUIObjectByName("resourceCarryingIcon").tooltip = "Number of builders";
Engine.GetGUIObjectByName("resourceCarryingIcon").tooltip = translate("Number of builders");
}
else if (entState.resourceSupply && (!entState.resourceSupply.killBeforeGather || !entState.hitpoints))
{
Engine.GetGUIObjectByName("resourceCarryingIcon").hidden = false;
Engine.GetGUIObjectByName("resourceCarryingText").hidden = false;
Engine.GetGUIObjectByName("resourceCarryingIcon").sprite = "stretched:session/icons/repair.png";
Engine.GetGUIObjectByName("resourceCarryingText").caption = entState.resourceSupply.gatherers.length + " / " + entState.resourceSupply.maxGatherers + " ";
Engine.GetGUIObjectByName("resourceCarryingIcon").tooltip = "Current/max gatherers";
Engine.GetGUIObjectByName("resourceCarryingText").caption = sprintf(translate("%(amount)s / %(max)s"), { amount: entState.resourceSupply.gatherers.length, max: entState.resourceSupply.maxGatherers }) + " ";
Engine.GetGUIObjectByName("resourceCarryingIcon").tooltip = translate("Current/max gatherers");
}
else
{
@ -179,7 +216,7 @@ function displaySingle(entState, template)
if (genericName)
{
Engine.GetGUIObjectByName("generic").caption = "(" + genericName + ")";
Engine.GetGUIObjectByName("generic").caption = sprintf(translate("(%(genericName)s)"), { genericName: genericName });
}
else
{
@ -209,31 +246,78 @@ function displaySingle(entState, template)
Engine.GetGUIObjectByName("icon").sprite = "bkFillBlack";
}
var armorLabel = "[font=\"serif-bold-13\"]" + translate("Armor:") + "[/font]"
var armorString = sprintf(translate("%(label)s %(details)s"), { label: armorLabel, details: armorTypeDetails(entState.armour) });
// Attack and Armor
var type = "";
var attack = "[font=\"serif-bold-13\"]"+type+"Attack:[/font] " + damageTypeDetails(entState.attack);
if (entState.attack)
{
type = entState.attack.type + " ";
// Rate
if (entState.buildingAI)
var rateLabel = "[font=\"serif-bold-13\"]" + translate("Interval:") + "[/font]";
else
var rateLabel = "[font=\"serif-bold-13\"]" + translate("Rate:") + "[/font]";
// Show max attack range if ranged attack, also convert to tiles (4m per tile)
var rate = sprintf(translate("%(label)s %(details)s"), {
label: rateLabel,
details: attackRateDetails(entState)
});
var attack;
var label = "[font=\"serif-bold-13\"]" + getAttackTypeLabel(entState.attack.type) + "[/font]"
if (entState.attack.type == "Ranged")
{
var realRange = entState.attack.elevationAdaptedRange;
var range = entState.attack.maxRange;
attack += ", [font=\"serif-bold-13\"]Range:[/font] " + Math.round(range) +
"[font=\"sans-10\"][color=\"orange\"] meters[/color][/font]";
if (Math.round(realRange - range) > 0)
attack += " (+" + Math.round(realRange - range) + ")";
else if (Math.round(realRange - range) < 0)
attack += " (" + Math.round(realRange - range) + ")";
var rangeLabel = "[font=\"serif-bold-13\"]" + translate("Range:") + "[/font]"
var relativeRange = Math.round((realRange - range));
var meters = "[font=\"sans-10\"][color=\"orange\"]" + translate("meters") + "[/color][/font]";
if (relativeRange > 0)
attack = sprintf(translate("%(label)s %(details)s, %(rangeLabel)s %(range)s %(meters)s (%(relative)s), %(rate)s"), {
label: label,
details: damageTypeDetails(entState.attack),
rangeLabel: rangeLabel,
range: Math.round(range),
meters: meters,
relative: "+" + relativeRange,
rate: rate
});
else if (relativeRange < 0)
attack = sprintf(translate("%(label)s %(details)s, %(rangeLabel)s %(range)s %(meters)s (%(relative)s), %(rate)s"), {
label: label,
details: damageTypeDetails(entState.attack),
rangeLabel: rangeLabel,
range: Math.round(range),
meters: meters,
relative: relativeRange,
rate: rate
});
else // don't show when it's 0
attack = sprintf(translate("%(label)s %(details)s, %(rangeLabel)s %(range)s %(meters)s, %(rate)s"), {
label: label,
details: damageTypeDetails(entState.attack),
rangeLabel: rangeLabel,
range: Math.round(range),
meters: meters,
rate: rate
});
}
else
{
attack = sprintf(translate("%(label)s %(details)s, %(rate)s"), {
label: label,
details: damageTypeDetails(entState.attack),
rate: rate
});
}
attack += ", [font=\"serif-bold-13\"]" + (entState.buildingAI ? "Rate" : "Interval") + ":[/font] " + attackRateDetails(entState);
}
Engine.GetGUIObjectByName("attackAndArmorStats").tooltip = attack + "\n[font=\"serif-bold-13\"]Armor:[/font] " + armorTypeDetails(entState.armour);
Engine.GetGUIObjectByName("attackAndArmorStats").tooltip = attack + "\n" + armorString;
}
else
{
Engine.GetGUIObjectByName("attackAndArmorStats").tooltip = armorString;
}
// Icon Tooltip
var iconTooltip = "";
@ -277,7 +361,8 @@ function displayMultiple(selection, template)
healthSize.rtop = 100-100*Math.max(0, Math.min(1, averageHealth / maxHealth));
unitHealthBar.size = healthSize;
var hitpoints = "[font=\"serif-bold-13\"]Hitpoints [/font]" + averageHealth + " / " + maxHealth;
var hitpointsLabel = "[font=\"serif-bold-13\"]" + translate("Hitpoints:") + "[/font]"
var hitpoints = sprintf(translate("%(label)s %(current)s / %(max)s"), { label: hitpointsLabel, current: averageHealth, max: maxHealth });
var healthMultiple = Engine.GetGUIObjectByName("healthMultiple");
healthMultiple.tooltip = hitpoints;
healthMultiple.hidden = false;

View File

@ -16,7 +16,7 @@ var g_CivData = {};
var g_GameSpeeds = {};
var g_CurrentSpeed;
var g_PlayerAssignments = { "local": { "name": "You", "player": 1 } };
var g_PlayerAssignments = { "local": { "name": translate("You"), "player": 1 } };
// Cache dev-mode settings that are frequently or widely used
var g_DevSettings = {
@ -100,6 +100,7 @@ function GetExtendedEntityState(entId)
// Cache TemplateData
var g_TemplateData = {}; // {id:template}
var g_TemplateDataWithoutLocalization = {};
function GetTemplateData(templateName)
@ -107,12 +108,24 @@ function GetTemplateData(templateName)
if (!(templateName in g_TemplateData))
{
var template = Engine.GuiInterfaceCall("GetTemplateData", templateName);
translateObjectKeys(template, ["specific", "generic", "tooltip"]);
g_TemplateData[templateName] = template;
}
return g_TemplateData[templateName];
}
function GetTemplateDataWithoutLocalization(templateName)
{
if (!(templateName in g_TemplateDataWithoutLocalization))
{
var template = Engine.GuiInterfaceCall("GetTemplateData", templateName);
g_TemplateDataWithoutLocalization[templateName] = template;
}
return g_TemplateDataWithoutLocalization[templateName];
}
// Cache TechnologyData
var g_TechnologyData = {}; // {id:template}
@ -121,6 +134,7 @@ function GetTechnologyData(technologyName)
if (!(technologyName in g_TechnologyData))
{
var template = Engine.GuiInterfaceCall("GetTechnologyData", technologyName);
translateObjectKeys(template, ["specific", "generic", "description", "tooltip", "requirementsTooltip"]);
g_TechnologyData[technologyName] = template;
}
@ -153,7 +167,7 @@ function init(initData, hotloadData)
// Cache civ data
g_CivData = loadCivData();
g_CivData["gaia"] = { "Code": "gaia", "Name": "Gaia"};
g_CivData["gaia"] = { "Code": "gaia", "Name": translate("Gaia") };
if (Engine.GetPlayerID() <= 0)
{
@ -295,32 +309,26 @@ function leaveGame(willRejoin)
if (g_IsObserver)
{
// Observers don't win/lose.
gameResult = "You have left the game.";
gameResult = translate("You have left the game.");
global.music.setState(global.music.states.VICTORY);
}
else
{
var playerState = extendedSimState.players[Engine.GetPlayerID()];
if (g_Disconnected)
{
gameResult = "You have been disconnected."
}
gameResult = translate("You have been disconnected.");
else if (playerState.state == "won")
{
gameResult = "You have won the battle!";
}
gameResult = translate("You have won the battle!");
else if (playerState.state == "defeated")
{
gameResult = "You have been defeated...";
}
gameResult = translate("You have been defeated...");
else // "active"
{
global.music.setState(global.music.states.DEFEAT);
if (willRejoin)
gameResult = "You have left the game.";
gameResult = translate("You have left the game.");
else
{
gameResult = "You have abandoned the game.";
gameResult = translate("You have abandoned the game.");
resignGame(true);
}
}
@ -471,21 +479,21 @@ function checkPlayerState()
if (Engine.IsAtlasRunning())
{
// If we're in Atlas, we can't leave the game
var btCaptions = ["OK"];
var btCaptions = [translate("OK")];
var btCode = [null];
var message = "Press OK to continue";
var message = translate("Press OK to continue");
}
else
{
var btCaptions = ["Yes", "No"];
var btCaptions = [translate("Yes"), translate("No")];
var btCode = [leaveGame, null];
var message = "Do you want to quit?";
var message = translate("Do you want to quit?");
}
if (playerState.state == "defeated")
{
global.music.setState(global.music.states.DEFEAT);
messageBox(400, 200, message, "DEFEATED!", 0, btCaptions, btCode);
messageBox(400, 200, message, translate("DEFEATED!"), 0, btCaptions, btCode);
}
else if (playerState.state == "won")
{
@ -493,9 +501,8 @@ function checkPlayerState()
// TODO: Reveal map directly instead of this silly proxy.
if (!Engine.GetGUIObjectByName("devCommandsRevealMap").checked)
Engine.GetGUIObjectByName("devCommandsRevealMap").checked = true;
messageBox(400, 200, message, "VICTORIOUS!", 0, btCaptions, btCode);
messageBox(400, 200, message, translate("VICTORIOUS!"), 0, btCaptions, btCode);
}
}
function changeGameSpeed(speed)
@ -621,14 +628,25 @@ function updateHero()
// 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)
var healthLabel = "[font=\"serif-bold-13\"]" + translate("Health:") + "[/font]";
tooltip += "\n" + sprintf(translate("%(label)s %(current)s / %(max)s"), { label: healthLabel, current: heroState.hitpoints, max: heroState.maxHitpoints });
var attackLabel = "[font=\"serif-bold-13\"]" + getAttackTypeLabel(heroState.attack.type) + "[/font]";
if (heroState.attack && heroState.attack.type == "Ranged")
tooltip += ", [font=\"serif-bold-13\"]Range:[/font] " + Math.round(heroState.attack.maxRange/4);
// Show max attack range if ranged attack, also convert to tiles (4m per tile)
tooltip += "\n" + sprintf(
translate("%(attackLabel)s %(details)s, %(rangeLabel)s %(range)s"),
{
attackLabel: attackLabel,
details: damageTypeDetails(heroState.attack),
rangeLabel: "[font=\"serif-bold-13\"]" + translate("Range:") + "[/font]",
range: Math.round(heroState.attack.maxRange/4)
}
);
else
tooltip += "\n" + sprintf(translate("%(label)s %(details)s"), { label: attackLabel, details: damageTypeDetails(heroState.armour) });
tooltip += "\n[font=\"serif-bold-13\"]Armor:[/font] " + damageTypeDetails(heroState.armour);
var armorLabel = "[font=\"serif-bold-13\"]" + translate("Armor:") + "[/font]";
tooltip += "\n" + sprintf(translate("%(label)s %(details)s"), { label: armorLabel, details: damageTypeDetails(heroState.attack) });
tooltip += "\n" + template.tooltip;
heroButton.tooltip = tooltip;
@ -792,9 +810,11 @@ function updateResearchDisplay()
function updateTimeElapsedCounter()
{
var simState = GetSimState();
var speed = g_CurrentSpeed != 1.0 ? " (" + g_CurrentSpeed + "x)" : "";
var timeElapsedCounter = Engine.GetGUIObjectByName("timeElapsedCounter");
timeElapsedCounter.caption = timeToString(simState.timeElapsed) + speed;
if (g_CurrentSpeed != 1.0)
timeElapsedCounter.caption = sprintf(translate("%(time)s (%(speed)sx)"), { time: timeToString(simState.timeElapsed), speed: Engine.FormatDecimalNumberIntoString(g_CurrentSpeed) });
else
timeElapsedCounter.caption = timeToString(simState.timeElapsed);
}
// Toggles the display of status bars for all of the player's entities.
@ -881,7 +901,7 @@ function playRandomAmbient(type)
break;
default:
Engine.Console_Write("Unrecognized ambient type: " + type);
Engine.Console_Write(sprintf(translate("Unrecognized ambient type: %(ambientType)s"), { ambientType: type }));
break;
}
}
@ -895,6 +915,17 @@ function stopAmbient()
currentAmbient = null;
}
}
function getBuildString()
{
return sprintf(translate("Build: %(buildDate)s (%(revision)s)"), { buildDate: Engine.GetBuildTimestamp(0), revision: Engine.GetBuildTimestamp(2) });
}
function showTimeWarpMessageBox()
{
messageBox(500, 250, translate("Note: time warp mode is a developer option, and not intended for use over long periods of time. Using it incorrectly may cause the game to run out of memory or crash."), translate("Time warp mode"), 2);
}
// Send a report on the game status to the lobby
function reportGame(extendedSimState)
{

View File

@ -243,7 +243,9 @@
hidden="true" hotkey="session.devcommands.toggle">
<action on="Press">toggleDeveloperOverlay();</action>
<object size="0 0 100%-18 16" type="text" style="devCommandsText">Control all units</object>
<object size="0 0 100%-18 16" type="text" style="devCommandsText">
<translatableAttribute id="caption">Control all units</translatableAttribute>
</object>
<object size="100%-16 0 100% 16" type="checkbox" name="devControlAll" style="ModernTickBox">
<action on="Press">
g_DevSettings.controlAll = this.checked;
@ -251,40 +253,56 @@
</action>
</object>
<object size="0 16 100%-18 32" type="text" style="devCommandsText">Change perspective</object>
<object size="0 16 100%-18 32" type="text" style="devCommandsText">
<translatableAttribute id="caption">Change perspective</translatableAttribute>
</object>
<object size="100%-16 16 100% 32" type="checkbox" style="ModernTickBox">
<action on="Press">Engine.GetGUIObjectByName("viewPlayer").hidden = !this.checked;</action>
</object>
<object size="0 32 100%-18 48" type="text" style="devCommandsText">Display selection state</object>
<object size="0 32 100%-18 48" type="text" style="devCommandsText">
<translatableAttribute id="caption">Display selection state</translatableAttribute>
</object>
<object size="100%-16 32 100% 48" type="checkbox" name="devDisplayState" style="ModernTickBox"/>
<object size="0 48 100%-18 64" type="text" style="devCommandsText">Pathfinder overlay</object>
<object size="0 48 100%-18 64" type="text" style="devCommandsText">
<translatableAttribute id="caption">Pathfinder overlay</translatableAttribute>
</object>
<object size="100%-16 48 100% 64" type="checkbox" style="ModernTickBox">
<action on="Press">Engine.GuiInterfaceCall("SetPathfinderDebugOverlay", this.checked);</action>
</object>
<object size="0 64 100%-18 80" type="text" style="devCommandsText">Obstruction overlay</object>
<object size="0 64 100%-18 80" type="text" style="devCommandsText">
<translatableAttribute id="caption">Obstruction overlay</translatableAttribute>
</object>
<object size="100%-16 64 100% 80" type="checkbox" style="ModernTickBox">
<action on="Press">Engine.GuiInterfaceCall("SetObstructionDebugOverlay", this.checked);</action>
</object>
<object size="0 80 100%-18 96" type="text" style="devCommandsText">Unit motion overlay</object>
<object size="0 80 100%-18 96" type="text" style="devCommandsText">
<translatableAttribute id="caption">Unit motion overlay</translatableAttribute>
</object>
<object size="100%-16 80 100% 96" type="checkbox" style="ModernTickBox">
<action on="Press">g_Selection.SetMotionDebugOverlay(this.checked);</action>
</object>
<object size="0 96 100%-18 112" type="text" style="devCommandsText">Range overlay</object>
<object size="0 96 100%-18 112" type="text" style="devCommandsText">
<translatableAttribute id="caption">Range overlay</translatableAttribute>
</object>
<object size="100%-16 96 100% 112" type="checkbox" style="ModernTickBox">
<action on="Press">Engine.GuiInterfaceCall("SetRangeDebugOverlay", this.checked);</action>
</object>
<object size="0 112 100%-18 128" type="text" style="devCommandsText">Bounding box overlay</object>
<object size="0 112 100%-18 128" type="text" style="devCommandsText">
<translatableAttribute id="caption">Bounding box overlay</translatableAttribute>
</object>
<object size="100%-16 112 100% 128" type="checkbox" style="ModernTickBox">
<action on="Press">Engine.SetBoundingBoxDebugOverlay(this.checked);</action>
</object>
<object size="0 128 100%-18 144" type="text" style="devCommandsText">Restrict camera</object>
<object size="0 128 100%-18 144" type="text" style="devCommandsText">
<translatableAttribute id="caption">Restrict camera</translatableAttribute>
</object>
<object size="100%-16 128 100% 144" type="checkbox" style="ModernTickBox" checked="true">
<action on="Press">
Engine.GameView_SetConstrainCameraEnabled(this.checked);
@ -296,21 +314,27 @@
</action>
</object>
<object size="0 144 100%-18 160" type="text" style="devCommandsText">Reveal map</object>
<object size="0 144 100%-18 160" type="text" style="devCommandsText">
<translatableAttribute id="caption">Reveal map</translatableAttribute>
</object>
<object size="100%-16 144 100% 160" type="checkbox" name="devCommandsRevealMap" style="ModernTickBox">
<action on="Load">this.checked = Engine.GuiInterfaceCall("IsMapRevealed");</action>
<action on="Press">Engine.PostNetworkCommand({"type": "reveal-map", "enable": this.checked});</action>
</object>
<object size="0 160 100%-18 176" type="text" style="devCommandsText">Enable time warp</object>
<object size="0 160 100%-18 176" type="text" style="devCommandsText">
<translatableAttribute id="caption">Enable time warp</translatableAttribute>
</object>
<object size="100%-16 160 100% 176" type="checkbox" name="devTimeWarp" style="ModernTickBox">
<action on="Press">
if (this.checked)
messageBox(500, 250, "Note: time warp mode is a developer option, and not intended\nfor use over long periods of time. Using it incorrectly may\ncause the game to run out of memory or crash.", "Time warp mode", 2);
showTimeWarpMessageBox();
Engine.EnableTimeWarpRecording(this.checked ? 10 : 0);</action>
</object>
<object size="0 176 100%-18 192" type="text" style="devCommandsText">Promote selected units</object>
<object size="0 176 100%-18 192" type="text" style="devCommandsText">
<translatableAttribute id="caption">Promote selected units</translatableAttribute>
</object>
<object size="100%-16 176 100% 192" type="button" style="ModernTickBox">
<action on="Press">Engine.PostNetworkCommand({"type": "promote", "entities": g_Selection.toList()});</action>
</object>
@ -335,8 +359,12 @@
z="0"
>
<object size="0 0 100% 100%" type="image" sprite="devCommandsBackground" ghost="true" z="0"/>
<object size="50%-128 50%-20 50%+128 50%+20" type="text" style="PauseText" ghost="true" z="0">Game Paused</object>
<object size="50%-128 50%+20 50%+128 50%+30" type="text" style="PauseMessageText" ghost="true" z="0">Click to Resume Game</object>
<object size="50%-128 50%-20 50%+128 50%+20" type="text" style="PauseText" ghost="true" z="0">
<translatableAttribute id="caption">Game Paused</translatableAttribute>
</object>
<object size="50%-128 50%+20 50%+128 50%+30" type="text" style="PauseMessageText" ghost="true" z="0">
<translatableAttribute id="caption">Click to Resume Game</translatableAttribute>
</object>
<action on="Press">togglePause();</action>
</object>
@ -367,13 +395,13 @@
</object>
<object size="16 100%-40 30%+16 100%-12" type="button" style="StoneButton">
Send
<action on="Press">submitChatInput();</action>
<translatableAttribute id="caption">Send</translatableAttribute>
<action on="Press">submitChatInput();</action>
</object>
<object size="30%+24 100%-40 60%+24 100%-12" type="button" style="StoneButton">
Cancel
<action on="Press">closeChat();</action>
<translatableAttribute id="caption">Cancel</translatableAttribute>
<action on="Press">closeChat();</action>
</object>
<object name="toggleTeamChat" size="60%+32 100%-34 60%+48 100%-6" type="checkbox" style="ModernTickBox"/>
<object size="60%+48 100%-40 100% 100%-12" type="text" style="ModernLeftLabelText">
@ -390,17 +418,38 @@
hidden="true"
sprite="ModernDialog"
>
<object type="text" style="TitleText" size="50%-96 -16 50%+96 16">Diplomacy</object>
<object type="text" style="TitleText" size="50%-96 -16 50%+96 16">
<translatableAttribute id="caption">Diplomacy</translatableAttribute>
</object>
<object name="diplomacyHeader" size="32 32 100%-32 64">
<object name="diplomacyHeaderName" size="0 0 150 100%" type="text" style="chatPanel" ghost="true" caption="Name"/>
<object name="diplomacyHeaderCiv" size="150 0 250 100%" type="text" style="chatPanel" ghost="true" caption="Civilization"/>
<object name="diplomacyHeaderTeam" size="250 0 300 100%" type="text" style="chatPanel" ghost="true" caption="Team"/>
<object name="diplomacyHeaderTheirs" size="300 0 360 100%" type="text" style="chatPanel" ghost="true" caption="Theirs"/>
<object name="diplomacyHeaderAlly" size="100%-180 0 100%-160 100%" type="text" style="chatPanel" caption="A" tooltip="Ally" tooltip_style="sessionToolTipBold"/>
<object name="diplomacyHeaderNeutral" size="100%-160 0 100%-140 100%" type="text" style="chatPanel" caption="N" tooltip="Neutral" tooltip_style="sessionToolTipBold"/>
<object name="diplomacyHeaderEnemy" size="100%-140 0 100%-120 100%" type="text" style="chatPanel" caption="E" tooltip="Enemy" tooltip_style="sessionToolTipBold"/>
<object name="diplomacyHeaderTribute" size="100%-110 0 100% 100%" type="text" style="chatPanel" caption="Tribute"/>
<object name="diplomacyHeaderName" size="0 0 150 100%" type="text" style="chatPanel" ghost="true">
<translatableAttribute id="caption">Name</translatableAttribute>
</object>
<object name="diplomacyHeaderCiv" size="150 0 250 100%" type="text" style="chatPanel" ghost="true">
<translatableAttribute id="caption">Civilization</translatableAttribute>
</object>
<object name="diplomacyHeaderTeam" size="250 0 300 100%" type="text" style="chatPanel" ghost="true">
<translatableAttribute id="caption">Team</translatableAttribute>
</object>
<object name="diplomacyHeaderTheirs" size="300 0 360 100%" type="text" style="chatPanel" ghost="true">
<translatableAttribute id="caption">Theirs</translatableAttribute>
</object>
<object name="diplomacyHeaderAlly" size="100%-180 0 100%-160 100%" type="text" style="chatPanel" tooltip_style="sessionToolTipBold">
<translatableAttribute id="caption">A</translatableAttribute>
<translatableAttribute id="tooltip">Ally</translatableAttribute>
</object>
<object name="diplomacyHeaderNeutral" size="100%-160 0 100%-140 100%" type="text" style="chatPanel" tooltip_style="sessionToolTipBold">
<translatableAttribute id="caption">N</translatableAttribute>
<translatableAttribute id="tooltip">Neutral</translatableAttribute>
</object>
<object name="diplomacyHeaderEnemy" size="100%-140 0 100%-120 100%" type="text" style="chatPanel" tooltip_style="sessionToolTipBold">
<translatableAttribute id="caption">E</translatableAttribute>
<translatableAttribute id="tooltip">Enemy</translatableAttribute>
</object>
<object name="diplomacyHeaderTribute" size="100%-110 0 100% 100%" type="text" style="chatPanel">
<translatableAttribute id="caption">Tribute</translatableAttribute>
</object>
</object>
<object size="32 64 100%-32 384">
@ -448,11 +497,15 @@
hidden="true"
sprite="ModernDialog"
>
<object type="text" style="TitleText" size="50%-96 -16 50%+96 16">Trade</object>
<object type="text" style="TitleText" size="50%-96 -16 50%+96 16">
<translatableAttribute id="caption">Trade</translatableAttribute>
</object>
<!-- Trading goods -->
<object name="tradeGoods" size="20 50 100%-20 82">
<object name="tradeHeader" size="0 0 180 100%" type="text" style="ModernLabelText" text_align="left" ghost="true" caption="Trading goods selection:"/>
<object name="tradeHeader" size="0 0 180 100%" type="text" style="ModernLabelText" text_align="left" ghost="true">
<translatableAttribute id="caption">Trading goods selection:</translatableAttribute>
</object>
<object size="180 0 100% 100%">
<repeat count="4">
@ -470,21 +523,20 @@
</object>
</object>
</repeat>
<object name="tradeHelp" size="100%-24 4 100% 28" enabled="false" type="button" style="StoneButton" tooltip_style="sessionToolTipBold" tooltip="Select one goods as origin of the changes, \nthen use the arrows of the target goods to make the changes.">
<object name="tradeHelp" size="100%-24 4 100% 28" enabled="false" type="button" style="StoneButton" tooltip_style="sessionToolTipBold">
<translatableAttribute id="tooltip">Select one goods as origin of the changes,\nthen use the arrows of the target goods to make the changes.</translatableAttribute>
<object size="20% 15% 80% 75%" type="image" ghost="true" sprite="iconInfoWhite"/>
</object>
</object>
</object>
<object name="tradeStatistics" size="20 120 100%-20 168">
<object size="0 0 130 50%" type="text" style="ModernLabelText" text_align="left" ghost="true" caption="Traders:"/>
<object name="landTraders" size="130 0 100% 50%" type="text" style="ModernLabelText" text_align="left" ghost="true" />
<object size="0 50% 130 100%" type="text" style="ModernLabelText" text_align="left" ghost="true" caption = "Merchant ships:"/>
<object name="shipTraders" size="130 50% 100% 100%" type="text" style="ModernLabelText" text_align="left" ghost="true" />
<object name="tradeStatistics" size="20 90 100%-20 168">
<object name="landTraders" size="0 0 100% 50%" type="text" style="ModernLabelText" text_align="left" ghost="true" />
<object name="shipTraders" size="0 50% 100% 100%" type="text" style="ModernLabelText" text_align="left" ghost="true" />
</object>
<object size="50%-64 100%-50 50%+64 100%-22" type="button" style="StoneButton">
Close
<translatableAttribute id="caption">Close</translatableAttribute>
<action on="Press">closeTrade();</action>
</object>
</object>
@ -499,97 +551,125 @@
hidden="true"
z="200"
>
<object type="text" style="TitleText" size="50%-96 -16 50%+96 16">Settings</object>
<object type="text" style="TitleText" size="50%-96 -16 50%+96 16">
<translatableAttribute id="caption">Settings</translatableAttribute>
</object>
<object style="TranslucentPanelThinBorder"
type="image"
size="32 32 100%-32 100%-70"
>
<!-- Settings / shadows -->
<object size="0 10 100%-80 35" type="text" style="ModernRightLabelText" ghost="true">Enable Shadows</object>
<object size="0 10 100%-80 35" type="text" style="ModernRightLabelText" ghost="true">
<translatableAttribute id="caption">Enable Shadows</translatableAttribute>
</object>
<object name="shadowsCheckbox" size="100%-56 15 100%-30 40" type="checkbox" style="ModernTickBox" checked="true">
<action on="Load">this.checked = Engine.Renderer_GetShadowsEnabled();</action>
<action on="Press">Engine.Renderer_SetShadowsEnabled(this.checked);</action>
</object>
<!-- Settings / Shadow PCF -->
<object size="0 35 100%-80 60" type="text" style="ModernRightLabelText" ghost="true">Enable Shadow Filtering</object>
<object size="0 35 100%-80 60" type="text" style="ModernRightLabelText" ghost="true">
<translatableAttribute id="caption">Enable Shadow Filtering</translatableAttribute>
</object>
<object name="shadowPCFCheckbox" size="100%-56 40 100%-30 65" type="checkbox" style="ModernTickBox" checked="true">
<action on="Load">this.checked = Engine.Renderer_GetShadowPCFEnabled();</action>
<action on="Press">Engine.Renderer_SetShadowPCFEnabled(this.checked);</action>
</object>
<!-- Settings / Water Normals -->
<object size="0 60 100%-80 85" type="text" style="ModernRightLabelText" ghost="true">Water - HQ Waviness</object>
<object size="0 60 100%-80 85" type="text" style="ModernRightLabelText" ghost="true">
<translatableAttribute id="caption">Water - HQ Waviness</translatableAttribute>
</object>
<object name="waterNormalCheckox" size="100%-56 65 100%-30 90" type="checkbox" style="ModernTickBox" checked="true">
<action on="Load">this.checked = Engine.Renderer_GetWaterNormalEnabled();</action>
<action on="Press">Engine.Renderer_SetWaterNormalEnabled(this.checked);</action>
</object>
<!-- Settings / Real Depth -->
<object size="0 85 100%-80 110" type="text" style="ModernRightLabelText" ghost="true">Water - Use Actual Depth</object>
<object size="0 85 100%-80 110" type="text" style="ModernRightLabelText" ghost="true">
<translatableAttribute id="caption">Water - Use Actual Depth</translatableAttribute>
</object>
<object name="waterRealDepthCheckbox" size="100%-56 90 100%-30 115" type="checkbox" style="ModernTickBox" checked="true">
<action on="Load">this.checked = Engine.Renderer_GetWaterRealDepthEnabled();</action>
<action on="Press">Engine.Renderer_SetWaterRealDepthEnabled(this.checked);</action>
</object>
<!-- Settings / Reflection -->
<object size="0 110 100%-80 135" type="text" style="ModernRightLabelText" ghost="true">Water - Enable Reflections</object>
<object size="0 110 100%-80 135" type="text" style="ModernRightLabelText" ghost="true">
<translatableAttribute id="caption">Water - Enable Reflections</translatableAttribute>
</object>
<object name="waterReflectionCheckbox" size="100%-56 115 100%-30 140" type="checkbox" style="ModernTickBox" checked="true">
<action on="Load">this.checked = Engine.Renderer_GetWaterReflectionEnabled();</action>
<action on="Press">Engine.Renderer_SetWaterReflectionEnabled(this.checked);</action>
</object>
<!-- Settings / Refraction -->
<object size="0 135 100%-80 160" type="text" style="ModernRightLabelText" ghost="true">Water - Enable Refraction</object>
<object size="0 135 100%-80 160" type="text" style="ModernRightLabelText" ghost="true">
<translatableAttribute id="caption">Water - Enable Refraction</translatableAttribute>
</object>
<object name="waterRefractionCheckbox" size="100%-56 140 100%-30 165" type="checkbox" style="ModernTickBox" checked="true">
<action on="Load">this.checked = Engine.Renderer_GetWaterRefractionEnabled();</action>
<action on="Press">Engine.Renderer_SetWaterRefractionEnabled(this.checked);</action>
</object>
<!-- Settings / Foam -->
<object size="0 160 100%-80 185" type="text" style="ModernRightLabelText" ghost="true">Water - Enable Shore Foam</object>
<object size="0 160 100%-80 185" type="text" style="ModernRightLabelText" ghost="true">
<translatableAttribute id="caption">Water - Enable Shore Foam</translatableAttribute>
</object>
<object name="waterFoamCheckbox" size="100%-56 165 100%-30 190" type="checkbox" style="ModernTickBox" checked="true">
<action on="Load">this.checked = Engine.Renderer_GetWaterFoamEnabled();</action>
<action on="Press">Engine.Renderer_SetWaterFoamEnabled(this.checked);</action>
</object>
<!-- Settings / Waves -->
<object size="0 185 100%-80 210" type="text" style="ModernRightLabelText" ghost="true">Water - Enable Shore Waves</object>
<object size="0 185 100%-80 210" type="text" style="ModernRightLabelText" ghost="true">
<translatableAttribute id="caption">Water - Enable Shore Waves</translatableAttribute>
</object>
<object name="waterCoastalWavesCheckbox" size="100%-56 190 100%-30 215" type="checkbox" style="ModernTickBox" checked="true">
<action on="Load">this.checked = Engine.Renderer_GetWaterCoastalWavesEnabled();</action>
<action on="Press">Engine.Renderer_SetWaterCoastalWavesEnabled(this.checked);</action>
</object>
<!-- Settings / Shadows -->
<object size="0 210 100%-80 235" type="text" style="ModernRightLabelText" ghost="true">Water - Use Surface Shadows</object>
<object size="0 210 100%-80 235" type="text" style="ModernRightLabelText" ghost="true">
<translatableAttribute id="caption">Water - Use Surface Shadows</translatableAttribute>
</object>
<object name="waterShadowsCheckbox" size="100%-56 215 100%-30 240" type="checkbox" style="ModernTickBox" checked="true">
<action on="Load">if (Engine.Renderer_GetWaterShadowEnabled()) this.checked = true; else this.checked = false;</action>
<action on="Press">Engine.Renderer_SetWaterShadowEnabled(this.checked);</action>
</object>
<!-- Settings / Particles -->
<object size="0 235 100%-80 260" type="text" style="ModernRightLabelText" ghost="true">Enable Particles</object>
<object size="0 235 100%-80 260" type="text" style="ModernRightLabelText" ghost="true">
<translatableAttribute id="caption">Enable Particles</translatableAttribute>
</object>
<object name="particlesCheckbox" size="100%-56 240 100%-30 265" type="checkbox" style="ModernTickBox" checked="true">
<action on="Load">this.checked = Engine.Renderer_GetParticlesEnabled();</action>
<action on="Press">Engine.Renderer_SetParticlesEnabled(this.checked);</action>
</object>
<!-- Settings / Unit Silhouettes -->
<object size="0 260 100%-80 285" type="text" style="ModernRightLabelText" ghost="true">Enable Unit Silhouettes</object>
<object size="0 260 100%-80 285" type="text" style="ModernRightLabelText" ghost="true">
<translatableAttribute id="caption">Enable Unit Silhouettes</translatableAttribute>
</object>
<object name="silhouettesCheckbox" size="100%-56 265 100%-30 290" type="checkbox" style="ModernTickBox" checked="true">
<action on="Load">this.checked = Engine.Renderer_GetSilhouettesEnabled();</action>
<action on="Press">Engine.Renderer_SetSilhouettesEnabled(this.checked);</action>
</object>
<!-- Settings / Music-->
<object size="0 285 100%-80 310" type="text" style="ModernRightLabelText" ghost="true">Enable Music</object>
<object size="0 285 100%-80 310" type="text" style="ModernRightLabelText" ghost="true">
<translatableAttribute id="caption">Enable Music</translatableAttribute>
</object>
<object name="musicCheckbox" size="100%-56 290 100%-30 315" type="checkbox" style="ModernTickBox" checked="true">
<action on="Press">if (this.checked) global.music.start(); else global.music.stop();</action>
</object>
<!-- Settings / Dev Overlay -->
<object size="0 310 100%-80 335" type="text" style="ModernRightLabelText" ghost="true">Developer Overlay</object>
<object size="0 310 100%-80 335" type="text" style="ModernRightLabelText" ghost="true">
<translatableAttribute id="caption">Developer Overlay</translatableAttribute>
</object>
<object name="developerOverlayCheckbox" size="100%-56 315 100%-30 340" type="checkbox" style="ModernTickBox" checked="false">
<action on="Load"><![CDATA[
if (Engine.IsRankedGame())
@ -625,31 +705,36 @@
size="10 0 45% 100%"
>
<!-- Food -->
<object name="food" size="0 0 90 100%" type="image" style="resourceCounter" tooltip="Food" tooltip_style="sessionToolTipBold">
<object name="food" size="0 0 90 100%" type="image" style="resourceCounter" tooltip_style="sessionToolTipBold">
<translatableAttribute id="tooltip">Food</translatableAttribute>
<object size="0 -4 40 36" type="image" sprite="stretched:session/icons/resources/food.png" ghost="true"/>
<object size="32 0 100% 100%-2" type="text" style="resourceText" name="resourceFood"/>
</object>
<!-- Wood -->
<object name="wood" size="90 0 180 100%" type="image" style="resourceCounter" tooltip="Wood" tooltip_style="sessionToolTipBold">
<object name="wood" size="90 0 180 100%" type="image" style="resourceCounter" tooltip_style="sessionToolTipBold">
<translatableAttribute id="tooltip">Wood</translatableAttribute>
<object size="0 -4 40 36" type="image" sprite="stretched:session/icons/resources/wood.png" ghost="true"/>
<object size="32 0 100% 100%-2" type="text" style="resourceText" name="resourceWood"/>
</object>
<!-- Stone -->
<object name="stone" size="180 0 270 100%" type="image" style="resourceCounter" tooltip="Stone" tooltip_style="sessionToolTipBold">
<object name="stone" size="180 0 270 100%" type="image" style="resourceCounter" tooltip_style="sessionToolTipBold">
<translatableAttribute id="tooltip">Stone</translatableAttribute>
<object size="0 -4 40 36" type="image" sprite="stretched:session/icons/resources/stone.png" ghost="true"/>
<object size="32 0 100% 100%-2" type="text" style="resourceText" name="resourceStone"/>
</object>
<!-- Metal -->
<object name="metal" size="270 0 360 100%" type="image" style="resourceCounter" tooltip="Metal" tooltip_style="sessionToolTipBold">
<object name="metal" size="270 0 360 100%" type="image" style="resourceCounter" tooltip_style="sessionToolTipBold">
<translatableAttribute id="tooltip">Metal</translatableAttribute>
<object size="0 -4 40 36" type="image" sprite="stretched:session/icons/resources/metal.png" ghost="true"/>
<object size="32 0 100% 100%-2" type="text" style="resourceText" name="resourceMetal"/>
</object>
<!-- Population -->
<object name="population" size="360 0 450 100%" type="image" style="resourceCounter" tooltip="Population (current / limit)" tooltip_style="sessionToolTipBold">
<object name="population" size="360 0 450 100%" type="image" style="resourceCounter" tooltip_style="sessionToolTipBold">
<translatableAttribute id="tooltip">Population (current / limit)</translatableAttribute>
<object size="0 -4 40 34" type="image" sprite="stretched:session/icons/resources/population.png" ghost="true"/>
<object size="32 0 100% 100%-2" type="text" style="resourceText" name="resourcePop"/>
</object>
@ -661,8 +746,9 @@
<object size="50%-48 -26 50%+48 70" name="civIcon" type="image" tooltip_style="sessionToolTipBold"/>
<!-- Switch the view perspective to another player's (largely for AI development) -->
<object size="50%+50 5 50%+150 100%-5" name="viewPlayer" type="dropdown" hidden="true" style="ModernDropDown" tooltip_style="sessionToolTipBold" tooltip="Choose player to view">
<action on="SelectionChange">selectViewPlayer(this.selected);</action>
<object size="50%+50 5 50%+150 100%-5" name="viewPlayer" type="dropdown" hidden="true" style="ModernDropDown" tooltip_style="sessionToolTipBold">
<translatableAttribute id="tooltip">Choose player to view</translatableAttribute>
<action on="SelectionChange">selectViewPlayer(this.selected);</action>
</object>
<!-- ================================ ================================ -->
@ -683,12 +769,13 @@
<!-- Displays Alpha name and number -->
<object size="50%+48 0 100%-226 100%" name="alphaLabel" type="text" style="ModernLabelText" text_valign="top" ghost="true">
ALPHA XV : Osiris<!-- IMPORTANT: remember to update pregame/mainmenu.xml in sync with this -->
<!-- IMPORTANT: remember to update pregame/mainmenu.xml in sync with this: -->
<translatableAttribute id="caption">ALPHA XV : Osiris</translatableAttribute>
<!-- Displays build date and revision number-->
<object size="50%-128 0 50%+128 100%-2" name="Engine.BuildTimeLabel" type="text" style="BuildNameText" ghost="true">
<action on="Load">this.caption = Engine.BuildTime(0) + " (" + Engine.BuildTime(2) + ")"</action>
</object>
<!-- Displays build date and revision number-->
<object size="50%-128 0 50%+128 100%-2" name="buildTimeLabel" type="text" style="BuildNameText" ghost="true">
<action on="Load">this.caption = getBuildString()</action>
</object>
</object>
<!-- ================================ ================================ -->
@ -699,15 +786,17 @@
size="100%-258 4 100%-230 32"
style="iconButton"
tooltip_style="sessionToolTip"
tooltip="Game speed"
>
>
<translatableAttribute id="tooltip">Game speed</translatableAttribute>
<object size="5 5 100%-5 100%-5" type="image" sprite="stretched:session/icons/resources/time_small.png" ghost="true"/>
<action on="Press">
toggleGameSpeed();
</action>
</object>
<object size="100%-380 40 100%-230 65" name="gameSpeed" type="dropdown" buffer_zone="5" style="ModernDropDown" hidden="true" tooltip="Choose game speed" tooltip_style="sessionToolTip"/>
<object size="100%-380 40 100%-230 65" name="gameSpeed" type="dropdown" buffer_zone="5" style="ModernDropDown" hidden="true" tooltip_style="sessionToolTip">
<translatableAttribute id="tooltip">Choose game speed</translatableAttribute>
</object>
<!-- ================================ ================================ -->
<!-- Diplomacy Button -->
@ -717,8 +806,8 @@
size="100%-226 4 100%-198 32"
style="iconButton"
tooltip_style="sessionToolTip"
tooltip="Diplomacy"
>
>
<translatableAttribute id="tooltip">Diplomacy</translatableAttribute>
<!-- TODO make the button less ugly -->
<object size="0 0 100% 100%" name="diplomacyButtonImage" type="image" sprite="stretched:session/icons/diplomacy.png" ghost="true"/>
<action on="Press">
@ -734,13 +823,13 @@
size="100%-194 4 100%-166 32"
style="iconButton"
tooltip_style="sessionToolTip"
tooltip="Trade"
>
<!-- TODO make the button less ugly -->
<object size="0 0 100% 100%" name="tradeButtonImage" type="image" sprite="stretched:session/icons/economics.png" ghost="true"/>
<action on="Press">
toggleTrade();
</action>
<!-- TODO make the button less ugly -->
<object size="0 0 100% 100%" name="tradeButtonImage" type="image" sprite="stretched:session/icons/economics.png" ghost="true"/>
<translatableAttribute id="tooltip">Trade</translatableAttribute>
<action on="Press">
toggleTrade();
</action>
</object>
<!-- ================================ ================================ -->
@ -758,7 +847,9 @@
them on top of the main menu button -->
<object size="0 -4 100% 0" type="image" sprite="horizontalThinBorder" ghost="true"/>
<object size="50%-32 50%-16 50%+32 50%+16" type="image" sprite="menuButton" ghost="true">MENU</object>
<object size="50%-32 50%-16 50%+32 50%+16" type="image" sprite="menuButton" ghost="true">
<translatableAttribute id="caption">MENU</translatableAttribute>
</object>
<action on="Press">
toggleMenu();
</action>
@ -783,7 +874,7 @@
size="0 0 100% 28"
tooltip_style="sessionToolTip"
>
Manual
<translatableAttribute id="caption">Manual</translatableAttribute>
<action on="Press">openManual();</action>
</object>
@ -794,7 +885,7 @@
size="0 32 100% 60"
tooltip_style="sessionToolTip"
>
Chat
<translatableAttribute id="caption">Chat</translatableAttribute>
<action on="Press">chatMenuButton();</action>
</object>
@ -805,7 +896,7 @@
size="0 64 100% 92"
tooltip_style="sessionToolTip"
>
Save
<translatableAttribute id="caption">Save</translatableAttribute>
<action on="Press">
openSave();
</action>
@ -818,7 +909,7 @@
size="0 96 100% 124"
tooltip_style="sessionToolTip"
>
Settings
<translatableAttribute id="caption">Settings</translatableAttribute>
<action on="Press">settingsMenuButton();</action>
</object>
@ -829,7 +920,9 @@
size="0 128 100% 156"
tooltip_style="sessionToolTip"
>
<object name="pauseButtonText" type="text" style="CenteredButtonText" ghost="true">Pause</object>
<object name="pauseButtonText" type="text" style="CenteredButtonText" ghost="true">
<translatableAttribute id="caption">Pause</translatableAttribute>
</object>
<action on="Press">togglePause();</action>
</object>
@ -840,7 +933,7 @@
size="0 160 100% 188"
tooltip_style="sessionToolTip"
>
Resign
<translatableAttribute id="caption">Resign</translatableAttribute>
<action on="Press">resignMenuButton();</action>
</object>
@ -851,7 +944,7 @@
size="0 192 100% 220"
tooltip_style="sessionToolTip"
>
Exit
<translatableAttribute id="caption">Exit</translatableAttribute>
<action on="Press">exitMenuButton();</action>
</object>
</object>
@ -870,14 +963,14 @@
<!-- ================================ ================================ -->
<!-- Hero Selection -->
<!-- ================================ ================================ -->
<object
<object
name="unitHeroPanel"
size="0 36 50 93"
hidden="true"
>
<object name="unitHeroButton" size="0 0 50 50" type="button" style="iconButton"
tooltip_style="sessionToolTip" tooltip="Attack and Armor"
>
tooltip_style="sessionToolTip">
<translatableAttribute id="tooltip">Attack and Armor</translatableAttribute>
<object name="unitHeroImage" size="5 5 100%-5 100%-5" type="image" ghost="true"/>
<object name="heroHitOverlay" hidden="true" type="image" ghost="true" size="5 5 100%-5 100%-5"/>
</object>
@ -900,9 +993,8 @@
size="0% 50%-216 0%+36 50%+144"
>
<repeat count="10">
<object name="unitGroupButton[n]" size="0 0 36 36" type="button" hidden="false" style="iconButton" tooltip_style="sessionToolTipBottomBold"
tooltip="Click to select grouped units, double-click to focus the grouped units and right-click to disband the group."
>
<object name="unitGroupButton[n]" size="0 0 36 36" type="button" hidden="false" style="iconButton" tooltip_style="sessionToolTipBottomBold">
<translatableAttribute id="tooltip">Click to select grouped units, double-click to focus the grouped units and right-click to disband the group.</translatableAttribute>
<object name="unitGroupIcon[n]" size="3 3 33 33" type="image" sprite="groupsIcon" ghost="true"/>
<object name="unitGroupLabel[n]" type="text" style="largeCenteredOutlinedText" ghost="true"/>
</object>
@ -939,10 +1031,10 @@
<object size="100%-80 100%-80 100%-5 100%-5">
<!-- TODO: We should disable this button if there are no idle workers. -->
<object type="button"
tooltip="Find idle worker"
tooltip_style="sessionToolTip"
hotkey="selection.idleworker"
>
<translatableAttribute id="tooltip">Find idle worker</translatableAttribute>
<action on="Press">findIdleUnit(["Female", "Trade", "FishingBoat", "CitizenSoldier", "Healer"]);</action>
<action on="MouseEnter">Engine.GetGUIObjectByName("idleOverlay").sprite = "stretched:session/minimap-idle-highlight.png";</action>
<action on="MouseLeave">Engine.GetGUIObjectByName("idleOverlay").sprite = "stretched:session/minimap-idle.png";</action>
@ -993,7 +1085,9 @@
size="6 36 100% 100%"
hidden="true"
>
<object ghost="true" style="resourceText" type="text" size="0 0 100% 20">Exchange resources:</object>
<object ghost="true" style="resourceText" type="text" size="0 0 100% 20">
<translatableAttribute id="tooltip">Exchange resources:</translatableAttribute>
</object>
<object size="0 32 100% 78">
<repeat count="4">
<object name="unitBarterSellButton[n]" style="iconButton" type="button" size="0 0 46 46" tooltip_style="sessionToolTipBottomBold">
@ -1050,7 +1144,9 @@
<object size="0 8 100% 60" type="image" sprite="edgedPanelShader">
<!-- Health bar -->
<object size="88 0 100% 24" name="healthSection">
<object size="0 0 100% 16" name="healthLabel" type="text" style="StatsTextLeft" ghost="true">Health:</object>
<object size="0 0 100% 16" name="healthLabel" type="text" style="StatsTextLeft" ghost="true">
<translatableAttribute id="tooltip">Health:</translatableAttribute>
</object>
<object size="0 0 100% 16" name="healthStats" type="text" style="StatsTextRight" ghost="true"/>
<object size="1 16 100% 23" name="health" type="image">
<object type="image" sprite="barBorder" ghost="true" size="-1 -1 100%+1 100%+1"/>
@ -1062,7 +1158,9 @@
<!-- Stamina bar -->
<object size="88 28 100% 52" name="staminaSection">
<object size="0 0 100% 16" name="staminaLabel" type="text" style="StatsTextLeft" ghost="true">Stamina:</object>
<object size="0 0 100% 16" name="staminaLabel" type="text" style="StatsTextLeft" ghost="true">
<translatableAttribute id="tooltip">Stamina:</translatableAttribute>
</object>
<object size="0 0 100% 16" name="staminaStats" type="text" style="StatsTextRight" ghost="true"/>
<object size="1 16 100% 23" name="stamina" type="image">
<object type="image" sprite="barBorder" ghost="true" size="-1 -1 100%+1 100%+1"/>
@ -1087,7 +1185,9 @@
<object size="0 60 100% 96" type="image" sprite="edgedPanelShader">
<!-- Attack and Armor -->
<object size="90 -2 126 34" name="attackAndArmorStats" type="image" sprite="stretched:session/icons/stances/defensive.png" tooltip="Attack and Armor" tooltip_style="sessionToolTip"/>
<object size="90 -2 126 34" name="attackAndArmorStats" type="image" sprite="stretched:session/icons/stances/defensive.png" tooltip_style="sessionToolTip">
<translatableAttribute id="tooltip">Attack and Armor</translatableAttribute>
</object>
<!-- Resource carrying icon/counter -->
<!-- Used also for number of gatherers/builders -->
@ -1100,14 +1200,17 @@
<object size="1 1 100%-1 100%-1" type="image" name="icon" ghost="true"/>
<!-- Experience bar -->
<object size="2 2 6 100%-2" type="image" name="experience" tooltip="Experience" tooltip_style="sessionToolTip">
<object type="image" sprite="barBorder" ghost="true" size="-1 -1 100%+1 100%+1"/>
<object type="image" sprite="experienceBackground" ghost="true"/>
<object type="image" sprite="experienceForeground" ghost="true" name="experienceBar"/>
<object type="image" sprite="statsBarShaderVertical" ghost="true"/>
<object size="2 2 6 100%-2" type="image" name="experience" tooltip_style="sessionToolTip">
<translatableAttribute id="tooltip">Experience</translatableAttribute>
<object type="image" sprite="barBorder" ghost="true" size="-1 -1 100%+1 100%+1"/>
<object type="image" sprite="experienceBackground" ghost="true"/>
<object type="image" sprite="experienceForeground" ghost="true" name="experienceBar"/>
<object type="image" sprite="statsBarShaderVertical" ghost="true"/>
</object>
<object z="20" size="4 4 20 20" name="rankIcon" type="image" tooltip="Rank" tooltip_style="sessionToolTip"/>
<object z="20" size="4 4 20 20" name="rankIcon" type="image" tooltip_style="sessionToolTip">
<translatableAttribute id="tooltip">Rank</translatableAttribute>
</object>
</object>
</object>
@ -1165,7 +1268,8 @@
<!-- Stats Bars -->
<object size= "100%-38 50 100%-18 100%-44" type="image" tooltip_style="sessionToolTip">
<!-- Health bar -->
<object size="4 0 11 100%" type="image" name="healthMultiple" tooltip="Hitpoints" tooltip_style="sessionToolTip">
<object size="4 0 11 100%" type="image" name="healthMultiple" tooltip_style="sessionToolTip">
<translatableAttribute id="tooltip">Hitpoints</translatableAttribute>
<object type="image" sprite="barBorder" ghost="true" size="-1 -1 100%+1 100%+1"/>
<object type="image" sprite="healthBackground" ghost="true"/>
<object type="image" sprite="healthForeground" ghost="true" name="healthBarMultiple"/>
@ -1173,7 +1277,8 @@
</object>
<!-- Stamina bar -->
<object size="15 0 22 100%" type="image" name="staminaMultiple" tooltip="Stamina" tooltip_style="sessionToolTipBold">
<object size="15 0 22 100%" type="image" name="staminaMultiple" tooltip_style="sessionToolTipBold">
<translatableAttribute id="tooltip">Stamina</translatableAttribute>
<object type="image" sprite="barBorder" ghost="true" size="-1 -1 100%+1 100%+1"/>
<object type="image" sprite="staminaBackground" ghost="true"/>
<object type="image" sprite="staminaForeground" ghost="true" name="staminaBarMultiple"/>
@ -1336,7 +1441,9 @@
size="50%-84 50%+128 50%+84 50%+160"
tooltip_style="sessionToolTip"
>
<object size="0 0 100% 100%" type="text" style="CenteredButtonText" name="disconnectedExitButtonText" ghost="true">Exit</object>
<object size="0 0 100% 100%" type="text" style="CenteredButtonText" name="disconnectedExitButtonText" ghost="true">
<translatableAttribute id="caption">Exit</translatableAttribute>
</object>
<action on="Press">leaveGame()</action>
</object>
</object>

View File

@ -27,7 +27,7 @@ const BARTER_RESOURCES = ["food", "wood", "stone", "metal"];
const BARTER_ACTIONS = ["Sell", "Buy"];
// Gate constants
const GATE_ACTIONS = ["Lock", "Unlock"];
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};
@ -146,15 +146,15 @@ function formatLimitString(trainEntLimit, trainEntCount, trainEntLimitChangers)
{
if (trainEntLimit == undefined)
return "";
var text = "\n\nCurrent Count: " + trainEntCount + ", Limit: " + trainEntLimit + ".";
var text = "\n\n" + sprintf(translate("Current Count: %(count)s, Limit: %(limit)s."), { 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] + ".";
text += "\n" + sprintf("%(changer)s enlarges the limit with %(change)s.", { changer: c, change: trainEntLimitChangers[c] });
else if (trainEntLimitChangers[c] < 0)
text += "\n" + c + " lessens the limit with " + (-trainEntLimitChangers[c]) + ".";
text += "\n" + sprintf("%(changer)s lessens the limit with %(change)s.", { changer: c, change: (-trainEntLimitChangers[c]) });
}
return text;
}
@ -181,25 +181,70 @@ function formatBatchTrainingString(buildingsCountToTrainFullBatch, fullBatchSize
if (buildingsCountToTrainFullBatch > 0)
{
if (buildingsCountToTrainFullBatch > 1)
fullBatchesString += buildingsCountToTrainFullBatch + "*";
fullBatchesString += fullBatchSize;
fullBatchesString = sprintf(translate("%(buildings)s*%(batchSize)s"), {
buildings: buildingsCountToTrainFullBatch,
batchSize: fullBatchSize
});
else
fullBatchesString = fullBatchSize;
}
var remainderBatchString = remainderBatch > 0 ? remainderBatch : "";
var batchDetailsString = "";
var action = "[font=\"serif-bold-13\"]" + translate("Shift-click") + "[/font][font=\"serif-13\"]"
// 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 += ")";
if (remainderBatch > 0)
return "\n[font=\"serif-13\"]" + sprintf(translate("%(action)s to train %(number)s (%(fullBatch)s + %(remainderBatch)s)."), {
action: action,
number: totalBatchTrainingCount,
fullBatch: fullBatchesString,
remainderBatch: remainderBatch
}) + "[/font]";
return "\n[font=\"serif-13\"]" + sprintf(translate("%(action)s to train %(number)s (%(fullBatch)s)."), {
action: action,
number: totalBatchTrainingCount,
fullBatch: fullBatchesString
}) + "[/font]";
}
return "\n[font=\"serif-bold-13\"]Shift-click[/font][font=\"serif-13\"] to train "
+ totalBatchTrainingCount + batchDetailsString + ".[/font]";
return "\n[font=\"serif-13\"]" + sprintf(translate("%(action)s to train %(number)s."), {
action: action,
number: totalBatchTrainingCount
}) + "[/font]";
}
function getStanceDisplayName(name)
{
var displayName;
switch(name)
{
case "violent":
displayName = translate("Violent");
break;
case "aggressive":
displayName = translate("Aggressive");
break;
case "passive":
displayName = translate("Passive");
break;
case "defensive":
displayName = translate("Defensive");
break;
case "standground":
displayName = translate("Standground");
break;
default:
warn(sprintf("Internationalization: Unexpected stance found with code ‘%(stance)s’. This stance must be internationalized.", { stance: name }));
displayName = name;
break;
}
return displayName;
}
/**
@ -393,7 +438,7 @@ function setupUnitPanel(guiName, usedPanels, unitEntState, playerState, items, c
case QUEUE:
var tooltip = getEntityNames(template);
if (item.neededSlots)
tooltip += "\n[color=\"red\"]Insufficient population capacity:\n[/color]"+getCostComponentDisplayName("population")+" "+item.neededSlots;
tooltip += "\n[color=\"red\"]" + translate("Insufficient population capacity:") + "\n[/color]" + sprintf(translate("%(population)s %(neededSlots)s"), { population: getCostComponentDisplayName("population"), neededSlots: item.neededSlots });
var progress = Math.round(item.progress*100) + "%";
Engine.GetGUIObjectByName("unit"+guiName+"Count["+i+"]").caption = (item.count > 1 ? item.count : "");
@ -411,7 +456,7 @@ function setupUnitPanel(guiName, usedPanels, unitEntState, playerState, items, c
case GARRISON:
var name = getEntityNames(template);
var tooltip = "Unload " + name + "\nSingle-click to unload 1. Shift-click to unload all of this type.";
var tooltip = sprintf(translate("Unload %(name)s"), { name: name })+ "\n" + translate("Single-click to unload 1. Shift-click to unload all of this type.");
var count = garrisonGroups.getCount(item);
Engine.GetGUIObjectByName("unit"+guiName+"Count["+i+"]").caption = (count > 1 ? count : "");
break;
@ -447,7 +492,7 @@ function setupUnitPanel(guiName, usedPanels, unitEntState, playerState, items, c
break;
case STANCE:
var tooltip = toTitleCase(item);
var tooltip = getStanceDisplayName(item);
break;
case TRAINING:
@ -580,7 +625,7 @@ function setupUnitPanel(guiName, usedPanels, unitEntState, playerState, items, c
{
var formationInfo = Engine.GuiInterfaceCall("GetFormationInfoFromTemplate", {"templateName": item});
button.tooltip = formationInfo.name;
button.tooltip = translate(formationInfo.name);
var formationOk = canMoveSelectionIntoFormation(item);
var grayscale = "";
button.enabled = formationOk;
@ -589,8 +634,8 @@ function setupUnitPanel(guiName, usedPanels, unitEntState, playerState, items, c
grayscale = "grayscale:";
// Display a meaningful tooltip why the formation is disabled
button.tooltip += " (disabled)"+formationInfo.tooltip;
}
button.tooltip += "\n" + "[color=\"red\"]" + translate(formationInfo.tooltip) + "[/color]";
}
var formationSelected = Engine.GuiInterfaceCall("IsFormationSelected", {
"ents": g_Selection.toList(),
@ -621,7 +666,7 @@ function setupUnitPanel(guiName, usedPanels, unitEntState, playerState, items, c
// If already a gate, show locking actions
if (item.gate)
{
gateIcon = "icons/lock_" + GATE_ACTIONS[item.locked ? 0 : 1].toLowerCase() + "ed.png";
gateIcon = "icons/lock_" + GATE_ACTIONS[item.locked ? 0 : 1] + "ed.png";
guiSelection.hidden = item.gate.locked === undefined ? false : item.gate.locked != item.locked;
}
// otherwise show gate upgrade icon
@ -660,7 +705,7 @@ function setupUnitPanel(guiName, usedPanels, unitEntState, playerState, items, c
{
button.enabled = false;
var techName = getEntityNames(GetTechnologyData(template.requiredTechnology));
button.tooltip += "\nRequires " + techName;
button.tooltip += "\n" + sprintf(translate("Requires %(technology)s"), { technology: techName });
grayscale = "grayscale:";
affordableMask.hidden = false;
affordableMask.sprite = "colour: 0 0 0 127";
@ -905,7 +950,7 @@ function setupUnitTradingPanel(usedPanels, unitEntState, selection)
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.";
button.tooltip = sprintf(translate("Set/unset %(resource)s as forced trading goods."), { resource: resource });
var icon = Engine.GetGUIObjectByName("unitTradingIcon["+i+"]");
var selected = Engine.GetGUIObjectByName("unitTradingSelection["+i+"]");
selected.hidden = !(resource == requiredGoods);
@ -1091,11 +1136,37 @@ function updateUnitCommands(entState, supplementalDetailsPanel, commandsPanel, s
var gateTemplate = getWallGateTemplate(state.id);
if (gateTemplate)
{
var wallName = GetTemplateData(state.template).name.generic;
var gateName = GetTemplateData(gateTemplate).name.generic;
var wallName = GetTemplateDataWithoutLocalization(state.template).name.generic;
var gateName = GetTemplateDataWithoutLocalization(gateTemplate).name.generic;
var tooltipString;
// For internationalization purposes, when possible, available combinations should be provided
// as placeholder-free strings as below.
//
// The placeholder implementation is provided only so that undetected new combinations of wall
// and gate names are not simply printed in English, but as close to a perfect translation as
// possible.
if (wallName === "Wooden Wall" && gateName === "Wooden Gate")
{
tooltipString = translate("Convert Wooden Wall into Wooden Gate");
}
else if (wallName === "Stone Wall" && gateName === "City Gate")
{
tooltipString = translate("Convert Stone Wall into City Gate");
}
else if (wallName === "Siege Wall" && gateName === "Siege Wall Gate")
{
tooltipString = translate("Convert Siege Wall into Siege Wall Gate");
}
else
{
warn(sprintf("Internationalization: Unexpected combination of ‘%(localizedWall)s’ (%(englishWall)s) and ‘%(localizedGate)s’ (%(englishGate)s). This combination of wall and gate types must be internationalized.", { localizedWall: translate(wallName), englishWall: wallName, localizedGate: translate(gateName), englishGate: gateName }));
tooltipString = sprintf(translate("Convert %(wall)s into %(gate)s"), { wall: translate(wallName), gate: translate(gateName) });
}
walls.push({
"tooltip": "Convert " + wallName + " to " + gateName,
"tooltip": tooltipString,
"template": gateTemplate,
"callback": function (item) { transformWallToGate(item.template); }
});
@ -1105,13 +1176,20 @@ function updateUnitCommands(entState, supplementalDetailsPanel, commandsPanel, s
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); }
});
{
gates.push({
"gate": state.gate,
"tooltip": translate("Lock Gate"),
"locked": true,
"callback": function (item) { lockGate(item.locked); }
});
gates.push({
"gate": state.gate,
"tooltip": translate("Unlock Gate"),
"locked": false,
"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)
@ -1156,13 +1234,13 @@ function updateUnitCommands(entState, supplementalDetailsPanel, commandsPanel, s
}
}
if (packButton)
items.push({ "packing": false, "packed": false, "tooltip": "Pack", "callback": function() { packUnit(true); } });
items.push({ "packing": false, "packed": false, "tooltip": translate("Pack"), "callback": function() { packUnit(true); } });
if (unpackButton)
items.push({ "packing": false, "packed": true, "tooltip": "Unpack", "callback": function() { packUnit(false); } });
items.push({ "packing": false, "packed": true, "tooltip": translate("Unpack"), "callback": function() { packUnit(false); } });
if (packCancelButton)
items.push({ "packing": true, "packed": false, "tooltip": "Cancel packing", "callback": function() { cancelPackUnit(true); } });
items.push({ "packing": true, "packed": false, "tooltip": translate("Cancel Packing"), "callback": function() { cancelPackUnit(true); } });
if (unpackCancelButton)
items.push({ "packing": true, "packed": true, "tooltip": "Cancel unpacking", "callback": function() { cancelPackUnit(false); } });
items.push({ "packing": true, "packed": true, "tooltip": translate("Cancel Unpacking"), "callback": function() { cancelPackUnit(false); } });
if (items.length)
setupUnitPanel(PACK, usedPanels, entState, playerState, items);

View File

@ -32,17 +32,6 @@ function toTitleCase(word)
return word;
}
function pluralize(word, count, pluralWord)
{
if (count == 1 && pluralWord != null)
return pluralWord;
var plural = "s";
if (word[word.length - 1] == "s")
plural = "es";
return word + (count == 1 ? "" : plural);
}
// Get the basic player data
function getPlayerData(playerAssignments)
{
@ -162,110 +151,132 @@ function damageValues(dmg)
// For the unit details panel
function damageTypeDetails(dmg)
{
if (dmg)
{
var dmgArray = [];
if (dmg.hack) dmgArray.push(dmg.hack + "[font=\"sans-10\"][color=\"orange\"] Hack[/color][/font]");
if (dmg.pierce) dmgArray.push(dmg.pierce + "[font=\"sans-10\"][color=\"orange\"] Pierce[/color][/font]");
if (dmg.crush) dmgArray.push(dmg.crush + "[font=\"sans-10\"][color=\"orange\"] Crush[/color][/font]");
if (!dmg)
return "[font=\"serif-12\"]" + translate("(None)") + "[/font]";
return dmgArray.join(", ");
}
else
{
return "[font=\"serif-12\"](None)[/font]";
}
var dmgArray = [];
if (dmg.hack)
dmgArray.push(sprintf(translate("%(damage)s %(damageType)s"), {
damage: dmg.hack,
damageType: "[font=\"sans-10\"][color=\"orange\"]" + translate("Hack") + "[/color][/font]"
}));
if (dmg.pierce)
dmgArray.push(sprintf(translate("%(damage)s %(damageType)s"), {
damage: dmg.pierce,
damageType: "[font=\"sans-10\"][color=\"orange\"]" + translate("Pierce") + "[/color][/font]"
}));
if (dmg.crush)
dmgArray.push(sprintf(translate("%(damage)s %(damageType)s"), {
damage: dmg.crush,
damageType: "[font=\"sans-10\"][color=\"orange\"]" + translate("Crush") + "[/color][/font]"
}));
return dmgArray.join(translate(", "));
}
function attackRateDetails(entState) {
if (entState.buildingAI)
var arrows = entState.buildingAI.arrowCount;
var time = entState.attack.repeatTime / 1000;
if (entState.buildingAI) {
return Math.max(arrows, entState.buildingAI.defaultArrowCount) + "[font=\"sans-10\"][color=\"orange\"] " +
pluralize("arrow", arrows) + "[/color][/font]" + " / " + (time == 1 ? "" : time) +
" [font=\"sans-10\"][color=\"orange\"]" + pluralize("second", time) + "[/color][/font]";
var arrows = Math.max(entState.buildingAI.arrowCount, entState.buildingAI.defaultArrowCount);
// TODO TODO TODO color, font
return sprintf(translate("%(arrowString)s / %(timeString)s"), {
arrowString: sprintf(translatePlural("%(arrows)s arrow", "%(arrows)s arrows", arrows), { arrows: arrows}),
timeString: sprintf(translatePlural("%(time)s second", "%(time)s seconds", time), { time: time })
});
}
return time + "[font=\"sans-10\"][color=\"orange\"] " + pluralize("second", time) + "[/color][/font]";
// TODO TODO TODO color, font
return sprintf(translatePlural("%(time)s second", "%(time)s seconds", time), { time: time });
}
// Converts an armor level into the actual reduction percentage
function armorLevelToPercentage(level)
function armorLevelToPercentageString(level)
{
return 100 - Math.round(Math.pow(0.9, level) * 100);
return (100 - Math.round(Math.pow(0.9, level) * 100)) + "%";
// return sprintf(translate("%(armorPercentage)s%"), { armorPercentage: (100 - Math.round(Math.pow(0.9, level) * 100)) }); // Not supported by our sprintf implementation.
}
// Also for the unit details panel
function armorTypeDetails(dmg)
{
if (dmg)
{
var dmgArray = [];
if (dmg.hack)
{
dmgArray.push(dmg.hack + "[font=\"sans-10\"][color=\"orange\"] Hack[/color][/font] " +
" [font=\"sans-10\"](" + armorLevelToPercentage(dmg.hack) + "%)[/font]");
}
if (dmg.pierce)
{
dmgArray.push(dmg.pierce + "[font=\"sans-10\"][color=\"orange\"] Pierce[/color][/font] " +
" [font=\"sans-10\"](" + armorLevelToPercentage(dmg.pierce) + "%)[/font]");
}
if (dmg.crush)
{
dmgArray.push(dmg.crush + "[font=\"sans-10\"][color=\"orange\"] Crush[/color][/font] " +
" [font=\"sans-10\"](" + armorLevelToPercentage(dmg.crush) + "%)[/font]");
}
if (!dmg)
return "[font=\"serif-12\"]" + translate("(None)") + "[/font]";
return dmgArray.join(", ");
}
else
{
return "[font=\"serif-12\"](None)[/font]";
}
var dmgArray = [];
if (dmg.hack)
dmgArray.push(sprintf(translate("%(damage)s %(damageType)s %(armorPercentage)s"), {
damage: dmg.hack,
damageType: "[font=\"sans-10\"][color=\"orange\"]" + translate("Hack") + "[/color][/font]",
armorPercentage: "[font=\"sans-10\"]" + sprintf(translate("(%(armorPercentage)s)"), { armorPercentage: armorLevelToPercentageString(dmg.hack) }) + "[/font]"
}));
if (dmg.pierce)
dmgArray.push(sprintf(translate("%(damage)s %(damageType)s %(armorPercentage)s"), {
damage: dmg.pierce,
damageType: "[font=\"sans-10\"][color=\"orange\"]" + translate("Pierce") + "[/color][/font]",
armorPercentage: "[font=\"sans-10\"]" + sprintf(translate("(%(armorPercentage)s)"), { armorPercentage: armorLevelToPercentageString(dmg.pierce) }) + "[/font]"
}));
if (dmg.crush)
dmgArray.push(sprintf(translate("%(damage)s %(damageType)s %(armorPercentage)s"), {
damage: dmg.crush,
damageType: "[font=\"sans-10\"][color=\"orange\"]" + translate("Crush") + "[/color][/font]",
armorPercentage: "[font=\"sans-10\"]" + sprintf(translate("(%(armorPercentage)s)"), { armorPercentage: armorLevelToPercentageString(dmg.crush) }) + "[/font]"
}));
return dmgArray.join(translate(", "));
}
// For the training tooltip
function damageTypesToText(dmg)
{
if (!dmg)
return "[font=\"serif-12\"](None)[/font]";
var hackLabel = "[font=\"serif-12\"] Hack[/font]";
var pierceLabel = "[font=\"serif-12\"] Pierce[/font]";
var crushLabel = "[font=\"serif-12\"] Crush[/font]";
var hackDamage = dmg.hack;
var pierceDamage = dmg.pierce;
var crushDamage = dmg.crush;
return "[font=\"serif-12\"]" + translate("(None)") + "[/font]";
var dmgArray = [];
if (hackDamage) dmgArray.push(Math.round(hackDamage) + hackLabel);
if (pierceDamage) dmgArray.push(Math.round(pierceDamage) + pierceLabel);
if (crushDamage) dmgArray.push(Math.round(crushDamage) + crushLabel);
if (dmg.hack)
dmgArray.push(sprintf(translate("%(damage)s %(damageType)s"), {
damage: dmg.hack,
damageType: "[font=\"serif-12\"]" + translate("Hack") + "[/font]"
}));
if (dmg.pierce)
dmgArray.push(sprintf(translate("%(damage)s %(damageType)s"), {
damage: dmg.pierce,
damageType: "[font=\"serif-12\"]" + translate("Pierce") + "[/font]"
}));
if (dmg.crush)
dmgArray.push(sprintf(translate("%(damage)s %(damageType)s"), {
damage: dmg.crush,
damageType: "[font=\"serif-12\"]" + translate("Crush") + "[/font]"
}));
return dmgArray.join("[font=\"serif-12\"], [/font]");
return dmgArray.join("[font=\"serif-12\"]" + translate(", ") + "[/font]");
}
// Also for the training tooltip
function armorTypesToText(dmg)
{
if (!dmg)
return "[font=\"serif-12\"](None)[/font]";
var hackDamage = dmg.hack;
var pierceDamage = dmg.pierce;
var crushDamage = dmg.crush;
var hackLabel = "[font=\"serif-12\"] Hack (" + armorLevelToPercentage(hackDamage) + "%)[/font]";
var pierceLabel = "[font=\"serif-12\"] Pierce (" + armorLevelToPercentage(pierceDamage) + "%)[/font]";
var crushLabel = "[font=\"serif-12\"] Crush (" + armorLevelToPercentage(crushDamage) + "%)[/font]";
return "[font=\"serif-12\"]" + translate("(None)") + "[/font]";
var dmgArray = [];
if (hackDamage) dmgArray.push(hackDamage + hackLabel);
if (pierceDamage) dmgArray.push(pierceDamage + pierceLabel);
if (crushDamage) dmgArray.push(crushDamage + crushLabel);
if (dmg.hack)
dmgArray.push(sprintf(translate("%(damage)s %(damageType)s %(armorPercentage)s"), {
damage: dmg.hack,
damageType: "[font=\"serif-12\"]" + translate("Hack") + "[/font]",
armorPercentage: "[font=\"sans-10\"]" + sprintf(translate("(%(armorPercentage)s)"), { armorPercentage: armorLevelToPercentageString(dmg.hack) }) + "[/font]"
}));
if (dmg.pierce)
dmgArray.push(sprintf(translate("%(damage)s %(damageType)s %(armorPercentage)s"), {
damage: dmg.pierce,
damageType: "[font=\"serif-12\"]" + translate("Pierce") + "[/font]",
armorPercentage: "[font=\"sans-10\"]" + sprintf(translate("(%(armorPercentage)s)"), { armorPercentage: armorLevelToPercentageString(dmg.pierce) }) + "[/font]"
}));
if (dmg.crush)
dmgArray.push(sprintf(translate("%(damage)s %(damageType)s %(armorPercentage)s"), {
damage: dmg.crush,
damageType: "[font=\"serif-12\"]" + translate("Crush") + "[/font]",
armorPercentage: "[font=\"sans-10\"]" + sprintf(translate("(%(armorPercentage)s)"), { armorPercentage: armorLevelToPercentageString(dmg.crush) }) + "[/font]"
}));
return dmgArray.join("[font=\"serif-12\"], [/font]");
return dmgArray.join("[font=\"serif-12\"]" + translate(", ") + "[/font]");
}
function getEntityCommandsList(entState)
@ -275,14 +286,14 @@ function getEntityCommandsList(entState)
{
commands.push({
"name": "unload-all",
"tooltip": "Unload All",
"tooltip": translate("Unload All"),
"icon": "garrison-out.png"
});
}
commands.push({
"name": "delete",
"tooltip": "Delete",
"tooltip": translate("Delete"),
"icon": "kill_small.png"
});
@ -290,12 +301,12 @@ function getEntityCommandsList(entState)
{
commands.push({
"name": "stop",
"tooltip": "Stop",
"tooltip": translate("Stop"),
"icon": "stop.png"
});
commands.push({
"name": "garrison",
"tooltip": "Garrison",
"tooltip": translate("Garrison"),
"icon": "garrison.png"
});
}
@ -304,7 +315,7 @@ function getEntityCommandsList(entState)
{
commands.push({
"name": "repair",
"tooltip": "Repair",
"tooltip": translate("Repair"),
"icon": "repair.png"
});
}
@ -313,7 +324,7 @@ function getEntityCommandsList(entState)
{
commands.push({
"name": "focus-rally",
"tooltip": "Focus on Rally Point",
"tooltip": translate("Focus on Rally Point"),
"icon": "focus-rally.png"
});
}
@ -322,7 +333,7 @@ function getEntityCommandsList(entState)
{
commands.push({
"name": "back-to-work",
"tooltip": "Back to Work",
"tooltip": translate("Back to Work"),
"icon": "production.png"
});
}
@ -331,7 +342,7 @@ function getEntityCommandsList(entState)
{
commands.push({
"name": "add-guard",
"tooltip": "Guard",
"tooltip": translate("Guard"),
"icon": "add-guard.png"
});
}
@ -340,7 +351,7 @@ function getEntityCommandsList(entState)
{
commands.push({
"name": "remove-guard",
"tooltip": "Remove guard",
"tooltip": translate("Remove guard"),
"icon": "remove-guard.png"
});
}
@ -349,7 +360,7 @@ function getEntityCommandsList(entState)
{
commands.push({
"name": "select-trading-goods",
"tooltip": "Select trading goods",
"tooltip": translate("Select trading goods"),
"icon": "economics.png"
});
}
@ -359,9 +370,9 @@ function getEntityCommandsList(entState)
if(entState.alertRaiser.canIncreaseLevel)
{
if(entState.alertRaiser.hasRaisedAlert)
var tooltip = "Increase the alert level to protect more units";
var tooltip = translate("Increase the alert level to protect more units");
else
var tooltip = "Raise an alert!";
var tooltip = translate("Raise an alert!");
commands.push({
"name": "increase-alert-level",
"tooltip": tooltip,
@ -372,7 +383,7 @@ function getEntityCommandsList(entState)
if(entState.alertRaiser.hasRaisedAlert)
commands.push({
"name": "alert-end",
"tooltip": "End of alert.",
"tooltip": translate("End of alert."),
"icon": "bell_level0.png"
});
}
@ -413,12 +424,12 @@ function getEntityCostComponentsTooltipString(template, trainNum, entity)
totalCosts.time = Math.ceil(template.cost.time * (entity ? Engine.GuiInterfaceCall("GetBatchTime", {"entity": entity, "batchSize": trainNum}) : 1));
var costs = [];
if (totalCosts.food) costs.push(getCostComponentDisplayName("food") + " " + totalCosts.food);
if (totalCosts.wood) costs.push(getCostComponentDisplayName("wood") + " " + totalCosts.wood);
if (totalCosts.metal) costs.push(getCostComponentDisplayName("metal") + " " + totalCosts.metal);
if (totalCosts.stone) costs.push(getCostComponentDisplayName("stone") + " " + totalCosts.stone);
if (totalCosts.population) costs.push(getCostComponentDisplayName("population") + " " + totalCosts.population);
if (totalCosts.time) costs.push(getCostComponentDisplayName("time") + " " + totalCosts.time);
if (totalCosts.food) costs.push(sprintf(translate("%(component)s %(cost)s"), { component: getCostComponentDisplayName("food"), cost: totalCosts.food }));
if (totalCosts.wood) costs.push(sprintf(translate("%(component)s %(cost)s"), { component: getCostComponentDisplayName("wood"), cost: totalCosts.wood }));
if (totalCosts.metal) costs.push(sprintf(translate("%(component)s %(cost)s"), { component: getCostComponentDisplayName("metal"), cost: totalCosts.metal }));
if (totalCosts.stone) costs.push(sprintf(translate("%(component)s %(cost)s"), { component: getCostComponentDisplayName("stone"), cost: totalCosts.stone }));
if (totalCosts.population) costs.push(sprintf(translate("%(component)s %(cost)s"), { component: getCostComponentDisplayName("population"), cost: totalCosts.population }));
if (totalCosts.time) costs.push(sprintf(translate("%(component)s %(cost)s"), { component: getCostComponentDisplayName("time"), cost: totalCosts.time }));
return costs;
}
@ -499,14 +510,13 @@ function getEntityCostTooltip(template, trainNum, entity)
var wallCosts = getWallPieceTooltip([templateShort, templateMedium, templateLong]);
var towerCosts = getEntityCostComponentsTooltipString(templateTower);
cost += "\n";
cost += " Walls: " + wallCosts.join(" ") + "\n";
cost += " Towers: " + towerCosts.join(" ");
cost += " " + sprintf(translate("Walls: %(costs)s"), { costs: wallCosts.join(translate(" ")) }) + "\n";
cost += " " + sprintf(translate("Towers: %(costs)s"), { costs: towerCosts.join(translate(" ")) });
}
else if (template.cost)
{
var costs = getEntityCostComponentsTooltipString(template, trainNum, entity);
cost += costs.join(" ");
cost = costs.join(translate(" "));
}
else
{
@ -523,7 +533,10 @@ function getPopulationBonusTooltip(template)
{
var popBonus = "";
if (template.cost && template.cost.populationBonus)
popBonus = "\n[font=\"serif-bold-13\"]Population Bonus:[/font] " + template.cost.populationBonus;
popBonus = "\n" + sprintf(translate("%(label)s %(populationBonus)s"), {
label: "[font=\"serif-bold-13\"]" + translate("Population Bonus:") + "[/font]",
populationBonus: template.cost.populationBonus
});
return popBonus;
}
@ -534,9 +547,12 @@ function getNeededResourcesTooltip(resources)
{
var formatted = [];
for (var resource in resources)
formatted.push("[font=\"serif-12\"]" + getCostComponentDisplayName(resource) + "[/font] " + resources[resource]);
formatted.push(sprintf(translate("%(component)s %(cost)s"), {
component: "[font=\"serif-12\"]" + getCostComponentDisplayName(resource) + "[/font]",
cost: resources[resource]
}));
return "\n\n[font=\"serif-bold-13\"][color=\"red\"]Insufficient resources:[/color][/font]\n" + formatted.join(" ");
return "\n\n[font=\"serif-bold-13\"][color=\"red\"]" + translate("Insufficient resources:") + "[/color][/font]\n" + formatted.join(translate(" "));
}
function getEntitySpeed(template)
@ -544,12 +560,12 @@ function getEntitySpeed(template)
var speed = "";
if (template.speed)
{
speed += "[font=\"serif-bold-13\"]Speed:[/font] ";
var label = "[font=\"serif-bold-13\"]" + translate("Speed:") + "[/font]";
var speeds = [];
if (template.speed.walk) speeds.push(template.speed.walk + " [font=\"serif-12\"]Walk[/font]");
if (template.speed.run) speeds.push(template.speed.run + " [font=\"serif-12\"]Run[/font]");
if (template.speed.walk) speeds.push(sprintf(translate("%(speed)s %(movementType)s"), { speed: template.speed.walk, movementType: "[font=\"serif-12\"]" + translate("Walk") + "[/font]"}));
if (template.speed.run) speeds.push(sprintf(translate("%(speed)s %(movementType)s"), { speed: template.speed.run, movementType: "[font=\"serif-12\"]" + translate("Run") + "[/font]"}));
speed += speeds.join(", ");
speed = sprintf(translate("%(label)s %(speeds)s"), { label: label, speeds: speeds.join(translate(", ")) })
}
return speed;
}
@ -563,34 +579,47 @@ function getEntityAttack(template)
delete template.attack['Slaughter'];
for (var type in template.attack)
{
var attack = "[font=\"serif-bold-13\"]" + type + " Attack:[/font] " + damageTypesToText(template.attack[type]);
// Show max attack range if ranged attack, also convert to tiles (4m per tile)
var attack = "";
var attackLabel = "[font=\"serif-bold-13\"]" + getAttackTypeLabel(type) + "[/font]";
if (type == "Ranged")
attack += ", [font=\"serif-bold-13\"]Range:[/font] "+Math.round(template.attack[type].maxRange/4);
{
// Show max attack range if ranged attack, also convert to tiles (4m per tile)
attack = sprintf(translate("%(attackLabel)s %(damageTypes)s, %(rangeLabel)s %(range)s"), {
attackLabel: attackLabel,
damageTypes: damageTypesToText(template.attack[type]),
rangeLabel: "[font=\"serif-bold-13\"]" + translate("Range:") + "[/font]",
range: Math.round(template.attack[type].maxRange/4)
});
}
else
{
attack = sprintf(translate("%(attackLabel)s %(damageTypes)s"), {
attackLabel: attackLabel,
damageTypes: damageTypesToText(template.attack[type])
});
}
attacks.push(attack);
}
}
return attacks.join("\n");
}
function getEntityName(template)
{
return template.name.specific || template.name.generic || "???";
}
function getEntityNames(template)
{
var names = [];
if (template.name.specific)
{
names.push(template.name.specific);
if (template.name.generic && names[0] != template.name.generic)
names.push("(" + template.name.generic + ")");
}
else if (template.name.generic)
names.push(template.name.generic);
{
if (template.name.generic && template.name.specific != template.name.generic)
return sprintf(translate("%(specificName)s (%(genericName)s)"), {
specificName: template.name.specific,
genericName: template.name.generic
});
return template.name.specific;
}
if (template.name.generic)
return template.name.generic;
return (names.length) ? names.join(" ") : "???";
warn("Entity name requested on an entity without a name, specific or generic.");
return translate("???");
}
function getEntityNamesFormatted(template)
@ -620,7 +649,7 @@ function getEntityRankedName(entState)
var template = GetTemplateData(entState.template)
var rank = entState.identity.rank;
if (rank)
return rank + " " + template.name.specific;
return sprintf(translate("%(rank)s %(name)s"), { rank: rank, name: template.name.specific });
else
return template.name.specific;
}
@ -642,21 +671,41 @@ function getRankIconSprite(entState)
*/
function getTradingTooltip(gain)
{
var tooltip = gain.traderGain;
var gainString = gain.traderGain;
if (gain.market1Gain && gain.market1Owner == gain.traderOwner)
tooltip += "+" + gain.market1Gain;
gainString += translate("+") + gain.market1Gain;
if (gain.market2Gain && gain.market2Owner == gain.traderOwner)
tooltip += "+" + gain.market2Gain;
tooltip += " (you)";
gainString += translate("+") + gain.market2Gain;
var tooltip = sprintf(translate("%(gain)s (%(player)s)"), {
gain: gainString,
player: translate("you")
});
if (gain.market1Gain && gain.market1Owner != gain.traderOwner)
tooltip += ", " + gain.market1Gain + " (player " + gain.market1Owner + ")";
tooltip += translate(", ") + sprintf(translate("%(gain)s (%(player)s)"), {
gain: gain.market1Gain,
player: sprintf(translate("player %(name)s"), { name: gain.market1Owner })
});
if (gain.market2Gain && gain.market2Owner != gain.traderOwner)
tooltip += ", " + gain.market2Gain + " (player " + gain.market2Owner + ")";
tooltip += translate(", ") + sprintf(translate("%(gain)s (%(player)s)"), {
gain: gain.market2Gain,
player: sprintf(translate("player %(name)s"), { name: gain.market2Owner })
});
return tooltip;
}
function getAttackTypeLabel(type)
{
if (type === "Charge") return translate("Charge Attack:");
if (type === "Melee") return translate("Melee Attack:");
if (type === "Ranged") return translate("Ranged Attack:");
warn(sprintf("Internationalization: Unexpected attack type found with code ‘%(attackType)s’. This attack type must be internationalized.", { attackType: type }));
return translate("Attack:");
}
/**
* Returns the entity itself except when garrisoned where it returns its garrisonHolder
*/

View File

@ -1,6 +1,10 @@
function init(data)
{
Engine.GetGUIObjectByName("mainText").caption = Engine.ReadFile("gui/splashscreen/" + data.page + ".txt");
Engine.GetGUIObjectByName("mainText").caption = Engine.TranslateLines(Engine.ReadFile("gui/splashscreen/" + data.page + ".txt"));
}
function openURL(url)
{
Engine.OpenURL(url);
messageBox(600, 200, sprintf(translate("Opening %(url)s\n in default web browser. Please wait..."), { url: url }), translate("Opening page"), 2);
}

View File

@ -8,19 +8,23 @@
<object type="image" z="0" sprite="ModernFade"/>
<object type="image" style="ModernDialog" size="50%-274 50%-200 50%+274 50%+200">
<object type="text" style="TitleText" size="50%-128 0%-16 50%+128 16">Welcome to 0 A.D. !</object>
<object type="text" style="TitleText" size="50%-128 0%-16 50%+128 16">
<translatableAttribute id="caption">Welcome to 0 A.D. !</translatableAttribute>
</object>
<object type="image" sprite="BackgroundTranslucent" size="20 20 100%-20 100%-52">
<object name="mainText" type="text" style="textPanel"/>
<object size="8 100%-60 100% 100%">
<object size="0 20 100% 100%">
<object name="displaySplashScreenText" size="20 0 100% 100%" type="text" style="ModernLeftLabelText">Show this message in the future</object>
<object name="displaySplashScreenText" size="20 0 100% 100%" type="text" style="ModernLeftLabelText">
<translatableAttribute id="caption">Show this message in the future</translatableAttribute>
</object>
<object name="displaySplashScreen" checked="true" size="0 50%-8 16 50%+8" type="checkbox" style="ModernTickBox"/>
</object>
</object>
</object>
<object name="btnOK" type="button" style="StoneButton" tooltip_style="snToolTip" size="24 100%-52 188 100%-24">
OK
<translatableAttribute id="caption">OK</translatableAttribute>
<action on="Press"><![CDATA[
Engine.ConfigDB_CreateValue("user", "splashscreenenable", Engine.GetGUIObjectByName("displaySplashScreen").checked ? "true" : "false");
Engine.ConfigDB_WriteFile("user", "config/user.cfg");
@ -28,12 +32,10 @@
]]></action>
</object>
<object type="button" style="StoneButton" size="192 100%-52 356 100%-24">
Known issues (web)
<translatableAttribute id="caption">Known issues (web)</translatableAttribute>
<action on="Press"><![CDATA[
var url = "http://www.wildfiregames.com/forum/index.php?showtopic=15796";
Engine.OpenURL(url);
messageBox(600, 200, "Opening "+url+"\n in default web browser. Please wait...", "Opening page", 2);
]]></action>
openURL("http://www.wildfiregames.com/forum/index.php?showtopic=15796");
]]></action>
</object>
</object>

View File

@ -505,9 +505,9 @@ function init(data)
// Load data
var civData = loadCivData();
// Map
var mapSize = "Scenario";
var mapDisplayType = translate("Scenario");
Engine.GetGUIObjectByName("timeElapsed").caption = "Time elapsed: " + timeToString(data.timeElapsed);
Engine.GetGUIObjectByName("timeElapsed").caption = sprintf(translate("Time elapsed: %(time)s"), { time: timeToString(data.timeElapsed) });
Engine.GetGUIObjectByName("summaryText").caption = data.gameResult;
@ -522,13 +522,13 @@ function init(data)
{
if (mapSizes.tiles[mapSizeIndex] == data.mapSettings.Size)
{
mapSize = mapSizes.names[mapSizeIndex];
mapDisplayType = mapSizes.names[mapSizeIndex];
break;
}
}
}
Engine.GetGUIObjectByName("mapName").caption = data.mapSettings.Name + " - " + mapSize;
Engine.GetGUIObjectByName("mapName").caption = sprintf(translate("%(mapName)s - %(mapType)s"), { mapName: data.mapSettings.Name, mapType: mapDisplayType});
// Panels
// Align headers

View File

@ -20,7 +20,7 @@
</action>
<object style="TitleText" type="text" size="50%-128 4 50%+128 36">
Summary
<translatableAttribute id="caption">Summary</translatableAttribute>
</object>
<object type="image" sprite="ForegroundBody" size="20 24 100%-20 80">
@ -59,51 +59,63 @@
<object name="scorePanelButton" type="button" sprite="ForegroundTab" size="20 92 136 120">
<action on="Press">selectPanel(0);</action>
<object type="text" style="TitleText" ghost="true">Score</object>
<object type="text" style="TitleText" ghost="true">
<translatableAttribute id="caption">Score</translatableAttribute>
</object>
</object>
<object name="buildingsPanelButton" type="button" sprite="BackgroundTab" size="142 92 260 120">
<action on="Press">selectPanel(1);</action>
<object type="text" style="TitleText" ghost="true">Buildings</object>
<object type="text" style="TitleText" ghost="true">
<translatableAttribute id="caption">Buildings</translatableAttribute>
</object>
</object>
<object name="unitsPanelButton" type="button" sprite="BackgroundTab" size="266 92 384 120">
<action on="Press">selectPanel(2);</action>
<object type="text" style="TitleText" ghost="true">Units</object>
<object type="text" style="TitleText" ghost="true">
<translatableAttribute id="caption">Units</translatableAttribute>
</object>
</object>
<object name="resourcesPanelButton" type="button" sprite="BackgroundTab" size="390 92 508 120">
<action on="Press">selectPanel(3);</action>
<object type="text" style="TitleText" ghost="true">Resources</object>
<object type="text" style="TitleText" ghost="true">
<translatableAttribute id="caption">Resources</translatableAttribute>
</object>
</object>
<object name="marketPanelButton" type="button" sprite="BackgroundTab" size="514 92 632 120">
<action on="Press">selectPanel(4);</action>
<object type="text" style="TitleText" ghost="true">Market</object>
<object type="text" style="TitleText" ghost="true">
<translatableAttribute id="caption">Market</translatableAttribute>
</object>
</object>
<object name="miscPanelButton" type="button" sprite="BackgroundTab" size="638 92 756 120">
<action on="Press">selectPanel(5);</action>
<object type="text" style="TitleText" ghost="true">Miscellaneous</object>
<object type="text" style="TitleText" ghost="true">
<translatableAttribute id="caption">Miscellaneous</translatableAttribute>
</object>
</object>
<object name="scorePanel" type="image" sprite="ForegroundBody" size="20 120 100%-20 100%-58">
<object size="0 0 100% 100%-50">
<object name="playerName0Heading" type="text" style="ModernLeftTabLabelText">
Player name
<translatableAttribute id="caption">Player name</translatableAttribute>
</object>
<object name="economyScoreHeading" type="text" style="ModernTabLabelText">
Economy&#10;score
<translatableAttribute id="caption">Economy score</translatableAttribute>
</object>
<object name="militaryScoreHeading" type="text" style="ModernTabLabelText">
Military&#10;score
<translatableAttribute id="caption">Military score</translatableAttribute>
</object>
<object name="explorationScoreHeading" type="text" style="ModernTabLabelText">
Exploration&#10;score
<translatableAttribute id="caption">Exploration score</translatableAttribute>
</object>
<object name="totalScoreHeading" type="text" style="ModernTabLabelText">
Total&#10;score
<translatableAttribute id="caption">Total score</translatableAttribute>
</object>
</object>
@ -213,34 +225,34 @@
<object size="0 0 100% 100%-50">
<object name="playerName1Heading" type="text" style="ModernLeftTabLabelText">
Player name
<translatableAttribute id="caption">Player name</translatableAttribute>
</object>
<object name="buildingsHeading" type="text" style="ModernTabLabelText">
Buildings Statistics (Constructed / Lost / Destroyed)
<translatableAttribute id="caption">Buildings Statistics (Constructed / Lost / Destroyed)</translatableAttribute>
</object>
<object name="totalBuildingsHeading" type="text" style="ModernTabLabelText">
Total
<translatableAttribute id="caption">Total</translatableAttribute>
</object>
<object name="houseBuildingsHeading" type="text" style="ModernTabLabelText">
Houses
<translatableAttribute id="caption">Houses</translatableAttribute>
</object>
<object name="economicBuildingsHeading" type="text" style="ModernTabLabelText">
Economic
<translatableAttribute id="caption">Economic</translatableAttribute>
</object>
<object name="outpostBuildingsHeading" type="text" style="ModernTabLabelText">
Outposts
<translatableAttribute id="caption">Outposts</translatableAttribute>
</object>
<object name="militaryBuildingsHeading" type="text" style="ModernTabLabelText">
Military
<translatableAttribute id="caption">Military</translatableAttribute>
</object>
<object name="fortressBuildingsHeading" type="text" style="ModernTabLabelText">
Fortresses
<translatableAttribute id="caption">Fortresses</translatableAttribute>
</object>
<object name="civCentreBuildingsHeading" type="text" style="ModernTabLabelText">
Civ&#160;Centers
<translatableAttribute id="caption">Civ Centers</translatableAttribute>
</object>
<object name="wonderBuildingsHeading" type="text" style="ModernTabLabelText">
Wonders
<translatableAttribute id="caption">Wonders</translatableAttribute>
</object>
</object>
@ -387,31 +399,31 @@
<object size="0 0 100% 100%-50">
<object name="playerName2Heading" type="text" style="ModernLeftTabLabelText">
Player name
<translatableAttribute id="caption">Player name</translatableAttribute>
</object>
<object name="unitsHeading" type="text" style="ModernTabLabelText">
Units Statistics (Trained / Lost / Killed)
<translatableAttribute id="caption">Units Statistics (Trained / Lost / Killed)</translatableAttribute>
</object>
<object name="totalUnitsHeading" type="text" style="ModernTabLabelText">
Total
<translatableAttribute id="caption">Total</translatableAttribute>
</object>
<object name="infantryUnitsHeading" type="text" style="ModernTabLabelText">
Infantry
<translatableAttribute id="caption">Infantry</translatableAttribute>
</object>
<object name="workerUnitsHeading" type="text" style="ModernTabLabelText">
Worker
<translatableAttribute id="caption">Worker</translatableAttribute>
</object>
<object name="cavalryUnitsHeading" type="text" style="ModernTabLabelText">
Cavalry
<translatableAttribute id="caption">Cavalry</translatableAttribute>
</object>
<object name="championUnitsHeading" type="text" style="ModernTabLabelText">
Champion
<translatableAttribute id="caption">Champion</translatableAttribute>
</object>
<object name="heroesUnitsHeading" type="text" style="ModernTabLabelText">
Heroes
<translatableAttribute id="caption">Heroes</translatableAttribute>
</object>
<object name="navyUnitsHeading" type="text" style="ModernTabLabelText">
Navy
<translatableAttribute id="caption">Navy</translatableAttribute>
</object>
</object>
@ -548,31 +560,31 @@
<object size="0 0 100% 100%-50">
<object name="playerName3Heading" type="text" style="ModernLeftTabLabelText">
Player name
<translatableAttribute id="caption">Player name</translatableAttribute>
</object>
<object name="resourceHeading" type="text" style="ModernTabLabelText">
Resource Statistics (Gathered / Used)
<translatableAttribute id="caption">Resource Statistics (Gathered / Used)</translatableAttribute>
</object>
<object name="foodGatheredHeading" type="text" style="ModernTabLabelText">
Food
<translatableAttribute id="caption">Food</translatableAttribute>
</object>
<object name="woodGatheredHeading" type="text" style="ModernTabLabelText">
Wood
<translatableAttribute id="caption">Wood</translatableAttribute>
</object>
<object name="stoneGatheredHeading" type="text" style="ModernTabLabelText">
Stone
<translatableAttribute id="caption">Stone</translatableAttribute>
</object>
<object name="metalGatheredHeading" type="text" style="ModernTabLabelText">
Metal
<translatableAttribute id="caption">Metal</translatableAttribute>
</object>
<object name="totalGatheredHeading" type="text" style="ModernTabLabelText">
Total
<translatableAttribute id="caption">Total</translatableAttribute>
</object>
<object name="treasuresCollectedHeading" type="text" style="ModernTabLabelText">
Treasures&#10;collected
<translatableAttribute id="caption">Treasures collected</translatableAttribute>
</object>
<object name="resourcesTributedHeading" type="text" style="ModernTabLabelText">
Tributes&#10;(Sent / Received)
<translatableAttribute id="caption">Tributes (Sent / Received)</translatableAttribute>
</object>
</object>
@ -709,25 +721,25 @@
<object size="0 0 100% 100%-50">
<object name="playerName4Heading" type="text" style="ModernLeftTabLabelText">
Player name
<translatableAttribute id="caption">Player name</translatableAttribute>
</object>
<object name="exchangedFoodHeading" type="text" style="ModernTabLabelText">
Food&#10;exchanged
<translatableAttribute id="caption">Food exchanged</translatableAttribute>
</object>
<object name="exchangedWoodHeading" type="text" style="ModernTabLabelText">
Wood&#10;exchanged
<translatableAttribute id="caption">Wood exchanged</translatableAttribute>
</object>
<object name="exchangedStoneHeading" type="text" style="ModernTabLabelText">
Stone&#10;exchanged
<translatableAttribute id="caption">Stone exchanged</translatableAttribute>
</object>
<object name="exchangedMetalHeading" type="text" style="ModernTabLabelText">
Metal&#10;exchanged
<translatableAttribute id="caption">Metal exchanged</translatableAttribute>
</object>
<object name="barterEfficiencyHeading" type="text" style="ModernTabLabelText">
Barter&#10;efficiency
<translatableAttribute id="caption">Barter efficiency</translatableAttribute>
</object>
<object name="tradeIncomeHeading" type="text" style="ModernTabLabelText">
Trade&#10;income
<translatableAttribute id="caption">Trade income</translatableAttribute>
</object>
</object>
@ -855,19 +867,19 @@
<object size="0 0 100% 100%-50">
<object name="playerName5Heading" type="text" style="ModernLeftTabLabelText">
Player name
<translatableAttribute id="caption">Player name</translatableAttribute>
</object>
<object name="vegetarianRatioHeading" type="text" style="ModernTabLabelText">
Vegetarian&#10;ratio
<translatableAttribute id="caption">Vegetarian\nratio</translatableAttribute>
</object>
<object name="feminisationHeading" type="text" style="ModernTabLabelText">
Feminisation
<translatableAttribute id="caption">Feminisation</translatableAttribute>
</object>
<object name="killDeathRatioHeading" type="text" style="ModernTabLabelText">
Kill / Death&#10;ratio
<translatableAttribute id="caption">Kill / Death\nratio</translatableAttribute>
</object>
<object name="mapExplorationHeading" type="text" style="ModernTabLabelText">
Map&#10;exploration
<translatableAttribute id="caption">Map\nexploration</translatableAttribute>
</object>
</object>
@ -974,7 +986,7 @@
</object>
<object type="button" style="StoneButton" size="100%-164 100%-52 100%-24 100%-24">
Continue
<translatableAttribute id="caption">Continue</translatableAttribute>
<action on="Press"><![CDATA[
if(!Engine.HasXmppClient())
{

View File

@ -64,8 +64,9 @@ BuildRestrictions.prototype.Init = function()
*
* Returns result object:
* {
* "success": true iff the placement is valid, else false
* "message": message to display in UI for invalid placement, else empty string
* "success": true iff the placement is valid, else false
* "message": message to display in UI for invalid placement, else ""
* "parameters": parameters to use in the GUI message
* }
*
* Note: The entity which is used to check this should be a preview entity
@ -79,7 +80,10 @@ BuildRestrictions.prototype.CheckPlacement = function()
var result = {
"success": false,
"message": name+" cannot be built due to unknown error",
"message": markForTranslation("%(name)s cannot be built due to unknown error"),
"parameters": {
"name": name,
}
};
// TODO: AI has no visibility info
@ -97,7 +101,7 @@ BuildRestrictions.prototype.CheckPlacement = function()
var explored = (cmpRangeManager.GetLosVisibility(this.entity, cmpOwnership.GetOwner(), true) != "hidden");
if (!explored)
{
result.message = name+" cannot be built in unexplored area";
result.message = markForTranslation("%(name)s cannot be built in unexplored area");
return result; // Fail
}
}
@ -145,11 +149,11 @@ BuildRestrictions.prototype.CheckPlacement = function()
error("CheckPlacement: Error returned from CheckFoundation");
break;
case "fail_obstructs_foundation":
result.message = name+" cannot be built on another building or resource";
result.message = markForTranslation("%(name)s cannot be built on another building or resource");
break;
case "fail_terrain_class":
// TODO: be more specific and/or list valid terrain?
result.message = name+" cannot be built on invalid terrain";
result.message = markForTranslation("%(name)s cannot be built on invalid terrain");
}
return result; // Fail
}
@ -172,19 +176,26 @@ BuildRestrictions.prototype.CheckPlacement = function()
var territoryFail = true;
var territoryType = "";
if (isAlly && !this.HasTerritory("ally"))
territoryType = "ally";
// Translation: territoryType being displayed in a translated sentence in the form: "House cannot be built in %(territoryType)s territory.".
territoryType = markForTranslationWithContext("Territory type", "ally");
else if (isOwn && !this.HasTerritory("own"))
territoryType = "own";
// Translation: territoryType being displayed in a translated sentence in the form: "House cannot be built in %(territoryType)s territory.".
territoryType = markForTranslationWithContext("Territory type", "own");
else if (isNeutral && !this.HasTerritory("neutral"))
territoryType = "neutral";
// Translation: territoryType being displayed in a translated sentence in the form: "House cannot be built in %(territoryType)s territory.".
territoryType = markForTranslationWithContext("Territory type", "neutral");
else if (isEnemy && !this.HasTerritory("enemy"))
territoryType = "enemy";
// Translation: territoryType being displayed in a translated sentence in the form: "House cannot be built in %(territoryType)s territory.".
territoryType = markForTranslationWithContext("Territory type", "enemy");
else
territoryFail = false;
if (territoryFail)
{
result.message = name+" cannot be built in "+territoryType+" territory. Valid territories: " + this.GetTerritories().join(", ");
result.message = markForTranslation("%(name)s cannot be built in %(territoryType)s territory. Valid territories: %(validTerritories)s");
result.parameters.territoryType = {"context": "Territory type", "message": territoryType};
// gui code will join this array to a string
result.parameters.validTerritories = {"context": "Territory type list", "list": this.GetTerritories()};
return result; // Fail
}
@ -218,7 +229,7 @@ BuildRestrictions.prototype.CheckPlacement = function()
if ((cmpWaterManager.GetWaterLevel(pos.x + sz, pos.y + cz) - cmpTerrain.GetGroundLevel(pos.x + sz, pos.y + cz)) < 1.0 // front
|| (cmpWaterManager.GetWaterLevel(pos.x - sz, pos.y - cz) - cmpTerrain.GetGroundLevel(pos.x - sz, pos.y - cz)) > 2.0) // back
{
result.message = name+" must be built on a valid shoreline";
result.message = markForTranslation("%(name)s must be built on a valid shoreline");
return result; // Fail
}
}
@ -242,7 +253,9 @@ BuildRestrictions.prototype.CheckPlacement = function()
var nearEnts = cmpRangeManager.ExecuteQuery(this.entity, 0, dist, [cmpPlayer.GetPlayerID()], IID_BuildRestrictions).filter(filter);
if (nearEnts.length)
{
result.message = name+" too close to a "+cat+", must be at least "+ +this.template.Distance.MinDistance+" units away";
result.message = markForTranslation("%(name)s too close to a %(category)s, must be at least %(distance)s meters away");
result.parameters.category = cat;
result.parameters.distance = this.template.Distance.MinDistance;
return result; // Fail
}
}
@ -252,7 +265,9 @@ BuildRestrictions.prototype.CheckPlacement = function()
var nearEnts = cmpRangeManager.ExecuteQuery(this.entity, 0, dist, [cmpPlayer.GetPlayerID()], IID_BuildRestrictions).filter(filter);
if (!nearEnts.length)
{
result.message = name+" too far away from a "+cat+", must be within "+ +this.template.Distance.MaxDistance+" units";
result.message = markForTranslation("%(name)s too far from a %(category)s, must be within %(distance)s meters");
result.parameters.category = cat;
result.parameters.distance = this.template.Distance.MinDistance;
return result; // Fail
}
}
@ -279,4 +294,13 @@ BuildRestrictions.prototype.HasTerritory = function(territory)
return (this.GetTerritories().indexOf(territory) != -1);
};
// Translation: Territory types being displayed as part of a list like "Valid territories: own, ally".
markForTranslationWithContext("Territory type list", "own");
// Translation: Territory types being displayed as part of a list like "Valid territories: own, ally".
markForTranslationWithContext("Territory type list", "ally");
// Translation: Territory types being displayed as part of a list like "Valid territories: own, ally".
markForTranslationWithContext("Territory type list", "neutral");
// Translation: Territory types being displayed as part of a list like "Valid territories: own, ally".
markForTranslationWithContext("Territory type list", "enemy");
Engine.RegisterComponentType(IID_BuildRestrictions, "BuildRestrictions", BuildRestrictions);

View File

@ -783,7 +783,7 @@ GuiInterface.prototype.GetNeededResources = function(player, amounts)
GuiInterface.prototype.AddTimeNotification = function(notification)
{
var time = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer).GetTime();
notification.endTime = notification.time + time;
notification.endTime = notification.duration + time;
notification.id = ++this.timeNotificationID;
this.timeNotifications.push(notification);
this.timeNotifications.sort(function (n1, n2){return n2.endTime - n1.endTime});
@ -802,32 +802,19 @@ GuiInterface.prototype.DeleteTimeNotification = function(notificationID)
}
};
GuiInterface.prototype.GetTimeNotificationText = function(playerID)
GuiInterface.prototype.GetTimeNotifications = function(playerID)
{
var formatTime = function(time)
{
// add 1000 ms to get ceiled instead of floored millisecons
// displaying 00:00 for a full second isn't nice
time += 1000;
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 ? hours + ':' : "") + (minutes < 10 ? '0' + minutes : minutes) + ':' + (seconds < 10 ? '0' + seconds : seconds);
};
var time = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer).GetTime();
var text = "";
for each (var n in this.timeNotifications)
var toDelete = [];
for (var n of this.timeNotifications)
{
if (time >= n.endTime)
{
// delete the notification and start over
this.DeleteTimeNotification(n.id);
return this.GetTimeNotificationText(playerID);
}
if (n.players.indexOf(playerID) >= 0)
text += n.message.replace("%T",formatTime(n.endTime - time))+"\n";
n.time = n.endTime - time;
if (n.time < 0)
toDelete.push(n.id);
}
return text;
for (var id of toDelete)
this.DeleteTimeNotification(id);
return this.timeNotifications;
};
GuiInterface.prototype.PushNotification = function(notification)
@ -868,7 +855,7 @@ GuiInterface.prototype.GetFormationInfoFromTemplate = function(player, data)
if (!template || !template.Formation)
return r;
r.name = template.Formation.FormationName;
r.tooltip = template.Formation.DisabledTooltip;
r.tooltip = template.Formation.DisabledTooltip || "";
return r;
};
@ -1027,8 +1014,9 @@ GuiInterface.prototype.DisplayRallyPoint = function(player, cmd)
*
* Returns result object from CheckPlacement:
* {
* "success": true iff the placement is valid, else false
* "message": message to display in UI for invalid placement, else empty string
* "success": true iff the placement is valid, else false
* "message": message to display in UI for invalid placement, else ""
"parameters": parameters to use in the message
* }
*/
GuiInterface.prototype.SetBuildingPlacementPreview = function(player, cmd)
@ -1036,6 +1024,7 @@ GuiInterface.prototype.SetBuildingPlacementPreview = function(player, cmd)
var result = {
"success": false,
"message": "",
"parameters": {},
}
// See if we're changing template
@ -1981,7 +1970,7 @@ var exposedFunctions = {
"GetIncomingAttacks": 1,
"GetNeededResources": 1,
"GetNextNotification": 1,
"GetTimeNotificationText": 1,
"GetTimeNotifications": 1,
"GetAvailableFormations": 1,
"GetFormationRequirements": 1,

View File

@ -36,6 +36,12 @@ Player.prototype.Init = function()
this.cheatsEnabled = false;
this.cheatTimeMultiplier = 1;
this.heroes = [];
this.resourceNames = {
"food": markForTranslation("Food"),
"wood": markForTranslation("Wood"),
"metal": markForTranslation("Metal"),
"stone": markForTranslation("Stone"),
}
Engine.QueryInterface(SYSTEM_ENTITY, IID_EndGameManager).CheckConquestCriticalEntities();
};
@ -212,10 +218,38 @@ Player.prototype.SubtractResourcesOrNotify = function(amounts)
// If we don't have enough resources, send a notification to the player
if (amountsNeeded)
{
var formatted = [];
var parameters = {};
var i = 0;
for (var type in amountsNeeded)
formatted.push(amountsNeeded[type] + " " + type[0].toUpperCase() + type.substr(1) );
var notification = {"player": this.playerID, "message": "Insufficient resources - " + formatted.join(", ")};
{
i++;
parameters["resourceType"+i] = this.resourceNames[type];
parameters["resourceAmount"+i] = amountsNeeded[type];
}
var msg = "";
// when marking strings for translations, you need to include the actual string,
// not some way to derive the string
if (i < 1)
warn("Amounts needed but no amounts given?");
else if (i == 1)
msg = markForTranslation("Insufficient resources - %(resourceAmount1)s %(resourceType1)s");
else if (i == 2)
msg = markForTranslation("Insufficient resources - %(resourceAmount1)s %(resourceType1)s, %(resourceAmount2)s %(resourceType2)s");
else if (i == 3)
msg = markForTranslation("Insufficient resources - %(resourceAmount1)s %(resourceType1)s, %(resourceAmount2)s %(resourceType2)s, %(resourceAmount3)s %(resourceType3)s");
else if (i == 4)
msg = markForTranslation("Insufficient resources - %(resourceAmount1)s %(resourceType1)s, %(resourceAmount2)s %(resourceType2)s, %(resourceAmount3)s %(resourceType3)s, %(resourceAmount4)s %(resourceType4)s");
else
warn("Localisation: Strings are not localised for more than 4 resources");
var notification = {
"player": this.playerID,
"message": msg,
"parameters": parameters,
"translateMessage": true,
"translateParameters": ["resourceType1", "resourceType2", "resourceType3", "resourceType4"],
};
var cmpGUIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
cmpGUIInterface.PushNotification(notification);
return false;

View File

@ -50,14 +50,18 @@ Wonder.prototype.ResetTimer = function(ownerID)
players.push(i);
this.otherMessage = cmpGuiInterface.AddTimeNotification({
"message": cmpPlayer.GetName() + " will have won in %T",
"message": markForTranslation("%(player)s will have won in %(time)s"),
"players": players,
"time": +this.template.TimeTillVictory*1000
"duration": +this.template.TimeTillVictory*1000,
"parameters": {"player": cmpPlayer.GetName()},
"translateMessage": true,
"translateParameters": [],
});
this.ownMessage = cmpGuiInterface.AddTimeNotification({
"message": "You will have won in %T",
"message": markForTranslation("You will have won in %(time)s"),
"players": [ownerID],
"time": +this.template.TimeTillVictory*1000
"duration": +this.template.TimeTillVictory*1000,
"translateMessage": true,
});
this.timer = cmpTimer.SetTimeout(SYSTEM_ENTITY, IID_EndGameManager, "MarkPlayerAsWon", +this.template.TimeTillVictory*1000, ownerID);
};

View File

@ -899,7 +899,7 @@ void GuiScriptingInit(ScriptInterface& scriptInterface)
scriptInterface.RegisterFunction<bool, &IsPaused>("IsPaused");
scriptInterface.RegisterFunction<void, bool, &SetPaused>("SetPaused");
scriptInterface.RegisterFunction<int, &GetFps>("GetFPS");
scriptInterface.RegisterFunction<std::wstring, int, &GetBuildTimestamp>("BuildTime");
scriptInterface.RegisterFunction<std::wstring, int, &GetBuildTimestamp>("GetBuildTimestamp");
// User report functions
scriptInterface.RegisterFunction<bool, &IsUserReportEnabled>("IsUserReportEnabled");