Index: ps/trunk/binaries/data/mods/public/gui/reference/civinfo/CivInfoPage.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/reference/civinfo/CivInfoPage.js (revision 24491) +++ ps/trunk/binaries/data/mods/public/gui/reference/civinfo/CivInfoPage.js (revision 24492) @@ -1,122 +1,116 @@ class CivInfoPage extends ReferencePage { constructor(data) { super(); this.civSelection = new CivSelectDropdown(this.civData); this.civSelection.registerHandler(this.selectCiv.bind(this)); this.gameplaySection = new GameplaySection(this); this.historySection = new HistorySection(this); let structreeButton = new StructreeButton(this); let closeButton = new CloseButton(this); Engine.SetGlobalHotkey("civinfo", "Press", this.closePage.bind(this)); } switchToStructreePage() { Engine.PopGuiPage({ "civ": this.activeCiv, "nextPage": "page_structree.xml" }); } closePage() { Engine.PopGuiPage({ "civ": this.activeCiv, "page": "page_civinfo.xml" }); } /** * Updates the GUI after the user selected a civ from dropdown. * * @param code {string} */ selectCiv(civCode) { this.setActiveCiv(civCode); let civInfo = this.civData[civCode]; if(!civInfo) error(sprintf("Error loading civ data for \"%(code)s\"", { "code": civCode })); // Update civ gameplay display - this.gameplaySection.update(civInfo); + this.gameplaySection.update(this.activeCiv, civInfo); // Update civ history display this.historySection.update(civInfo); } /** * Give the first character a larger font. */ bigFirstLetter(text, size) { return setStringTags(text[0], { "font": "sans-bold-" + (size + 6) }) + text.substring(1); } /** * Set heading font - bold and mixed caps * * @param text {string} * @param size {number} - Font size * @returns {string} */ formatHeading(text, size) { let textArray = []; for (let word of text.split(" ")) { let wordCaps = word.toUpperCase(); // Usually we wish a big first letter, however this isn't always desirable. Check if // `.toLowerCase()` changes the character to avoid false positives from special characters. if (word.length && word[0].toLowerCase() != word[0]) word = this.bigFirstLetter(wordCaps, size); textArray.push(setStringTags(word, { "font": "sans-bold-" + size })); } return textArray.join(" "); } /** - * Returns a styled concatenation of the Name, History, and Description of the given object. - * - * @param obj {Object} * @returns {string} */ - formatEntry(obj) + formatEntry(name, tooltip, description) { - if (!obj.Name) - return ""; - - let history_icon = ""; - if (obj.History) - history_icon = '[icon="iconInfo" tooltip="' + escapeQuotation(obj.History) + '" tooltip_style="civInfoTooltip"]'; + let tooltip_icon = ""; + if (tooltip) + tooltip_icon = '[icon="iconInfo" tooltip="' + escapeQuotation(tooltip) + '" tooltip_style="civInfoTooltip"]'; - let description = ""; - if (obj.Description) + let description_text = ""; + if (description) // Translation: Description of an item in the CivInfo page, on a new line and indented. - description = sprintf(translate('\n %(description)s'), { "description": obj.Description, }); + description_text = sprintf(translate('\n %(description)s'), { "description": description, }); return sprintf( // Translation: An entry in the CivInfo Page. The newline and indentation of the description is handled elsewhere. // Example: // > • Name of a Special Something (i) // > A brief description of the aforementioned something. translate("• %(name)s %(info_icon)s%(description)s"), { - "name": setStringTags(obj.Name, { "font": "sans-bold-14" }), - "info_icon": history_icon, - "description": description, + "name": setStringTags(name, { "font": "sans-bold-14" }), + "info_icon": tooltip_icon, + "description": description_text, } ); } } CivInfoPage.prototype.CloseButtonTooltip = translate("%(hotkey)s: Close Civilization Overview."); CivInfoPage.prototype.SectionHeaderSize = 16; CivInfoPage.prototype.SubsectionHeaderSize = 12; Index: ps/trunk/binaries/data/mods/public/gui/reference/civinfo/Sections/GameplaySection.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/reference/civinfo/Sections/GameplaySection.js (revision 24491) +++ ps/trunk/binaries/data/mods/public/gui/reference/civinfo/Sections/GameplaySection.js (revision 24492) @@ -1,30 +1,30 @@ class GameplaySection { constructor(page) { this.page = page; this.CivGameplayHeading = Engine.GetGUIObjectByName('civGameplayHeading'); this.BonusesSubsection = new BonusesSubsection(this.page); this.HeroesSubsection = new HeroesSubsection(this.page); this.StructuresSubsection = new StructuresSubsection(this.page); this.TechnologiesSubsection = new TechnologiesSubsection(this.page); } - update(civInfo) + update(civCode, civInfo) { this.CivGameplayHeading.caption = this.page.formatHeading( sprintf(this.headingCaption, { "civilization": civInfo.Name }), this.page.SectionHeaderSize ); - this.BonusesSubsection.update(civInfo); - this.TechnologiesSubsection.update(civInfo); - this.StructuresSubsection.update(civInfo); - this.HeroesSubsection.update(civInfo); + this.BonusesSubsection.update(civCode, civInfo); + this.TechnologiesSubsection.update(civCode); + this.StructuresSubsection.update(civCode); + this.HeroesSubsection.update(civCode); } } GameplaySection.prototype.headingCaption = translate("%(civilization)s Gameplay"); Index: ps/trunk/binaries/data/mods/public/gui/reference/civinfo/Sections/Subsection.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/reference/civinfo/Sections/Subsection.js (revision 24491) +++ ps/trunk/binaries/data/mods/public/gui/reference/civinfo/Sections/Subsection.js (revision 24492) @@ -1,7 +1,70 @@ class Subsection { constructor(page) { this.page = page; } + + getAuraCaptions(auraList, civCode) + { + let captions = []; + for (let auraCode of auraList) + { + let aura = this.page.TemplateParser.getAura(auraCode); + if (!aura.civ || aura.civ != civCode) + continue; + + captions.push(this.page.formatEntry( + getEntityNames(aura), + false, + getDescriptionTooltip(aura) + )); + } + return captions; + } + + getEntityCaptions(entityList, classList, civCode) + { + let captions = []; + for (let entityCode of entityList) + { + // Acquire raw template as we need to compare all classes an entity has, not just the visible ones. + let template = this.page.TemplateLoader.loadEntityTemplate(entityCode, civCode); + let classListFull = GetIdentityClasses(template.Identity); + if (!MatchesClassList(classListFull, classList)) + continue; + + let entity = this.page.TemplateParser.getEntity(entityCode, civCode); + captions.push(this.page.formatEntry( + getEntityNames(entity), + getDescriptionTooltip(entity), + getEntityTooltip(entity) + )); + } + return captions; + } + + getTechnologyCaptions(technologyList, civCode) + { + let captions = []; + for (let techCode of technologyList) + { + let technology = this.page.TemplateParser.getTechnology(techCode, civCode); + + // We deliberately pass an invalid civ code here. + // If it returns with a value other than false, then + // we know that this tech can be researched by any civ + let genericReqs = this.page.TemplateParser.getTechnology(techCode, "anyciv").reqs; + + if (!technology.reqs || genericReqs) + continue; + + captions.push(this.page.formatEntry( + getEntityNames(technology), + getDescriptionTooltip(technology), + getEntityTooltip(technology) + )); + } + return captions; + } } Index: ps/trunk/binaries/data/mods/public/gui/reference/civinfo/Sections/Subsections/BonusesSubsection.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/reference/civinfo/Sections/Subsections/BonusesSubsection.js (revision 24491) +++ ps/trunk/binaries/data/mods/public/gui/reference/civinfo/Sections/Subsections/BonusesSubsection.js (revision 24492) @@ -1,35 +1,50 @@ class BonusesSubsection extends Subsection { constructor(page) { super(page); this.CivBonuses = Engine.GetGUIObjectByName("civBonuses"); } - update(civInfo) + update(civCode, civInfo) { - let civBonuses = civInfo.CivBonuses.map(bonus => this.page.formatEntry(bonus)); + // Not all civ bonuses can be represented by a single auto-researched technology (e.g. + // Athenian "Silver Owls", Roman "Testudo Formation"). Thus we also display descriptions + // of civ bonuses as written in the {civ}.json files. + let civBonuses = this.getTechnologyCaptions( + this.page.TemplateLoader.autoResearchTechList, + civCode + ); + for (let bonus of civInfo.CivBonuses) + civBonuses.push(this.page.formatEntry( + bonus.Name, + bonus.History || false, + bonus.Description || false + )); civBonuses.unshift( this.page.formatHeading( this.HeadingCivBonusCaption(civBonuses.length), this.page.SubsectionHeaderSize ) ); - let teamBonuses = civInfo.TeamBonuses.map(bonus => this.page.formatEntry(bonus)); + let teamBonuses = this.getAuraCaptions( + this.page.TemplateLoader.teamBonusAuraList, + civCode + ); teamBonuses.unshift( this.page.formatHeading( this.HeadingTeamBonusCaption(teamBonuses.length), this.page.SubsectionHeaderSize ) ); this.CivBonuses.caption = civBonuses.join("\n") + "\n\n" + teamBonuses.join("\n"); } } BonusesSubsection.prototype.HeadingCivBonusCaption = count => translatePlural("Civilization Bonus", "Civilization Bonuses", count); BonusesSubsection.prototype.HeadingTeamBonusCaption = count => translatePlural("Team Bonus", "Team Bonuses", count); Index: ps/trunk/binaries/data/mods/public/gui/reference/civinfo/Sections/Subsections/HeroesSubsection.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/reference/civinfo/Sections/Subsections/HeroesSubsection.js (revision 24491) +++ ps/trunk/binaries/data/mods/public/gui/reference/civinfo/Sections/Subsections/HeroesSubsection.js (revision 24492) @@ -1,30 +1,32 @@ class HeroesSubsection extends Subsection { constructor(page) { super(page); this.CivHeroes = Engine.GetGUIObjectByName("civHeroes"); } - update(civInfo) + update(civCode) { - let heroes = []; - for (let faction of civInfo.Factions) - Array.prototype.push.apply( - heroes, - faction.Heroes.map(hero => this.page.formatEntry(hero)) - ); + let heroes = this.getEntityCaptions( + this.page.TemplateLister.getTemplateLists(civCode).units.keys(), + this.IdentifyingClassList, + civCode + ); heroes.unshift( this.page.formatHeading( this.HeadingCaption(heroes.length), this.page.SubsectionHeaderSize ) ); this.CivHeroes.caption = heroes.join("\n"); } } HeroesSubsection.prototype.HeadingCaption = count => translatePlural("Hero", "Heroes", count); + +HeroesSubsection.prototype.IdentifyingClassList = + ["Hero"]; Index: ps/trunk/binaries/data/mods/public/gui/reference/civinfo/Sections/Subsections/StructuresSubsection.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/reference/civinfo/Sections/Subsections/StructuresSubsection.js (revision 24491) +++ ps/trunk/binaries/data/mods/public/gui/reference/civinfo/Sections/Subsections/StructuresSubsection.js (revision 24492) @@ -1,24 +1,32 @@ class StructuresSubsection extends Subsection { constructor(page) { super(page); this.CivStructures = Engine.GetGUIObjectByName("civStructures"); } - update(civInfo) + update(civCode) { - let structures = civInfo.Structures.map(bonus => this.page.formatEntry(bonus)); + let structures = this.getEntityCaptions( + this.page.TemplateLister.getTemplateLists(civCode).structures.keys(), + this.IdentifyingClassList, + civCode + ); + structures.unshift( this.page.formatHeading( this.HeadingCaption(structures.length), this.page.SubsectionHeaderSize ) ); this.CivStructures.caption = structures.join("\n"); } } StructuresSubsection.prototype.HeadingCaption = count => translatePlural("Special Structure", "Special Structures", count); + +StructuresSubsection.prototype.IdentifyingClassList = + [["SpecialBuilding"], ["Wonder"]]; Index: ps/trunk/binaries/data/mods/public/gui/reference/civinfo/Sections/Subsections/StructuresSubsection.xml =================================================================== --- ps/trunk/binaries/data/mods/public/gui/reference/civinfo/Sections/Subsections/StructuresSubsection.xml (revision 24491) +++ ps/trunk/binaries/data/mods/public/gui/reference/civinfo/Sections/Subsections/StructuresSubsection.xml (revision 24492) @@ -1,9 +1,9 @@ - + Index: ps/trunk/binaries/data/mods/public/gui/reference/civinfo/Sections/Subsections/TechnologiesSubsection.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/reference/civinfo/Sections/Subsections/TechnologiesSubsection.js (revision 24491) +++ ps/trunk/binaries/data/mods/public/gui/reference/civinfo/Sections/Subsections/TechnologiesSubsection.js (revision 24492) @@ -1,30 +1,28 @@ class TechnologiesSubsection extends Subsection { constructor(page) { super(page); this.CivTechs = Engine.GetGUIObjectByName("civTechs"); } - update(civInfo) + update(civCode) { - let techs = []; - for (let faction of civInfo.Factions) - Array.prototype.push.apply( - techs, - faction.Technologies.map(tech => this.page.formatEntry(tech)) - ); + let techs = this.getTechnologyCaptions( + this.page.TemplateLister.getTemplateLists(civCode).techs.keys(), + civCode + ); techs.unshift( this.page.formatHeading( this.HeadingCaption(techs.length), this.page.SubsectionHeaderSize ) ); this.CivTechs.caption = techs.join("\n"); } } TechnologiesSubsection.prototype.HeadingCaption = count => translatePlural("Special Technology", "Special Technologies", count); Index: ps/trunk/binaries/data/mods/public/gui/reference/civinfo/Sections/Subsections/TechnologiesSubsection.xml =================================================================== --- ps/trunk/binaries/data/mods/public/gui/reference/civinfo/Sections/Subsections/TechnologiesSubsection.xml (revision 24491) +++ ps/trunk/binaries/data/mods/public/gui/reference/civinfo/Sections/Subsections/TechnologiesSubsection.xml (revision 24492) @@ -1,9 +1,9 @@ - + Index: ps/trunk/binaries/data/mods/public/gui/reference/common/TemplateLoader.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/reference/common/TemplateLoader.js (revision 24491) +++ ps/trunk/binaries/data/mods/public/gui/reference/common/TemplateLoader.js (revision 24492) @@ -1,264 +1,293 @@ /** * 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(); + this.teamBonusAuraList = this.findAllTeamBonusAuras(); } /** * 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)); } let appendTechnology = (technologyName) => { let technology = this.loadTechnologyTemplate(technologyName, civCode); if (DeriveTechnologyRequirements(technology, civCode)) production.techs.push(technologyName); }; 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)) { let technologyPair = this.loadTechnologyPairTemplate(technologyName, civCode); if (technologyPair.reqs) for (technologyName of technologyPair.techs) appendTechnology(technologyName); } else appendTechnology(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; } /** + * Iterates through and loads all team bonus auras. + * + * We make an assumption in this method: that all team bonus auras are + * in a single folder. + * + * Team bonuses must have a "civ" attribute to indicate what civ they + * belong to. + * + * @return {array} List of teambonus auras + */ + findAllTeamBonusAuras() + { + let auraList = []; + let path = this.AuraPath + TemplateLoader.prototype.AuraTeamBonusSubpath; + for (let templateName of listFiles(path, ".json", true)) + { + let filename = TemplateLoader.prototype.AuraTeamBonusSubpath + templateName; + let data = this.loadAuraTemplate(filename); + if (!data || !data.civ) + continue; + + auraList.push(filename); + } + return auraList; + } + + /** * 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.Civ && parentTemplate.Identity.Civ != template.Identity.Civ || 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.AuraTeamBonusSubpath = "teambonuses/"; 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 (revision 24491) +++ ps/trunk/binaries/data/mods/public/gui/reference/common/TemplateParser.js (revision 24492) @@ -1,323 +1,342 @@ /** * This class parses and stores parsed template data. */ class TemplateParser { constructor(TemplateLoader) { this.TemplateLoader = TemplateLoader; /** * Parsed Data Stores */ + this.auras = {}; this.entities = {}; this.techs = {}; this.phases = {}; this.modifiers = {}; this.phaseList = []; } + getAura(auraName) + { + if (auraName in this.auras) + return this.auras[auraName]; + + if (!AuraTemplateExists(auraName)) + return null; + + let template = this.TemplateLoader.loadAuraTemplate(auraName); + let parsed = GetAuraDataHelper(template); + + if (template.civ) + parsed.civ = template.civ; + + this.auras[auraName] = parsed; + return this.auras[auraName]; + } + /** * 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 (!(civCode in this.entities)) this.entities[civCode] = {}; else if (templateName in this.entities[civCode]) return this.entities[civCode][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 resistance and auras let struct = this.getEntity(parsed.wallSet.templates.long, civCode); parsed.resistance = struct.resistance; 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[civCode][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 (revision 24491) +++ ps/trunk/binaries/data/mods/public/gui/reference/common/common.js (revision 24492) @@ -1,28 +1,33 @@ /** * 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"); } + +function AuraTemplateExists(templateName) +{ + return Engine.FileExists(g_Page.TemplateLoader.AuraPath + templateName + ".json"); +} Index: ps/trunk/binaries/data/mods/public/simulation/data/auras/teambonuses/athen_player_teambonus.json =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/data/auras/teambonuses/athen_player_teambonus.json (revision 24491) +++ ps/trunk/binaries/data/mods/public/simulation/data/auras/teambonuses/athen_player_teambonus.json (revision 24492) @@ -1,10 +1,11 @@ { "type": "global", "affects": ["Warship"], "affectedPlayers": ["ExclusiveMutualAlly"], + "civ": "athen", "modifications": [ { "value": "Cost/BuildTime", "multiply": 0.75 } ], "auraName": "Delian League", "auraDescription": "Allied Warships −25% construction time." } Index: ps/trunk/binaries/data/mods/public/simulation/data/auras/teambonuses/brit_player_teambonus.json =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/data/auras/teambonuses/brit_player_teambonus.json (revision 24491) +++ ps/trunk/binaries/data/mods/public/simulation/data/auras/teambonuses/brit_player_teambonus.json (revision 24492) @@ -1,13 +1,14 @@ { "type": "global", "affects": ["Healer"], "affectedPlayers": ["ExclusiveMutualAlly"], + "civ": "brit", "modifications": [ { "value": "Cost/Resources/food", "multiply": 0.8 }, { "value": "Cost/Resources/wood", "multiply": 0.8 }, { "value": "Cost/Resources/stone", "multiply": 0.8 }, { "value": "Cost/Resources/metal", "multiply": 0.8 } ], "auraName": "Druids", "auraDescription": "Allied Healers −20% resource costs." } Index: ps/trunk/binaries/data/mods/public/simulation/data/auras/teambonuses/cart_player_teambonus.json =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/data/auras/teambonuses/cart_player_teambonus.json (revision 24491) +++ ps/trunk/binaries/data/mods/public/simulation/data/auras/teambonuses/cart_player_teambonus.json (revision 24492) @@ -1,10 +1,11 @@ { "type": "global", "affects": ["Trade"], "affectedPlayers": ["ExclusiveMutualAlly"], + "civ": "cart", "modifications": [ { "value": "Market/InternationalBonus", "add": 0.1 } ], "auraName": "Trademasters", "auraDescription": "Allies +10% international trade bonus." } Index: ps/trunk/binaries/data/mods/public/simulation/data/auras/teambonuses/gaul_player_teambonus.json =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/data/auras/teambonuses/gaul_player_teambonus.json (revision 24491) +++ ps/trunk/binaries/data/mods/public/simulation/data/auras/teambonuses/gaul_player_teambonus.json (revision 24492) @@ -1,14 +1,15 @@ { "type": "global", "affects": ["Forge"], "affectedPlayers": ["ExclusiveMutualAlly"], + "civ": "gaul", "modifications": [ { "value": "ProductionQueue/TechCostMultiplier/food", "multiply": 0.85 }, { "value": "ProductionQueue/TechCostMultiplier/wood", "multiply": 0.85 }, { "value": "ProductionQueue/TechCostMultiplier/stone", "multiply": 0.85 }, { "value": "ProductionQueue/TechCostMultiplier/metal", "multiply": 0.85 }, { "value": "ProductionQueue/TechCostMultiplier/time", "multiply": 0.85 } ], "auraName": "Products from Gaul", "auraDescription": "Forges −15% technology resource costs and research time." } Index: ps/trunk/binaries/data/mods/public/simulation/data/auras/teambonuses/iber_player_teambonus.json =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/data/auras/teambonuses/iber_player_teambonus.json (revision 24491) +++ ps/trunk/binaries/data/mods/public/simulation/data/auras/teambonuses/iber_player_teambonus.json (revision 24492) @@ -1,13 +1,14 @@ { "type": "global", "affects": ["Citizen Javelineer"], "affectedPlayers": ["ExclusiveMutualAlly"], + "civ": "iber", "modifications": [ { "value": "Cost/Resources/food", "multiply": 0.9 }, { "value": "Cost/Resources/wood", "multiply": 0.9 }, { "value": "Cost/Resources/stone", "multiply": 0.9 }, { "value": "Cost/Resources/metal", "multiply": 0.9 } ], "auraName": "Saripeko", "auraDescription": "Allied Citizen Javelineers −10% resource costs." } Index: ps/trunk/binaries/data/mods/public/simulation/data/auras/teambonuses/kush_player_teambonus.json =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/data/auras/teambonuses/kush_player_teambonus.json (revision 24491) +++ ps/trunk/binaries/data/mods/public/simulation/data/auras/teambonuses/kush_player_teambonus.json (revision 24492) @@ -1,14 +1,15 @@ { "type": "global", "affects": ["Elephant"], "affectedPlayers": ["ExclusiveMutualAlly"], + "civ": "kush", "modifications": [ { "value": "Cost/BuildTime", "multiply": 0.8 }, { "value": "Cost/Resources/food", "multiply": 0.8 }, { "value": "Cost/Resources/wood", "multiply": 0.8 }, { "value": "Cost/Resources/stone", "multiply": 0.8 }, { "value": "Cost/Resources/metal", "multiply": 0.8 } ], "auraName": "Elephant Suppliers", "auraDescription": "Allied Elephants −20% resource costs and training time." } Index: ps/trunk/binaries/data/mods/public/simulation/data/auras/teambonuses/mace_player_teambonus.json =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/data/auras/teambonuses/mace_player_teambonus.json (revision 24491) +++ ps/trunk/binaries/data/mods/public/simulation/data/auras/teambonuses/mace_player_teambonus.json (revision 24492) @@ -1,13 +1,14 @@ { "type": "player", "affects": ["Player"], "affectedPlayers": ["ExclusiveMutualAlly"], + "civ": "mace", "modifications": [ { "value": "Player/BarterMultiplier/Sell/food", "multiply": 1.2 }, { "value": "Player/BarterMultiplier/Sell/wood", "multiply": 1.2 }, { "value": "Player/BarterMultiplier/Sell/stone", "multiply": 1.2 }, { "value": "Player/BarterMultiplier/Sell/metal", "multiply": 1.2 } ], "auraName": "Standardized Currency", "auraDescription": "Allies +20% barter sell prices." } Index: ps/trunk/binaries/data/mods/public/simulation/data/auras/teambonuses/maur_player_teambonus.json =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/data/auras/teambonuses/maur_player_teambonus.json (revision 24491) +++ ps/trunk/binaries/data/mods/public/simulation/data/auras/teambonuses/maur_player_teambonus.json (revision 24492) @@ -1,17 +1,18 @@ { "type": "global", "affects": ["Temple"], "affectedPlayers": ["ExclusiveMutualAlly"], + "civ": "maur", "modifications": [ { "value": "Cost/BuildTime", "multiply": 0.5 }, { "value": "Cost/Resources/wood", "multiply": 0.5 }, { "value": "Cost/Resources/stone", "multiply": 0.5 }, { "value": "ProductionQueue/TechCostMultiplier/food", "multiply": 0.5 }, { "value": "ProductionQueue/TechCostMultiplier/wood", "multiply": 0.5 }, { "value": "ProductionQueue/TechCostMultiplier/stone", "multiply": 0.5 }, { "value": "ProductionQueue/TechCostMultiplier/metal", "multiply": 0.5 }, { "value": "ProductionQueue/TechCostMultiplier/time", "multiply": 0.5 } ], "auraName": "Ashoka's Religious Support", "auraDescription": "Allied Temples −50% resource costs and building time; Temple technologies −50% resource costs and research time." } Index: ps/trunk/binaries/data/mods/public/simulation/data/auras/teambonuses/pers_player_teambonus.json =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/data/auras/teambonuses/pers_player_teambonus.json (revision 24491) +++ ps/trunk/binaries/data/mods/public/simulation/data/auras/teambonuses/pers_player_teambonus.json (revision 24492) @@ -1,10 +1,11 @@ { "type": "global", "affects": ["Trader !Ship"], "affectedPlayers": ["ExclusiveMutualAlly"], + "civ": "pers", "modifications": [ { "value": "Trader/GainMultiplier", "multiply": 1.15 } ], "auraName": "The Royal Road", "auraDescription": "Allied Land Traders +15% trade gain." } Index: ps/trunk/binaries/data/mods/public/simulation/data/auras/teambonuses/ptol_player_teambonus.json =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/data/auras/teambonuses/ptol_player_teambonus.json (revision 24491) +++ ps/trunk/binaries/data/mods/public/simulation/data/auras/teambonuses/ptol_player_teambonus.json (revision 24492) @@ -1,10 +1,11 @@ { "type": "player", "affects": ["Player"], "affectedPlayers": ["ExclusiveMutualAlly"], + "civ": "ptol", "modifications": [ { "value": "ResourceTrickle/Rates/food", "add": 1 } ], "auraName": "Breadbasket of the Mediterranean", "auraDescription": "Allies +1.0 food trickle rate." } Index: ps/trunk/binaries/data/mods/public/simulation/data/auras/teambonuses/rome_player_teambonus.json =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/data/auras/teambonuses/rome_player_teambonus.json (revision 24491) +++ ps/trunk/binaries/data/mods/public/simulation/data/auras/teambonuses/rome_player_teambonus.json (revision 24492) @@ -1,10 +1,11 @@ { "type": "global", "affects": ["Citizen Infantry"], "affectedPlayers": ["ExclusiveMutualAlly"], + "civ": "rome", "modifications": [ { "value": "Cost/BuildTime", "multiply": 0.9 } ], "auraName": "Conscription", "auraDescription": "Allied Citizen Infantry −10% training time." } Index: ps/trunk/binaries/data/mods/public/simulation/data/auras/teambonuses/sele_player_teambonus.json =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/data/auras/teambonuses/sele_player_teambonus.json (revision 24491) +++ ps/trunk/binaries/data/mods/public/simulation/data/auras/teambonuses/sele_player_teambonus.json (revision 24492) @@ -1,13 +1,14 @@ { "type": "global", "affects": ["CivilCentre"], "affectedPlayers": ["ExclusiveMutualAlly"], + "civ": "sele", "modifications": [ { "value": "Cost/Resources/food", "multiply": 0.8 }, { "value": "Cost/Resources/wood", "multiply": 0.8 }, { "value": "Cost/Resources/stone", "multiply": 0.8 }, { "value": "Cost/Resources/metal", "multiply": 0.8 } ], "auraName": "Syrian Tetrapolis", "auraDescription": "Allied Civil Centers −20% resource costs." } Index: ps/trunk/binaries/data/mods/public/simulation/data/auras/teambonuses/spart_player_teambonus.json =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/data/auras/teambonuses/spart_player_teambonus.json (revision 24491) +++ ps/trunk/binaries/data/mods/public/simulation/data/auras/teambonuses/spart_player_teambonus.json (revision 24492) @@ -1,10 +1,11 @@ { "type": "global", "affects": ["Citizen Infantry Spearman"], "affectedPlayers": ["ExclusiveMutualAlly"], + "civ": "spart", "modifications": [ { "value": "Health/Max", "multiply": 1.1 } ], "auraName": "Peloponnesian League", "auraDescription": "Allied Citizen Infantry Spearmen +10% health." }