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"]', '[/color][/font]'], "header": ['[font="sans-bold-13"]', '[/font]'], "body": ['[font="sans-13"]', '[/font]'], - "comma": ['[font="sans-12"]', '[/font]'] + "comma": ['[font="sans-12"]', '[/font]'], + "entityNameSpecificBig": ['[font="sans-bold-16"]', '[/font]'], + "entityNameSpecificSmall": ['[font="sans-bold-12"]', '[/font]'], + "entityNameGeneric": ['[font="sans-bold-16"]', '[/font]'] }; var g_AttackTypes = { @@ -110,6 +113,10 @@ }); } +/** + * XML-based templates have a `Tooltip` tag. + * (The contents of which are copied to a `tooltip` attribute in globalscripts.) + */ function getEntityTooltip(template) { if (!template.tooltip) @@ -118,6 +125,30 @@ return bodyFont(template.tooltip); } +/** + * Technologies have a `description` attribute, and Auras have an `auraDescription` + * attribute, which becomes `description` during the loading/parsing process. + */ +function getDescriptionTooltip(template) +{ + if (!template.description) + return ""; + + return bodyFont(template.description); +} + +/** + * XML-based templates have a `History` tag. + * (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 +584,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 ""; } @@ -568,6 +607,25 @@ }); } +/** + * 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), + "amount": Number.isFinite(+supply.amount) ? supply.amount : translate("∞") + }); +} + /** * Returns the population bonus information to display in the specified entity's construction button tooltip. */ @@ -702,16 +760,16 @@ function getEntityNamesFormatted(template) { if (!template.name.specific) - return '[font="sans-bold-16"]' + template.name.generic + "[/font]"; + return g_TooltipTextFormats.entityNameSpecificBig[0] + template.name.generic + g_TooltipTextFormats.entityNameSpecificBig[1]; // 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]', + g_TooltipTextFormats.entityNameSpecificBig[0] + template.name.specific[0] + g_TooltipTextFormats.entityNameSpecificBig[1] + + g_TooltipTextFormats.entityNameSpecificSmall[0] + template.name.specific.slice(1).toUpperCase() + g_TooltipTextFormats.entityNameSpecificSmall[1], "genericName": template.name.generic, - "fontStart": '[font="sans-bold-16"]', - "fontEnd": '[/font]' + "fontStart": g_TooltipTextFormats.entityNameGeneric[0], + "fontEnd": g_TooltipTextFormats.entityNameGeneric[1] }); } @@ -761,3 +819,16 @@ "details": lootLabels.join(" ") }); } + +/** + * @param {boolean} rightClickOnly + */ +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() @@ -95,6 +95,11 @@ return {}; let template = loadTemplate(templateName); + // 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 templateLists = { "structures": [], "units": [], @@ -151,8 +156,15 @@ for (let build of template.ProductionQueue.Entities._string.split(" ")) { build = build.replace("{civ}", g_SelectedCiv); - if (Engine.TemplateExists(build) && templateLists.units.indexOf(build) === -1) - templateLists.units.push(build); + if (Engine.TemplateExists(build)) + { + if (templateLists.units.indexOf(build) == -1) + templateLists.units.push(build); + + let baseBuild = getBaseTemplateName(build); + if (baseBuild != build && templateLists.units.indexOf(baseBuild) == -1) + templateLists.units.push(baseBuild); + } } if (template.ProductionQueue.Technologies && template.ProductionQueue.Technologies._string) @@ -174,3 +186,64 @@ return templateLists; } + +/** + * 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. + * + * We have entities, such as the female-citizen and some champions, that + * can be trained initially in certain structures (civic centre, fortress) + * and then, once a given technology has been researched, in further + * structures (houses, barracks, ships). In this case, a `*_house`, + * `*_barracks`, `*_trireme`, or other `{original}_{*}` variant is used + * which is functionally identical to the original it is based on, but + * with a different technology requirement. + * + * We don't test for differing tech-requirements so as to support other + * potential variant types, such as a unit having a different cost + * depending on where it is trained (although admittedly there are other + * ways of achieving that without using a different template file). + * + * The exception to this is that we don't return a base that is of a + * different promotion rank to the provided template. + * + * @param {string} templateName + * @return {string} The base template name. + */ +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 (!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; +} + +/** + * @param {string} objectName + * @param {string} templateName + */ +function setViewerOnPress(objectName, templateName) +{ + let viewerFunc = () => { + Engine.PushGuiPage("page_viewer.xml", { + "templateName": templateName, + "civ": g_SelectedCiv, + }); + }; + Engine.GetGUIObjectByName(objectName).onPress = viewerFunc; + Engine.GetGUIObjectByName(objectName).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 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}", 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; } @@ -103,6 +107,9 @@ 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; if (template.ProductionQueue) { @@ -129,6 +136,12 @@ } } + 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 = []; @@ -143,6 +156,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; } @@ -159,6 +178,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 = { "technology": [], @@ -237,9 +259,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); + 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. * @@ -250,6 +294,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 @@ - - + Index: binaries/data/mods/public/gui/reference/structree/structree.xml =================================================================== --- binaries/data/mods/public/gui/reference/structree/structree.xml +++ binaries/data/mods/public/gui/reference/structree/structree.xml @@ -99,12 +99,12 @@ - - + Index: binaries/data/mods/public/gui/reference/viewer/sprites.xml =================================================================== --- /dev/null +++ binaries/data/mods/public/gui/reference/viewer/sprites.xml @@ -0,0 +1,18 @@ + + + + + + + + Index: binaries/data/mods/public/gui/reference/viewer/styles.xml =================================================================== --- /dev/null +++ binaries/data/mods/public/gui/reference/viewer/styles.xml @@ -0,0 +1,14 @@ + + + +