mirror of
https://gitea.wildfiregames.com/0ad/0ad
synced 2026-08-15 14:43:32 -07:00
Define technology pairs in an array
This patch renames the "pair" property of paired technologies to "partOfPair" for clarity and in the pair parent packs the two techs (previously "top" and "bottom") into an array called "pair". This makes the hierarchy more clear in the code and pairs might not be always placed vertically in the future.
This commit is contained in:
parent
8491b7f084
commit
280a19587f
23 changed files with 51 additions and 83 deletions
|
|
@ -132,7 +132,7 @@ class TemplateLoader
|
||||||
{
|
{
|
||||||
const template = this.loadTechnologyTemplate(templateName);
|
const template = this.loadTechnologyTemplate(templateName);
|
||||||
return {
|
return {
|
||||||
"techs": [template.top, template.bottom],
|
"techs": template.pair,
|
||||||
"reqs": DeriveTechnologyRequirements(template, civCode)
|
"reqs": DeriveTechnologyRequirements(template, civCode)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -308,7 +308,7 @@ class TemplateLoader
|
||||||
|
|
||||||
isPairTech(technologyCode)
|
isPairTech(technologyCode)
|
||||||
{
|
{
|
||||||
return !!this.loadTechnologyTemplate(technologyCode).top;
|
return !!this.loadTechnologyTemplate(technologyCode).pair;
|
||||||
}
|
}
|
||||||
|
|
||||||
isPhaseTech(technologyCode)
|
isPhaseTech(technologyCode)
|
||||||
|
|
|
||||||
|
|
@ -196,10 +196,10 @@ class TemplateParser
|
||||||
const tech = GetTechnologyDataHelper(template, civCode, g_ResourceData, this.modifiers[civCode] || {});
|
const tech = GetTechnologyDataHelper(template, civCode, g_ResourceData, this.modifiers[civCode] || {});
|
||||||
tech.name.internal = technologyName;
|
tech.name.internal = technologyName;
|
||||||
|
|
||||||
if (template.pair !== undefined)
|
if (template.partOfPair !== undefined)
|
||||||
{
|
{
|
||||||
tech.pair = template.pair;
|
tech.partOfPair = template.partOfPair;
|
||||||
tech.reqs = this.mergeRequirements(tech.reqs, this.TemplateLoader.loadTechnologyPairTemplate(template.pair).reqs);
|
tech.reqs = this.mergeRequirements(tech.reqs, this.TemplateLoader.loadTechnologyPairTemplate(template.partOfPair).reqs);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.TemplateLoader.isPhaseTech(technologyName))
|
if (this.TemplateLoader.isPhaseTech(technologyName))
|
||||||
|
|
|
||||||
|
|
@ -684,8 +684,8 @@ g_SelectionPanels.Research = {
|
||||||
(item.tech == tech ||
|
(item.tech == tech ||
|
||||||
item.tech.pair &&
|
item.tech.pair &&
|
||||||
tech.pair &&
|
tech.pair &&
|
||||||
item.tech.bottom == tech.bottom &&
|
item.tech.pair?.[0] == tech.pair?.[0] &&
|
||||||
item.tech.top == tech.top) &&
|
item.tech.pair?.[1] == tech.pair?.[1]) &&
|
||||||
Object.keys(item.techCostMultiplier).every(
|
Object.keys(item.techCostMultiplier).every(
|
||||||
k => item.techCostMultiplier[k] == state.researcher.techCostMultiplier[k])
|
k => item.techCostMultiplier[k] == state.researcher.techCostMultiplier[k])
|
||||||
));
|
));
|
||||||
|
|
@ -731,7 +731,7 @@ g_SelectionPanels.Research = {
|
||||||
// Handle one or two techs (tech pair)
|
// Handle one or two techs (tech pair)
|
||||||
const player = data.player;
|
const player = data.player;
|
||||||
const playerState = GetSimState().players[player];
|
const playerState = GetSimState().players[player];
|
||||||
for (const tech of data.item.tech.pair ? [data.item.tech.bottom, data.item.tech.top] : [data.item.tech])
|
for (const tech of (data.item.tech.pair || [data.item.tech]))
|
||||||
{
|
{
|
||||||
// Don't change the object returned by GetTechnologyData
|
// Don't change the object returned by GetTechnologyData
|
||||||
const template = clone(GetTechnologyData(tech, playerState.civ));
|
const template = clone(GetTechnologyData(tech, playerState.civ));
|
||||||
|
|
|
||||||
|
|
@ -48,8 +48,8 @@ GameState.prototype.init = function(SharedScript, state, player)
|
||||||
{
|
{
|
||||||
// Cannot call pickrandom because this function is called on rejoin and that causes oos.
|
// Cannot call pickrandom because this function is called on rejoin and that causes oos.
|
||||||
// (reverting rP20750)
|
// (reverting rP20750)
|
||||||
techName = this.playerData.disabledTechnologies[techData._template.bottom] ?
|
techName = this.playerData.disabledTechnologies[techData._template.pair[0]] ?
|
||||||
techData._template.top : techData._template.bottom;
|
techData._template.pair[1] : techData._template.pair[0];
|
||||||
|
|
||||||
const supersedes = techData._template.supersedes;
|
const supersedes = techData._template.supersedes;
|
||||||
techData = clone(this.getTemplate(techName));
|
techData = clone(this.getTemplate(techName));
|
||||||
|
|
|
||||||
|
|
@ -7,15 +7,14 @@ export function Technology(templateName)
|
||||||
const template = TechnologyTemplates.Get(templateName);
|
const template = TechnologyTemplates.Get(templateName);
|
||||||
|
|
||||||
// check if this is one of two paired technologies.
|
// check if this is one of two paired technologies.
|
||||||
this._isPair = template.pair !== undefined;
|
if (template.partOfPair)
|
||||||
if (this._isPair)
|
|
||||||
{
|
{
|
||||||
const pairTech = TechnologyTemplates.Get(template.pair);
|
const parentTech = TechnologyTemplates.Get(template.partOfPair);
|
||||||
this._pairedWith = pairTech.top == templateName ? pairTech.bottom : pairTech.top;
|
this._pairedWith = parentTech.pair[0] == templateName ? parentTech.pair[1] : parentTech.pair[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if it only defines a pair:
|
// check if it only defines a pair:
|
||||||
this._definesPair = template.top !== undefined;
|
this._definesPair = !!template.pair;
|
||||||
this._template = template;
|
this._template = template;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -41,22 +40,17 @@ Technology.prototype.getPairedTechs = function()
|
||||||
if (!this._definesPair)
|
if (!this._definesPair)
|
||||||
return undefined;
|
return undefined;
|
||||||
|
|
||||||
return [
|
return this._template.pair.map(name => new Technology(name));
|
||||||
new Technology(this._template.top),
|
|
||||||
new Technology(this._template.bottom)
|
|
||||||
];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Technology.prototype.pair = function()
|
Technology.prototype.pair = function()
|
||||||
{
|
{
|
||||||
if (!this._isPair)
|
return this._template.partOfPair;
|
||||||
return undefined;
|
|
||||||
return this._template.pair;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Technology.prototype.pairedWith = function()
|
Technology.prototype.pairedWith = function()
|
||||||
{
|
{
|
||||||
if (!this._isPair)
|
if (!this._template.partOfPair)
|
||||||
return undefined;
|
return undefined;
|
||||||
return this._pairedWith;
|
return this._pairedWith;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -234,36 +234,22 @@ Researcher.prototype.GetTechnologiesList = function()
|
||||||
superseded[template.supersedes] = tech;
|
superseded[template.supersedes] = tech;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now make researched/in progress techs invisible.
|
return techList.map(tech =>
|
||||||
for (const i in techList)
|
|
||||||
{
|
{
|
||||||
let tech = techList[i];
|
// Make researched/in progress techs invisible.
|
||||||
while (this.IsTechnologyResearchedOrInProgress(tech))
|
while (tech && this.IsTechnologyResearchedOrInProgress(tech))
|
||||||
tech = superseded[tech];
|
tech = superseded[tech]; // might be undefined
|
||||||
|
|
||||||
techList[i] = tech;
|
|
||||||
}
|
|
||||||
|
|
||||||
const ret = [];
|
|
||||||
|
|
||||||
// This inserts the techs into the correct positions to line up the technology pairs.
|
|
||||||
for (let i = 0; i < techList.length; ++i)
|
|
||||||
{
|
|
||||||
const tech = techList[i];
|
|
||||||
if (!tech)
|
if (!tech)
|
||||||
{
|
return undefined;
|
||||||
ret[i] = undefined;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// "Unwrap" tech pairs
|
||||||
const template = TechnologyTemplates.Get(tech);
|
const template = TechnologyTemplates.Get(tech);
|
||||||
if (template.top)
|
if (template?.pair)
|
||||||
ret[i] = { "pair": true, "top": template.top, "bottom": template.bottom };
|
tech = { "pair": template.pair };
|
||||||
else
|
|
||||||
ret[i] = tech;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
return tech;
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -294,11 +280,9 @@ Researcher.prototype.IsTechnologyResearchedOrInProgress = function(tech)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
const template = TechnologyTemplates.Get(tech);
|
const template = TechnologyTemplates.Get(tech);
|
||||||
if (template.top)
|
if (template.pair)
|
||||||
return cmpTechnologyManager.IsTechnologyResearched(template.top) ||
|
return template.pair.some(t => cmpTechnologyManager.IsTechnologyResearched(t) ||
|
||||||
cmpTechnologyManager.IsInProgress(template.top) ||
|
cmpTechnologyManager.IsInProgress(t));
|
||||||
cmpTechnologyManager.IsTechnologyResearched(template.bottom) ||
|
|
||||||
cmpTechnologyManager.IsInProgress(template.bottom);
|
|
||||||
|
|
||||||
return cmpTechnologyManager.IsTechnologyResearched(tech) || cmpTechnologyManager.IsInProgress(tech);
|
return cmpTechnologyManager.IsTechnologyResearched(tech) || cmpTechnologyManager.IsInProgress(tech);
|
||||||
};
|
};
|
||||||
|
|
@ -310,9 +294,7 @@ Researcher.prototype.IsTechnologyResearchedOrInProgress = function(tech)
|
||||||
*/
|
*/
|
||||||
Researcher.prototype.QueueTechnology = function(templateName, metadata)
|
Researcher.prototype.QueueTechnology = function(templateName, metadata)
|
||||||
{
|
{
|
||||||
if (!this.GetTechnologiesList().some(tech =>
|
if (!this.GetTechnologiesList().some(tech => tech === templateName || tech?.pair?.includes(templateName)))
|
||||||
tech && (tech == templateName ||
|
|
||||||
tech.pair && (tech.top == templateName || tech.bottom == templateName))))
|
|
||||||
{
|
{
|
||||||
error("This entity cannot research " + templateName + ".");
|
error("This entity cannot research " + templateName + ".");
|
||||||
return -1;
|
return -1;
|
||||||
|
|
|
||||||
|
|
@ -212,7 +212,7 @@ TechnologyManager.prototype.Init = function()
|
||||||
this.unresearchedAutoResearchTechs = new Set();
|
this.unresearchedAutoResearchTechs = new Set();
|
||||||
const allTechs = TechnologyTemplates.GetAll();
|
const allTechs = TechnologyTemplates.GetAll();
|
||||||
for (const key in allTechs)
|
for (const key in allTechs)
|
||||||
if (allTechs[key].autoResearch || allTechs[key].top)
|
if (allTechs[key].autoResearch || allTechs[key].pair)
|
||||||
this.unresearchedAutoResearchTechs.add(key);
|
this.unresearchedAutoResearchTechs.add(key);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -263,8 +263,7 @@ TechnologyManager.prototype.UpdateAutoResearch = function()
|
||||||
for (const key of this.unresearchedAutoResearchTechs)
|
for (const key of this.unresearchedAutoResearchTechs)
|
||||||
{
|
{
|
||||||
const tech = TechnologyTemplates.Get(key);
|
const tech = TechnologyTemplates.Get(key);
|
||||||
if ((tech.autoResearch && this.CanResearch(key)) ||
|
if ((tech.autoResearch && this.CanResearch(key)) || (tech.pair?.some(t => this.IsTechnologyResearched(t))))
|
||||||
(tech.top && (this.IsTechnologyResearched(tech.top) || this.IsTechnologyResearched(tech.bottom))))
|
|
||||||
{
|
{
|
||||||
this.unresearchedAutoResearchTechs.delete(key);
|
this.unresearchedAutoResearchTechs.delete(key);
|
||||||
this.ResearchTechnology(key);
|
this.ResearchTechnology(key);
|
||||||
|
|
@ -306,11 +305,10 @@ TechnologyManager.prototype.CanResearch = function(tech)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (template.top && this.IsInProgress(template.top) ||
|
if (template.pair?.some(t => this.IsInProgress(t)))
|
||||||
template.bottom && this.IsInProgress(template.bottom))
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (template.pair && !this.CanResearch(template.pair))
|
if (template.partOfPair && !this.CanResearch(template.partOfPair))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (this.IsInProgress(tech))
|
if (this.IsInProgress(tech))
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"pair": "pair_unlock_civil_engineering_han",
|
"partOfPair": "pair_unlock_civil_engineering_han",
|
||||||
"genericName": "Improved Construction",
|
"genericName": "Improved Construction",
|
||||||
"specificName": { "han": "Gōngchéng" },
|
"specificName": { "han": "Gōngchéng" },
|
||||||
"description": "",
|
"description": "",
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"pair": "pair_unlock_civil_engineering_han",
|
"partOfPair": "pair_unlock_civil_engineering_han",
|
||||||
"genericName": "Robust Architecture",
|
"genericName": "Robust Architecture",
|
||||||
"specificName": { "han": "Gōngchéng" },
|
"specificName": { "han": "Gōngchéng" },
|
||||||
"description": "",
|
"description": "",
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"pair": "pair_unlock_civil_service_han",
|
"partOfPair": "pair_unlock_civil_service_han",
|
||||||
"genericName": "Efficient Bureaucracy",
|
"genericName": "Efficient Bureaucracy",
|
||||||
"specificName": { "han": "Guānliáo" },
|
"specificName": { "han": "Guānliáo" },
|
||||||
"description": "",
|
"description": "",
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"pair": "pair_unlock_civil_service_han",
|
"partOfPair": "pair_unlock_civil_service_han",
|
||||||
"genericName": "Intensive Training",
|
"genericName": "Intensive Training",
|
||||||
"specificName": { "han": "Guānliáo" },
|
"specificName": { "han": "Guānliáo" },
|
||||||
"description": "",
|
"description": "",
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"pair": "pair_gather_food_maur",
|
"partOfPair": "pair_gather_food_maur",
|
||||||
"genericName": "Ahimsa",
|
"genericName": "Ahimsa",
|
||||||
"description": "Ahimsa is the ancient Indian principle of nonviolence which applies to actions towards all living beings. It is a key virtue in Indian religions like Jainism, Buddhism, Hinduism, and Sikhism.",
|
"description": "Ahimsa is the ancient Indian principle of nonviolence which applies to actions towards all living beings. It is a key virtue in Indian religions like Jainism, Buddhism, Hinduism, and Sikhism.",
|
||||||
"cost": {
|
"cost": {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"pair": "pair_gather_food_maur",
|
"partOfPair": "pair_gather_food_maur",
|
||||||
"genericName": "Wicker Baskets",
|
"genericName": "Wicker Baskets",
|
||||||
"description": "Equip your foragers with wicker baskets for foraging.",
|
"description": "Equip your foragers with wicker baskets for foraging.",
|
||||||
"cost": {
|
"cost": {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
{
|
{
|
||||||
"genericName": "Wicker Basket vs Ahimsa",
|
"genericName": "Wicker Basket vs Ahimsa",
|
||||||
"top": "gather_wicker_baskets_maur",
|
"pair": ["gather_wicker_baskets_maur", "gather_ahimsa"],
|
||||||
"bottom": "gather_ahimsa",
|
|
||||||
"requirements": { "civ": "maur" }
|
"requirements": { "civ": "maur" }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
{
|
{
|
||||||
"genericName": "Traditional Army vs Reform Army",
|
"genericName": "Traditional Army vs Reform Army",
|
||||||
"top": "traditional_army_sele",
|
"pair": ["traditional_army_sele", "reformed_army_sele"],
|
||||||
"bottom": "reformed_army_sele",
|
|
||||||
"requirements": { "civ": "sele" }
|
"requirements": { "civ": "sele" }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
{
|
{
|
||||||
"genericName": "Civil Engineering",
|
"genericName": "Civil Engineering",
|
||||||
"top": "civil_engineering_01",
|
"pair": ["civil_engineering_01", "civil_engineering_02"],
|
||||||
"bottom": "civil_engineering_02",
|
|
||||||
"requirements": { "civ": "han" }
|
"requirements": { "civ": "han" }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
{
|
{
|
||||||
"genericName": "Civil Service",
|
"genericName": "Civil Service",
|
||||||
"top": "civil_service_01",
|
"pair": ["civil_service_01", "civil_service_02"],
|
||||||
"bottom": "civil_service_02",
|
|
||||||
"requirements": { "civ": "han" }
|
"requirements": { "civ": "han" }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
{
|
{
|
||||||
"genericName": "Cult",
|
"genericName": "Cult",
|
||||||
"top": "pharaonic_cult",
|
"pair": ["pharaonic_cult", "serapis_cult"],
|
||||||
"bottom": "serapis_cult",
|
|
||||||
"requirements": { "civ": "ptol" }
|
"requirements": { "civ": "ptol" }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"pair": "pair_unlock_cult_ptol",
|
"partOfPair": "pair_unlock_cult_ptol",
|
||||||
"genericName": "Pharaonic Cult",
|
"genericName": "Pharaonic Cult",
|
||||||
"description": "The Pharaohs were worshipped as living gods. Their word was sacrosanct and beyond reproach, at least among the common people. The Ptolemaic dynasts certainly took advantage of this ancient custom to the fullest, to varying degrees of success.",
|
"description": "The Pharaohs were worshipped as living gods. Their word was sacrosanct and beyond reproach, at least among the common people. The Ptolemaic dynasts certainly took advantage of this ancient custom to the fullest, to varying degrees of success.",
|
||||||
"cost": {
|
"cost": {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"pair": "pair_unlock_champions_sele",
|
"partOfPair": "pair_unlock_champions_sele",
|
||||||
"genericName": "Reform Army",
|
"genericName": "Reform Army",
|
||||||
"description": "The Roman-style core of the Seleucid army.",
|
"description": "The Roman-style core of the Seleucid army.",
|
||||||
"requirements": {
|
"requirements": {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"pair": "pair_unlock_cult_ptol",
|
"partOfPair": "pair_unlock_cult_ptol",
|
||||||
"genericName": "Serapis Cult",
|
"genericName": "Serapis Cult",
|
||||||
"description": "The cult of Serapis was introduced during the 3rd century BC on the orders of Ptolemy I of Egypt as a means to unify the Greeks and Egyptians in his realm. The god was depicted as Greek in appearance, but with Egyptian trappings, and combined iconography from a great many cults, signifying both abundance and resurrection. A serapeion was any temple or religious precinct devoted to Serapis. The cult of Serapis was spread as a matter of deliberate policy by the Ptolemaic kings, who also built an immense serapeum in Alexandria.",
|
"description": "The cult of Serapis was introduced during the 3rd century BC on the orders of Ptolemy I of Egypt as a means to unify the Greeks and Egyptians in his realm. The god was depicted as Greek in appearance, but with Egyptian trappings, and combined iconography from a great many cults, signifying both abundance and resurrection. A serapeion was any temple or religious precinct devoted to Serapis. The cult of Serapis was spread as a matter of deliberate policy by the Ptolemaic kings, who also built an immense serapeum in Alexandria.",
|
||||||
"cost": {
|
"cost": {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"pair": "pair_unlock_champions_sele",
|
"partOfPair": "pair_unlock_champions_sele",
|
||||||
"genericName": "Traditional Army",
|
"genericName": "Traditional Army",
|
||||||
"description": "The Macedonian-style core of the Seleucid army.",
|
"description": "The Macedonian-style core of the Seleucid army.",
|
||||||
"requirements": {
|
"requirements": {
|
||||||
|
|
|
||||||
|
|
@ -151,7 +151,6 @@ function Cheat(input)
|
||||||
// try to spilt the input
|
// try to spilt the input
|
||||||
const tmp = input.parameter.split(/\s+/);
|
const tmp = input.parameter.split(/\s+/);
|
||||||
const number = +tmp[0];
|
const number = +tmp[0];
|
||||||
const pair = tmp.length > 1 && (tmp[1] == "top" || tmp[1] == "bottom") ? tmp[1] : "top"; // use top as default value
|
|
||||||
|
|
||||||
// check, if valid number was parsed.
|
// check, if valid number was parsed.
|
||||||
if (!isNaN(number))
|
if (!isNaN(number))
|
||||||
|
|
@ -166,7 +165,7 @@ function Cheat(input)
|
||||||
|
|
||||||
// get name of tech
|
// get name of tech
|
||||||
if (tech.pair)
|
if (tech.pair)
|
||||||
techname = tech[pair];
|
techname = tech.pair[tmp[1] === "1" ? 1 : 0];
|
||||||
else
|
else
|
||||||
techname = tech;
|
techname = tech;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue