Index: binaries/data/mods/public/globalscripts/utility.js
===================================================================
--- binaries/data/mods/public/globalscripts/utility.js
+++ binaries/data/mods/public/globalscripts/utility.js
@@ -61,6 +61,18 @@
return path.slice(path.lastIndexOf("/") + 1);
}
+/**
+ * Returns the directories of a given path.
+ *
+ * ie. a/b/c/file.ext -> a/b/c
+ */
+function dirname(path)
+{
+ if (path.lastIndexOf("/") == -1)
+ return "";
+ return path.slice(0, path.lastIndexOf("/"));
+}
+
/**
* Returns names of files found in the given directory, stripping the directory path and file extension.
*/
Index: binaries/data/mods/public/gui/common/tooltips.js
===================================================================
--- binaries/data/mods/public/gui/common/tooltips.js
+++ binaries/data/mods/public/gui/common/tooltips.js
@@ -2,7 +2,10 @@
"unit": { "font": "sans-10", "color": "orange" },
"header": { "font": "sans-bold-13" },
"body": { "font": "sans-13" },
- "comma": { "font": "sans-12" }
+ "comma": { "font": "sans-12" },
+ "nameSpecificBig": { "font": "sans-bold-16" },
+ "nameSpecificSmall": { "font": "sans-bold-12" },
+ "nameGeneric": { "font": "sans-bold-16" }
};
var g_AttackTypes = {
@@ -110,6 +113,10 @@
});
}
+/**
+ * Entity templates have a `Tooltip` tag in the Identity component.
+ * (The contents of which are copied to a `tooltip` attribute in globalscripts.)
+ */
function getEntityTooltip(template)
{
if (!template.tooltip)
@@ -118,6 +125,35 @@
return bodyFont(template.tooltip);
}
+/**
+ * Technologies have a `description` attribute, and Auras have an `auraDescription`
+ * attribute, which becomes `description`.
+ *
+ * (For technologies, this happens in globalscripts.)
+ *
+ * (For auras, this happens either in the Auras component (for session gui) or
+ * reference/common/load.js (for Reference Suite gui))
+ */
+function getDescriptionTooltip(template)
+{
+ if (!template.description)
+ return "";
+
+ return bodyFont(template.description);
+}
+
+/**
+ * Entity templates have a `History` tag in the Identity component.
+ * (The contents of which are copied to a `history` attribute in globalscripts.)
+ */
+function getHistoryTooltip(template)
+{
+ if (!template.history)
+ return "";
+
+ return bodyFont(template.history);
+}
+
function getHealthTooltip(template)
{
if (!template.health)
@@ -553,7 +589,15 @@
}
if (template.cost)
- return getEntityCostComponentsTooltipString(template, entity, buildingsCountToTrainFullBatch, fullBatchSize, remainderBatch).join(" ");
+ {
+ let costs = getEntityCostComponentsTooltipString(template, entity, buildingsCountToTrainFullBatch, fullBatchSize, remainderBatch).join(" ");
+ if (costs)
+ // Translation: Label in tooltip showing cost of a unit, structure or technology.
+ return sprintf(translate("%(label)s %(costs)s"), {
+ "label": headerFont(translate("Cost:")),
+ "costs": costs
+ });
+ }
return "";
}
@@ -701,15 +745,15 @@
function getEntityNamesFormatted(template)
{
if (!template.name.specific)
- return '[font="sans-bold-16"]' + template.name.generic + "[/font]";
+ return setStringTags(template.name.generic, g_TooltipTextFormats.nameSpecificBig);
// Translation: Example: "Epibátēs Athēnaîos [font="sans-bold-16"](Athenian Marine)[/font]"
return sprintf(translate("%(specificName)s %(fontStart)s(%(genericName)s)%(fontEnd)s"), {
"specificName":
- '[font="sans-bold-16"]' + template.name.specific[0] + '[/font]' +
- '[font="sans-bold-12"]' + template.name.specific.slice(1).toUpperCase() + '[/font]',
+ setStringTags(template.name.specific[0], g_TooltipTextFormats.nameSpecificBig) +
+ setStringTags(template.name.specific.slice(1).toUpperCase(), g_TooltipTextFormats.nameSpecificSmall),
"genericName": template.name.generic,
- "fontStart": '[font="sans-bold-16"]',
+ "fontStart": '[font="' + g_TooltipTextFormats.nameGeneric.font + '"]',
"fontEnd": '[/font]'
});
}
@@ -760,3 +804,13 @@
"details": lootLabels.join(" ")
});
}
+
+function templateViewerOnClickMessage(rightClickOnly)
+{
+ if (rightClickOnly)
+ // Translation: Appears in a tooltip to indicate that right-clicking the corresponding GUI element will open the Template Details GUI page.
+ return translate("Right-click to view more information.");
+
+ // Translation: Appears in a tooltip to indicate that clicking the corresponding GUI element will open the Template Details GUI page.
+ return translate("Click to view more information.");
+}
Index: binaries/data/mods/public/gui/page_viewer.xml
===================================================================
--- /dev/null
+++ binaries/data/mods/public/gui/page_viewer.xml
@@ -0,0 +1,19 @@
+
+
+ common/modern/setup.xml
+ common/modern/styles.xml
+ common/modern/sprites.xml
+
+ common/setup_resources.xml
+ common/sprites.xml
+ common/styles.xml
+
+ reference/common/sprites.xml
+ reference/common/styles.xml
+
+ reference/viewer/styles.xml
+ reference/viewer/sprites.xml
+ reference/viewer/viewer.xml
+
+ reference/common/setup.xml
+
Index: binaries/data/mods/public/gui/reference/common/core.js
===================================================================
--- binaries/data/mods/public/gui/reference/common/core.js
+++ binaries/data/mods/public/gui/reference/common/core.js
@@ -1,4 +1,4 @@
-var g_SelectedCiv = "";
+var g_SelectedCiv = "gaia";
var g_CallbackSet = false;
function closePage()
@@ -94,6 +94,11 @@
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);
@@ -152,7 +157,7 @@
{
templateName = templateName.replace(/\{(civ|native)\}/g, g_SelectedCiv);
if (Engine.TemplateExists(templateName))
- production.units.push(templateName);
+ production.units.push(getBaseTemplateName(templateName));
}
if (template.ProductionQueue.Technologies && template.ProductionQueue.Technologies._string)
@@ -173,3 +178,45 @@
return production;
}
+
+/**
+ * Returns the name of a template's base (as dictated by the selection
+ * group name), or the template's own name if the template is its own base
+ * or the base is of a different promotion rank.
+ */
+function getBaseTemplateName(templateName)
+{
+ if (!templateName || !Engine.TemplateExists(templateName))
+ return undefined;
+
+ let template = loadTemplate(templateName);
+ if (!template.Identity || !template.Identity.SelectionGroupName ||
+ templateName == template.Identity.SelectionGroupName)
+ return templateName;
+
+ let selectionName = template.Identity.SelectionGroupName;
+ if (dirname(selectionName) != dirname(templateName))
+ return templateName;
+
+ if (!template.Identity.Rank)
+ return selectionName;
+
+ let selectionTemplate = loadTemplate(selectionName);
+ if (!selectionTemplate.Identity || !selectionTemplate.Identity.Rank ||
+ selectionTemplate.Identity.Rank == template.Identity.Rank)
+ return selectionName;
+
+ 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: binaries/data/mods/public/gui/reference/common/draw.js
===================================================================
--- binaries/data/mods/public/gui/reference/common/draw.js
+++ binaries/data/mods/public/gui/reference/common/draw.js
@@ -4,13 +4,12 @@
var g_DrawLimits = {};
/**
- * These functions are defined in gui/common/tooltips.js
+ * 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_TooltipFunctions = [
- getEntityNamesFormatted,
- getEntityCostTooltip,
- getEntityTooltip,
- getAurasTooltip,
+var g_StatsFunctions = [
getHealthTooltip,
getHealerTooltip,
getAttackTooltip,
@@ -20,6 +19,7 @@
getProjectilesTooltip,
getSpeedTooltip,
getGatherTooltip,
+ getResourceSupplyTooltip,
getPopulationBonusTooltip,
getResourceTrickleTooltip,
getLootTooltip
@@ -37,3 +37,23 @@
{
return textFunctions.map(func => func(template)).filter(tip => tip).join(joiner);
}
+
+/**
+ * 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: binaries/data/mods/public/gui/reference/common/helper.js
===================================================================
--- binaries/data/mods/public/gui/reference/common/helper.js
+++ binaries/data/mods/public/gui/reference/common/helper.js
@@ -24,6 +24,7 @@
upgrade.entity = upgrade.entity.replace(/\{(civ|native)\}/g, g_SelectedCiv);
let data = GetTemplateDataHelper(loadTemplate(upgrade.entity), null, g_AuraData, g_ResourceData, g_DamageTypes);
+ data.name.internal = upgrade.entity;
data.cost = upgrade.cost;
data.icon = upgrade.icon || data.icon;
data.tooltip = upgrade.tooltip || data.tooltip;
Index: binaries/data/mods/public/gui/reference/common/load.js
===================================================================
--- binaries/data/mods/public/gui/reference/common/load.js
+++ binaries/data/mods/public/gui/reference/common/load.js
@@ -36,12 +36,16 @@
{
// We need to clone the template because we want to perform some translations.
let data = clone(Engine.GetTemplate(templateName));
- translateObjectKeys(data, ["GenericName", "SpecificName", "Tooltip"]);
+ 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;
}
@@ -61,7 +65,7 @@
if (!(templateName in g_TechnologyData))
{
let data = Engine.ReadJSONFile(g_TechnologyPath + templateName + ".json");
- translateObjectKeys(data, ["genericName", "tooltip"]);
+ translateObjectKeys(data, ["genericName", "tooltip", "description"]);
g_TechnologyData[templateName] = data;
}
@@ -108,8 +112,18 @@
let template = loadTemplate(templateName);
let unit = GetTemplateDataHelper(template, null, g_AuraData, g_ResourceData, g_DamageTypes, g_CurrentModifiers);
+ unit.name.internal = templateName;
+
+ unit.history = template.Identity.History;
+
unit.production = loadProductionQueue(template);
+ if (template.Identity.Rank)
+ unit.promotion = {
+ "current_rank": template.Identity.Rank,
+ "entity": template.Promotion && template.Promotion.Entity
+ };
+
if (template.Builder && template.Builder.Entities._string)
{
unit.builder = [];
@@ -124,6 +138,12 @@
if (unit.upgrades)
unit.upgrades = getActualUpgradeData(unit.upgrades);
+ if (template.ResourceSupply)
+ unit.supply = {
+ "type": template.ResourceSupply.Type.split("."),
+ "amount": template.ResourceSupply.Amount,
+ };
+
return unit;
}
@@ -140,6 +160,9 @@
let template = loadTemplate(templateName);
let structure = GetTemplateDataHelper(template, null, g_AuraData, g_ResourceData, g_DamageTypes, g_CurrentModifiers);
+ structure.name.internal = templateName;
+
+ structure.history = template.Identity.History;
structure.production = loadProductionQueue(template);
@@ -208,9 +231,31 @@
});
}
+ if (template.ResourceSupply)
+ structure.supply = {
+ "type": template.ResourceSupply.Type.split("."),
+ "amount": template.ResourceSupply.Amount,
+ };
+
return structure;
}
+function loadResource(templateName)
+{
+ let template = loadTemplate(templateName);
+ let resource = GetTemplateDataHelper(template, null, g_AuraData, g_ResourceData, g_DamageTypes, g_CurrentModifiers);
+ resource.name.internal = templateName;
+
+ resource.history = template.Identity.History;
+
+ resource.supply = {
+ "type": template.ResourceSupply.Type.split("."),
+ "amount": template.ResourceSupply.Amount,
+ };
+
+ return resource;
+}
+
/**
* Load and parse technology from json template.
*
@@ -221,6 +266,7 @@
{
let template = loadTechData(techName);
let tech = GetTechnologyDataHelper(template, g_SelectedCiv, g_ResourceData);
+ tech.name.internal = "tech/" + techName;
if (template.pair !== undefined)
{
Index: binaries/data/mods/public/gui/reference/structree/draw.js
===================================================================
--- binaries/data/mods/public/gui/reference/structree/draw.js
+++ binaries/data/mods/public/gui/reference/structree/draw.js
@@ -1,3 +1,13 @@
+/**
+ * Functions used to collate the contents of a tooltip.
+ */
+var g_StructreeTooltipFunctions = [
+ getEntityNamesFormatted,
+ getEntityCostTooltip,
+ getEntityTooltip,
+ getAurasTooltip
+].concat(g_StatsFunctions);
+
/**
* Draw the structree
*
@@ -44,11 +54,12 @@
"stretched:session/portraits/"+stru.icon;
Engine.GetGUIObjectByName("phase["+i+"]_struct["+s+"]_icon").tooltip =
- buildText(stru, g_TooltipFunctions);
+ 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)
@@ -154,8 +165,9 @@
trainer = g_ParsedData.units[trainer];
Engine.GetGUIObjectByName("trainer["+t+"]_icon").sprite = "stretched:session/portraits/"+trainer.icon;
- Engine.GetGUIObjectByName("trainer["+t+"]_icon").tooltip = buildText(trainer, g_TooltipFunctions);
+ 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;
@@ -242,11 +254,17 @@
}
prodEle.sprite = "stretched:session/portraits/"+template.icon;
- prodEle.tooltip = buildText(template, g_TooltipFunctions);
+ prodEle.tooltip = compileTooltip(template);
prodEle.hidden = false;
+ setViewerOnPress(prodEle.name, template.name.internal);
return true;
}
+function compileTooltip(template)
+{
+ return buildText(template, g_StructreeTooltipFunctions) + "\n" + templateViewerOnClickMessage(false);
+}
+
/**
* Calculate row position offset (accounting for different number of prod rows per phase).
*
Index: binaries/data/mods/public/gui/reference/structree/rows.xml
===================================================================
--- binaries/data/mods/public/gui/reference/structree/rows.xml
+++ binaries/data/mods/public/gui/reference/structree/rows.xml
@@ -6,14 +6,14 @@