mirror of
https://gitea.wildfiregames.com/0ad/0ad.git
synced 2026-06-21 14:43:52 +00:00
Implement an in-game Template Details Viewer
Right-click on icons of units, structures, technologies, flora, fauna, etc. and view additional details. Reviewed By: elexis Fixes: #3212 Differential Revision: https://code.wildfiregames.com/D297 This was SVN commit r21310.
This commit is contained in:
@@ -390,6 +390,7 @@ function GetTemplateDataHelper(template, player, auraTemplates, resources, damag
|
||||
ret.tooltip = template.Identity.Tooltip;
|
||||
ret.requiredTechnology = template.Identity.RequiredTechnology;
|
||||
ret.visibleIdentityClasses = GetVisibleIdentityClasses(template.Identity);
|
||||
ret.nativeCiv = template.Identity.Civ;
|
||||
}
|
||||
|
||||
if (template.UnitMotion)
|
||||
|
||||
@@ -2,7 +2,10 @@ var g_TooltipTextFormats = {
|
||||
"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,12 @@ function getSecondsString(seconds)
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Entity templates have a `Tooltip` tag in the Identity component.
|
||||
* (The contents of which are copied to a `tooltip` attribute in globalscripts.)
|
||||
*
|
||||
* Technologies have a `tooltip` attribute.
|
||||
*/
|
||||
function getEntityTooltip(template)
|
||||
{
|
||||
if (!template.tooltip)
|
||||
@@ -118,6 +127,35 @@ function getEntityTooltip(template)
|
||||
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)
|
||||
@@ -569,7 +607,15 @@ function getEntityCostTooltip(template, entity, buildingsCountToTrainFullBatch,
|
||||
}
|
||||
|
||||
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 "";
|
||||
}
|
||||
@@ -717,15 +763,15 @@ function getEntityNames(template)
|
||||
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]'
|
||||
});
|
||||
}
|
||||
@@ -776,3 +822,15 @@ function getLootTooltip(template)
|
||||
"details": lootLabels.join(" ")
|
||||
});
|
||||
}
|
||||
|
||||
function showTemplateViewerOnRightClickTooltip()
|
||||
{
|
||||
// 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.");
|
||||
}
|
||||
|
||||
function showTemplateViewerOnClickTooltip()
|
||||
{
|
||||
// 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.");
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<page>
|
||||
<include>common/modern/setup.xml</include>
|
||||
<include>common/modern/styles.xml</include>
|
||||
<include>common/modern/sprites.xml</include>
|
||||
|
||||
<include>common/setup_resources.xml</include>
|
||||
<include>common/sprites.xml</include>
|
||||
<include>common/styles.xml</include>
|
||||
|
||||
<include>reference/common/sprites.xml</include>
|
||||
<include>reference/common/styles.xml</include>
|
||||
|
||||
<include>reference/viewer/styles.xml</include>
|
||||
<include>reference/viewer/sprites.xml</include>
|
||||
<include>reference/viewer/viewer.xml</include>
|
||||
|
||||
<include>reference/common/setup.xml</include>
|
||||
</page>
|
||||
@@ -1,4 +1,4 @@
|
||||
var g_SelectedCiv = "";
|
||||
var g_SelectedCiv = "gaia";
|
||||
var g_CallbackSet = false;
|
||||
|
||||
function closePage()
|
||||
@@ -94,6 +94,11 @@ function getTemplateListsFromUnit(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);
|
||||
@@ -144,7 +149,7 @@ function loadProductionQueue(template)
|
||||
{
|
||||
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)
|
||||
@@ -182,3 +187,46 @@ function loadBuildQueue(template)
|
||||
|
||||
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(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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -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 @@ var g_TooltipFunctions = [
|
||||
getProjectilesTooltip,
|
||||
getSpeedTooltip,
|
||||
getGatherTooltip,
|
||||
getResourceSupplyTooltip,
|
||||
getPopulationBonusTooltip,
|
||||
getResourceTrickleTooltip,
|
||||
getLootTooltip
|
||||
@@ -37,3 +37,37 @@ 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("∞")
|
||||
});
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ function getActualUpgradeData(upgradesInfo)
|
||||
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;
|
||||
|
||||
@@ -36,12 +36,16 @@ function loadTemplate(templateName)
|
||||
{
|
||||
// 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 @@ function loadTechData(templateName)
|
||||
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;
|
||||
}
|
||||
@@ -100,7 +104,7 @@ function loadAuraData(templateName)
|
||||
*
|
||||
* @return {(object|null)} Sanitized object about the requested template or null if entity template doesn't exist.
|
||||
*/
|
||||
function loadEntityTemplate (templateName)
|
||||
function loadEntityTemplate(templateName)
|
||||
{
|
||||
if (!Engine.TemplateExists(templateName))
|
||||
return null;
|
||||
@@ -109,10 +113,18 @@ function loadEntityTemplate (templateName)
|
||||
let parsed = GetTemplateDataHelper(template, null, g_AuraData, g_ResourceData, g_DamageTypes, 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("."),
|
||||
@@ -197,6 +209,7 @@ function loadTechnology(techName)
|
||||
{
|
||||
let template = loadTechData(techName);
|
||||
let tech = GetTechnologyDataHelper(template, g_SelectedCiv, g_ResourceData);
|
||||
tech.name.internal = techName;
|
||||
|
||||
if (template.pair !== undefined)
|
||||
{
|
||||
|
||||
@@ -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 @@ function draw()
|
||||
"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 @@ function draw()
|
||||
|
||||
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 @@ function drawProdIcon(phase, parentID, rowID, iconID, template)
|
||||
}
|
||||
|
||||
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" + showTemplateViewerOnClickTooltip();
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate row position offset (accounting for different number of prod rows per phase).
|
||||
*
|
||||
|
||||
@@ -6,14 +6,14 @@
|
||||
<repeat count="20" var="s">
|
||||
<object type="image" style="StructBox" name="phase[k]_struct[s]">
|
||||
<object type="text" style="StructNameSpecific" name="phase[k]_struct[s]_name"/>
|
||||
<object type="image" style="StructIcon" name="phase[k]_struct[s]_icon"
|
||||
<object type="button" style="StructIcon" name="phase[k]_struct[s]_icon"
|
||||
sprite="stretched:pregame/shell/logo/wfg_logo_white.png"
|
||||
/>
|
||||
<object name="phase[k]_struct[s]_rows">
|
||||
<repeat count="4" var="r">
|
||||
<object name="phase[k]_struct[s]_row[r]">
|
||||
<repeat count="24" var="p">
|
||||
<object type="image" style="ProdBox" name="phase[k]_struct[s]_row[r]_prod[p]"/>
|
||||
<object type="button" style="ProdBox" name="phase[k]_struct[s]_row[r]_prod[p]"/>
|
||||
</repeat>
|
||||
</object>
|
||||
</repeat>
|
||||
|
||||
@@ -96,12 +96,12 @@
|
||||
<repeat count="6" var="t">
|
||||
<object type="image" style="StructBox" name="trainer[t]">
|
||||
<object type="text" style="StructNameSpecific" name="trainer[t]_name"/>
|
||||
<object type="image" style="StructIcon" name="trainer[t]_icon"
|
||||
<object type="button" style="StructIcon" name="trainer[t]_icon"
|
||||
sprite="stretched:pregame/shell/logo/wfg_logo_white.png"
|
||||
/>
|
||||
<object name="trainer[t]_row">
|
||||
<repeat count="4" var="p">
|
||||
<object type="image" style="ProdBox" name="trainer[t]_prod[p]"/>
|
||||
<object type="button" style="ProdBox" name="trainer[t]_prod[p]"/>
|
||||
</repeat>
|
||||
</object>
|
||||
</object>
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<sprites>
|
||||
<sprite name="IconFrame_Gold">
|
||||
<image
|
||||
border="true"
|
||||
bordercolor="163 163 103"
|
||||
backcolor="0 0 0 0"
|
||||
size="0 0 100% 100%"
|
||||
/>
|
||||
<image
|
||||
border="true"
|
||||
bordercolor="0 0 0"
|
||||
backcolor="0 0 0 0"
|
||||
size="1 1 100%-1 100%-1"
|
||||
/>
|
||||
</sprite>
|
||||
</sprites>
|
||||
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<styles>
|
||||
<style name="IconFrame"
|
||||
sprite="IconFrame_Gold"
|
||||
size="0 0 100% 100%"
|
||||
ghost="true"
|
||||
/>
|
||||
|
||||
<style name="RankGlyph"
|
||||
size="4 6 20 22"
|
||||
ghost="true"
|
||||
/>
|
||||
</styles>
|
||||
@@ -0,0 +1,225 @@
|
||||
/**
|
||||
* 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";
|
||||
g_TooltipTextFormats.nameSpecificSmall.font = "sans-bold-16";
|
||||
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.
|
||||
* @param {string} data.templateName
|
||||
* @param {string} [data.civ]
|
||||
* @param {*} [data.callback] - If set and loosely equivalent to true, a callback is
|
||||
* assumed to be setup ready be called by the Engine upon
|
||||
* closure of this page.
|
||||
*/
|
||||
function init(data)
|
||||
{
|
||||
if (!data || !data.templateName)
|
||||
{
|
||||
error("Viewer: No template provided");
|
||||
closePage();
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.callback)
|
||||
g_CallbackSet = true;
|
||||
|
||||
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.
|
||||
*
|
||||
* @todo (c++ change) Implement and use a function that fetches height of rendered text block from text object.
|
||||
*/
|
||||
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);
|
||||
|
||||
// This is something of a crude hack. See above todo.
|
||||
let entityInfo = Engine.GetGUIObjectByName("entityInfo");
|
||||
let lines = entityStats.caption.split("\n").length;
|
||||
let fontSize = +entityStats.font.split("-")[1] + 4;
|
||||
let infoSize = entityInfo.size;
|
||||
infoSize.top = Math.max(entityIcon.size.bottom, lines * fontSize + entityStats.size.top) + 8;
|
||||
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 buildings 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 buildings/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))));
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<objects>
|
||||
<script directory="gui/common/"/>
|
||||
<script directory="gui/reference/common/"/>
|
||||
<script directory="gui/reference/viewer/"/>
|
||||
|
||||
<!-- Add a translucent black background to fade out whatever's behind this -->
|
||||
<object type="image" z="0" sprite="BackgroundTranslucent"/>
|
||||
|
||||
<object type="image" sprite="ModernDialog" size="50%-250 50%-300 50%+250 50%+300">
|
||||
<object style="TitleText" type="text" size="50%-128 -18 50%+128 14">
|
||||
<translatableAttribute id="caption">Information</translatableAttribute>
|
||||
</object>
|
||||
|
||||
<object name="entityName" type="text" size="8 16 100%-8 48" text_align="center" text_valign="top" textcolor="white"/>
|
||||
|
||||
<object name="entityIcon" type="image"
|
||||
size="16 48 128+16 128+48"
|
||||
sprite="stretched:pregame/shell/logo/wfg_logo_white.png"
|
||||
>
|
||||
<object type="image" style="IconFrame"/>
|
||||
<object type="image" style="RankGlyph" name="entityRankGlyph"/>
|
||||
</object>
|
||||
|
||||
<object name="entityStats" type="text" style="ModernText" size="128+20 44 100%-8 50%" scrollbar="false"/>
|
||||
|
||||
<object name="entityInfo" type="text" style="ModernText" size="16 128+48+8 100%-16 100%-48"/>
|
||||
|
||||
<!-- Close page -->
|
||||
<object
|
||||
type="button"
|
||||
style="StoneButton"
|
||||
size="100%-164 100%-44 100%-16 100%-16"
|
||||
hotkey="cancel"
|
||||
>
|
||||
<translatableAttribute id="caption">Close</translatableAttribute>
|
||||
<action on="Press">closePage();</action>
|
||||
</object>
|
||||
</object>
|
||||
|
||||
</objects>
|
||||
@@ -284,6 +284,10 @@ function displaySingle(entState)
|
||||
|
||||
// TODO: we should require all entities to have icons
|
||||
Engine.GetGUIObjectByName("icon").sprite = template.icon ? ("stretched:session/portraits/" + template.icon) : "BackgroundBlack";
|
||||
if (template.icon)
|
||||
Engine.GetGUIObjectByName("iconBorder").onPressRight = () => {
|
||||
showTemplateDetails(entState.template);
|
||||
};
|
||||
|
||||
Engine.GetGUIObjectByName("attackAndArmorStats").tooltip = [
|
||||
getAttackTooltip,
|
||||
@@ -306,7 +310,8 @@ function displaySingle(entState)
|
||||
iconTooltips = iconTooltips.concat([
|
||||
getVisibleEntityClassesFormatted,
|
||||
getAurasTooltip,
|
||||
getEntityTooltip
|
||||
getEntityTooltip,
|
||||
showTemplateViewerOnRightClickTooltip
|
||||
].map(func => func(template)));
|
||||
|
||||
Engine.GetGUIObjectByName("iconBorder").tooltip = iconTooltips.filter(tip => tip).join("\n");
|
||||
|
||||
@@ -259,6 +259,7 @@ g_SelectionPanels.Construction = {
|
||||
});
|
||||
|
||||
data.button.onPress = function() { startBuildingPlacement(data.item, data.playerState); };
|
||||
data.button.onPressRight = function() { showTemplateDetails(data.item); };
|
||||
|
||||
let tooltips = [
|
||||
getEntityNamesFormatted,
|
||||
@@ -267,7 +268,8 @@ g_SelectionPanels.Construction = {
|
||||
getEntityTooltip,
|
||||
getEntityCostTooltip,
|
||||
getGarrisonTooltip,
|
||||
getPopulationBonusTooltip
|
||||
getPopulationBonusTooltip,
|
||||
showTemplateViewerOnRightClickTooltip
|
||||
].map(func => func(template));
|
||||
|
||||
let limits = getEntityLimitAndCount(data.playerState, data.item);
|
||||
@@ -742,7 +744,8 @@ g_SelectionPanels.Research = {
|
||||
let tooltips = [
|
||||
getEntityNamesFormatted,
|
||||
getEntityTooltip,
|
||||
getEntityCostTooltip
|
||||
getEntityCostTooltip,
|
||||
showTemplateViewerOnRightClickTooltip
|
||||
].map(func => func(template));
|
||||
|
||||
if (!requirementsPassed)
|
||||
@@ -793,6 +796,14 @@ g_SelectionPanels.Research = {
|
||||
addResearchToQueue(data.item.researchFacilityId, tech);
|
||||
};
|
||||
|
||||
button.onPressRight = function () {
|
||||
let researcherTemplate;
|
||||
for (let selectedEntity of data.unitEntStates)
|
||||
if (selectedEntity.id == data.item.researchFacilityId)
|
||||
researcherTemplate = selectedEntity.template;
|
||||
showTemplateDetails(data.item.tech, GetTemplateData(researcherTemplate).nativeCiv);
|
||||
};
|
||||
|
||||
if (data.item.tech.pair)
|
||||
{
|
||||
// On mouse enter, show a cross over the other icon
|
||||
@@ -980,6 +991,9 @@ g_SelectionPanels.Training = {
|
||||
data.button.onPress = function() {
|
||||
addTrainingToQueue(data.unitEntStates.map(state => state.id), data.item, data.playerState);
|
||||
};
|
||||
data.button.onPressRight = function() {
|
||||
showTemplateDetails(data.item);
|
||||
};
|
||||
|
||||
data.countDisplay.caption = trainNum > 1 ? trainNum : "";
|
||||
|
||||
@@ -1008,6 +1022,7 @@ g_SelectionPanels.Training = {
|
||||
getSpeedTooltip
|
||||
].map(func => func(template)));
|
||||
|
||||
tooltips.push(showTemplateViewerOnRightClickTooltip());
|
||||
tooltips.push(
|
||||
formatBatchTrainingString(buildingsCountToTrainFullBatch, fullBatchSize, remainderBatch),
|
||||
getRequiredTechnologyTooltip(technologyEnabled, template.requiredTechnology, GetSimState().players[data.player].civ),
|
||||
@@ -1093,7 +1108,8 @@ g_SelectionPanels.Upgrade = {
|
||||
getEntityCostComponentsTooltipString(data.item, undefined, data.unitEntStates.length),
|
||||
formatLimitString(limits.entLimit, limits.entCount, limits.entLimitChangers),
|
||||
getRequiredTechnologyTooltip(technologyEnabled, data.item.requiredTechnology, GetSimState().players[data.player].civ),
|
||||
getNeededResourcesTooltip(neededResources));
|
||||
getNeededResourcesTooltip(neededResources),
|
||||
showTemplateViewerOnRightClickTooltip());
|
||||
|
||||
tooltip = tooltips.filter(tip => tip).join("\n");
|
||||
|
||||
@@ -1112,6 +1128,10 @@ g_SelectionPanels.Upgrade = {
|
||||
data.button.enabled = controlsPlayer(data.player);
|
||||
data.button.tooltip = tooltip;
|
||||
|
||||
data.button.onPressRight = function() {
|
||||
showTemplateDetails(data.item.entity);
|
||||
};
|
||||
|
||||
let modifier = "";
|
||||
if (!isUpgrading)
|
||||
if (progress || !technologyEnabled || limits.canBeAddedCount == 0 &&
|
||||
@@ -1146,6 +1166,25 @@ g_SelectionPanels.Upgrade = {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Pauses game and opens the template details viewer for a selected entity or technology.
|
||||
*
|
||||
* Technologies don't have a set civ, so we pass along the native civ of
|
||||
* the template of the entity that's researching it.
|
||||
*
|
||||
* @param {string} [civCode] - The template name of the entity that researches the selected technology.
|
||||
*/
|
||||
function showTemplateDetails(templateName, civCode)
|
||||
{
|
||||
pauseGame();
|
||||
|
||||
Engine.PushGuiPage("page_viewer.xml", {
|
||||
"templateName": templateName,
|
||||
"callback": "resumeGame",
|
||||
"civ": civCode
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* If two panels need the same space, so they collide,
|
||||
* the one appearing first in the order is rendered.
|
||||
|
||||
+1
-1
@@ -67,7 +67,7 @@
|
||||
</object>
|
||||
|
||||
<!-- Big unit icon -->
|
||||
<object size="-8 -8 88 88" type="image" name="iconBorder" sprite="iconBorder" tooltip_style="sessionToolTip">
|
||||
<object size="-8 -8 88 88" type="button" name="iconBorder" sprite="iconBorder" tooltip_style="sessionToolTip">
|
||||
<object size="1 1 100%-1 100%-1" type="image" name="icon" ghost="true"/>
|
||||
|
||||
<!-- Experience bar -->
|
||||
|
||||
@@ -245,6 +245,7 @@
|
||||
"gui/pregame/**.js",
|
||||
"gui/reference/common/**.js",
|
||||
"gui/reference/structree/**.js",
|
||||
"gui/reference/viewer/**.js",
|
||||
"gui/replaymenu/**.js",
|
||||
"gui/savedgames/**.js",
|
||||
"gui/splashscreen/**.js",
|
||||
@@ -279,6 +280,7 @@
|
||||
"gui/options/**.xml",
|
||||
"gui/pregame/**.xml",
|
||||
"gui/reference/structree/**.xml",
|
||||
"gui/reference/viewer/**.xml",
|
||||
"gui/replaymenu/**.xml",
|
||||
"gui/savedgames/**.xml",
|
||||
"gui/splashscreen/**.xml",
|
||||
@@ -400,6 +402,7 @@
|
||||
"keywords": {
|
||||
"GenericName": {},
|
||||
"SpecificName": {},
|
||||
"History": {},
|
||||
"VisibleClasses": {
|
||||
"splitOnWhitespace": true
|
||||
},
|
||||
@@ -431,6 +434,7 @@
|
||||
"keywords": {
|
||||
"GenericName": {},
|
||||
"SpecificName": {},
|
||||
"History": {},
|
||||
"VisibleClasses": {
|
||||
"splitOnWhitespace": true
|
||||
},
|
||||
@@ -469,6 +473,7 @@
|
||||
"keywords": {
|
||||
"GenericName": {},
|
||||
"SpecificName": {},
|
||||
"History": {},
|
||||
"VisibleClasses": {
|
||||
"splitOnWhitespace": true
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user