Index: ps/trunk/binaries/data/mods/public/gui/common/tooltips.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/common/tooltips.js
+++ ps/trunk/binaries/data/mods/public/gui/common/tooltips.js
@@ -621,6 +621,26 @@
});
}
+/**
+ * Returns the resources this entity supplies in the specified entity's tooltip
+ */
+function getResourceSupplyTooltip(template)
+{
+ if (!template.supply)
+ return "";
+
+ let supply = template.supply;
+ let type = supply.type[0] == "treasure" ? supply.type[1] : supply.type[0];
+
+ // Translation: Label in tooltip showing the resource type and quantity of a given resource supply.
+ return sprintf(translate("%(label)s %(component)s %(amount)s"), {
+ "label": headerFont(translate("Resource Supply:")),
+ "component": resourceIcon(type),
+ // Translation: Marks that a resource supply entity has an unending, infinite, supply of its resource.
+ "amount": Number.isFinite(+supply.amount) ? supply.amount : translate("∞")
+ });
+}
+
function getResourceTrickleTooltip(template)
{
if (!template.resourceTrickle)
Index: ps/trunk/binaries/data/mods/public/gui/reference/common/Buttons/CivInfoButton.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/reference/common/Buttons/CivInfoButton.js
+++ ps/trunk/binaries/data/mods/public/gui/reference/common/Buttons/CivInfoButton.js
@@ -0,0 +1,27 @@
+class CivInfoButton
+{
+ constructor(parentPage)
+ {
+ this.parentPage = parentPage;
+
+ this.civInfoButton = Engine.GetGUIObjectByName("civInfoButton");
+ this.civInfoButton.onPress = this.onPress.bind(this);
+ this.civInfoButton.caption = this.Caption;
+ this.civInfoButton.tooltip = colorizeHotkey(this.Tooltip, this.Hotkey);
+ }
+
+ onPress()
+ {
+ Engine.PopGuiPage({ "civ": this.parentPage.activeCiv, "nextPage": "page_civinfo.xml" });
+ }
+
+}
+
+CivInfoButton.prototype.Caption =
+ translate("Civilization Overview");
+
+CivInfoButton.prototype.Hotkey =
+ "civinfo";
+
+CivInfoButton.prototype.Tooltip =
+ translate("%(hotkey)s: Switch to Civilization Overview.");
Index: ps/trunk/binaries/data/mods/public/gui/reference/common/Buttons/CivInfoButton.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/reference/common/Buttons/CivInfoButton.xml
+++ ps/trunk/binaries/data/mods/public/gui/reference/common/Buttons/CivInfoButton.xml
@@ -0,0 +1,8 @@
+
+
Index: ps/trunk/binaries/data/mods/public/gui/reference/common/Buttons/CloseButton.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/reference/common/Buttons/CloseButton.js
+++ ps/trunk/binaries/data/mods/public/gui/reference/common/Buttons/CloseButton.js
@@ -0,0 +1,16 @@
+class CloseButton
+{
+ constructor(parentPage)
+ {
+ this.closeButton = Engine.GetGUIObjectByName("closeButton");
+ this.closeButton.onPress = parentPage.closePage.bind(parentPage);
+ this.closeButton.caption = this.Caption;
+ this.closeButton.tooltip = colorizeHotkey(parentPage.CloseButtonTooltip, this.Hotkey);
+ }
+}
+
+CloseButton.prototype.Caption =
+ translate("Close");
+
+CloseButton.prototype.Hotkey =
+ "close";
Index: ps/trunk/binaries/data/mods/public/gui/reference/common/Buttons/CloseButton.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/reference/common/Buttons/CloseButton.xml
+++ ps/trunk/binaries/data/mods/public/gui/reference/common/Buttons/CloseButton.xml
@@ -0,0 +1,8 @@
+
+
Index: ps/trunk/binaries/data/mods/public/gui/reference/common/Dropdowns/CivSelectDropdown.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/reference/common/Dropdowns/CivSelectDropdown.js
+++ ps/trunk/binaries/data/mods/public/gui/reference/common/Dropdowns/CivSelectDropdown.js
@@ -0,0 +1,63 @@
+class CivSelectDropdown
+{
+ constructor(civData)
+ {
+ this.handlers = new Set();
+
+ let civList = Object.keys(civData).map(civ => ({
+ "name": civData[civ].Name,
+ "code": civ,
+ })).sort(sortNameIgnoreCase);
+
+ this.civSelectionHeading = Engine.GetGUIObjectByName("civSelectionHeading");
+ this.civSelectionHeading.caption = this.Caption;
+
+ this.civSelection = Engine.GetGUIObjectByName("civSelection");
+ this.civSelection.list = civList.map(c => c.name);
+ this.civSelection.list_data = civList.map(c => c.code);
+ this.civSelection.onSelectionChange = () => this.onSelectionChange(this);
+ }
+
+ onSelectionChange()
+ {
+ let civCode = this.civSelection.list_data[this.civSelection.selected];
+
+ for (let handler of this.handlers)
+ handler(civCode);
+ }
+
+ registerHandler(handler)
+ {
+ this.handlers.add(handler);
+ }
+
+ unregisterHandler(handler)
+ {
+ this.handlers.delete(handler);
+ }
+
+ hasCivs()
+ {
+ return this.civSelection.list.length != 0;
+ }
+
+ selectCiv(civCode)
+ {
+ if (!civCode)
+ return;
+
+ let index = this.civSelection.list_data.indexOf(civCode);
+ if (index == -1)
+ return;
+
+ this.civSelection.selected = index;
+ }
+
+ selectFirstCiv()
+ {
+ this.civSelection.selected = 0;
+ }
+}
+
+CivSelectDropdown.prototype.Caption =
+ translate("Civilization:");
Index: ps/trunk/binaries/data/mods/public/gui/reference/common/Dropdowns/CivSelectDropdown.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/reference/common/Dropdowns/CivSelectDropdown.xml
+++ ps/trunk/binaries/data/mods/public/gui/reference/common/Dropdowns/CivSelectDropdown.xml
@@ -0,0 +1,12 @@
+
+
Index: ps/trunk/binaries/data/mods/public/gui/reference/common/ReferencePage.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/reference/common/ReferencePage.js
+++ ps/trunk/binaries/data/mods/public/gui/reference/common/ReferencePage.js
@@ -0,0 +1,67 @@
+/**
+ * This class contains code common to the Structure Tree, Template Viewer, and any other "Reference Page" that may be added in the future.
+ */
+class ReferencePage
+{
+ constructor()
+ {
+ this.civData = loadCivData(true, false);
+
+ this.TemplateLoader = new TemplateLoader();
+ this.TemplateLister = new TemplateLister(this.TemplateLoader);
+ this.TemplateParser = new TemplateParser(this.TemplateLoader);
+
+ this.activeCiv = this.TemplateLoader.DefaultCiv;
+
+ this.currentTemplateLists = {};
+ }
+
+ setActiveCiv(civCode)
+ {
+ if (civCode == this.TemplateLoader.DefaultCiv)
+ return;
+
+ this.activeCiv = civCode;
+
+ this.currentTemplateLists = this.TemplateLister.compileTemplateLists(this.activeCiv, this.civData);
+ this.TemplateParser.deriveModifications(this.activeCiv);
+ this.TemplateParser.derivePhaseList(this.currentTemplateLists.techs.keys(), this.activeCiv);
+ }
+
+ /**
+ * Concatanates the return values of the array of passed functions.
+ *
+ * @param {object} template
+ * @param {array} textFunctions
+ * @param {string} joiner
+ * @return {string} The built text.
+ */
+ static buildText(template, textFunctions=[], joiner="\n")
+ {
+ return textFunctions.map(func => func(template)).filter(tip => tip).join(joiner);
+ }
+}
+
+ReferencePage.prototype.IconPath = "session/portraits/";
+
+/**
+ * List of functions that get the statistics of any template or entity,
+ * formatted in such a way as to appear in a tooltip.
+ *
+ * The functions listed are defined in gui/common/tooltips.js
+ */
+ReferencePage.prototype.StatsFunctions = [
+ getHealthTooltip,
+ getHealerTooltip,
+ getAttackTooltip,
+ getSplashDamageTooltip,
+ getArmorTooltip,
+ getGarrisonTooltip,
+ getProjectilesTooltip,
+ getSpeedTooltip,
+ getGatherTooltip,
+ getResourceSupplyTooltip,
+ getPopulationBonusTooltip,
+ getResourceTrickleTooltip,
+ getLootTooltip
+];
Index: ps/trunk/binaries/data/mods/public/gui/reference/common/TemplateLister.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/reference/common/TemplateLister.js
+++ ps/trunk/binaries/data/mods/public/gui/reference/common/TemplateLister.js
@@ -0,0 +1,137 @@
+/**
+ * This class compiles and stores lists of which templates can be built/trained/researched by other templates.
+ */
+class TemplateLister
+{
+ constructor(TemplateLoader)
+ {
+ this.TemplateLoader = TemplateLoader;
+ this.templateLists = new Map();
+ }
+
+ /**
+ * Compile lists of templates buildable/trainable/researchable of a given civ.
+ *
+ * @param {object} civCode
+ * @param {object} civData - Data defining every civ in the game.
+ */
+ compileTemplateLists(civCode, civData)
+ {
+ if (this.hasTemplateLists(civCode))
+ return this.templateLists.get(civCode);
+
+ let templatesToParse = civData[civCode].StartEntities.map(entity => entity.Template);
+
+ let templateLists = {
+ "units": new Map(),
+ "structures": new Map(),
+ "techs": new Map(),
+ "wallsetPieces": new Map()
+ };
+
+ do
+ {
+ const templatesThisIteration = templatesToParse;
+ templatesToParse = [];
+
+ for (let templateBeingParsed of templatesThisIteration)
+ {
+ let list = this.deriveTemplateListsFromTemplate(templateBeingParsed, civCode);
+ for (let type in list)
+ for (let templateName of list[type])
+ if (!templateLists[type].has(templateName))
+ {
+ templateLists[type].set(templateName, [templateBeingParsed]);
+ if (type != "techs")
+ templatesToParse.push(templateName);
+ }
+ else if (templateLists[type].get(templateName).indexOf(templateBeingParsed) == -1)
+ templateLists[type].get(templateName).push(templateBeingParsed);
+ }
+ } while (templatesToParse.length);
+
+ // Expand/filter tech pairs
+ for (let [techCode, researcherList] of templateLists.techs)
+ {
+ if (!this.TemplateLoader.isPairTech(techCode))
+ continue;
+
+ for (let subTech of this.TemplateLoader.loadTechnologyPairTemplate(techCode, civCode).techs)
+ if (!templateLists.techs.has(subTech))
+ templateLists.techs.set(subTech, researcherList);
+ else
+ for (let researcher of researcherList)
+ if (templateLists.techs.get(subTech).indexOf(researcher) == -1)
+ templateLists.techs.get(subTech).push(researcher);
+
+ templateLists.techs.delete(techCode);
+ }
+
+ // Remove wallset pieces, as they've served their purpose.
+ delete templateLists.wallsetPieces;
+
+ this.templateLists.set(civCode, templateLists);
+ return this.templateLists.get(civCode);
+ }
+
+ /**
+ * Returns a civ's template list.
+ *
+ * Note: this civ must have gone through the compilation process above!
+ *
+ * @param {string} civCode
+ * @return {object} containing lists of template names, grouped by type.
+ */
+ getTemplateLists(civCode)
+ {
+ if (this.hasTemplateLists(civCode))
+ return this.templateLists.get(civCode);
+
+ error("Template lists of \"" + civCode + "\" requested, but this civ has not been loaded.");
+ return {};
+ }
+
+ /**
+ * Returns whether the civ of the given civCode has been loaded into cache.
+ *
+ * @param {string} civCode
+ * @return {boolean}
+ */
+ hasTemplateLists(civCode)
+ {
+ return this.templateLists.has(civCode);
+ }
+
+ /**
+ * Compiles lists of buildable, trainable, or researchable entities from
+ * a named template.
+ */
+ deriveTemplateListsFromTemplate(templateName, civCode)
+ {
+ if (!templateName || !Engine.TemplateExists(templateName))
+ return {};
+
+ // If this is a non-promotion variant (ie. {civ}_support_female_citizen_house)
+ // then it is functionally equivalent to another unit being processed, so skip it.
+ if (this.TemplateLoader.getBaseTemplateName(templateName, civCode) != templateName)
+ return {};
+
+ let template = this.TemplateLoader.loadEntityTemplate(templateName, civCode);
+
+ let templateLists = this.TemplateLoader.deriveProductionQueue(template, civCode);
+ templateLists.structures = this.TemplateLoader.deriveBuildQueue(template, civCode);
+
+ if (template.WallSet)
+ {
+ templateLists.wallsetPieces = [];
+ for (let segment in template.WallSet.Templates)
+ {
+ segment = template.WallSet.Templates[segment].replace(/\{(civ|native)\}/g, civCode);
+ if (Engine.TemplateExists(segment))
+ templateLists.wallsetPieces.push(segment);
+ }
+ }
+
+ return templateLists;
+ }
+}
Index: ps/trunk/binaries/data/mods/public/gui/reference/common/TemplateLoader.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/reference/common/TemplateLoader.js
+++ ps/trunk/binaries/data/mods/public/gui/reference/common/TemplateLoader.js
@@ -0,0 +1,251 @@
+/**
+ * This class handles the loading of files.
+ */
+class TemplateLoader
+{
+ constructor()
+ {
+ /**
+ * Raw Data Caches.
+ */
+ this.auraData = {};
+ this.technologyData = {};
+ this.templateData = {};
+
+ /**
+ * Partly-composed data.
+ */
+ this.autoResearchTechList = this.findAllAutoResearchedTechs();
+ }
+
+ /**
+ * Loads raw aura template.
+ *
+ * Loads from local cache if available, else from file system.
+ *
+ * @param {string} templateName
+ * @return {object} Object containing raw template data.
+ */
+ loadAuraTemplate(templateName)
+ {
+ if (!(templateName in this.auraData))
+ {
+ let data = Engine.ReadJSONFile(this.AuraPath + templateName + ".json");
+ translateObjectKeys(data, this.AuraTranslateKeys);
+
+ this.auraData[templateName] = data;
+ }
+
+ return this.auraData[templateName];
+ }
+
+ /**
+ * Loads raw entity template.
+ *
+ * Loads from local cache if data present, else from file system.
+ *
+ * @param {string} templateName
+ * @param {string} civCode
+ * @return {object} Object containing raw template data.
+ */
+ loadEntityTemplate(templateName, civCode)
+ {
+ if (!(templateName in this.templateData))
+ {
+ // We need to clone the template because we want to perform some translations.
+ let data = clone(Engine.GetTemplate(templateName));
+ translateObjectKeys(data, this.EntityTranslateKeys);
+
+ if (data.Auras)
+ for (let auraID of data.Auras._string.split(/\s+/))
+ this.loadAuraTemplate(auraID);
+
+ if (data.Identity.Civ != this.DefaultCiv && civCode != this.DefaultCiv && data.Identity.Civ != civCode)
+ warn("The \"" + templateName + "\" template has a defined civ of \"" + data.Identity.Civ + "\". " +
+ "This does not match the currently selected civ \"" + civCode + "\".");
+
+ this.templateData[templateName] = data;
+ }
+
+ return this.templateData[templateName];
+ }
+
+ /**
+ * Loads raw technology template.
+ *
+ * Loads from local cache if available, else from file system.
+ *
+ * @param {string} templateName
+ * @return {object} Object containing raw template data.
+ */
+ loadTechnologyTemplate(templateName)
+ {
+ if (!(templateName in this.technologyData))
+ {
+ let data = Engine.ReadJSONFile(this.TechnologyPath + templateName + ".json");
+ translateObjectKeys(data, this.TechnologyTranslateKeys);
+
+ this.technologyData[templateName] = data;
+ }
+
+ return this.technologyData[templateName];
+ }
+
+ /**
+ * @param {string} templateName
+ * @param {string} civCode
+ * @return {object} Contains a list and the requirements of the techs in the pair
+ */
+ loadTechnologyPairTemplate(templateName, civCode)
+ {
+ let template = this.loadTechnologyTemplate(templateName);
+ return {
+ "techs": [template.top, template.bottom],
+ "reqs": DeriveTechnologyRequirements(template, civCode)
+ };
+ }
+
+ deriveProductionQueue(template, civCode)
+ {
+ let production = {
+ "techs": [],
+ "units": []
+ };
+
+ if (!template.ProductionQueue)
+ return production;
+
+ if (template.ProductionQueue.Entities && template.ProductionQueue.Entities._string)
+ for (let templateName of template.ProductionQueue.Entities._string.split(" "))
+ {
+ templateName = templateName.replace(/\{(civ|native)\}/g, civCode);
+ if (Engine.TemplateExists(templateName))
+ production.units.push(this.getBaseTemplateName(templateName, civCode));
+ }
+
+ if (template.ProductionQueue.Technologies && template.ProductionQueue.Technologies._string)
+ for (let technologyName of template.ProductionQueue.Technologies._string.split(" "))
+ {
+ if (technologyName.indexOf("{civ}") != -1)
+ {
+ let civTechName = technologyName.replace("{civ}", civCode);
+ technologyName = TechnologyTemplateExists(civTechName) ? civTechName : technologyName.replace("{civ}", "generic");
+ }
+
+ if (this.isPairTech(technologyName))
+ Array.prototype.push.apply(production.techs, this.loadTechnologyPairTemplate(technologyName, civCode).techs);
+ else
+ production.techs.push(technologyName);
+ }
+
+ return production;
+ }
+
+ deriveBuildQueue(template, civCode)
+ {
+ let buildQueue = [];
+
+ if (!template.Builder || !template.Builder.Entities._string)
+ return buildQueue;
+
+ for (let build of template.Builder.Entities._string.split(" "))
+ {
+ build = build.replace(/\{(civ|native)\}/g, civCode);
+ if (Engine.TemplateExists(build))
+ buildQueue.push(build);
+ }
+
+ return buildQueue;
+ }
+
+ deriveModifications(civCode)
+ {
+ let techData = [];
+ for (let techName of this.autoResearchTechList)
+ techData.push(GetTechnologyBasicDataHelper(this.loadTechnologyTemplate(techName), civCode));
+
+ return DeriveModificationsFromTechnologies(techData);
+ }
+
+ /**
+ * Crudely iterates through every tech JSON file and identifies those
+ * that are auto-researched.
+ *
+ * @return {array} List of techs that are researched automatically
+ */
+ findAllAutoResearchedTechs()
+ {
+ let techList = [];
+ for (let templateName of listFiles(this.TechnologyPath, ".json", true))
+ {
+ let data = this.loadTechnologyTemplate(templateName);
+ if (data && data.autoResearch)
+ techList.push(templateName);
+ }
+ return techList;
+ }
+
+ /**
+ * Returns the name of a template's base form (without `_house`, `_trireme`, or similar),
+ * or the template's own name if the base is of a different promotion rank.
+ */
+ getBaseTemplateName(templateName, civCode)
+ {
+ if (!templateName || !Engine.TemplateExists(templateName))
+ return undefined;
+
+ templateName = removeFiltersFromTemplateName(templateName);
+ let template = this.loadEntityTemplate(templateName, civCode);
+
+ if (!dirname(templateName) || dirname(template["@parent"]) != dirname(templateName))
+ return templateName;
+
+ let parentTemplate = this.loadEntityTemplate(template["@parent"], civCode);
+
+ if (parentTemplate.Identity && parentTemplate.Identity.Rank &&
+ parentTemplate.Identity.Rank != template.Identity.Rank)
+ return templateName;
+
+ if (!parentTemplate.Cost)
+ return templateName;
+
+ if (parentTemplate.Upgrade)
+ for (let upgrade in parentTemplate.Upgrade)
+ if (parentTemplate.Upgrade[upgrade].Entity)
+ return templateName;
+
+ for (let res in parentTemplate.Cost.Resources)
+ if (+parentTemplate.Cost.Resources[res])
+ return this.getBaseTemplateName(template["@parent"], civCode);
+
+ return templateName;
+ }
+
+ isPairTech(technologyCode)
+ {
+ return !!this.loadTechnologyTemplate(technologyCode).top;
+ }
+
+ isPhaseTech(technologyCode)
+ {
+ return basename(technologyCode).startsWith("phase");
+ }
+
+}
+
+/**
+ * Paths to certain files.
+ *
+ * It might be nice if we could get these from somewhere, instead of having them hardcoded here.
+ */
+TemplateLoader.prototype.AuraPath = "simulation/data/auras/";
+TemplateLoader.prototype.TechnologyPath = "simulation/data/technologies/";
+
+TemplateLoader.prototype.DefaultCiv = "gaia";
+
+/**
+ * Keys of template values that are to be translated on load.
+ */
+TemplateLoader.prototype.AuraTranslateKeys = ["auraName", "auraDescription"];
+TemplateLoader.prototype.EntityTranslateKeys = ["GenericName", "SpecificName", "Tooltip", "History"];
+TemplateLoader.prototype.TechnologyTranslateKeys = ["genericName", "tooltip", "description"];
Index: ps/trunk/binaries/data/mods/public/gui/reference/common/TemplateParser.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/reference/common/TemplateParser.js
+++ ps/trunk/binaries/data/mods/public/gui/reference/common/TemplateParser.js
@@ -0,0 +1,321 @@
+/**
+ * This class parses and stores parsed template data.
+ */
+class TemplateParser
+{
+ constructor(TemplateLoader)
+ {
+ this.TemplateLoader = TemplateLoader;
+
+ /**
+ * Parsed Data Stores
+ */
+ this.entities = {};
+ this.techs = {};
+ this.phases = {};
+ this.modifiers = {};
+
+ this.phaseList = [];
+ }
+
+ /**
+ * Load and parse a structure, unit, resource, etc from its entity template file.
+ *
+ * @param {string} templateName
+ * @param {string} civCode
+ * @return {(object|null)} Sanitized object about the requested template or null if entity template doesn't exist.
+ */
+ getEntity(templateName, civCode)
+ {
+ if (templateName in this.entities)
+ return this.entities[templateName];
+
+ if (!Engine.TemplateExists(templateName))
+ return null;
+
+ let template = this.TemplateLoader.loadEntityTemplate(templateName, civCode);
+ let parsed = GetTemplateDataHelper(template, null, this.TemplateLoader.auraData, this.modifiers[civCode] || {});
+ parsed.name.internal = templateName;
+
+ parsed.history = template.Identity.History;
+
+ parsed.production = this.TemplateLoader.deriveProductionQueue(template, civCode);
+ if (template.Builder)
+ parsed.builder = this.TemplateLoader.deriveBuildQueue(template, civCode);
+
+ // Set the minimum phase that this entity is available.
+ // For gaia objects, this is meaningless.
+ if (!parsed.requiredTechnology)
+ parsed.phase = this.phaseList[0];
+ else if (this.TemplateLoader.isPhaseTech(parsed.requiredTechnology))
+ parsed.phase = this.getActualPhase(parsed.requiredTechnology);
+ else
+ parsed.phase = this.getPhaseOfTechnology(parsed.requiredTechnology, civCode);
+
+ if (template.Identity.Rank)
+ parsed.promotion = {
+ "current_rank": template.Identity.Rank,
+ "entity": template.Promotion && template.Promotion.Entity
+ };
+
+ if (template.ResourceSupply)
+ parsed.supply = {
+ "type": template.ResourceSupply.Type.split("."),
+ "amount": template.ResourceSupply.Amount,
+ };
+
+ if (parsed.upgrades)
+ parsed.upgrades = this.getActualUpgradeData(parsed.upgrades, civCode);
+
+ if (parsed.wallSet)
+ {
+ parsed.wallset = {};
+
+ if (!parsed.upgrades)
+ parsed.upgrades = [];
+
+ // Note: An assumption is made here that wall segments all have the same armor and auras
+ let struct = this.getEntity(parsed.wallSet.templates.long, civCode);
+ parsed.armour = struct.armour;
+ parsed.auras = struct.auras;
+
+ // For technology cost multiplier, we need to use the tower
+ struct = this.getEntity(parsed.wallSet.templates.tower, civCode);
+ parsed.techCostMultiplier = struct.techCostMultiplier;
+
+ let health;
+
+ for (let wSegm in parsed.wallSet.templates)
+ {
+ if (wSegm == "fort" || wSegm == "curves")
+ continue;
+
+ let wPart = this.getEntity(parsed.wallSet.templates[wSegm], civCode);
+ parsed.wallset[wSegm] = wPart;
+
+ for (let research of wPart.production.techs)
+ parsed.production.techs.push(research);
+
+ if (wPart.upgrades)
+ Array.prototype.push.apply(parsed.upgrades, wPart.upgrades);
+
+ if (["gate", "tower"].indexOf(wSegm) != -1)
+ continue;
+
+ if (!health)
+ {
+ health = { "min": wPart.health, "max": wPart.health };
+ continue;
+ }
+
+ health.min = Math.min(health.min, wPart.health);
+ health.max = Math.max(health.max, wPart.health);
+ }
+
+ if (parsed.wallSet.templates.curves)
+ for (let curve of parsed.wallSet.templates.curves)
+ {
+ let wPart = this.getEntity(curve, civCode);
+ health.min = Math.min(health.min, wPart.health);
+ health.max = Math.max(health.max, wPart.health);
+ }
+
+ if (health.min == health.max)
+ parsed.health = health.min;
+ else
+ parsed.health = sprintf(translate("%(health_min)s to %(health_max)s"), {
+ "health_min": health.min,
+ "health_max": health.max
+ });
+ }
+
+ this.entities[templateName] = parsed;
+ return parsed;
+ }
+
+ /**
+ * Load and parse technology from json template.
+ *
+ * @param {string} technologyName
+ * @param {string} civCode
+ * @return {object} Sanitized data about the requested technology.
+ */
+ getTechnology(technologyName, civCode)
+ {
+ if (!TechnologyTemplateExists(technologyName))
+ return null;
+
+ if (this.TemplateLoader.isPhaseTech(technologyName) && technologyName in this.phases)
+ return this.phases[technologyName];
+
+ if (!(civCode in this.techs))
+ this.techs[civCode] = {};
+ else if (technologyName in this.techs[civCode])
+ return this.techs[civCode][technologyName];
+
+ let template = this.TemplateLoader.loadTechnologyTemplate(technologyName);
+ let tech = GetTechnologyDataHelper(template, civCode, g_ResourceData);
+ tech.name.internal = technologyName;
+
+ if (template.pair !== undefined)
+ {
+ tech.pair = template.pair;
+ tech.reqs = this.mergeRequirements(tech.reqs, this.TemplateLoader.loadTechnologyPairTemplate(template.pair).reqs);
+ }
+
+ if (this.TemplateLoader.isPhaseTech(technologyName))
+ {
+ tech.actualPhase = technologyName;
+ if (tech.replaces !== undefined)
+ tech.actualPhase = tech.replaces[0];
+ this.phases[technologyName] = tech;
+ }
+ else
+ this.techs[civCode][technologyName] = tech;
+ return tech;
+ }
+
+ /**
+ * @param {string} phaseCode
+ * @param {string} civCode
+ * @return {object} Sanitized object containing phase data
+ */
+ getPhase(phaseCode, civCode)
+ {
+ return this.getTechnology(phaseCode, civCode);
+ }
+
+ /**
+ * Provided with an array containing basic information about possible
+ * upgrades, such as that generated by globalscript's GetTemplateDataHelper,
+ * this function loads the actual template data of the upgrades, overwrites
+ * certain values within, then passes an array containing the template data
+ * back to caller.
+ */
+ getActualUpgradeData(upgradesInfo, civCode)
+ {
+ let newUpgrades = [];
+ for (let upgrade of upgradesInfo)
+ {
+ upgrade.entity = upgrade.entity.replace(/\{(civ|native)\}/g, civCode);
+
+ let data = GetTemplateDataHelper(this.TemplateLoader.loadEntityTemplate(upgrade.entity, civCode), null, this.TemplateLoader.auraData);
+ data.name.internal = upgrade.entity;
+ data.cost = upgrade.cost;
+ data.icon = upgrade.icon || data.icon;
+ data.tooltip = upgrade.tooltip || data.tooltip;
+ data.requiredTechnology = upgrade.requiredTechnology || data.requiredTechnology;
+
+ if (!data.requiredTechnology)
+ data.phase = this.phaseList[0];
+ else if (this.TemplateLoader.isPhaseTech(data.requiredTechnology))
+ data.phase = this.getActualPhase(data.requiredTechnology);
+ else
+ data.phase = this.getPhaseOfTechnology(data.requiredTechnology, civCode);
+
+ newUpgrades.push(data);
+ }
+ return newUpgrades;
+ }
+
+ /**
+ * Determines and returns the phase in which a given technology can be
+ * first researched. Works recursively through the given tech's
+ * pre-requisite and superseded techs if necessary.
+ *
+ * @param {string} techName - The Technology's name
+ * @param {string} civCode
+ * @return The name of the phase the technology belongs to, or false if
+ * the current civ can't research this tech
+ */
+ getPhaseOfTechnology(techName, civCode)
+ {
+ let phaseIdx = -1;
+
+ if (basename(techName).startsWith("phase"))
+ {
+ if (!this.phases[techName].reqs)
+ return false;
+
+ phaseIdx = this.phaseList.indexOf(this.getActualPhase(techName));
+ if (phaseIdx > 0)
+ return this.phaseList[phaseIdx - 1];
+ }
+
+ let techReqs = this.getTechnology(techName, civCode).reqs;
+ if (!techReqs)
+ return false;
+
+ for (let option of techReqs)
+ if (option.techs)
+ for (let tech of option.techs)
+ {
+ if (basename(tech).startsWith("phase"))
+ return tech;
+ if (basename(tech).startsWith("pair"))
+ continue;
+ phaseIdx = Math.max(phaseIdx, this.phaseList.indexOf(this.getPhaseOfTechnology(tech, civCode)));
+ }
+ return this.phaseList[phaseIdx] || false;
+ }
+
+ /**
+ * Returns the actual phase a certain phase tech represents or stands in for.
+ *
+ * For example, passing `phase_city_athen` would result in `phase_city`.
+ *
+ * @param {string} phaseName
+ * @return {string}
+ */
+ getActualPhase(phaseName)
+ {
+ if (this.phases[phaseName])
+ return this.phases[phaseName].actualPhase;
+
+ warn("Unrecognized phase (" + phaseName + ")");
+ return this.phaseList[0];
+ }
+
+ getModifiers(civCode)
+ {
+ return this.modifiers[civCode];
+ }
+
+ deriveModifications(civCode)
+ {
+ this.modifiers[civCode] = this.TemplateLoader.deriveModifications(civCode);
+ }
+
+ derivePhaseList(technologyList, civCode)
+ {
+ // Load all of a civ's specific phase technologies
+ for (let techcode of technologyList)
+ if (this.TemplateLoader.isPhaseTech(techcode))
+ this.getTechnology(techcode, civCode);
+
+ this.phaseList = UnravelPhases(this.phases);
+
+ // Make sure all required generic phases are loaded and parsed
+ for (let phasecode of this.phaseList)
+ this.getTechnology(phasecode, civCode);
+ }
+
+ mergeRequirements(reqsA, reqsB)
+ {
+ if (!reqsA || !reqsB)
+ return false;
+
+ let finalReqs = clone(reqsA);
+
+ for (let option of reqsB)
+ for (let type in option)
+ for (let opt in finalReqs)
+ {
+ if (!finalReqs[opt][type])
+ finalReqs[opt][type] = [];
+ Array.prototype.push.apply(finalReqs[opt][type], option[type]);
+ }
+
+ return finalReqs;
+ }
+}
Index: ps/trunk/binaries/data/mods/public/gui/reference/common/common.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/reference/common/common.js
+++ ps/trunk/binaries/data/mods/public/gui/reference/common/common.js
@@ -0,0 +1,28 @@
+/**
+ * This needs to stay in the global scope, as it is used by various functions
+ * within gui/common/tooltip.js
+ */
+var g_ResourceData = new Resources();
+
+var g_Page;
+
+/**
+ * This is needed because getEntityCostTooltip in tooltip.js needs to get
+ * the template data of the different wallSet pieces. In the session this
+ * function does some caching, but here we do that in the TemplateLoader
+ * class already.
+ */
+function GetTemplateData(templateName)
+{
+ let template = g_Page.TemplateLoader.loadEntityTemplate(templateName, g_Page.activeCiv);
+ return GetTemplateDataHelper(template, null, g_Page.TemplateLoader.auraData, g_Page.TemplateParser.getModifiers(g_Page.activeCiv));
+}
+
+/**
+ * This would ideally be an Engine method.
+ * Or part of globalscripts. Either would be better than here.
+ */
+function TechnologyTemplateExists(templateName)
+{
+ return Engine.FileExists(g_Page.TemplateLoader.TechnologyPath + templateName + ".json");
+}
Index: ps/trunk/binaries/data/mods/public/gui/reference/common/core.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/reference/common/core.js
+++ ps/trunk/binaries/data/mods/public/gui/reference/common/core.js
@@ -1,202 +0,0 @@
-var g_SelectedCiv = "gaia";
-
-/**
- * Compile lists of templates buildable/trainable/researchable of a given civ.
- *
- * @param {string} civCode - Code of the civ to get template list for. Optional,
- * defaults to g_SelectedCiv.
- * @return {object} containing lists of template names, grouped by type.
- */
-function compileTemplateLists(civCode)
-{
- if (!civCode || civCode == "gaia")
- return {};
-
- let templatesToParse = [];
- for (let entity of g_CivData[civCode].StartEntities)
- templatesToParse.push(entity.Template);
-
- let templateLists = {
- "units": new Map(),
- "structures": new Map(),
- "techs": new Map(),
- "wallsetPieces": new Map()
- };
-
- do {
- const templatesThisIteration = templatesToParse;
- templatesToParse = [];
-
- for (let templateBeingParsed of templatesThisIteration)
- {
- let list = getTemplateListsFromTemplate(templateBeingParsed);
- for (let type in list)
- for (let templateName of list[type])
- if (!templateLists[type].has(templateName))
- {
- templateLists[type].set(templateName, [templateBeingParsed]);
- if (type != "techs")
- templatesToParse.push(templateName);
- }
- else if (templateLists[type].get(templateName).indexOf(templateBeingParsed) == -1)
- templateLists[type].get(templateName).push(templateBeingParsed);
- }
- } while (templatesToParse.length);
-
- // Expand/filter tech pairs
- for (let [techCode, researcherList] of templateLists.techs)
- {
- if (!isPairTech(techCode))
- continue;
-
- for (let subTech of loadTechnologyPair(techCode).techs)
- if (!templateLists.techs.has(subTech))
- templateLists.techs.set(subTech, researcherList);
- else
- for (let researcher of researcherList)
- if (templateLists.techs.get(subTech).indexOf(researcher) == -1)
- templateLists.techs.get(subTech).push(researcher);
-
- templateLists.techs.delete(techCode);
- }
-
- // Remove wallset pieces, as they've served their purpose.
- delete templateLists.wallsetPieces;
-
- return templateLists;
-}
-
-/**
- * Compiles lists of buildable, trainable, or researchable entities from
- * a named template.
- */
-function getTemplateListsFromTemplate(templateName)
-{
- if (!templateName || !Engine.TemplateExists(templateName))
- return {};
-
- // If this is a non-promotion variant (ie. {civ}_support_female_citizen_house)
- // then it is functionally equivalent to another unit being processed, so skip it.
- if (getBaseTemplateName(templateName) != templateName)
- return {};
-
- let template = loadTemplate(templateName);
-
- let templateLists = loadProductionQueue(template);
- templateLists.structures = loadBuildQueue(template);
-
- if (template.WallSet)
- {
- templateLists.wallsetPieces = [];
- for (let segment in template.WallSet.Templates)
- {
- segment = template.WallSet.Templates[segment].replace(/\{(civ|native)\}/g, g_SelectedCiv);
- if (Engine.TemplateExists(segment))
- templateLists.wallsetPieces.push(segment);
- }
- }
-
- return templateLists;
-}
-
-function loadProductionQueue(template)
-{
- let production = {
- "techs": [],
- "units": []
- };
-
- if (!template.ProductionQueue)
- return production;
-
- if (template.ProductionQueue.Entities && template.ProductionQueue.Entities._string)
- for (let templateName of template.ProductionQueue.Entities._string.split(" "))
- {
- templateName = templateName.replace(/\{(civ|native)\}/g, g_SelectedCiv);
- if (Engine.TemplateExists(templateName))
- production.units.push(getBaseTemplateName(templateName));
- }
-
- if (template.ProductionQueue.Technologies && template.ProductionQueue.Technologies._string)
- for (let technologyName of template.ProductionQueue.Technologies._string.split(" "))
- {
- if (technologyName.indexOf("{civ}") != -1)
- {
- let civTechName = technologyName.replace("{civ}", g_SelectedCiv);
- technologyName = techDataExists(civTechName) ? civTechName : technologyName.replace("{civ}", "generic");
- }
-
- if (isPairTech(technologyName))
- for (let pairTechnologyName of loadTechnologyPair(technologyName).techs)
- production.techs.push(pairTechnologyName);
- else
- production.techs.push(technologyName);
- }
-
- return production;
-}
-
-function loadBuildQueue(template)
-{
- let buildQueue = [];
-
- if (!template.Builder || !template.Builder.Entities._string)
- return buildQueue;
-
- for (let build of template.Builder.Entities._string.split(" "))
- {
- build = build.replace(/\{(civ|native)\}/g, g_SelectedCiv);
- if (Engine.TemplateExists(build))
- buildQueue.push(build);
- }
-
- return buildQueue;
-}
-
-/**
- * Returns the name of a template's base form (without `_house`, `_trireme`, or similar),
- * or the template's own name if the base is of a different promotion rank.
- */
-function getBaseTemplateName(templateName)
-{
- if (!templateName || !Engine.TemplateExists(templateName))
- return undefined;
-
- templateName = removeFiltersFromTemplateName(templateName);
- let template = loadTemplate(templateName);
-
- if (!dirname(templateName) || dirname(template["@parent"]) != dirname(templateName))
- return templateName;
-
- let parentTemplate = loadTemplate(template["@parent"]);
-
- if (parentTemplate.Identity && parentTemplate.Identity.Rank &&
- parentTemplate.Identity.Rank != template.Identity.Rank)
- return templateName;
-
- if (!parentTemplate.Cost)
- return templateName;
-
- if (parentTemplate.Upgrade)
- for (let upgrade in parentTemplate.Upgrade)
- if (parentTemplate.Upgrade[upgrade].Entity)
- return templateName;
-
- for (let res in parentTemplate.Cost.Resources)
- if (+parentTemplate.Cost.Resources[res])
- return getBaseTemplateName(template["@parent"]);
-
- return templateName;
-}
-
-function setViewerOnPress(guiObjectName, templateName)
-{
- let viewerFunc = () => {
- Engine.PushGuiPage("page_viewer.xml", {
- "templateName": templateName,
- "civ": g_SelectedCiv
- });
- };
- Engine.GetGUIObjectByName(guiObjectName).onPress = viewerFunc;
- Engine.GetGUIObjectByName(guiObjectName).onPressRight = viewerFunc;
-}
Index: ps/trunk/binaries/data/mods/public/gui/reference/common/draw.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/reference/common/draw.js
+++ ps/trunk/binaries/data/mods/public/gui/reference/common/draw.js
@@ -1,73 +0,0 @@
-/**
- * GUI limits. Populated if needed by a predraw() function.
- */
-var g_DrawLimits = {};
-
-/**
- * List of functions that get the statistics of any template or entity,
- * formatted in such a way as to appear in a tooltip.
- *
- * The functions listed are defined in gui/common/tooltips.js
- */
-var g_StatsFunctions = [
- getHealthTooltip,
- getHealerTooltip,
- getAttackTooltip,
- getSplashDamageTooltip,
- getArmorTooltip,
- getGarrisonTooltip,
- getProjectilesTooltip,
- getSpeedTooltip,
- getGatherTooltip,
- getResourceSupplyTooltip,
- getPopulationBonusTooltip,
- getResourceTrickleTooltip,
- getLootTooltip
-];
-
-/**
- * Concatanates the return values of the array of passed functions.
- *
- * @param {object} template
- * @param {array} textFunctions
- * @param {string} joiner
- * @return {string} The built text.
- */
-function buildText(template, textFunctions=[], joiner="\n")
-{
- return textFunctions.map(func => func(template)).filter(tip => tip).join(joiner);
-}
-
-/**
- * Creates text in the following format:
- * Header: value1, value2, ..., valueN
- */
-function buildListText(headerString, arrayOfValues)
-{
- // Translation: Label followed by a list of values.
- return sprintf(translate("%(listHeader)s %(listOfValues)s"), {
- "listHeader": headerFont(headerString),
- // Translation: List separator.
- "listOfValues": bodyFont(arrayOfValues.join(translate(", ")))
- });
-}
-
-/**
- * Returns the resources this entity supplies in the specified entity's tooltip
- */
-function getResourceSupplyTooltip(template)
-{
- if (!template.supply)
- return "";
-
- let supply = template.supply;
- let type = supply.type[0] == "treasure" ? supply.type[1] : supply.type[0];
-
- // Translation: Label in tooltip showing the resource type and quantity of a given resource supply.
- return sprintf(translate("%(label)s %(component)s %(amount)s"), {
- "label": headerFont(translate("Resource Supply:")),
- "component": resourceIcon(type),
- // Translation: Marks that a resource supply entity has an unending, infinite, supply of its resource.
- "amount": Number.isFinite(+supply.amount) ? supply.amount : translate("∞")
- });
-}
Index: ps/trunk/binaries/data/mods/public/gui/reference/common/helper.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/reference/common/helper.js
+++ ps/trunk/binaries/data/mods/public/gui/reference/common/helper.js
@@ -1,153 +0,0 @@
-var g_CurrentModifiers = {};
-
-function deriveModifications(techList)
-{
- let techData = [];
- for (let techName of techList)
- techData.push(GetTechnologyBasicDataHelper(loadTechData(techName), g_SelectedCiv));
-
- return DeriveModificationsFromTechnologies(techData);
-}
-
-/**
- * Provided with an array containing basic information about possible
- * upgrades, such as that generated by globalscript's GetTemplateDataHelper,
- * this function loads the actual template data of the upgrades, overwrites
- * certain values within, then passes an array containing the template data
- * back to caller.
- */
-function getActualUpgradeData(upgradesInfo)
-{
- let newUpgrades = [];
- for (let upgrade of upgradesInfo)
- {
- upgrade.entity = upgrade.entity.replace(/\{(civ|native)\}/g, g_SelectedCiv);
-
- let data = GetTemplateDataHelper(loadTemplate(upgrade.entity), null, g_AuraData);
- data.name.internal = upgrade.entity;
- data.cost = upgrade.cost;
- data.icon = upgrade.icon || data.icon;
- data.tooltip = upgrade.tooltip || data.tooltip;
- data.requiredTechnology = upgrade.requiredTechnology || data.requiredTechnology;
-
- newUpgrades.push(data);
- }
- return newUpgrades;
-}
-
-/**
- * Determines and returns the phase in which a given technology can be
- * first researched. Works recursively through the given tech's
- * pre-requisite and superseded techs if necessary.
- *
- * @param {string} techName - The Technology's name
- * @return The name of the phase the technology belongs to, or false if
- * the current civ can't research this tech
- */
-function getPhaseOfTechnology(techName)
-{
- let phaseIdx = -1;
-
- if (basename(techName).startsWith("phase"))
- {
- if (!g_ParsedData.phases[techName].reqs)
- return false;
-
- phaseIdx = g_ParsedData.phaseList.indexOf(getActualPhase(techName));
- if (phaseIdx > 0)
- return g_ParsedData.phaseList[phaseIdx - 1];
- }
-
- if (!g_ParsedData.techs[g_SelectedCiv][techName])
- {
- let techData = loadTechnology(techName);
- g_ParsedData.techs[g_SelectedCiv][techName] = techData;
- warn("The \"" + techName + "\" technology is not researchable in any structure buildable by the " +
- g_SelectedCiv + " civilisation, but is required by something that this civ can research, train or build!");
- }
-
- let techReqs = g_ParsedData.techs[g_SelectedCiv][techName].reqs;
- if (!techReqs)
- return false;
-
- for (let option of techReqs)
- if (option.techs)
- for (let tech of option.techs)
- {
- if (basename(tech).startsWith("phase"))
- return tech;
- if (basename(tech).startsWith("pair"))
- continue;
- phaseIdx = Math.max(phaseIdx, g_ParsedData.phaseList.indexOf(getPhaseOfTechnology(tech)));
- }
- return g_ParsedData.phaseList[phaseIdx] || false;
-}
-
-/**
- * Returns the actual phase a certain phase tech represents or stands in for.
- *
- * For example, passing `phase_city_athen` would result in `phase_city`.
- *
- * @param {string} phaseName
- * @return {string}
- */
-function getActualPhase(phaseName)
-{
- if (g_ParsedData.phases[phaseName])
- return g_ParsedData.phases[phaseName].actualPhase;
-
- warn("Unrecognised phase (" + phaseName + ")");
- return g_ParsedData.phaseList[0];
-}
-
-/**
- * Returns the required phase of a given unit or structure.
- *
- * @param {object} template
- * @return {string}
- */
-function getPhaseOfTemplate(template)
-{
- if (!template.requiredTechnology)
- return g_ParsedData.phaseList[0];
-
- if (basename(template.requiredTechnology).startsWith("phase"))
- return getActualPhase(template.requiredTechnology);
-
- return getPhaseOfTechnology(template.requiredTechnology);
-}
-
-/**
- * This is needed because getEntityCostTooltip in tooltip.js needs to get
- * the template data of the different wallSet pieces. In the session this
- * function does some caching, but here we do that in loadTemplate already.
- */
-function GetTemplateData(templateName)
-{
- let template = loadTemplate(templateName);
- return GetTemplateDataHelper(template, null, g_AuraData, g_CurrentModifiers);
-}
-
-function isPairTech(technologyCode)
-{
- return !!loadTechData(technologyCode).top;
-}
-
-function mergeRequirements(reqsA, reqsB)
-{
- if (reqsA === false || reqsB === false)
- return false;
-
- let finalReqs = clone(reqsA);
-
- for (let option of reqsB)
- for (let type in option)
- for (let opt in finalReqs)
- {
- if (!finalReqs[opt][type])
- finalReqs[opt][type] = [];
- finalReqs[opt][type] = finalReqs[opt][type].concat(option[type]);
- }
-
- return finalReqs;
-}
Index: ps/trunk/binaries/data/mods/public/gui/reference/common/load.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/reference/common/load.js
+++ ps/trunk/binaries/data/mods/public/gui/reference/common/load.js
@@ -1,283 +0,0 @@
-/**
- * Paths to certain files.
- */
-const g_TechnologyPath = "simulation/data/technologies/";
-const g_AuraPath = "simulation/data/auras/";
-
-/**
- * Raw Data Caches.
- */
-var g_AuraData = {};
-var g_TemplateData = {};
-var g_TechnologyData = {};
-var g_CivData = loadCivData(true, false);
-
-/**
- * Parsed Data Stores.
- */
-var g_ParsedData = {};
-var g_ResourceData = new Resources();
-
-// This must be defined after the g_TechnologyData cache object is declared.
-var g_AutoResearchTechList = findAllAutoResearchedTechs();
-
-/**
- * Loads raw entity template.
- *
- * Loads from local cache if data present, else from file system.
- *
- * @param {string} templateName
- * @return {object} Object containing raw template data.
- */
-function loadTemplate(templateName)
-{
- if (!(templateName in g_TemplateData))
- {
- // We need to clone the template because we want to perform some translations.
- let data = clone(Engine.GetTemplate(templateName));
- translateObjectKeys(data, ["GenericName", "SpecificName", "Tooltip", "History"]);
-
- if (data.Auras)
- for (let auraID of data.Auras._string.split(/\s+/))
- loadAuraData(auraID);
-
- if (data.Identity.Civ != "gaia" && g_SelectedCiv != "gaia" && data.Identity.Civ != g_SelectedCiv)
- warn("The \"" + templateName + "\" template has a defined civ of \"" + data.Identity.Civ + "\". " +
- "This does not match the currently selected civ \"" + g_SelectedCiv + "\".");
-
- g_TemplateData[templateName] = data;
- }
-
- return g_TemplateData[templateName];
-}
-
-/**
- * Loads raw technology template.
- *
- * Loads from local cache if available, else from file system.
- *
- * @param {string} templateName
- * @return {object} Object containing raw template data.
- */
-function loadTechData(templateName)
-{
- if (!(templateName in g_TechnologyData))
- {
- let data = Engine.ReadJSONFile(g_TechnologyPath + templateName + ".json");
- translateObjectKeys(data, ["genericName", "tooltip", "description"]);
-
- g_TechnologyData[templateName] = data;
- }
-
- return g_TechnologyData[templateName];
-}
-
-function techDataExists(templateName)
-{
- return Engine.FileExists("simulation/data/technologies/" + templateName + ".json");
-}
-
-/**
- * Loads raw aura template.
- *
- * Loads from local cache if available, else from file system.
- *
- * @param {string} templateName
- * @return {object} Object containing raw template data.
- */
-function loadAuraData(templateName)
-{
- if (!(templateName in g_AuraData))
- {
- let data = Engine.ReadJSONFile(g_AuraPath + templateName + ".json");
- translateObjectKeys(data, ["auraName", "auraDescription"]);
-
- g_AuraData[templateName] = data;
- }
-
- return g_AuraData[templateName];
-}
-
-/**
- * Load and parse a structure, unit, resource, etc from its entity template file.
- *
- * @return {(object|null)} Sanitized object about the requested template or null if entity template doesn't exist.
- */
-function loadEntityTemplate(templateName)
-{
- if (!Engine.TemplateExists(templateName))
- return null;
-
- let template = loadTemplate(templateName);
- let parsed = GetTemplateDataHelper(template, null, g_AuraData, g_CurrentModifiers);
- parsed.name.internal = templateName;
-
- parsed.history = template.Identity.History;
-
- parsed.production = loadProductionQueue(template);
- if (template.Builder)
- parsed.builder = loadBuildQueue(template);
-
- if (template.Identity.Rank)
- parsed.promotion = {
- "current_rank": template.Identity.Rank,
- "entity": template.Promotion && template.Promotion.Entity
- };
-
- if (template.ResourceSupply)
- parsed.supply = {
- "type": template.ResourceSupply.Type.split("."),
- "amount": template.ResourceSupply.Amount,
- };
-
- if (parsed.upgrades)
- parsed.upgrades = getActualUpgradeData(parsed.upgrades);
-
- if (parsed.wallSet)
- {
- parsed.wallset = {};
-
- if (!parsed.upgrades)
- parsed.upgrades = [];
-
- // Note: An assumption is made here that wall segments all have the same armor and auras
- let struct = loadEntityTemplate(parsed.wallSet.templates.long);
- parsed.armour = struct.armour;
- parsed.auras = struct.auras;
-
- // For technology cost multiplier, we need to use the tower
- struct = loadEntityTemplate(parsed.wallSet.templates.tower);
- parsed.techCostMultiplier = struct.techCostMultiplier;
-
- let health;
-
- for (let wSegm in parsed.wallSet.templates)
- {
- if (wSegm == "fort" || wSegm == "curves")
- continue;
-
- let wPart = loadEntityTemplate(parsed.wallSet.templates[wSegm]);
- parsed.wallset[wSegm] = wPart;
-
- for (let research of wPart.production.techs)
- parsed.production.techs.push(research);
-
- if (wPart.upgrades)
- parsed.upgrades = parsed.upgrades.concat(wPart.upgrades);
-
- if (["gate", "tower"].indexOf(wSegm) != -1)
- continue;
-
- if (!health)
- {
- health = { "min": wPart.health, "max": wPart.health };
- continue;
- }
-
- health.min = Math.min(health.min, wPart.health);
- health.max = Math.max(health.max, wPart.health);
- }
-
- if (parsed.wallSet.templates.curves)
- for (let curve of parsed.wallSet.templates.curves)
- {
- let wPart = loadEntityTemplate(curve);
- health.min = Math.min(health.min, wPart.health);
- health.max = Math.max(health.max, wPart.health);
- }
-
- if (health.min == health.max)
- parsed.health = health.min;
- else
- parsed.health = sprintf(translate("%(health_min)s to %(health_max)s"), {
- "health_min": health.min,
- "health_max": health.max
- });
- }
-
- return parsed;
-}
-
-/**
- * Load and parse technology from json template.
- *
- * @param {string} templateName
- * @return {object} Sanitized data about the requested technology.
- */
-function loadTechnology(techName)
-{
- let template = loadTechData(techName);
- let tech = GetTechnologyDataHelper(template, g_SelectedCiv, g_ResourceData);
- tech.name.internal = techName;
-
- if (template.pair !== undefined)
- {
- tech.pair = template.pair;
- tech.reqs = mergeRequirements(tech.reqs, loadTechnologyPair(template.pair).reqs);
- }
-
- return tech;
-}
-
-/**
- * Crudely iterates through every tech JSON file and identifies those
- * that are auto-researched.
- *
- * @return {array} List of techs that are researched automatically
- */
-function findAllAutoResearchedTechs()
-{
- let techList = [];
-
- for (let filename of Engine.ListDirectoryFiles(g_TechnologyPath, "*.json", true))
- {
- // -5 to strip off the file extension
- let templateName = filename.slice(g_TechnologyPath.length, -5);
- let data = loadTechData(templateName);
-
- if (data && data.autoResearch)
- techList.push(templateName);
- }
-
- return techList;
-}
-
-/**
- * @param {string} phaseCode
- * @return {object} Sanitized object containing phase data
- */
-function loadPhase(phaseCode)
-{
- let phase = loadTechnology(phaseCode);
-
- phase.actualPhase = phaseCode;
- if (phase.replaces !== undefined)
- phase.actualPhase = phase.replaces[0];
-
- return phase;
-}
-
-/**
- * @param {string} pairCode
- * @return {object} Contains a list and the requirements of the techs in the pair
- */
-function loadTechnologyPair(pairCode)
-{
- var pairInfo = loadTechData(pairCode);
-
- return {
- "techs": [ pairInfo.top, pairInfo.bottom ],
- "reqs": DeriveTechnologyRequirements(pairInfo, g_SelectedCiv)
- };
-}
-
-/**
- * @param {string} modCode
- * @return {object} Sanitized object containing modifier tech data
- */
-function loadModifierTech(modCode)
-{
- if (!Engine.FileExists("simulation/data/technologies/"+modCode+".json"))
- return {};
- return loadTechData(modCode);
-}
Index: ps/trunk/binaries/data/mods/public/gui/reference/common/tooltips.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/reference/common/tooltips.js
+++ ps/trunk/binaries/data/mods/public/gui/reference/common/tooltips.js
@@ -0,0 +1,76 @@
+/**
+ * Creates text in the following format:
+ * Header: value1, value2, ..., valueN
+ *
+ * This function is only used below, nowhere else.
+ */
+function buildListText(headerString, arrayOfValues)
+{
+ // Translation: Label followed by a list of values.
+ return sprintf(translate("%(listHeader)s %(listOfValues)s"), {
+ "listHeader": headerFont(headerString),
+ // Translation: List separator.
+ "listOfValues": bodyFont(arrayOfValues.join(translate(", ")))
+ });
+}
+
+/**
+ * The following functions in this file work on the same basis as those in gui/common/tooltips.js
+ *
+ * Note: Due to quirks in loading order, this file might not be loaded before ReferencePage.js.
+ * Do not put anything in here that you wish to access static'ly there.
+ */
+
+function getBuiltByText(template)
+{
+ // Translation: Label before a list of the names of units that build the structure selected.
+ return template.builtByListOfNames ? buildListText(translate("Built by:"), template.builtByListOfNames) : "";
+}
+
+function getTrainedByText(template)
+{
+ // Translation: Label before a list of the names of structures or units that train the unit selected.
+ return template.trainedByListOfNames ? buildListText(translate("Trained by:"), template.trainedByListOfNames) : "";
+}
+
+function getResearchedByText(template)
+{
+ // Translation: Label before a list of names of structures or units that research the technology selected.
+ return template.researchedByListOfNames ? buildListText(translate("Researched at:"), template.researchedByListOfNames) : "";
+}
+
+/**
+ * @return {string} List of the names of the buildings the selected unit can build.
+ */
+function getBuildText(template)
+{
+ // Translation: Label before a list of the names of structures the selected unit can construct or build.
+ return template.buildListOfNames ? buildListText(translate("Builds:"), template.buildListOfNames) : "";
+}
+
+/**
+ * @return {string} List of the names of the technologies the selected structure/unit can research.
+ */
+function getResearchText(template)
+{
+ // Translation: Label before a list of the names of technologies the selected unit or structure can research.
+ return template.researchListOfNames ? buildListText(translate("Researches:"), template.researchListOfNames) : "";
+}
+
+/**
+ * @return {string} List of the names of the units the selected unit can train.
+ */
+function getTrainText(template)
+{
+ // Translation: Label before a list of the names of units the selected unit or structure can train.
+ return template.trainListOfNames ? buildListText(translate("Trains:"), template.trainListOfNames) : "";
+}
+
+/**
+ * @return {string} List of the names of the buildings/units the selected structure/unit can upgrade to.
+ */
+function getUpgradeText(template)
+{
+ // Translation: Label before a list of the names of units or structures the selected unit or structure can be upgradable to.
+ return template.upgradeListOfNames ? buildListText(translate("Upgradable to:"), template.upgradeListOfNames) : "";
+}
Index: ps/trunk/binaries/data/mods/public/gui/reference/structree/Boxes/EntityBox.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/reference/structree/Boxes/EntityBox.js
+++ ps/trunk/binaries/data/mods/public/gui/reference/structree/Boxes/EntityBox.js
@@ -0,0 +1,83 @@
+/**
+ * Class inherited by StructureBox and TrainerBox classes.
+ */
+class EntityBox
+{
+ constructor(page)
+ {
+ this.page = page;
+ }
+
+ static setViewerOnPress(guiObject, templateName, civCode)
+ {
+ let viewerFunc = () => {
+ Engine.PushGuiPage("page_viewer.xml", {
+ "templateName": templateName,
+ "civ": civCode
+ });
+ };
+ guiObject.onPress = viewerFunc;
+ guiObject.onPressRight = viewerFunc;
+ }
+
+ draw(templateName, civCode)
+ {
+ this.template = this.page.TemplateParser.getEntity(templateName, civCode);
+ this.gui.hidden = false;
+
+ let caption = this.gui.children[0];
+ caption.caption = translate(this.template.name.specific);
+
+ let icon = this.gui.children[1];
+ icon.sprite = "stretched:" + this.page.IconPath + this.template.icon;
+ icon.tooltip = this.constructor.compileTooltip(this.template);
+ this.constructor.setViewerOnPress(icon, this.template.name.internal, civCode);
+ }
+
+ captionWidth()
+ {
+ // We make the assumption that the caption's padding is equal on both sides
+ let caption = this.gui.children[0];
+ return Engine.GetTextWidth(caption.font, caption.caption) + (caption.size.left + caption.buffer_zone) * 2;
+ }
+
+ static compileTooltip(template)
+ {
+ return ReferencePage.buildText(template, this.prototype.TooltipFunctions) + "\n" + showTemplateViewerOnClickTooltip();
+ }
+
+ /**
+ * Returns the height between the top of the EntityBox, and the top of the production rows.
+ *
+ * Used within the TreeSection class to position the production rows,
+ * and used with the PhaseIdent class to position grey bars under them.
+ */
+ static IconAndCaptionHeight()
+ {
+ let height = Engine.GetGUIObjectByName("structure[0]_icon").size.bottom + this.prototype.IconPadding;
+
+ // Replace function so the above is only run once.
+ this.IconAndCaptionHeight = () => height;
+ return height;
+ }
+
+}
+
+/**
+ * Minimum width of the boxes, the margins between them (Horizontally and Vertically),
+ * and the padding between the main icon and the production row(s) beneath it.
+ */
+EntityBox.prototype.MinWidth = 96;
+EntityBox.prototype.HMargin = 8;
+EntityBox.prototype.VMargin = 12;
+EntityBox.prototype.IconPadding = 8;
+
+/**
+ * Functions used to collate the contents of a tooltip.
+ */
+EntityBox.prototype.TooltipFunctions = [
+ getEntityNamesFormatted,
+ getEntityCostTooltip,
+ getEntityTooltip,
+ getAurasTooltip
+].concat(ReferencePage.prototype.StatsFunctions);
Index: ps/trunk/binaries/data/mods/public/gui/reference/structree/Boxes/ProductionIcon.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/reference/structree/Boxes/ProductionIcon.js
+++ ps/trunk/binaries/data/mods/public/gui/reference/structree/Boxes/ProductionIcon.js
@@ -0,0 +1,52 @@
+class ProductionIcon
+{
+ constructor(page, guiObject)
+ {
+ this.page = page;
+ this.productionIcon = guiObject;
+ }
+
+ /* Returns the dimensions of a single icon, including some "helper" attributes.
+ *
+ * Two assumptions are made: (1) that all production icons are the same size,
+ * and (2) that the size will never change for the duration of the life of the
+ * containing page.
+ *
+ * As such, the method replaces itself after being run once, so the calculations
+ * within are only performed once.
+ */
+ static Size()
+ {
+ let baseObject = Engine.GetGUIObjectByName("phase[0]_bar[0]_icon").size;
+ let size = {};
+
+ // Icon dimensions
+ size.width = baseObject.right - baseObject.left;
+ size.height = baseObject.bottom - baseObject.top;
+
+ // Horizontal and Vertical Margins.
+ size.hMargin = baseObject.left;
+ size.vMargin = baseObject.top;
+
+ // Width and Height padded with margins on all sides.
+ size.paddedWidth = size.width + size.hMargin * 2;
+ size.paddedHeight = size.height + size.vMargin * 2;
+
+ // Padded dimensions to use when in production rows.
+ size.rowWidth = size.width + size.hMargin;
+ size.rowHeight = size.paddedHeight + size.vMargin * 2;
+ size.rowGap = size.rowHeight - size.paddedHeight;
+
+ // Replace static method and return
+ this.Size = () => size;
+ return size;
+ }
+
+ draw(template, civCode)
+ {
+ this.productionIcon.sprite = "stretched:" + this.page.IconPath + template.icon;
+ this.productionIcon.tooltip = EntityBox.compileTooltip(template);
+ this.productionIcon.hidden = false;
+ EntityBox.setViewerOnPress(this.productionIcon, template.name.internal, civCode);
+ }
+}
Index: ps/trunk/binaries/data/mods/public/gui/reference/structree/Boxes/ProductionRow.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/reference/structree/Boxes/ProductionRow.js
+++ ps/trunk/binaries/data/mods/public/gui/reference/structree/Boxes/ProductionRow.js
@@ -0,0 +1,58 @@
+class ProductionRow
+{
+ constructor(page, guiObject, rowIndex)
+ {
+ this.page = page;
+ this.productionRow = guiObject;
+ this.productionIconsDrawn = 0;
+ this.rowIndex = rowIndex;
+ this.phaseOffset = 0;
+
+ horizontallySpaceObjects(this.productionRow.name, ProductionIcon.Size().hMargin);
+
+ this.productionIcons = [];
+ for (let icon of guiObject.children)
+ this.productionIcons.push(new ProductionIcon(this.page, icon));
+ }
+
+ startDraw(phaseOffset)
+ {
+ this.productionIconsDrawn = 0;
+ this.phaseOffset = phaseOffset;
+ }
+
+ drawIcon(template, civCode)
+ {
+ if (this.productionIconsDrawn == this.productionIcons.length)
+ {
+ error("The currently displayed civ has more production options " +
+ "than can be supported by the current GUI layout");
+ return;
+ }
+
+ this.productionIcons[this.productionIconsDrawn].draw(template, civCode);
+ ++this.productionIconsDrawn;
+ }
+
+ finishDraw()
+ {
+ hideRemaining(this.productionRow.name, this.productionIconsDrawn);
+
+ const IconSize = ProductionIcon.Size();
+ let rowOffset = IconSize.rowHeight * (this.phaseOffset - this.rowIndex);
+ let rowWidth = this.productionIconsDrawn * IconSize.rowWidth + IconSize.hMargin;
+
+ let size = this.productionRow.size;
+ size.left = -rowWidth / 2;
+ size.top = -rowOffset;
+ this.productionRow.size = size;
+
+ this.productionRow.hidden = false;
+ return rowWidth;
+ }
+
+ hide()
+ {
+ this.productionRow.hidden = true
+ }
+}
Index: ps/trunk/binaries/data/mods/public/gui/reference/structree/Boxes/ProductionRowManager.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/reference/structree/Boxes/ProductionRowManager.js
+++ ps/trunk/binaries/data/mods/public/gui/reference/structree/Boxes/ProductionRowManager.js
@@ -0,0 +1,79 @@
+class ProductionRowManager
+{
+ constructor(page, guiName, sortByPhase)
+ {
+ this.page = page;
+ this.width = 0;
+ this.sortProductionsByPhase = sortByPhase;
+
+ this.productionRows = [];
+ for (let row of Engine.GetGUIObjectByName(guiName).children)
+ this.productionRows.push(new ProductionRow(this.page, row, this.productionRows.length));
+ }
+
+ draw(template, civCode, phaseIdx=0)
+ {
+ this.width = 0;
+
+ if (this.sortProductionsByPhase)
+ for (let r = 0; r < this.page.TemplateParser.phaseList.length; ++r)
+ this.productionRows[r].startDraw(this.page.TemplateParser.phaseList.length - phaseIdx);
+ else
+ this.productionRows[0].startDraw(1);
+
+ // (Want to draw Units before Techs)
+ for (let prodType of Object.keys(template.production).reverse())
+ for (let prod of template.production[prodType])
+ {
+ let pIdx = 0;
+ switch (prodType)
+ {
+
+ case "units":
+ prod = this.page.TemplateParser.getEntity(prod, civCode);
+ pIdx = this.page.TemplateParser.phaseList.indexOf(prod.phase);
+ break;
+
+ case "techs":
+ pIdx = this.page.TemplateParser.phaseList.indexOf(this.page.TemplateParser.getPhaseOfTechnology(prod, civCode));
+ prod = clone(this.page.TemplateParser.getTechnology(prod, civCode));
+ for (let res in template.techCostMultiplier)
+ if (prod.cost[res])
+ prod.cost[res] *= template.techCostMultiplier[res];
+ break;
+
+ default:
+ continue;
+ }
+
+ let rowIdx = this.sortProductionsByPhase ? Math.max(0, pIdx - phaseIdx) : 0;
+ this.productionRows[rowIdx].drawIcon(prod, civCode)
+ }
+
+ if (template.upgrades)
+ for (let upgrade of template.upgrades)
+ {
+ let pIdx = 0;
+ if (this.phaseSort)
+ pIdx = this.page.TemplateParser.phaseList.indexOf(upgrade.phase);
+ let rowIdx = Math.max(0, pIdx - phaseIdx);
+ this.productionRows[rowIdx].drawIcon(upgrade, civCode);
+ }
+
+ if (template.wallset)
+ this.productionRows[0].drawIcon(template.wallset.tower, civCode);
+
+ let r = 0;
+
+ // Tell the production rows used we've finished
+ if (this.sortProductionsByPhase)
+ for (; r < this.page.TemplateParser.phaseList.length; ++r)
+ this.width = Math.max(this.width, this.productionRows[r].finishDraw());
+ else
+ this.width = this.productionRows[r++].finishDraw();
+
+ // Hide any remaining phase rows
+ for (; r < this.productionRows.length; ++r)
+ this.productionRows[r].hide();
+ }
+}
Index: ps/trunk/binaries/data/mods/public/gui/reference/structree/Boxes/StructureBox.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/reference/structree/Boxes/StructureBox.js
+++ ps/trunk/binaries/data/mods/public/gui/reference/structree/Boxes/StructureBox.js
@@ -0,0 +1,37 @@
+/**
+ * This code wraps the gui representing buildable structures within the structree.
+ *
+ * An instance of this class is created for each child of the gui element named "structures".
+ */
+class StructureBox extends EntityBox
+{
+ constructor(page, structureIdx)
+ {
+ super(page);
+ this.gui = Engine.GetGUIObjectByName("structure[" + structureIdx + "]");
+ this.ProductionRows = new ProductionRowManager(this.page, "structure[" + structureIdx + "]_productionRows", true);
+ }
+
+ draw(templateName, civCode, runningWidths)
+ {
+ super.draw(templateName, civCode);
+
+ this.phaseIdx = this.page.TemplateParser.phaseList.indexOf(this.template.phase);
+
+ // Draw the production rows
+ this.ProductionRows.draw(this.template, civCode, this.phaseIdx);
+
+ let boxWidth = Math.max(this.MinWidth, this.captionWidth(), this.ProductionRows.width);
+
+ // Set position of the Structure Box
+ let size = this.gui.size;
+ size.left = this.HMargin + runningWidths[this.phaseIdx];
+ size.right = this.HMargin + runningWidths[this.phaseIdx] + boxWidth;
+ size.top = TreeSection.getPositionOffset(this.phaseIdx, this.page.TemplateParser);
+ size.bottom = TreeSection.getPositionOffset(this.phaseIdx + 1, this.page.TemplateParser) - this.VMargin;
+ this.gui.size = size;
+
+ // Update new right-side-edge dimension
+ runningWidths[this.phaseIdx] += boxWidth + this.HMargin / 2;
+ }
+}
Index: ps/trunk/binaries/data/mods/public/gui/reference/structree/Boxes/TrainerBox.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/reference/structree/Boxes/TrainerBox.js
+++ ps/trunk/binaries/data/mods/public/gui/reference/structree/Boxes/TrainerBox.js
@@ -0,0 +1,42 @@
+/**
+ * This code wraps the gui representing "trainer units" (a unit that can train other units) within the structree.
+ *
+ * An instance of this class is created for each child of the gui element named "trainers".
+ */
+class TrainerBox extends EntityBox
+{
+ constructor(page, trainerIdx)
+ {
+ super(page);
+
+ this.gui = Engine.GetGUIObjectByName("trainer[" + trainerIdx + "]");
+ this.ProductionRows = new ProductionRowManager(this.page, "trainer[" + trainerIdx + "]_productionRows", false);
+
+ let rowHeight = ProductionIcon.Size().rowHeight;
+ let size = this.gui.size;
+
+ // Adjust height to accommodate production row
+ size.bottom += rowHeight;
+
+ // We make the assumuption that all trainer boxes have the same height
+ let boxHeight = this.VMargin / 2 + (size.bottom - size.top + this.VMargin) * trainerIdx;
+ size.top += boxHeight;
+ size.bottom += boxHeight;
+
+ // Make the box adjust automatically to column width
+ size.rright = 100;
+ size.right = -size.left;
+
+ this.gui.size = size;
+ }
+
+ draw(templateName, civCode)
+ {
+ super.draw(templateName, civCode);
+
+ this.ProductionRows.draw(this.template, civCode);
+
+ // Return the box width
+ return Math.max(this.MinWidth, this.captionWidth(), this.ProductionRows.width);
+ }
+}
Index: ps/trunk/binaries/data/mods/public/gui/reference/structree/Sections/Trainer/TrainerSection.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/reference/structree/Sections/Trainer/TrainerSection.js
+++ ps/trunk/binaries/data/mods/public/gui/reference/structree/Sections/Trainer/TrainerSection.js
@@ -0,0 +1,65 @@
+class TrainerSection
+{
+ constructor(page)
+ {
+ this.page = page;
+ this.width = 0;
+ this.widthChangedHandlers = new Set();
+
+ this.TrainerSection = Engine.GetGUIObjectByName("trainerSection");
+ this.Trainers = Engine.GetGUIObjectByName("trainers");
+
+ this.TrainerSectionHeading = Engine.GetGUIObjectByName("trainerSectionHeading");
+ this.TrainerSectionHeading.caption = this.Caption;
+
+ this.trainerBoxes = [];
+ for (let boxIdx in this.Trainers.children)
+ this.trainerBoxes.push(new TrainerBox(this.page, boxIdx));
+ }
+
+ registerWidthChangedHandler(handler)
+ {
+ this.widthChangedHandlers.add(handler);
+ }
+
+ draw(units, civCode)
+ {
+ let caption = this.TrainerSectionHeading;
+ this.width = Engine.GetTextWidth(caption.font, caption.caption) + (caption.size.left + caption.buffer_zone) * 2;
+ let count = 0;
+
+ for (let unitCode of units.keys())
+ {
+ let unitTemplate = this.page.TemplateParser.getEntity(unitCode, civCode);
+ if (!unitTemplate.production.units.length && !unitTemplate.production.techs.length && !unitTemplate.upgrades)
+ continue;
+
+ if (count > this.trainerBoxes.length)
+ {
+ error("\"" + this.activeCiv + "\" has more unit trainers than can be supported by the current GUI layout");
+ break;
+ }
+
+ this.width = Math.max(
+ this.width,
+ this.trainerBoxes[count].draw(unitCode, civCode)
+ );
+
+ ++count;
+ }
+ hideRemaining(this.Trainers.name, count);
+
+ // Update width and visibility of section
+ let size = this.TrainerSection.size;
+ this.width += EntityBox.prototype.HMargin;
+ size.left = -this.width + size.right;
+ this.TrainerSection.size = size;
+ this.TrainerSection.hidden = count == 0;
+
+ for (let handler of this.widthChangedHandlers)
+ handler(this.width, !this.TrainerSection.hidden);
+ }
+}
+
+TrainerSection.prototype.Caption =
+ translate("Trainer Units");
Index: ps/trunk/binaries/data/mods/public/gui/reference/structree/Sections/Trainer/TrainerSection.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/reference/structree/Sections/Trainer/TrainerSection.xml
+++ ps/trunk/binaries/data/mods/public/gui/reference/structree/Sections/Trainer/TrainerSection.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Index: ps/trunk/binaries/data/mods/public/gui/reference/structree/Sections/Tree/PhaseIdent.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/reference/structree/Sections/Tree/PhaseIdent.js
+++ ps/trunk/binaries/data/mods/public/gui/reference/structree/Sections/Tree/PhaseIdent.js
@@ -0,0 +1,58 @@
+class PhaseIdent
+{
+ constructor(page, phaseIdx)
+ {
+ this.page = page;
+ this.phaseIdx = +phaseIdx;
+
+ this.Ident = Engine.GetGUIObjectByName("phase[" + this.phaseIdx + "]_ident");
+ this.Icon = Engine.GetGUIObjectByName("phase[" + this.phaseIdx + "]_icon");
+ this.Bars = Engine.GetGUIObjectByName("phase[" + this.phaseIdx + "]_bars");
+
+ let prodIconSize = ProductionIcon.Size();
+ let entityBoxHeight = EntityBox.IconAndCaptionHeight();
+ for (let i = 0; i < this.Bars.children.length; ++i)
+ {
+ let size = this.Bars.children[i].size;
+ size.top = entityBoxHeight + prodIconSize.rowHeight * (i + 1);
+ size.bottom = entityBoxHeight + prodIconSize.rowHeight * (i + 2) - prodIconSize.rowGap;
+ this.Bars.children[i].size = size;
+ }
+ }
+
+ draw(phaseList, barLength, civCode)
+ {
+ // Position ident
+ let identSize = this.Ident.size;
+ identSize.top = TreeSection.getPositionOffset(this.phaseIdx, this.page.TemplateParser);
+ identSize.bottom = TreeSection.getPositionOffset(this.phaseIdx + 1, this.page.TemplateParser);
+ this.Ident.size = identSize;
+
+ // Draw main icon
+ this.drawPhaseIcon(this.Icon, this.phaseIdx, civCode);
+
+ // Draw the phase bars
+ let i = 1;
+ for (; i < phaseList.length - this.phaseIdx; ++i)
+ {
+ let prodBar = this.Bars.children[(i - 1)];
+ let prodBarSize = prodBar.size;
+ prodBarSize.right = barLength;
+ prodBar.size = prodBarSize;
+ prodBar.hidden = false;
+
+ this.drawPhaseIcon(prodBar.children[0], this.phaseIdx + i, civCode);
+ }
+ hideRemaining(this.Bars.name, i - 1);
+ }
+
+ drawPhaseIcon(phaseIcon, phaseIndex, civCode)
+ {
+ let phaseName = this.page.TemplateParser.phaseList[phaseIndex];
+ let prodPhaseTemplate = this.page.TemplateParser.getTechnology(phaseName + "_" + civCode, civCode) || this.page.TemplateParser.getTechnology(phaseName, civCode);
+
+ phaseIcon.sprite = "stretched:" + this.page.IconPath + prodPhaseTemplate.icon;
+ phaseIcon.tooltip = getEntityNamesFormatted(prodPhaseTemplate);
+ };
+
+}
Index: ps/trunk/binaries/data/mods/public/gui/reference/structree/Sections/Tree/PhaseIdentManager.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/reference/structree/Sections/Tree/PhaseIdentManager.js
+++ ps/trunk/binaries/data/mods/public/gui/reference/structree/Sections/Tree/PhaseIdentManager.js
@@ -0,0 +1,23 @@
+class PhaseIdentManager
+{
+ constructor(page)
+ {
+ this.page = page;
+ this.idents = [];
+
+ this.PhaseIdents = Engine.GetGUIObjectByName("phaseIdents");
+ this.Idents = [];
+ for (let identIdx in this.PhaseIdents.children)
+ this.Idents.push(new PhaseIdent(this.page, identIdx));
+ }
+
+ draw(phaseList, civCode, runningWidths, leftMargin)
+ {
+ for (let i = 0; i < phaseList.length; ++i)
+ {
+ let barLength = leftMargin + runningWidths[i] + EntityBox.prototype.HMargin * 0.75;
+ this.Idents[i].draw(phaseList, barLength, civCode);
+ }
+ hideRemaining(this.PhaseIdents.name, phaseList.length);
+ }
+}
Index: ps/trunk/binaries/data/mods/public/gui/reference/structree/Sections/Tree/TreeSection.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/reference/structree/Sections/Tree/TreeSection.js
+++ ps/trunk/binaries/data/mods/public/gui/reference/structree/Sections/Tree/TreeSection.js
@@ -0,0 +1,77 @@
+class TreeSection
+{
+ constructor(page)
+ {
+ this.page = page;
+
+ this.TreeSection = Engine.GetGUIObjectByName("treeSection");
+ this.Structures = Engine.GetGUIObjectByName("structures");
+
+ this.PhaseIdents = new PhaseIdentManager(this.page);
+
+ this.rightMargin = this.TreeSection.size.right;
+
+ this.structureBoxes = [];
+ for (let boxIdx in this.Structures.children)
+ this.structureBoxes.push(new StructureBox(this.page, boxIdx));
+
+ page.TrainerSection.registerWidthChangedHandler(this.onTrainerSectionWidthChange.bind(this));
+ }
+
+ draw(structures, civCode)
+ {
+ if (structures.size > this.structureBoxes.length)
+ error("\"" + this.activeCiv + "\" has more structures than can be supported by the current GUI layout");
+
+ // Draw structures
+ let phaseList = this.page.TemplateParser.phaseList;
+ let count = Math.min(structures.size, this.structureBoxes.length);
+ let runningWidths = Array(phaseList.length).fill(0);
+ let structureIterator = structures.keys();
+ for (let idx = 0; idx < count; ++idx)
+ this.structureBoxes[idx].draw(structureIterator.next().value, civCode, runningWidths);
+ hideRemaining(this.Structures.name, count);
+
+ // Position phase idents
+ this.PhaseIdents.draw(phaseList, civCode, runningWidths, this.Structures.size.left);
+ }
+
+ drawPhaseIcon(phaseIcon, phaseIndex, civCode)
+ {
+ let phaseName = this.page.TemplateParser.phaseList[phaseIndex];
+ let prodPhaseTemplate = this.page.TemplateParser.getTechnology(phaseName + "_" + civCode, civCode) || this.page.TemplateParser.getTechnology(phaseName, civCode);
+
+ phaseIcon.sprite = "stretched:" + this.page.IconPath + prodPhaseTemplate.icon;
+ phaseIcon.tooltip = getEntityNamesFormatted(prodPhaseTemplate);
+ };
+
+ onTrainerSectionWidthChange(trainerSectionWidth, trainerSectionVisible)
+ {
+ let size = this.TreeSection.size;
+ size.right = this.rightMargin;
+ if (trainerSectionVisible)
+ size.right -= trainerSectionWidth + this.page.SectionGap;
+ this.TreeSection.size = size;
+ }
+
+ /**
+ * Calculate row position offset (accounting for different number of prod rows per phase).
+ *
+ * This is a static method as it is also used from within the StructureBox and PhaseIdent classes.
+ *
+ * @param {number} idx
+ * @return {number}
+ */
+ static getPositionOffset(idx, TemplateParser)
+ {
+ let phases = TemplateParser.phaseList.length;
+ let rowHeight = ProductionIcon.Size().rowHeight;
+
+ let size = EntityBox.IconAndCaptionHeight() * idx; // text, image and offset
+ size += EntityBox.prototype.VMargin * (idx + 1); // Margin above StructureBoxes
+ size += rowHeight * (phases * idx - (idx - 1) * idx / 2); // phase rows (phase-currphase+1 per row)
+
+ return size;
+ };
+
+}
Index: ps/trunk/binaries/data/mods/public/gui/reference/structree/Sections/Tree/TreeSection.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/reference/structree/Sections/Tree/TreeSection.xml
+++ ps/trunk/binaries/data/mods/public/gui/reference/structree/Sections/Tree/TreeSection.xml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Index: ps/trunk/binaries/data/mods/public/gui/reference/structree/StructreePage.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/reference/structree/StructreePage.js
+++ ps/trunk/binaries/data/mods/public/gui/reference/structree/StructreePage.js
@@ -0,0 +1,58 @@
+/**
+ * This class represents the Structure Tree GUI page.
+ *
+ * Further methods are described within draw.js
+ */
+class StructreePage extends ReferencePage
+{
+ constructor(data)
+ {
+ super();
+
+ this.structureBoxes = [];
+ this.trainerBoxes = [];
+
+ this.CivEmblem = Engine.GetGUIObjectByName("civEmblem");
+ this.CivName = Engine.GetGUIObjectByName("civName");
+ this.CivHistory = Engine.GetGUIObjectByName("civHistory");
+
+ this.TrainerSection = new TrainerSection(this);
+ this.TreeSection = new TreeSection(this);
+
+ this.civSelection = new CivSelectDropdown(this.civData);
+ if (!this.civSelection.hasCivs())
+ {
+ this.closePage();
+ return;
+ }
+ this.civSelection.registerHandler(this.selectCiv.bind(this));
+
+ let civInfoButton = new CivInfoButton(this);
+ let closeButton = new CloseButton(this);
+ Engine.SetGlobalHotkey("structree", "Press", this.closePage.bind(this));
+ }
+
+ closePage()
+ {
+ Engine.PopGuiPage({ "civ": this.activeCiv, "page": "page_structree.xml" });
+ }
+
+ selectCiv(civCode)
+ {
+ this.setActiveCiv(civCode);
+
+ this.CivEmblem.sprite = "stretched:" + this.civData[this.activeCiv].Emblem;
+ this.CivName.caption = this.civData[this.activeCiv].Name;
+ this.CivHistory.caption = this.civData[this.activeCiv].History;
+
+ let templateLists = this.TemplateLister.getTemplateLists(this.activeCiv);
+ this.TreeSection.draw(templateLists.structures, this.activeCiv);
+ this.TrainerSection.draw(templateLists.units, this.activeCiv);
+ }
+}
+
+StructreePage.prototype.CloseButtonTooltip =
+ translate("%(hotkey)s: Close Structure Tree.");
+
+// Gap between the `TreeSection` and `TrainerSection` gui objects (when the latter is visible)
+StructreePage.prototype.SectionGap = 12;
Index: ps/trunk/binaries/data/mods/public/gui/reference/structree/draw.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/reference/structree/draw.js
+++ ps/trunk/binaries/data/mods/public/gui/reference/structree/draw.js
@@ -1,432 +0,0 @@
-/**
- * Functions used to collate the contents of a tooltip.
- */
-var g_StructreeTooltipFunctions = [
- getEntityNamesFormatted,
- getEntityCostTooltip,
- getEntityTooltip,
- getAurasTooltip
-].concat(g_StatsFunctions);
-
-/**
- * Draw the structree
- *
- * (Actually resizes and changes visibility of elements, and populates text)
- */
-function draw()
-{
- // Set basic state (positioning of elements mainly), but only once
- if (!Object.keys(g_DrawLimits).length)
- predraw();
-
- let leftMargin = Engine.GetGUIObjectByName("tree_display").size.left;
- let defWidth = 96;
- let defMargin = 4;
-
- let phaseList = g_ParsedData.phaseList;
-
- Engine.GetGUIObjectByName("civEmblem").sprite = "stretched:" + g_CivData[g_SelectedCiv].Emblem;
- Engine.GetGUIObjectByName("civName").caption = g_CivData[g_SelectedCiv].Name;
- Engine.GetGUIObjectByName("civHistory").caption = g_CivData[g_SelectedCiv].History;
-
- let i = 0;
- for (let pha of phaseList)
- {
- let prodBarWidth = 0;
- let s = 0;
- let y = 0;
-
- for (let stru of g_BuildList[g_SelectedCiv][pha])
- {
- let thisEle = Engine.GetGUIObjectByName("phase["+i+"]_struct["+s+"]");
- if (thisEle === undefined)
- {
- error("\""+g_SelectedCiv+"\" has more structures in phase " +
- pha + " than can be supported by the current GUI layout");
- break;
- }
-
- let c = 0;
- let rowCounts = [];
- stru = g_ParsedData.structures[stru];
-
- Engine.GetGUIObjectByName("phase["+i+"]_struct["+s+"]_icon").sprite =
- "stretched:session/portraits/"+stru.icon;
-
- Engine.GetGUIObjectByName("phase["+i+"]_struct["+s+"]_icon").tooltip =
- compileTooltip(stru);
-
- Engine.GetGUIObjectByName("phase["+i+"]_struct["+s+"]_name").caption =
- translate(stru.name.specific);
-
- setViewerOnPress("phase["+i+"]_struct["+s+"]_icon", stru.name.internal);
- thisEle.hidden = false;
-
- for (let r in g_DrawLimits[pha].prodQuant)
- {
- let p = 0;
- r = +r; // force int
- let prod_pha = phaseList[phaseList.indexOf(pha) + r];
-
- if (stru.production.units[prod_pha])
- for (let prod of stru.production.units[prod_pha])
- {
- prod = g_ParsedData.units[prod];
- if (!drawProdIcon(i, s, r, p, prod))
- break;
- ++p;
- }
-
- if (stru.upgrades[prod_pha])
- for (let upgrade of stru.upgrades[prod_pha])
- {
- if (!drawProdIcon(i, s, r, p, upgrade))
- break;
- ++p;
- }
-
- if (stru.wallset && prod_pha == pha)
- {
- if (!drawProdIcon(i, s, r, p, stru.wallset.tower))
- break;
- ++p;
- }
-
- if (stru.production.techs[prod_pha])
- for (let prod of stru.production.techs[prod_pha])
- {
- prod = clone(basename(prod).startsWith("phase") ?
- g_ParsedData.phases[prod] :
- g_ParsedData.techs[g_SelectedCiv][prod]);
-
- for (let res in stru.techCostMultiplier)
- if (prod.cost[res])
- prod.cost[res] *= stru.techCostMultiplier[res];
-
- if (!drawProdIcon(i, s, r, p, prod))
- break;
-
- ++p;
- }
-
- rowCounts[r] = p;
-
- if (p>c)
- c = p;
-
- hideRemaining("phase["+i+"]_struct["+s+"]_row["+r+"]", p);
- }
-
- let size = thisEle.size;
- size.left = y;
- size.right = size.left + ((c*24 < defWidth) ? defWidth : c*24) + 4;
- y = size.right + defMargin;
- thisEle.size = size;
-
- let eleWidth = size.right - size.left;
- let r;
- for (r in rowCounts)
- {
- let wid = rowCounts[r] * 24 - 4;
- let phaEle = Engine.GetGUIObjectByName("phase["+i+"]_struct["+s+"]_row["+r+"]");
- size = phaEle.size;
- size.left = (eleWidth - wid)/2;
- phaEle.size = size;
- }
- ++r;
- hideRemaining("phase["+i+"]_struct["+s+"]_rows", r);
- ++s;
- prodBarWidth += eleWidth + defMargin;
- }
-
- hideRemaining("phase["+i+"]", s);
-
- // Resize phase bars
- for (let j = 1; j < phaseList.length - i; ++j)
- {
- let prodBar = Engine.GetGUIObjectByName("phase["+i+"]_bar["+(j-1)+"]");
- let prodBarSize = prodBar.size;
- prodBarSize.right = leftMargin + prodBarWidth;
- prodBar.size = prodBarSize;
- }
-
- ++i;
- }
-
- let t = 0;
- for (let trainer of g_TrainList[g_SelectedCiv])
- {
- let thisEle = Engine.GetGUIObjectByName("trainer["+t+"]");
- if (thisEle === undefined)
- {
- error("\""+g_SelectedCiv+"\" has more unit trainers than can be supported by the current GUI layout");
- break;
- }
-
- trainer = g_ParsedData.units[trainer];
- Engine.GetGUIObjectByName("trainer["+t+"]_icon").sprite = "stretched:session/portraits/"+trainer.icon;
- Engine.GetGUIObjectByName("trainer["+t+"]_icon").tooltip = compileTooltip(trainer);
- Engine.GetGUIObjectByName("trainer["+t+"]_name").caption = translate(trainer.name.specific);
- setViewerOnPress("trainer["+t+"]_icon", trainer.name.internal);
- thisEle.hidden = false;
-
- let p = 0;
- if (trainer.production)
- for (let prodType in trainer.production)
- for (let prod of trainer.production[prodType])
- {
- switch (prodType)
- {
- case "units":
- prod = g_ParsedData.units[prod];
- break;
- case "techs":
- prod = clone(g_ParsedData.techs[g_SelectedCiv][prod]);
- for (let res in trainer.techCostMultiplier)
- if (prod.cost[res])
- prod.cost[res] *= trainer.techCostMultiplier[res];
- break;
- default:
- continue;
- }
- if (!drawProdIcon(null, t, null, p, prod))
- break;
- ++p;
- }
-
- if (trainer.upgrades)
- for (let upgrade of trainer.upgrades)
- {
- if (!drawProdIcon(null, t, null, p, upgrade))
- break;
- ++p;
- }
-
- hideRemaining("trainer["+t+"]_row", p);
-
- let size = thisEle.size;
- size.right = size.left + Math.max(p*24, defWidth) + 4;
- thisEle.size = size;
-
- let eleWidth = size.right - size.left;
- let wid = p * 24 - 4;
- let phaEle = Engine.GetGUIObjectByName("trainer["+t+"]_row");
- size = phaEle.size;
- size.left = (eleWidth - wid)/2;
- phaEle.size = size;
- ++t;
- }
- hideRemaining("trainers", t);
-
- let size = Engine.GetGUIObjectByName("display_tree").size;
- size.right = t > 0 ? -124 : -4;
- Engine.GetGUIObjectByName("display_tree").size = size;
-
- Engine.GetGUIObjectByName("display_trainers").hidden = t === 0;
-}
-
-/**
- * Draws production icons.
- *
- * These are the small icons on the gray bars.
- *
- * @param {string} phase - The phase that the parent entity (the entity that
- * builds/trains/researches this entity) belongs to.
- * @param {number} parentID - Which parent entity it belongs to on the main phase rows.
- * @param {number} rowID - Which production row of the parent entity the production
- * icon sits on, if applicable.
- * @param {number} iconID - Which production icon to affect.
- * @param {object} template - The entity being produced.
- * @return {boolean} True is successfully drawn, False if no space left to draw.
- */
-function drawProdIcon(phase, parentID, rowID, iconID, template)
-{
- let prodEle = Engine.GetGUIObjectByName("phase["+phase+"]_struct["+parentID+"]_row["+rowID+"]_prod["+iconID+"]");
-
- if (phase === null)
- prodEle = Engine.GetGUIObjectByName("trainer["+parentID+"]_prod["+iconID+"]");
-
- if (prodEle === undefined)
- {
- error("The " + (phase === null ? "trainer units" : "structures") + " of \"" + g_SelectedCiv +
- "\" have more production icons than can be supported by the current GUI layout");
- return false;
- }
-
- prodEle.sprite = "stretched:session/portraits/"+template.icon;
- prodEle.tooltip = compileTooltip(template);
- prodEle.hidden = false;
- setViewerOnPress(prodEle.name, template.name.internal);
- return true;
-}
-
-function compileTooltip(template)
-{
- return buildText(template, g_StructreeTooltipFunctions) + "\n" + showTemplateViewerOnClickTooltip();
-}
-
-/**
- * Calculate row position offset (accounting for different number of prod rows per phase).
- *
- * @param {number} idx
- * @return {number}
- */
-function getPositionOffset(idx)
-{
- let phases = g_ParsedData.phaseList.length;
-
- let size = 92*idx; // text, image and offset
- size += 24 * (phases*idx - (idx-1)*idx/2); // phase rows (phase-currphase+1 per row)
-
- return size;
-}
-
-/**
- * Positions certain elements that only need to be positioned once
- * (as does not position automatically).
- *
- * Also detects limits on what the GUI can display by iterating through the set
- * elements of the GUI. These limits are then used by draw().
- */
-function predraw()
-{
- let phaseList = g_ParsedData.phaseList;
- let initIconSize = Engine.GetGUIObjectByName("phase[0]_struct[0]_row[0]_prod[0]").size;
-
- let phaseCount = phaseList.length;
- let i = 0;
- for (let pha of phaseList)
- {
- let offset = getPositionOffset(i);
- // Align the phase row
- Engine.GetGUIObjectByName("phase["+i+"]").size = "8 16+" + offset + " 100% 100%";
-
- // Set phase icon
- let phaseIcon = Engine.GetGUIObjectByName("phase["+i+"]_phase");
- phaseIcon.size = "16 32+"+offset+" 48+16 48+32+"+offset;
-
- // Set initial prod bar size
- let j = 1;
- for (; j < phaseList.length - i; ++j)
- {
- let prodBar = Engine.GetGUIObjectByName("phase["+i+"]_bar["+(j-1)+"]");
- prodBar.size = "40 1+"+(24*j)+"+98+"+offset+" 0 1+"+(24*j)+"+98+"+offset+"+22";
- }
- // Hide remaining prod bars
- hideRemaining("phase["+i+"]_bars", j-1);
-
- let s = 0;
- let ele = Engine.GetGUIObjectByName("phase["+i+"]_struct["+s+"]");
- g_DrawLimits[pha] = {
- "structQuant": 0,
- "prodQuant": []
- };
-
- do
- {
- // Position production icons
- for (let r in phaseList.slice(phaseList.indexOf(pha)))
- {
- let p=1;
- let prodEle = Engine.GetGUIObjectByName("phase["+i+"]_struct["+s+"]_row["+r+"]_prod["+p+"]");
-
- do
- {
- let prodsize = prodEle.size;
- prodsize.left = (initIconSize.right+4) * p;
- prodsize.right = (initIconSize.right+4) * (p+1) - 4;
- prodEle.size = prodsize;
-
- p++;
- prodEle = Engine.GetGUIObjectByName("phase["+i+"]_struct["+s+"]_row["+r+"]_prod["+p+"]");
- } while (prodEle !== undefined);
-
- // Set quantity of productions in this row
- g_DrawLimits[pha].prodQuant[r] = p;
-
- // Position the prod row
- Engine.GetGUIObjectByName("phase["+i+"]_struct["+s+"]_row["+r+"]").size = "4 100%-"+24*(phaseCount - i - r)+" 100%-4 100%";
- }
-
- // Hide unused struct rows
- for (let r = phaseCount - i; r < phaseCount; ++r)
- Engine.GetGUIObjectByName("phase["+i+"]_struct["+s+"]_row["+r+"]").hidden = true;
-
- let size = ele.size;
- size.bottom += Object.keys(g_DrawLimits[pha].prodQuant).length*24;
- ele.size = size;
-
- s++;
- ele = Engine.GetGUIObjectByName("phase["+i+"]_struct["+s+"]");
- } while (ele !== undefined);
-
- // Set quantity of structures in each phase
- g_DrawLimits[pha].structQuant = s;
- ++i;
- }
- hideRemaining("phase_rows", i);
- hideRemaining("phase_ident", i);
-
- let t = 0;
- let ele = Engine.GetGUIObjectByName("trainer["+t+"]");
- g_DrawLimits.trainer = {
- "trainerQuant": 0,
- "prodQuant": 0
- };
-
- let x = 4;
- do
- {
- let p = 0;
- let prodEle = Engine.GetGUIObjectByName("trainer["+t+"]_prod["+p+"]");
- do
- {
- let prodsize = prodEle.size;
- prodsize.left = (initIconSize.right+4) * p;
- prodsize.right = (initIconSize.right+4) * (p+1) - 4;
- prodEle.size = prodsize;
-
- p++;
- prodEle = Engine.GetGUIObjectByName("trainer["+t+"]_prod["+p+"]");
- } while (prodEle !== undefined);
- Engine.GetGUIObjectByName("trainer["+t+"]_row").size = "4 100%-24 100%-4 100%";
- g_DrawLimits.trainer.prodQuant = p;
-
- let size = ele.size;
- size.top += x;
- size.bottom += x + 24;
- x += size.bottom - size.top + 8;
- ele.size = size;
-
- t++;
- ele = Engine.GetGUIObjectByName("trainer["+t+"]");
-
- } while (ele !== undefined);
-
- g_DrawLimits.trainer.trainerQuant = t;
-}
-
-function drawPhaseIcons()
-{
- for (let i = 0; i < g_ParsedData.phaseList.length; ++i)
- {
- drawPhaseIcon("phase["+i+"]_phase", i);
-
- for (let j = 1; j < g_ParsedData.phaseList.length - i; ++j)
- drawPhaseIcon("phase["+i+"]_bar["+(j-1)+"]_icon", j+i);
- }
-}
-
-/**
- * @param {string} guiObjectName
- * @param {number} phaseIndex
- */
-function drawPhaseIcon(guiObjectName, phaseIndex)
-{
- let phaseName = g_ParsedData.phaseList[phaseIndex];
- let prodPhaseTemplate = g_ParsedData.phases[phaseName + "_" + g_SelectedCiv] || g_ParsedData.phases[phaseName];
-
- let phaseIcon = Engine.GetGUIObjectByName(guiObjectName);
- phaseIcon.sprite = "stretched:session/portraits/" + prodPhaseTemplate.icon;
- phaseIcon.tooltip = getEntityNamesFormatted(prodPhaseTemplate);
-}
Index: ps/trunk/binaries/data/mods/public/gui/reference/structree/rows.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/reference/structree/rows.xml
+++ ps/trunk/binaries/data/mods/public/gui/reference/structree/rows.xml
@@ -1,25 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Index: ps/trunk/binaries/data/mods/public/gui/reference/structree/structree.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/reference/structree/structree.js
+++ ps/trunk/binaries/data/mods/public/gui/reference/structree/structree.js
@@ -1,194 +1,14 @@
/**
- * Array of structure template names when given a civ and a phase name.
- */
-var g_BuildList = {};
-
-/**
- * Array of template names that can be trained from a unit, given a civ and unit template name.
- */
-var g_TrainList = {};
-
-/**
* Initialize the page
*
* @param {object} data - Parameters passed from the code that calls this page into existence.
*/
function init(data = {})
{
- let civList = Object.keys(g_CivData).map(civ => ({
- "name": g_CivData[civ].Name,
- "code": civ,
- })).sort(sortNameIgnoreCase);
-
- if (!civList.length)
- {
- closePage();
- return;
- }
-
- g_ParsedData = {
- "units": {},
- "structures": {},
- "techs": {},
- "phases": {}
- };
-
- let civSelection = Engine.GetGUIObjectByName("civSelection");
- civSelection.list = civList.map(c => c.name);
- civSelection.list_data = civList.map(c => c.code);
- civSelection.selected = data.civ ? civSelection.list_data.indexOf(data.civ) : 0;
-
- Engine.GetGUIObjectByName("civinfo").tooltip = colorizeHotkey(translate("%(hotkey)s: Switch to Civilization Overview."), "civinfo");
- Engine.GetGUIObjectByName("close").tooltip = colorizeHotkey(translate("%(hotkey)s: Close Structure Tree."), "cancel");
-}
-
-function switchToCivInfoPage()
-{
- Engine.PopGuiPage({ "civ": g_SelectedCiv, "nextPage": "page_civinfo.xml" });
-}
-
-function closePage()
-{
- Engine.PopGuiPage({ "civ": g_SelectedCiv, "page": "page_structree.xml" });
-}
-
-/**
- * @param {string} civCode
- */
-function selectCiv(civCode)
-{
- if (civCode === g_SelectedCiv || !g_CivData[civCode])
- return;
-
- g_SelectedCiv = civCode;
-
- g_CurrentModifiers = deriveModifications(g_AutoResearchTechList);
-
- // If a buildList already exists, then this civ has already been parsed
- if (g_BuildList[g_SelectedCiv])
- {
- draw();
- drawPhaseIcons();
- return;
- }
-
- let templateLists = compileTemplateLists(civCode);
-
- for (let u of templateLists.units.keys())
- if (!g_ParsedData.units[u])
- g_ParsedData.units[u] = loadEntityTemplate(u);
-
- for (let s of templateLists.structures.keys())
- if (!g_ParsedData.structures[s])
- g_ParsedData.structures[s] = loadEntityTemplate(s);
-
- // Load technologies
- g_ParsedData.techs[civCode] = {};
- for (let techcode of templateLists.techs.keys())
- if (basename(techcode).startsWith("phase"))
- g_ParsedData.phases[techcode] = loadPhase(techcode);
- else
- g_ParsedData.techs[civCode][techcode] = loadTechnology(techcode);
-
- // Establish phase order
- g_ParsedData.phaseList = UnravelPhases(g_ParsedData.phases);
-
- // Load any required generic phases that aren't already loaded
- for (let phasecode of g_ParsedData.phaseList)
- if (!g_ParsedData.phases[phasecode])
- g_ParsedData.phases[phasecode] = loadPhase(phasecode);
-
- // Group production and upgrade lists of structures by phase
- for (let structCode of templateLists.structures.keys())
- {
- let structInfo = g_ParsedData.structures[structCode];
- structInfo.phase = getPhaseOfTemplate(structInfo);
- let structPhaseIdx = g_ParsedData.phaseList.indexOf(structInfo.phase);
-
- // If this structure is shared with another civ,
- // it may have already gone through the grouping process already.
- if (!Array.isArray(structInfo.production.techs))
- continue;
-
- // Sort techs by phase
- let newProdTech = {};
- for (let prod of structInfo.production.techs)
- {
- let phase = getPhaseOfTechnology(prod);
- if (phase === false)
- continue;
-
- if (g_ParsedData.phaseList.indexOf(phase) < structPhaseIdx)
- phase = structInfo.phase;
-
- if (!(phase in newProdTech))
- newProdTech[phase] = [];
-
- newProdTech[phase].push(prod);
- }
-
- // Sort units by phase
- let newProdUnits = {};
- for (let prod of structInfo.production.units)
- {
- let phase = getPhaseOfTemplate(g_ParsedData.units[prod]);
- if (phase === false)
- continue;
-
- if (g_ParsedData.phaseList.indexOf(phase) < structPhaseIdx)
- phase = structInfo.phase;
-
- if (!(phase in newProdUnits))
- newProdUnits[phase] = [];
-
- newProdUnits[phase].push(prod);
- }
-
- g_ParsedData.structures[structCode].production = {
- "techs": newProdTech,
- "units": newProdUnits
- };
-
- // Sort upgrades by phase
- let newUpgrades = {};
- if (structInfo.upgrades)
- for (let upgrade of structInfo.upgrades)
- {
- let phase = getPhaseOfTemplate(upgrade);
-
- if (g_ParsedData.phaseList.indexOf(phase) < structPhaseIdx)
- phase = structInfo.phase;
-
- if (!newUpgrades[phase])
- newUpgrades[phase] = [];
- newUpgrades[phase].push(upgrade);
- }
- g_ParsedData.structures[structCode].upgrades = newUpgrades;
- }
-
- // Determine the buildList for the civ (grouped by phase)
- let buildList = {};
- let trainerList = [];
- for (let pha of g_ParsedData.phaseList)
- buildList[pha] = [];
- for (let structCode of templateLists.structures.keys())
- {
- let phase = g_ParsedData.structures[structCode].phase;
- buildList[phase].push(structCode);
- }
-
- for (let unitCode of templateLists.units.keys())
- {
- let unitTemplate = g_ParsedData.units[unitCode];
- if (!unitTemplate.production.units.length && !unitTemplate.production.techs.length && !unitTemplate.upgrades)
- continue;
-
- trainerList.push(unitCode);
- }
-
- g_BuildList[g_SelectedCiv] = buildList;
- g_TrainList[g_SelectedCiv] = trainerList;
+ g_Page = new StructreePage(data);
- draw();
- drawPhaseIcons();
+ if (data.civ)
+ g_Page.civSelection.selectCiv(data.civ);
+ else
+ g_Page.civSelection.selectFirstCiv();
}
Index: ps/trunk/binaries/data/mods/public/gui/reference/structree/structree.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/reference/structree/structree.xml
+++ ps/trunk/binaries/data/mods/public/gui/reference/structree/structree.xml
@@ -2,14 +2,15 @@
+
-
+
+
-
-
- closePage();
-
-
+
+
+
+
@@ -20,21 +21,7 @@
-
-
- Civilization:
-
-
-
- selectCiv(this.list_data[this.selected]);
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
-
-
-
- Trainer Units
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
-
-
- Civilization Overview
- switchToCivInfoPage();
-
-
-
- Close
- closePage();
-
Index: ps/trunk/binaries/data/mods/public/gui/reference/structree/styles.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/reference/structree/styles.xml
+++ ps/trunk/binaries/data/mods/public/gui/reference/structree/styles.xml
@@ -6,7 +6,7 @@
@@ -18,12 +18,22 @@
+
+
+
+
Index: ps/trunk/binaries/data/mods/public/gui/reference/viewer/ViewerPage.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/reference/viewer/ViewerPage.js
+++ ps/trunk/binaries/data/mods/public/gui/reference/viewer/ViewerPage.js
@@ -0,0 +1,169 @@
+/**
+ * This class represents the Template Viewer GUI page.
+ */
+class ViewerPage extends ReferencePage
+{
+ constructor(data)
+ {
+ super();
+
+ this.currentTemplate = undefined;
+
+ this.guiElements = {
+ "entityName": Engine.GetGUIObjectByName("entityName"),
+ "entityIcon": Engine.GetGUIObjectByName("entityIcon"),
+ "entityStats": Engine.GetGUIObjectByName("entityStats"),
+ "entityInfo": Engine.GetGUIObjectByName("entityInfo"),
+ "entityRankGlyph": Engine.GetGUIObjectByName("entityRankGlyph"),
+ };
+
+ let closeButton = new CloseButton(this);
+ }
+
+ selectTemplate(data)
+ {
+ if (!data || !data.templateName)
+ {
+ error("Viewer: No template provided");
+ this.closePage();
+ return;
+ }
+
+ let templateName = removeFiltersFromTemplateName(data.templateName);
+ let isTech = TechnologyTemplateExists(templateName);
+
+ // Attempt to get the civ code from the template, or, if
+ // it's a technology, from the researcher's template.
+ if (!isTech)
+ {
+ // Catch and redirect if template is a non-promotion variant of
+ // another (ie. units/{civ}_support_female_citizen_house).
+ templateName = this.TemplateLoader.getBaseTemplateName(templateName, this.TemplateLoader.DefaultCiv);
+
+ this.setActiveCiv(this.TemplateLoader.loadEntityTemplate(templateName, this.TemplateLoader.DefaultCiv).Identity.Civ);
+ }
+
+ if (this.activeCiv == this.TemplateLoader.DefaultCiv && data.civ)
+ this.setActiveCiv(data.civ);
+
+ this.currentTemplate = isTech ? this.TemplateParser.getTechnology(templateName, this.activeCiv) : this.TemplateParser.getEntity(templateName, this.activeCiv);
+ if (!this.currentTemplate)
+ {
+ error("Viewer: unable to recognize or load template (" + templateName + ")");
+ this.closePage();
+ return;
+ }
+
+ // Here we compile lists of the names of all the entities that can build/train/research this template,
+ // and the entities and technologies that this template can build/train/research.
+ // We do that here, so we don't do it later in the tooltip callback functions, as that would be messier.
+ if (this.activeCiv != this.TemplateLoader.DefaultCiv)
+ {
+ let templateLists = this.TemplateLister.getTemplateLists(this.activeCiv);
+
+ let builders = templateLists.structures.get(this.currentTemplate.name.internal);
+ if (builders && builders.length)
+ this.currentTemplate.builtByListOfNames = builders.map(builder => getEntityNames(this.TemplateParser.getEntity(builder, this.activeCiv)));
+
+ let trainers = templateLists.units.get(this.currentTemplate.name.internal);
+ if (trainers && trainers.length)
+ this.currentTemplate.trainedByListOfNames = trainers.map(trainer => getEntityNames(this.TemplateParser.getEntity(trainer, this.activeCiv)));
+
+ let researchers = templateLists.techs.get(this.currentTemplate.name.internal);
+ if (researchers && researchers.length)
+ this.currentTemplate.researchedByListOfNames = researchers.map(researcher => getEntityNames(this.TemplateParser.getEntity(researcher, this.activeCiv)));
+ }
+
+ if (this.currentTemplate.builder && this.currentTemplate.builder.length)
+ this.currentTemplate.buildListOfNames = this.currentTemplate.builder.map(prod => getEntityNames(this.TemplateParser.getEntity(prod, this.activeCiv)));
+
+ if (this.currentTemplate.production)
+ {
+ if (this.currentTemplate.production.units && this.currentTemplate.production.units.length)
+ this.currentTemplate.trainListOfNames = this.currentTemplate.production.units.map(prod => getEntityNames(this.TemplateParser.getEntity(prod, this.activeCiv)));
+
+ if (this.currentTemplate.production.techs && this.currentTemplate.production.techs.length)
+ {
+ this.currentTemplate.researchListOfNames = [];
+ for (let tech of this.currentTemplate.production.techs)
+ {
+ let techTemplate = this.TemplateParser.getTechnology(tech, this.activeCiv);
+ if (techTemplate.reqs)
+ this.currentTemplate.researchListOfNames.push(getEntityNames(techTemplate));
+ }
+ }
+ }
+
+ if (this.currentTemplate.upgrades)
+ this.currentTemplate.upgradeListOfNames = this.currentTemplate.upgrades.map(upgrade =>
+ getEntityNames(upgrade.name ? upgrade : this.TemplateParser.getEntity(upgrade.entity, this.activeCiv))
+ );
+
+ this.draw();
+ }
+
+ /**
+ * Populate the UI elements.
+ */
+ draw()
+ {
+ this.guiElements.entityName.caption = getEntityNamesFormatted(this.currentTemplate);
+
+ let entityIcon = this.guiElements.entityIcon;
+ entityIcon.sprite = "stretched:" + this.IconPath + this.currentTemplate.icon;
+
+ let entityStats = this.guiElements.entityStats;
+ entityStats.caption = this.constructor.buildText(this.currentTemplate, this.StatsFunctions);
+
+ let infoSize = this.guiElements.entityInfo.size;
+ // The magic '8' below provides a gap between the bottom of the icon, and the start of the info text.
+ infoSize.top = Math.max(entityIcon.size.bottom + 8, entityStats.size.top + entityStats.getTextSize().height);
+ this.guiElements.entityInfo.size = infoSize;
+
+ this.guiElements.entityInfo.caption = this.constructor.buildText(this.currentTemplate, this.InfoFunctions, "\n\n");
+
+ if (this.currentTemplate.promotion)
+ this.guiElements.entityRankGlyph.sprite = "stretched:" + this.RankIconPath + this.currentTemplate.promotion.current_rank + ".png";
+ this.guiElements.entityRankGlyph.hidden = !this.currentTemplate.promotion;
+ }
+
+ closePage()
+ {
+ Engine.PopGuiPage({ "civ": this.activeCiv, "page": "page_viewer.xml" });
+ }
+}
+
+/**
+ * The result of these functions appear to the right of the entity icon
+ * on the page.
+ */
+ViewerPage.prototype.StatsFunctions = [getEntityCostTooltip].concat(ReferencePage.prototype.StatsFunctions);
+
+/**
+ * Used to display textual information and the build/train lists of the
+ * template being displayed.
+ *
+ * At present, these are drawn in the main body of the page.
+ *
+ * The functions listed can be found in gui/common/tooltips.js or
+ * gui/reference/common/tooltips.js
+ */
+ViewerPage.prototype.InfoFunctions = [
+ getEntityTooltip,
+ getHistoryTooltip,
+ getDescriptionTooltip,
+ getAurasTooltip,
+ getVisibleEntityClassesFormatted,
+ getBuiltByText,
+ getTrainedByText,
+ getResearchedByText,
+ getBuildText,
+ getTrainText,
+ getResearchText,
+ getUpgradeText
+];
+
+ViewerPage.prototype.CloseButtonTooltip =
+ translate("%(hotkey)s: Close Template Viewer");
+
+ViewerPage.prototype.RankIconPath = "session/icons/ranks/";
Index: ps/trunk/binaries/data/mods/public/gui/reference/viewer/viewer.js
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/reference/viewer/viewer.js
+++ ps/trunk/binaries/data/mods/public/gui/reference/viewer/viewer.js
@@ -1,35 +1,4 @@
/**
- * Holder for the template file being displayed.
- */
-var g_Template = {};
-
-/**
- * Holder for the template lists generated by compileTemplateLists().
- */
-var g_TemplateLists = {};
-
-/**
- * Used to display textual information and the build/train lists of the
- * template being displayed.
- *
- * At present, these are drawn in the main body of the page.
- */
-var g_InfoFunctions = [
- getEntityTooltip,
- getHistoryTooltip,
- getDescriptionTooltip,
- getAurasTooltip,
- getVisibleEntityClassesFormatted,
- getBuiltByText,
- getTrainedByText,
- getResearchedByText,
- getBuildText,
- getTrainText,
- getResearchText,
- getUpgradeText
-];
-
-/**
* Override style so we can get a bigger specific name.
*/
g_TooltipTextFormats.nameSpecificBig.font = "sans-bold-20";
@@ -37,11 +6,6 @@
g_TooltipTextFormats.nameGeneric.font = "sans-bold-16";
/**
- * Path to unit rank icons.
- */
-var g_RankIconPath = "session/icons/ranks/";
-
-/**
* Page initialisation. May also eventually pre-draw/arrange objects.
*
* @param {object} data - Contains the civCode and the name of the template to display.
@@ -50,166 +14,6 @@
*/
function init(data)
{
- if (!data || !data.templateName)
- {
- error("Viewer: No template provided");
- closePage();
- return;
- }
-
- let templateName = removeFiltersFromTemplateName(data.templateName);
- let isTech = techDataExists(templateName);
-
- // Attempt to get the civ code from the template, or, if
- // it's a technology, from the researcher's template.
- if (!isTech)
- {
- // Catch and redirect if template is a non-promotion variant of
- // another (ie. units/{civ}_support_female_citizen_house).
- templateName = getBaseTemplateName(templateName);
-
- g_SelectedCiv = loadTemplate(templateName).Identity.Civ;
- }
-
- if (g_SelectedCiv == "gaia" && data.civ)
- g_SelectedCiv = data.civ;
-
- g_TemplateLists = compileTemplateLists(g_SelectedCiv);
- g_CurrentModifiers = deriveModifications(g_AutoResearchTechList);
-
- g_Template = isTech ? loadTechnology(templateName) : loadEntityTemplate(templateName);
- if (!g_Template)
- {
- error("Viewer: unable to recognise or load template (" + templateName + ")");
- closePage();
- return;
- }
-
- g_StatsFunctions = [getEntityCostTooltip].concat(g_StatsFunctions);
-
- draw();
-}
-
-/**
- * Populate the UI elements.
- */
-function draw()
-{
- Engine.GetGUIObjectByName("entityName").caption = getEntityNamesFormatted(g_Template);
-
- let entityIcon = Engine.GetGUIObjectByName("entityIcon");
- entityIcon.sprite = "stretched:session/portraits/" + g_Template.icon;
-
- let entityStats = Engine.GetGUIObjectByName("entityStats");
- entityStats.caption = buildText(g_Template, g_StatsFunctions);
-
- let entityInfo = Engine.GetGUIObjectByName("entityInfo");
- let infoSize = entityInfo.size;
- // The magic '8' below provides a gap between the bottom of the icon, and the start of the info text.
- infoSize.top = Math.max(entityIcon.size.bottom + 8, entityStats.size.top + entityStats.getTextSize().height);
- entityInfo.size = infoSize;
-
- entityInfo.caption = buildText(g_Template, g_InfoFunctions, "\n\n");
-
- if (g_Template.promotion)
- Engine.GetGUIObjectByName("entityRankGlyph").sprite = "stretched:" + g_RankIconPath + g_Template.promotion.current_rank + ".png";
- Engine.GetGUIObjectByName("entityRankGlyph").hidden = !g_Template.promotion;
-}
-
-function getBuiltByText(template)
-{
- if (g_SelectedCiv == "gaia" || !g_TemplateLists.structures.has(template.name.internal))
- return "";
-
- let builders = g_TemplateLists.structures.get(template.name.internal);
- if (!builders.length)
- return "";
-
- // Translation: Label before a list of the names of units that build the structure selected.
- return buildListText(translate("Built by:"), builders.map(builder => getEntityNames(loadEntityTemplate(builder))));
-}
-
-function getTrainedByText(template)
-{
- if (g_SelectedCiv == "gaia" || !g_TemplateLists.units.has(template.name.internal))
- return "";
-
- let trainers = g_TemplateLists.units.get(template.name.internal);
- if (!trainers.length)
- return "";
-
- // Translation: Label before a list of the names of structures or units that train the unit selected.
- return buildListText(translate("Trained by:"), trainers.map(trainer => getEntityNames(loadEntityTemplate(trainer))));
-}
-
-function getResearchedByText(template)
-{
- if (g_SelectedCiv == "gaia" || !g_TemplateLists.techs.has(template.name.internal))
- return "";
-
- let researchers = g_TemplateLists.techs.get(template.name.internal);
- if (!researchers.length)
- return "";
-
- // Translation: Label before a list of names of structures or units that research the technology selected.
- return buildListText(translate("Researched at:"), researchers.map(researcher => getEntityNames(loadEntityTemplate(researcher))));
-}
-
-/**
- * @return {string} List of the names of the structures the selected unit can build.
- */
-function getBuildText(template)
-{
- if (!template.builder || !template.builder.length)
- return "";
-
- // Translation: Label before a list of the names of structures the selected unit can construct or build.
- return buildListText(translate("Builds:"),
- template.builder.map(prod => getEntityNames(loadEntityTemplate(prod))));
-}
-
-/**
- * @return {string} List of the names of the technologies the selected structure/unit can research.
- */
-function getResearchText(template)
-{
- if (!template.production || !template.production.techs || !template.production.techs.length)
- return "";
-
- let researchNames = [];
- for (let tech of template.production.techs)
- {
- let techTemplate = loadTechnology(tech);
- if (techTemplate.reqs)
- researchNames.push(getEntityNames(techTemplate));
- }
-
- // Translation: Label before a list of the names of technologies the selected unit or structure can research.
- return buildListText(translate("Researches:"), researchNames);
-}
-
-/**
- * @return {string} List of the names of the units the selected unit can train.
- */
-function getTrainText(template)
-{
- if (!template.production || !template.production.units || !template.production.units.length)
- return "";
-
- // Translation: Label before a list of the names of units the selected unit or structure can train.
- return buildListText(translate("Trains:"),
- template.production.units.map(prod => getEntityNames(loadEntityTemplate(prod))));
-}
-
-/**
- * @return {string} List of the names of the structures/units the selected structure/unit can upgrade to.
- */
-function getUpgradeText(template)
-{
- if (!template.upgrades)
- return "";
-
- // Translation: Label before a list of the names of units or structures the selected unit or structure can be upgradable to.
- return buildListText(translate("Upgradable to:"),
- template.upgrades.map(upgrade => getEntityNames(upgrade.name ? upgrade : loadEntityTemplate(upgrade.entity))));
+ g_Page = new ViewerPage();
+ g_Page.selectTemplate(data);
}
Index: ps/trunk/binaries/data/mods/public/gui/reference/viewer/viewer.xml
===================================================================
--- ps/trunk/binaries/data/mods/public/gui/reference/viewer/viewer.xml
+++ ps/trunk/binaries/data/mods/public/gui/reference/viewer/viewer.xml
@@ -2,7 +2,10 @@
+
+
+
@@ -27,16 +30,9 @@
-
-
- Close
- Engine.PopGuiPage();
-
+
+
+