Index: ps/trunk/binaries/data/mods/public/gui/reference/common/helper.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/reference/common/helper.js (revision 20748) +++ ps/trunk/binaries/data/mods/public/gui/reference/common/helper.js (revision 20749) @@ -1,205 +1,197 @@ var g_CurrentModifiers = {}; function deriveModifications(techList) { let techData = []; for (let techName of techList) techData.push(GetTechnologyBasicDataHelper(loadTechData(techName), g_SelectedCiv)); return DeriveModificationsFromTechnologies(techData); } /** * 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. */ function getActualUpgradeData(upgradesInfo) { let newUpgrades = []; for (let upgrade of 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.cost = upgrade.cost; data.icon = upgrade.icon || data.icon; data.tooltip = upgrade.tooltip || data.tooltip; data.requiredTechnology = upgrade.requiredTechnology || data.requiredTechnology; 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 * @return The name of the phase the technology belongs to, or false if * the current civ can't research this tech */ function getPhaseOfTechnology(techName) { let phaseIdx = -1; if (basename(techName).startsWith("phase")) { if (!g_ParsedData.phases[techName].reqs) return false; phaseIdx = g_ParsedData.phaseList.indexOf(getActualPhase(techName)); if (phaseIdx > 0) return g_ParsedData.phaseList[phaseIdx - 1]; } if (!g_ParsedData.techs[g_SelectedCiv][techName]) { let techData = loadTechnology(techName); g_ParsedData.techs[g_SelectedCiv][techName] = techData; warn("The \"" + techData.name.generic + "\" technology is not researchable in any structure buildable by the " + g_SelectedCiv + " civilisation, but is required by something that this civ can research, train or build!"); } let techReqs = g_ParsedData.techs[g_SelectedCiv][techName].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, g_ParsedData.phaseList.indexOf(getPhaseOfTechnology(tech))); } return g_ParsedData.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} */ function getActualPhase(phaseName) { if (g_ParsedData.phases[phaseName]) return g_ParsedData.phases[phaseName].actualPhase; warn("Unrecognised phase (" + phaseName + ")"); return g_ParsedData.phaseList[0]; } /** * Returns the required phase of a given unit or structure. * * @param {object} template * @return {string} */ function getPhaseOfTemplate(template) { if (!template.requiredTechnology) return g_ParsedData.phaseList[0]; if (basename(template.requiredTechnology).startsWith("phase")) return getActualPhase(template.requiredTechnology); return getPhaseOfTechnology(template.requiredTechnology); } /** * Determine order of phases. * - * @param {object} techs - The current available store of techs. + * @param {object} phases - The current available store of phases. * @return {array} List of phases */ -function unravelPhases(techs) +function unravelPhases(phases) { let phaseList = []; - for (let techcode in techs) + for (let phaseName in phases) { - let techdata = techs[techcode]; - - if (!techdata.reqs || !techdata.reqs.length || !techdata.reqs[0].techs || techdata.reqs[0].techs.length < 2) - continue; - - let reqTech = techs[techcode].reqs[0].techs[1]; - - if (!techs[reqTech] || !techs[reqTech].reqs.length) + let phaseData = phases[phaseName]; + if (!phaseData.reqs.length || !phaseData.reqs[0].techs) continue; - // Assume the first tech to be a phase - let reqPhase = techs[reqTech].reqs[0].techs[0]; - let myPhase = techs[techcode].reqs[0].techs[0]; - - if (reqPhase == myPhase || !basename(reqPhase).startsWith("phase") || !basename(myPhase).startsWith("phase")) - continue; + let myPhase = phaseData.actualPhase; + let reqPhase = phaseData.reqs[0].techs[0]; + if (phases[reqPhase]) + reqPhase = phases[reqPhase].actualPhase; let reqPhasePos = phaseList.indexOf(reqPhase); let myPhasePos = phaseList.indexOf(myPhase); // Sort the phases in the order they can be researched if (!phaseList.length) phaseList = [reqPhase, myPhase]; else if (reqPhasePos < 0 && myPhasePos != -1) phaseList.splice(myPhasePos, 0, reqPhase); else if (myPhasePos < 0 && reqPhasePos != -1) phaseList.splice(reqPhasePos+1, 0, myPhase); else if (reqPhasePos > myPhasePos) { phaseList.splice(reqPhasePos+1, 0, myPhase); phaseList.splice(myPhasePos, 1); } else if (reqPhasePos < 0 && myPhasePos < 0) // If neither phase is in the list, then add them both to the end and // rely on later iterations relocating them to their correct place. phaseList.push(reqPhase, myPhase); } return phaseList; } /** * 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 loadTemplate already. */ function GetTemplateData(templateName) { var template = loadTemplate(templateName); return GetTemplateDataHelper(template, null, g_AuraData, g_ResourceData, g_DamageTypes, g_CurrentModifiers); } function isPairTech(technologyCode) { - return basename(technologyCode).startsWith("pair") || basename(technologyCode).indexOf("_pair") > -1; + return !!loadTechData(technologyCode).top; } function mergeRequirements(reqsA, reqsB) { if (reqsA === false || reqsB === false) 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] = []; finalReqs[opt][type] = finalReqs[opt][type].concat(option[type]); } return finalReqs; } Index: ps/trunk/binaries/data/mods/public/gui/reference/structree/structree.js =================================================================== --- ps/trunk/binaries/data/mods/public/gui/reference/structree/structree.js (revision 20748) +++ ps/trunk/binaries/data/mods/public/gui/reference/structree/structree.js (revision 20749) @@ -1,184 +1,184 @@ /** * Array of structure template names when given a civ and a phase name. */ var g_BuildList = {}; /** * Array of template names that can be trained from a unit, given a civ and unit template name. */ var g_TrainList = {}; /** * Initialize the page * * @param {object} data - Parameters passed from the code that calls this page into existence. */ function init(data = {}) { if (data.callback) g_CallbackSet = true; let civList = Object.keys(g_CivData).map(civ => ({ "name": g_CivData[civ].Name, "code": civ, })).sort(sortNameIgnoreCase); if (!civList.length) { closePage(); return; } g_ParsedData = { "units": {}, "structures": {}, "techs": {}, "phases": {} }; let civSelection = Engine.GetGUIObjectByName("civSelection"); civSelection.list = civList.map(c => c.name); civSelection.list_data = civList.map(c => c.code); civSelection.selected = data.civ ? civSelection.list_data.indexOf(data.civ) : 0; } /** * @param {string} civCode */ function selectCiv(civCode) { if (civCode === g_SelectedCiv || !g_CivData[civCode]) return; g_SelectedCiv = civCode; g_CurrentModifiers = deriveModifications(g_AutoResearchTechList); // If a buildList already exists, then this civ has already been parsed if (g_BuildList[g_SelectedCiv]) { draw(); drawPhaseIcons(); return; } let templateLists = compileTemplateLists(civCode); for (let u of templateLists.units.keys()) if (!g_ParsedData.units[u]) g_ParsedData.units[u] = loadUnit(u); for (let s of templateLists.structures.keys()) if (!g_ParsedData.structures[s]) g_ParsedData.structures[s] = loadStructure(s); // Load technologies g_ParsedData.techs[civCode] = {}; for (let techcode of templateLists.techs.keys()) if (basename(techcode).startsWith("phase")) g_ParsedData.phases[techcode] = loadPhase(techcode); else g_ParsedData.techs[civCode][techcode] = loadTechnology(techcode); // Establish phase order - g_ParsedData.phaseList = unravelPhases(g_ParsedData.techs[civCode]); + g_ParsedData.phaseList = unravelPhases(g_ParsedData.phases); // Load any required generic phases that aren't already loaded for (let phasecode of g_ParsedData.phaseList) if (!g_ParsedData.phases[phasecode]) g_ParsedData.phases[phasecode] = loadPhase(phasecode); // Group production and upgrade lists of structures by phase for (let structCode of templateLists.structures.keys()) { let structInfo = g_ParsedData.structures[structCode]; structInfo.phase = getPhaseOfTemplate(structInfo); let structPhaseIdx = g_ParsedData.phaseList.indexOf(structInfo.phase); // If this building is shared with another civ, // it may have already gone through the grouping process already if (!Array.isArray(structInfo.production.technology)) continue; // Sort techs by phase let newProdTech = {}; for (let prod of structInfo.production.technology) { let phase = getPhaseOfTechnology(prod); if (phase === false) continue; if (g_ParsedData.phaseList.indexOf(phase) < structPhaseIdx) phase = structInfo.phase; if (!(phase in newProdTech)) newProdTech[phase] = []; newProdTech[phase].push(prod); } // Sort units by phase let newProdUnits = {}; for (let prod of structInfo.production.units) { let phase = getPhaseOfTemplate(g_ParsedData.units[prod]); if (phase === false) continue; if (g_ParsedData.phaseList.indexOf(phase) < structPhaseIdx) phase = structInfo.phase; if (!(phase in newProdUnits)) newProdUnits[phase] = []; newProdUnits[phase].push(prod); } g_ParsedData.structures[structCode].production = { "technology": newProdTech, "units": newProdUnits }; // Sort upgrades by phase let newUpgrades = {}; if (structInfo.upgrades) for (let upgrade of structInfo.upgrades) { let phase = getPhaseOfTemplate(upgrade); if (g_ParsedData.phaseList.indexOf(phase) < structPhaseIdx) phase = structInfo.phase; if (!newUpgrades[phase]) newUpgrades[phase] = []; newUpgrades[phase].push(upgrade); } g_ParsedData.structures[structCode].upgrades = newUpgrades; } // Determine the buildList for the civ (grouped by phase) let buildList = {}; let trainerList = []; for (let pha of g_ParsedData.phaseList) buildList[pha] = []; for (let structCode of templateLists.structures.keys()) { let phase = g_ParsedData.structures[structCode].phase; buildList[phase].push(structCode); } for (let unitCode of templateLists.units.keys()) { let unitTemplate = g_ParsedData.units[unitCode]; if ((!unitTemplate.production || !Object.keys(unitTemplate.production).length) && !unitTemplate.upgrades) continue; trainerList.push(unitCode); } g_BuildList[g_SelectedCiv] = buildList; g_TrainList[g_SelectedCiv] = trainerList; draw(); drawPhaseIcons(); }