diff --git a/binaries/data/mods/public/globalscripts/Resources.js b/binaries/data/mods/public/globalscripts/Resources.js new file mode 100644 index 0000000000..8d6b00aa2c --- /dev/null +++ b/binaries/data/mods/public/globalscripts/Resources.js @@ -0,0 +1,98 @@ +/** + * Since the AI context can't access JSON functions, it gets passed an object + * containing the information from `GuiInterface.js::GetSimulationState()`. + */ +function Resources() +{ + let jsonFiles = []; + // Simulation context + if (Engine.FindJSONFiles) + { + jsonFiles = Engine.FindJSONFiles("resources", false); + for (let file in jsonFiles) + jsonFiles[file] = "resources/" + jsonFiles[file] + ".json"; + } + // GUI context + else if (Engine.BuildDirEntList) + jsonFiles = Engine.BuildDirEntList("simulation/data/resources/", "*.json", false); + else + { + error("Resources: JSON functions are not available"); + return; + } + + this.resourceData = []; + this.resourceDataObj = {}; + this.resourceCodes = []; + this.resourceNames = {}; + + for (let filename of jsonFiles) + { + let data = Engine.ReadJSONFile(filename); + if (!data) + continue; + + if (data.code != data.code.toLowerCase()) + warn("Resource codes should use lower case: " + data.code); + + // Treasures are supported for every specified resource + if (data.code == "treasure") + { + error("Encountered resource with reserved keyword: " + data.code); + continue; + } + + this.resourceData.push(data); + this.resourceDataObj[data.code] = data; + this.resourceCodes.push(data.code); + this.resourceNames[data.code] = data.name; + for (let subres in data.subtypes) + this.resourceNames[subres] = data.subtypes[subres] + } + + // Sort arrays by specified order + let resSort = (a, b) => + a.order < b.order ? -1 : + a.order > b.order ? +1 : 0; + + this.resourceData.sort(resSort); + this.resourceCodes.sort((a, b) => resSort( + this.resourceData.find(resource => resource.code == a), + this.resourceData.find(resource => resource.code == b) + )); +}; + +/** + * Returns the objects defined in the JSON files for all availbale resources, + * ordered as defined in these files. + */ +Resources.prototype.GetResources = function() +{ + return this.resourceData; +}; + +/** + * Returns the object defined in the JSON file for the given resource. + */ +Resources.prototype.GetResource = function(type) +{ + return this.resourceDataObj[type]; +}; + +/** + * Returns an array containing all resource codes ordered as defined in the resource files. + * For example ["food", "wood", "stone", "metal"]. + */ +Resources.prototype.GetCodes = function() +{ + return this.resourceCodes; +}; + +/** + * Returns an object mapping resource codes to translatable resource names. Includes subtypes. + * For example { "food": "Food", "fish": "Fish", "fruit": "Fruit", "metal": "Metal", ... } + */ +Resources.prototype.GetNames = function() +{ + return this.resourceNames; +}; diff --git a/binaries/data/mods/public/globalscripts/Templates.js b/binaries/data/mods/public/globalscripts/Templates.js index 45dfeb09aa..50b4a490c8 100644 --- a/binaries/data/mods/public/globalscripts/Templates.js +++ b/binaries/data/mods/public/globalscripts/Templates.js @@ -75,8 +75,9 @@ function MatchesClassList(classes, match) * @param player An optional player id to get the technology modifications * of properties. * @param auraTemplates An object in the form of {key: {auraName: "", auraDescription: ""}} + * @param resources An instance of the Resources prototype */ -function GetTemplateDataHelper(template, player, auraTemplates) +function GetTemplateDataHelper(template, player, auraTemplates, resources) { // Return data either from template (in tech tree) or sim state (ingame) let getEntityValue = function(tech_type) { @@ -193,17 +194,9 @@ function GetTemplateDataHelper(template, player, auraTemplates) if (template.Cost) { ret.cost = {}; - if (template.Cost.Resources.food) - ret.cost.food = getEntityValue("Cost/Resources/food"); - - if (template.Cost.Resources.wood) - ret.cost.wood = getEntityValue("Cost/Resources/wood"); - - if (template.Cost.Resources.stone) - ret.cost.stone = getEntityValue("Cost/Resources/stone"); - - if (template.Cost.Resources.metal) - ret.cost.metal = getEntityValue("Cost/Resources/metal"); + for (let resCode of resources.GetCodes()) + if (template.Cost.Resources[resCode]) + ret.cost[resCode] = getEntityValue("Cost/Resources/" + resCode); if (template.Cost.Population) ret.cost.population = getEntityValue("Cost/Population"); @@ -348,8 +341,9 @@ function GetTemplateDataHelper(template, player, auraTemplates) * Get information about a technology template. * @param template A valid template as obtained by loading the tech JSON file. * @param civ Civilization for which the specific name should be returned. + * @param resources An instance of the Resources prototype. */ -function GetTechnologyDataHelper(template, civ) +function GetTechnologyDataHelper(template, civ, resources) { var ret = {}; @@ -370,13 +364,9 @@ function GetTechnologyDataHelper(template, civ) ret.icon = template.icon ? "technologies/" + template.icon : null; - ret.cost = { - "food": template.cost ? +template.cost.food : 0, - "wood": template.cost ? +template.cost.wood : 0, - "metal": template.cost ? +template.cost.metal : 0, - "stone": template.cost ? +template.cost.stone : 0, - "time": template.researchTime ? +template.researchTime : 0, - } + ret.cost = { "time": template.researchTime ? +template.researchTime : 0 } + for (let type of resources.GetCodes()) + ret.cost[type] = template.cost ? +template.cost[type] : 0; ret.tooltip = template.tooltip; ret.requirementsTooltip = template.requirementsTooltip || ""; diff --git a/binaries/data/mods/public/gui/common/functions_utility.js b/binaries/data/mods/public/gui/common/functions_utility.js index 12380bbeaf..59dfcc6a0e 100644 --- a/binaries/data/mods/public/gui/common/functions_utility.js +++ b/binaries/data/mods/public/gui/common/functions_utility.js @@ -245,3 +245,32 @@ function notifyUser(userName, msgText) g_LastNickNotification = timeNow; } + +/** + * Horizontally spaces objects within a parent + * + * @param margin The gap, in px, between the objects + */ +function horizontallySpaceObjects(parentName, margin=0) +{ + let objects = Engine.GetGUIObjectByName(parentName).children; + for (let i = 0; i < objects.length; ++i) + { + let size = objects[i].size; + let width = size.right - size.left; + size.left = i * (width + margin) + margin; + size.right = (i + 1) * (width + margin); + objects[i].size = size; + } +} + +/** + * Hide all children after a certain index + */ +function hideRemaining(parentName, start = 0) +{ + let objects = Engine.GetGUIObjectByName(parentName).children; + + for (let i = start; i < objects.length; ++i) + objects[i].hidden = true; +} diff --git a/binaries/data/mods/public/gui/common/l10n.js b/binaries/data/mods/public/gui/common/l10n.js index 53c16c2576..740db62193 100644 --- a/binaries/data/mods/public/gui/common/l10n.js +++ b/binaries/data/mods/public/gui/common/l10n.js @@ -1,75 +1,6 @@ -const localisedResourceNames = { - "firstWord": { - // Translation: Word as used at the beginning of a sentence or as a single-word sentence. - "food": translateWithContext("firstWord", "Food"), - // Translation: Word as used at the beginning of a sentence or as a single-word sentence. - "meat": translateWithContext("firstWord", "Meat"), - // Translation: Word as used at the beginning of a sentence or as a single-word sentence. - "metal": translateWithContext("firstWord", "Metal"), - // Translation: Word as used at the beginning of a sentence or as a single-word sentence. - "ore": translateWithContext("firstWord", "Ore"), - // Translation: Word as used at the beginning of a sentence or as a single-word sentence. - "rock": translateWithContext("firstWord", "Rock"), - // Translation: Word as used at the beginning of a sentence or as a single-word sentence. - "ruins": translateWithContext("firstWord", "Ruins"), - // Translation: Word as used at the beginning of a sentence or as a single-word sentence. - "stone": translateWithContext("firstWord", "Stone"), - // Translation: Word as used at the beginning of a sentence or as a single-word sentence. - "treasure": translateWithContext("firstWord", "Treasure"), - // Translation: Word as used at the beginning of a sentence or as a single-word sentence. - "tree": translateWithContext("firstWord", "Tree"), - // Translation: Word as used at the beginning of a sentence or as a single-word sentence. - "wood": translateWithContext("firstWord", "Wood"), - // Translation: Word as used at the beginning of a sentence or as a single-word sentence. - "fruit": translateWithContext("firstWord", "Fruit"), - // Translation: Word as used at the beginning of a sentence or as a single-word sentence. - "grain": translateWithContext("firstWord", "Grain"), - // Translation: Word as used at the beginning of a sentence or as a single-word sentence. - "fish": translateWithContext("firstWord", "Fish"), - }, - "withinSentence": { - // Translation: Word as used in the middle of a sentence (which may require using lowercase for your language). - "food": translateWithContext("withinSentence", "Food"), - // Translation: Word as used in the middle of a sentence (which may require using lowercase for your language). - "meat": translateWithContext("withinSentence", "Meat"), - // Translation: Word as used in the middle of a sentence (which may require using lowercase for your language). - "metal": translateWithContext("withinSentence", "Metal"), - // Translation: Word as used in the middle of a sentence (which may require using lowercase for your language). - "ore": translateWithContext("withinSentence", "Ore"), - // Translation: Word as used in the middle of a sentence (which may require using lowercase for your language). - "rock": translateWithContext("withinSentence", "Rock"), - // Translation: Word as used in the middle of a sentence (which may require using lowercase for your language). - "ruins": translateWithContext("withinSentence", "Ruins"), - // Translation: Word as used in the middle of a sentence (which may require using lowercase for your language). - "stone": translateWithContext("withinSentence", "Stone"), - // Translation: Word as used in the middle of a sentence (which may require using lowercase for your language). - "treasure": translateWithContext("withinSentence", "Treasure"), - // Translation: Word as used in the middle of a sentence (which may require using lowercase for your language). - "tree": translateWithContext("withinSentence", "Tree"), - // Translation: Word as used in the middle of a sentence (which may require using lowercase for your language). - "wood": translateWithContext("withinSentence", "Wood"), - // Translation: Word as used in the middle of a sentence (which may require using lowercase for your language). - "fruit": translateWithContext("withinSentence", "Fruit"), - // Translation: Word as used in the middle of a sentence (which may require using lowercase for your language). - "grain": translateWithContext("withinSentence", "Grain"), - // Translation: Word as used in the middle of a sentence (which may require using lowercase for your language). - "fish": translateWithContext("withinSentence", "Fish"), - } -}; - -function getLocalizedResourceName(resourceCode, context) +function getLocalizedResourceName(resourceName, context) { - if (!localisedResourceNames[context]) - { - warn("Internationalization: Unexpected context for resource type localization found: ‘" + context + "’. This context is not supported."); - return resourceCode; - } - if (!localisedResourceNames[context][resourceCode]) - { - warn("Internationalization: Unexpected resource type found with code ‘" + resourceCode + ". This resource type must be internationalized."); - return resourceCode; - } - return localisedResourceNames[context][resourceCode]; + return translateWithContext(context, resourceName); } /** @@ -81,7 +12,7 @@ function getLocalizedResourceAmounts(resources) .filter(type => resources[type] > 0) .map(type => sprintf(translate("%(amount)s %(resourceType)s"), { "amount": resources[type], - "resourceType": getLocalizedResourceName(type, "withinSentence") + "resourceType": getLocalizedResourceName(g_ResourceData.GetResource(type).name, "withinSentence") })); if (amounts.length > 1) diff --git a/binaries/data/mods/public/gui/common/tooltips.js b/binaries/data/mods/public/gui/common/tooltips.js index e98c60c841..26bb54550c 100644 --- a/binaries/data/mods/public/gui/common/tooltips.js +++ b/binaries/data/mods/public/gui/common/tooltips.js @@ -603,7 +603,7 @@ function getLootTooltip(template) template.trader && template.trader.goods ); - const lootTypes = ["xp", "food", "wood", "stone", "metal"]; + const lootTypes = g_ResourceData.GetCodes().concat(["xp"]); let lootLabels = []; for (let type of lootTypes) { diff --git a/binaries/data/mods/public/gui/session/diplomacy_window.xml b/binaries/data/mods/public/gui/session/diplomacy_window.xml index 7a46c6d7c8..4e4bcbba8a 100644 --- a/binaries/data/mods/public/gui/session/diplomacy_window.xml +++ b/binaries/data/mods/public/gui/session/diplomacy_window.xml @@ -1,7 +1,7 @@