mirror of
https://gitea.wildfiregames.com/0ad/0ad.git
synced 2026-06-21 10:03:43 +00:00
Merge different logics of technology requirements and fix the related bugs. Create a global script which derive the technology requirements objects in a form usable for structure tree, gui, simulation and AI. Reviewed by mimo (for AI) and fatherbushido. Fixes #3993, #1646, #4263, #4217. Refs #4108.
This was SVN commit r19120.
This commit is contained in:
@@ -50,3 +50,238 @@ function DoesModificationApply(modification, classes)
|
||||
{
|
||||
return MatchesClassList(classes, modification.affects);
|
||||
}
|
||||
|
||||
/**
|
||||
* Derives the technology requirements from a given technology template.
|
||||
* Takes into account the `supersedes` attribute.
|
||||
*
|
||||
* @param {object} template - The template object. Loading of the template must have already occured.
|
||||
*
|
||||
* @return Derived technology requirements. See `InterpretTechRequirements` for object's syntax.
|
||||
*/
|
||||
function DeriveTechnologyRequirements(template, civ)
|
||||
{
|
||||
let requirements = [];
|
||||
|
||||
if (template.requirements)
|
||||
{
|
||||
let op = Object.keys(template.requirements)[0];
|
||||
let val = template.requirements[op];
|
||||
requirements = InterpretTechRequirements(civ, op, val);
|
||||
}
|
||||
|
||||
if (template.supersedes && requirements)
|
||||
{
|
||||
if (!requirements.length)
|
||||
requirements.push({});
|
||||
|
||||
for (let req of requirements)
|
||||
{
|
||||
if (!req.techs)
|
||||
req.techs = [];
|
||||
req.techs.push(template.supersedes);
|
||||
}
|
||||
}
|
||||
|
||||
return requirements;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interprets the prerequisite requirements of a technology.
|
||||
*
|
||||
* Takes the initial { key: value } from the short-form requirements object in entity templates,
|
||||
* and parses it into an object that can be more easily checked by simulation and gui.
|
||||
*
|
||||
* Works recursively if needed.
|
||||
*
|
||||
* The returned object is in the form:
|
||||
* ```
|
||||
* { "techs": ["tech1", "tech2"] },
|
||||
* { "techs": ["tech3"] }
|
||||
* ```
|
||||
* or
|
||||
* ```
|
||||
* { "entities": [[{
|
||||
* "class": "human",
|
||||
* "number": 2,
|
||||
* "check": "count"
|
||||
* }
|
||||
* or
|
||||
* ```
|
||||
* false;
|
||||
* ```
|
||||
* (Or, to translate:
|
||||
* 1. need either both `tech1` and `tech2`, or `tech3`
|
||||
* 2. need 2 entities with the `human` class
|
||||
* 3. cannot research this tech at all)
|
||||
*
|
||||
* @param {string} civ - The civ code
|
||||
* @param {string} operator - The base operation. Can be "civ", "notciv", "tech", "entity", "all" or "any".
|
||||
* @param {mixed} value - The value associated with the above operation.
|
||||
*
|
||||
* @return Object containing the requirements for the given civ, or false if the civ cannot research the tech.
|
||||
*/
|
||||
function InterpretTechRequirements(civ, operator, value)
|
||||
{
|
||||
let requirements = [];
|
||||
|
||||
switch (operator)
|
||||
{
|
||||
case "civ":
|
||||
return !civ || civ == value ? [] : false;
|
||||
|
||||
case "notciv":
|
||||
return civ == value ? false : [];
|
||||
|
||||
case "entity":
|
||||
{
|
||||
let number = value.number || value.numberOfTypes || 0;
|
||||
if (number > 0)
|
||||
requirements.push({
|
||||
"entities": [{
|
||||
"class": value.class,
|
||||
"number": number,
|
||||
"check": value.number ? "count" : "variants"
|
||||
}]
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
case "tech":
|
||||
requirements.push({
|
||||
"techs": [value]
|
||||
});
|
||||
break;
|
||||
|
||||
case "all":
|
||||
{
|
||||
let civPermitted = undefined; // tri-state (undefined, false, or true)
|
||||
for (let subvalue of value)
|
||||
{
|
||||
let newOper = Object.keys(subvalue)[0];
|
||||
let newValue = subvalue[newOper];
|
||||
let result = InterpretTechRequirements(civ, newOper, newValue);
|
||||
|
||||
switch (newOper)
|
||||
{
|
||||
case "civ":
|
||||
if (result)
|
||||
civPermitted = true;
|
||||
else if (civPermitted !== true)
|
||||
civPermitted = false;
|
||||
break;
|
||||
|
||||
case "notciv":
|
||||
if (!result)
|
||||
return false;
|
||||
break;
|
||||
|
||||
case "any":
|
||||
if (!result)
|
||||
return false;
|
||||
// else, fall through
|
||||
|
||||
case "all":
|
||||
if (!result)
|
||||
{
|
||||
let nullcivreqs = InterpretTechRequirements(null, newOper, newValue);
|
||||
if (!nullcivreqs || !nullcivreqs.length)
|
||||
civPermitted = false;
|
||||
continue;
|
||||
}
|
||||
// else, fall through
|
||||
|
||||
case "tech":
|
||||
case "entity":
|
||||
{
|
||||
if (result.length)
|
||||
{
|
||||
if (!requirements.length)
|
||||
requirements.push({});
|
||||
|
||||
let newRequirements = [];
|
||||
for (let currReq of requirements)
|
||||
for (let res of result)
|
||||
{
|
||||
let newReq = {}
|
||||
for (let subtype in currReq)
|
||||
newReq[subtype] = currReq[subtype];
|
||||
|
||||
for (let subtype in res)
|
||||
{
|
||||
if (!newReq[subtype])
|
||||
newReq[subtype] = [];
|
||||
newReq[subtype] = newReq[subtype].concat(res[subtype]);
|
||||
}
|
||||
newRequirements.push(newReq);
|
||||
}
|
||||
requirements = newRequirements;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
if (civPermitted === false) // if and only if false
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
|
||||
case "any":
|
||||
{
|
||||
let civPermitted = false;
|
||||
for (let subvalue of value)
|
||||
{
|
||||
let newOper = Object.keys(subvalue)[0];
|
||||
let newValue = subvalue[newOper];
|
||||
let result = InterpretTechRequirements(civ, newOper, newValue);
|
||||
|
||||
switch (newOper)
|
||||
{
|
||||
|
||||
case "civ":
|
||||
if (result)
|
||||
return [];
|
||||
break;
|
||||
|
||||
case "notciv":
|
||||
if (!result)
|
||||
return false;
|
||||
civPermitted = true;
|
||||
break;
|
||||
|
||||
case "any":
|
||||
if (!result)
|
||||
{
|
||||
let nullcivreqs = InterpretTechRequirements(null, newOper, newValue);
|
||||
if (!nullcivreqs || !nullcivreqs.length)
|
||||
continue;
|
||||
return false;
|
||||
}
|
||||
// else, fall through
|
||||
|
||||
case "all":
|
||||
if (!result)
|
||||
continue;
|
||||
civPermitted = true;
|
||||
// else, fall through
|
||||
|
||||
case "tech":
|
||||
case "entity":
|
||||
for (let res of result)
|
||||
requirements.push(res);
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
if (!civPermitted && !requirements.length)
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
warn("Unknown requirement operator: "+operator);
|
||||
}
|
||||
|
||||
return requirements;
|
||||
}
|
||||
|
||||
@@ -351,17 +351,12 @@ function GetTemplateDataHelper(template, player, auraTemplates, resources)
|
||||
*/
|
||||
function GetTechnologyDataHelper(template, civ, resources)
|
||||
{
|
||||
var ret = {};
|
||||
let ret = {};
|
||||
|
||||
// Get specific name for this civ or else the generic specific name
|
||||
var specific;
|
||||
let specific;
|
||||
if (template.specificName)
|
||||
{
|
||||
if (template.specificName[civ])
|
||||
specific = template.specificName[civ];
|
||||
else
|
||||
specific = template.specificName['generic'];
|
||||
}
|
||||
specific = template.specificName[civ] || template.specificName.generic;
|
||||
|
||||
ret.name = {
|
||||
"specific": specific,
|
||||
@@ -377,19 +372,7 @@ function GetTechnologyDataHelper(template, civ, resources)
|
||||
ret.tooltip = template.tooltip;
|
||||
ret.requirementsTooltip = template.requirementsTooltip || "";
|
||||
|
||||
// TODO: This doesn't handle all types of requirements
|
||||
if (template.requirements && template.requirements.class)
|
||||
ret.classRequirements = {
|
||||
"class": template.requirements.class,
|
||||
"number": template.requirements.number
|
||||
};
|
||||
else if (template.requirements && template.requirements.all)
|
||||
for (let req of template.requirements.all)
|
||||
if (req.class)
|
||||
ret.classRequirements = {
|
||||
"class": req.class,
|
||||
"number": req.number
|
||||
};
|
||||
ret.reqs = DeriveTechnologyRequirements(template, civ);
|
||||
|
||||
ret.description = template.description;
|
||||
|
||||
|
||||
@@ -34,3 +34,13 @@ function shuffleArray(source)
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes prefixing path from a path or filename, leaving just the file's name (with extension)
|
||||
*
|
||||
* ie. a/b/c/file.ext -> file.ext
|
||||
*/
|
||||
function basename(path)
|
||||
{
|
||||
return path.slice(path.lastIndexOf("/") + 1);
|
||||
}
|
||||
|
||||
@@ -459,13 +459,13 @@ function getEntityCostTooltip(template, trainNum, entity)
|
||||
return "";
|
||||
}
|
||||
|
||||
function getRequiredTechnologyTooltip(technologyEnabled, requiredTechnology)
|
||||
function getRequiredTechnologyTooltip(technologyEnabled, requiredTechnology, civ)
|
||||
{
|
||||
if (technologyEnabled)
|
||||
return "";
|
||||
|
||||
return sprintf(translate("Requires %(technology)s"), {
|
||||
"technology": getEntityNames(GetTechnologyData(requiredTechnology))
|
||||
"technology": getEntityNames(GetTechnologyData(requiredTechnology, civ))
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -350,7 +350,7 @@ g_SelectionPanels.Construction = {
|
||||
let limits = getEntityLimitAndCount(data.playerState, data.item);
|
||||
tooltips.push(
|
||||
formatLimitString(limits.entLimit, limits.entCount, limits.entLimitChangers),
|
||||
getRequiredTechnologyTooltip(technologyEnabled, template.requiredTechnology),
|
||||
getRequiredTechnologyTooltip(technologyEnabled, template.requiredTechnology, GetSimState().players[data.player].civ),
|
||||
getNeededResourcesTooltip(neededResources));
|
||||
|
||||
data.button.tooltip = tooltips.filter(tip => tip).join("\n");
|
||||
@@ -794,12 +794,14 @@ g_SelectionPanels.Research = {
|
||||
setPanelObjectPosition(pair, data.i, data.rowLength);
|
||||
|
||||
// Handle one or two techs (tech pair)
|
||||
let player = data.player;
|
||||
for (let i in techs)
|
||||
{
|
||||
let tech = techs[i];
|
||||
let playerState = GetSimState().players[player];
|
||||
|
||||
// Don't change the object returned by GetTechnologyData
|
||||
let template = clone(GetTechnologyData(tech));
|
||||
let template = clone(GetTechnologyData(tech, playerState.civ));
|
||||
if (!template)
|
||||
return false;
|
||||
|
||||
@@ -808,12 +810,12 @@ g_SelectionPanels.Research = {
|
||||
|
||||
let neededResources = Engine.GuiInterfaceCall("GetNeededResources", {
|
||||
"cost": template.cost,
|
||||
"player": data.player
|
||||
"player": player
|
||||
});
|
||||
|
||||
let requirementsPassed = Engine.GuiInterfaceCall("CheckTechnologyRequirements", {
|
||||
"tech": tech,
|
||||
"player": data.player
|
||||
"player": player
|
||||
});
|
||||
|
||||
let button = Engine.GetGUIObjectByName("unitResearchButton[" + position + "]");
|
||||
@@ -828,13 +830,40 @@ g_SelectionPanels.Research = {
|
||||
if (!requirementsPassed)
|
||||
{
|
||||
let tip = template.requirementsTooltip;
|
||||
if (template.classRequirements)
|
||||
let reqs = template.reqs;
|
||||
for (let req of reqs)
|
||||
{
|
||||
let player = data.player;
|
||||
let current = GetSimState().players[player].classCounts[template.classRequirements.class] || 0;
|
||||
let remaining = template.classRequirements.number - current;
|
||||
tip += " " + sprintf(translatePlural("Remaining: %(number)s to build.", "Remaining: %(number)s to build.", remaining), {
|
||||
"number": remaining
|
||||
if (!req.entities)
|
||||
continue;
|
||||
|
||||
let entityCounts = [];
|
||||
for (let entity of req.entities)
|
||||
{
|
||||
let current = 0;
|
||||
switch (entity.check)
|
||||
{
|
||||
case "count":
|
||||
current = playerState.classCounts[entity.class] || 0;
|
||||
break;
|
||||
|
||||
case "variants":
|
||||
current = playerState.typeCountsByClass[entity.class] ?
|
||||
Object.keys(playerState.typeCountsByClass[entity.class]).length : 0;
|
||||
break;
|
||||
}
|
||||
|
||||
let remaining = entity.number - current;
|
||||
if (remaining < 1)
|
||||
continue;
|
||||
|
||||
entityCounts.push(sprintf(translatePlural("%(number)s entity of class %(class)s", "%(number)s entities of class %(class)s", remaining), {
|
||||
"number": remaining,
|
||||
"class": entity.class
|
||||
}));
|
||||
}
|
||||
|
||||
tip += " " + sprintf(translate("Remaining: %(entityCounts)s"), {
|
||||
"entityCounts": entityCounts.join(translate(", "))
|
||||
});
|
||||
}
|
||||
tooltips.push(tip);
|
||||
@@ -1064,7 +1093,7 @@ g_SelectionPanels.Training = {
|
||||
"[color=\"" + g_HotkeyColor + "\"]" +
|
||||
formatBatchTrainingString(buildingsCountToTrainFullBatch, fullBatchSize, remainderBatch) +
|
||||
"[/color]",
|
||||
getRequiredTechnologyTooltip(technologyEnabled, template.requiredTechnology),
|
||||
getRequiredTechnologyTooltip(technologyEnabled, template.requiredTechnology, GetSimState().players[data.player].civ),
|
||||
getNeededResourcesTooltip(neededResources));
|
||||
|
||||
data.button.tooltip = tooltips.filter(tip => tip).join("\n");
|
||||
@@ -1154,7 +1183,7 @@ g_SelectionPanels.Upgrade = {
|
||||
tooltips.push(
|
||||
getEntityCostTooltip(data.item),
|
||||
formatLimitString(limits.entLimit, limits.entCount, limits.entLimitChangers),
|
||||
getRequiredTechnologyTooltip(technologyEnabled, data.item.requiredTechnology),
|
||||
getRequiredTechnologyTooltip(technologyEnabled, data.item.requiredTechnology, GetSimState().players[data.player].civ),
|
||||
getNeededResourcesTooltip(neededResources));
|
||||
|
||||
tooltip = tooltips.filter(tip => tip).join("\n");
|
||||
|
||||
@@ -222,16 +222,19 @@ function GetTemplateDataWithoutLocalization(templateName)
|
||||
return g_TemplateDataWithoutLocalization[templateName];
|
||||
}
|
||||
|
||||
function GetTechnologyData(technologyName)
|
||||
function GetTechnologyData(technologyName, civ)
|
||||
{
|
||||
if (!(technologyName in g_TechnologyData))
|
||||
if (!g_TechnologyData[civ])
|
||||
g_TechnologyData[civ] = {};
|
||||
|
||||
if (!(technologyName in g_TechnologyData[civ]))
|
||||
{
|
||||
let template = Engine.GuiInterfaceCall("GetTechnologyData", technologyName);
|
||||
let template = Engine.GuiInterfaceCall("GetTechnologyData", { "name": technologyName, "civ": civ });
|
||||
translateObjectKeys(template, ["specific", "generic", "description", "tooltip", "requirementsTooltip"]);
|
||||
g_TechnologyData[technologyName] = template;
|
||||
g_TechnologyData[civ][technologyName] = template;
|
||||
}
|
||||
|
||||
return g_TechnologyData[technologyName];
|
||||
return g_TechnologyData[civ][technologyName];
|
||||
}
|
||||
|
||||
function init(initData, hotloadData)
|
||||
|
||||
@@ -97,9 +97,9 @@ function draw()
|
||||
if (stru.production.technology[prod_pha])
|
||||
for (let prod of stru.production.technology[prod_pha])
|
||||
{
|
||||
prod = clone(depath(prod).slice(0,5) == "phase" ?
|
||||
prod = clone(basename(prod).slice(0,5) == "phase" ?
|
||||
g_ParsedData.phases[prod] :
|
||||
g_ParsedData.techs[prod]);
|
||||
g_ParsedData.techs[g_SelectedCiv][prod]);
|
||||
|
||||
for (let res in stru.techCostMultiplier)
|
||||
if (prod.cost[res])
|
||||
@@ -183,7 +183,7 @@ function draw()
|
||||
prod = g_ParsedData.units[prod];
|
||||
break;
|
||||
case "techs":
|
||||
prod = clone(g_ParsedData.techs[prod]);
|
||||
prod = clone(g_ParsedData.techs[civCode][prod]);
|
||||
for (let res in trainer.techCostMultiplier)
|
||||
if (prod.cost[res])
|
||||
prod.cost[res] *= trainer.techCostMultiplier[res];
|
||||
|
||||
@@ -48,11 +48,6 @@ function loadAuraData(templateName)
|
||||
return g_AuraData[templateName];
|
||||
}
|
||||
|
||||
function depath(path)
|
||||
{
|
||||
return path.slice(path.lastIndexOf("/") + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is needed because getEntityCostTooltip in tooltip.js needs to get
|
||||
* the template data of the different wallSet pieces. In the session this
|
||||
@@ -63,3 +58,60 @@ function GetTemplateData(templateName)
|
||||
var template = loadTemplate(templateName);
|
||||
return GetTemplateDataHelper(template, null, g_AuraData, g_ResourceData);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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).slice(0, 5) === "phase")
|
||||
{
|
||||
phaseIdx = g_ParsedData.phaseList.indexOf(GetActualPhase(techName));
|
||||
if (phaseIdx > 0)
|
||||
return g_ParsedData.phaseList[phaseIdx - 1];
|
||||
}
|
||||
|
||||
if (!g_ParsedData.techs[g_SelectedCiv][techName])
|
||||
warn(g_SelectedCiv + " : " + techName);
|
||||
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).slice(0, 5) === "phase")
|
||||
return tech;
|
||||
phaseIdx = Math.max(phaseIdx, g_ParsedData.phaseList.indexOf(GetPhaseOfTechnology(tech)));
|
||||
}
|
||||
return g_ParsedData.phaseList[phaseIdx] || false;
|
||||
}
|
||||
|
||||
function GetActualPhase(phaseName)
|
||||
{
|
||||
if (g_ParsedData.phases[phaseName])
|
||||
return g_ParsedData.phases[phaseName].actualPhase;
|
||||
|
||||
warn("Unrecognised phase (" + techName + ")");
|
||||
return g_ParsedData.phaseList[0];
|
||||
}
|
||||
|
||||
function GetPhaseOfTemplate(template)
|
||||
{
|
||||
if (!template.requiredTechnology)
|
||||
return g_ParsedData.phaseList[0];
|
||||
|
||||
if (basename(template.requiredTechnology).slice(0, 5) == "phase")
|
||||
return GetActualPhase(template.requiredTechnology);
|
||||
|
||||
return GetPhaseOfTechnology(template.requiredTechnology);
|
||||
}
|
||||
|
||||
@@ -2,18 +2,9 @@ function loadUnit(templateName)
|
||||
{
|
||||
if (!Engine.TemplateExists(templateName))
|
||||
return null;
|
||||
var template = loadTemplate(templateName);
|
||||
|
||||
var unit = GetTemplateDataHelper(template, null, g_AuraData, g_ResourceData);
|
||||
unit.phase = false;
|
||||
|
||||
if (unit.requiredTechnology)
|
||||
{
|
||||
if (depath(unit.requiredTechnology).slice(0, 5) == "phase")
|
||||
unit.phase = unit.requiredTechnology;
|
||||
else if (unit.requiredTechnology.length)
|
||||
unit.required = unit.requiredTechnology;
|
||||
}
|
||||
let template = loadTemplate(templateName);
|
||||
let unit = GetTemplateDataHelper(template, null, g_AuraData, g_ResourceData);
|
||||
|
||||
if (template.ProductionQueue)
|
||||
{
|
||||
@@ -54,17 +45,8 @@ function loadUnit(templateName)
|
||||
|
||||
function loadStructure(templateName)
|
||||
{
|
||||
var template = loadTemplate(templateName);
|
||||
var structure = GetTemplateDataHelper(template, null, g_AuraData, g_ResourceData);
|
||||
structure.phase = false;
|
||||
|
||||
if (structure.requiredTechnology)
|
||||
{
|
||||
if (depath(structure.requiredTechnology).slice(0, 5) == "phase")
|
||||
structure.phase = structure.requiredTechnology;
|
||||
else if (structure.requiredTechnology.length)
|
||||
structure.required = structure.requiredTechnology;
|
||||
}
|
||||
let template = loadTemplate(templateName);
|
||||
let structure = GetTemplateDataHelper(template, null, g_AuraData, g_ResourceData);
|
||||
|
||||
structure.production = {
|
||||
"technology": [],
|
||||
@@ -140,64 +122,12 @@ function loadStructure(templateName)
|
||||
|
||||
function loadTechnology(techName)
|
||||
{
|
||||
var template = loadTechData(techName);
|
||||
var tech = GetTechnologyDataHelper(template, g_SelectedCiv, g_ResourceData);
|
||||
tech.reqs = {};
|
||||
let template = loadTechData(techName);
|
||||
let tech = GetTechnologyDataHelper(template, g_SelectedCiv, g_ResourceData);
|
||||
|
||||
if (template.pair !== undefined)
|
||||
tech.pair = template.pair;
|
||||
|
||||
if (template.requirements !== undefined)
|
||||
{
|
||||
for (let op in template.requirements)
|
||||
{
|
||||
let val = template.requirements[op];
|
||||
let req = calcReqs(op, val);
|
||||
|
||||
switch (op)
|
||||
{
|
||||
case "tech":
|
||||
tech.reqs.generic = req;
|
||||
break;
|
||||
|
||||
case "civ":
|
||||
tech.reqs[req] = [];
|
||||
break;
|
||||
|
||||
case "any":
|
||||
if (req[0].length > 0)
|
||||
for (let r of req[0])
|
||||
{
|
||||
let v = req[0][r];
|
||||
if (typeof r == "number")
|
||||
tech.reqs[v] = [];
|
||||
else
|
||||
tech.reqs[r] = v;
|
||||
}
|
||||
if (req[1].length > 0)
|
||||
tech.reqs.generic = req[1];
|
||||
break;
|
||||
|
||||
case "all":
|
||||
if (!req[0].length)
|
||||
tech.reqs.generic = req[1];
|
||||
else
|
||||
for (let r of req[0])
|
||||
tech.reqs[r] = req[1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (template.supersedes !== undefined)
|
||||
{
|
||||
if (tech.reqs.generic !== undefined)
|
||||
tech.reqs.generic.push(template.supersedes);
|
||||
else
|
||||
for (let ck of Object.keys(tech.reqs))
|
||||
tech.reqs[ck].push(template.supersedes);
|
||||
}
|
||||
|
||||
return tech;
|
||||
}
|
||||
|
||||
@@ -219,76 +149,10 @@ function loadTechnologyPair(pairCode)
|
||||
|
||||
return {
|
||||
"techs": [ pairInfo.top, pairInfo.bottom ],
|
||||
"req": pairInfo.supersedes || ""
|
||||
"reqs": DeriveTechnologyRequirements(pairInfo, g_SelectedCiv)
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the prerequisite requirements of a technology.
|
||||
* Works recursively if needed.
|
||||
*
|
||||
* @param op The base operation. Can be "civ", "tech", "all" or "any".
|
||||
* @param val The value associated with the above operation.
|
||||
*
|
||||
* @return Sorted requirments.
|
||||
*/
|
||||
function calcReqs(op, val)
|
||||
{
|
||||
switch (op)
|
||||
{
|
||||
case "civ":
|
||||
case "class":
|
||||
case "notciv":
|
||||
case "number":
|
||||
// nothing needs doing
|
||||
break;
|
||||
|
||||
case "tech":
|
||||
if (depath(val).slice(0,4) === "pair")
|
||||
return loadTechnologyPair(val).techs;
|
||||
return [ val ];
|
||||
|
||||
case "all":
|
||||
case "any":
|
||||
let t = [];
|
||||
let c = [];
|
||||
for (let nv of val)
|
||||
{
|
||||
for (let o in nv)
|
||||
{
|
||||
let v = nv[o];
|
||||
let r = calcReqs(o, v);
|
||||
switch (o)
|
||||
{
|
||||
case "civ":
|
||||
case "notciv":
|
||||
c.push(r);
|
||||
break;
|
||||
|
||||
case "tech":
|
||||
t = t.concat(r);
|
||||
break;
|
||||
|
||||
case "any":
|
||||
c = c.concat(r[0]);
|
||||
t = t.concat(r[1]);
|
||||
break;
|
||||
|
||||
case "all":
|
||||
for (let ci in r[0])
|
||||
c[ci] = r[1];
|
||||
t = t;
|
||||
}
|
||||
}
|
||||
}
|
||||
return [ c, t ];
|
||||
|
||||
default:
|
||||
warn("Unknown reqs operator: "+op);
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unravel phases
|
||||
*
|
||||
@@ -304,28 +168,24 @@ function unravelPhases(techs)
|
||||
{
|
||||
let techdata = techs[techcode];
|
||||
|
||||
if (!("generic" in techdata.reqs) || techdata.reqs.generic.length < 2)
|
||||
if (!techdata.reqs || !techdata.reqs.length || !techdata.reqs[0].techs || techdata.reqs[0].techs.length < 2)
|
||||
continue;
|
||||
|
||||
let reqTech = techs[techcode].reqs.generic[1];
|
||||
let reqTech = techs[techcode].reqs[0].techs[1];
|
||||
|
||||
// Tech that can't be researched anywhere
|
||||
if (!(reqTech in techs))
|
||||
if (!techs[reqTech] || !techs[reqTech].reqs.length)
|
||||
continue;
|
||||
|
||||
if (!("generic" in techs[reqTech].reqs))
|
||||
continue;
|
||||
let reqPhase = techs[reqTech].reqs[0].techs[0];
|
||||
let myPhase = techs[techcode].reqs[0].techs[0];
|
||||
|
||||
let reqPhase = techs[reqTech].reqs.generic[0];
|
||||
let myPhase = techs[techcode].reqs.generic[0];
|
||||
|
||||
if (reqPhase == myPhase || depath(reqPhase).slice(0,5) !== "phase" || depath(myPhase).slice(0,5) !== "phase")
|
||||
if (reqPhase == myPhase || basename(reqPhase).slice(0,5) !== "phase" || basename(myPhase).slice(0,5) !== "phase")
|
||||
continue;
|
||||
|
||||
let reqPhasePos = phaseList.indexOf(reqPhase);
|
||||
let myPhasePos = phaseList.indexOf(myPhase);
|
||||
|
||||
if (phaseList.length === 0)
|
||||
if (!phaseList.length)
|
||||
phaseList = [reqPhase, myPhase];
|
||||
else if (reqPhasePos < 0 && myPhasePos > -1)
|
||||
phaseList.splice(myPhasePos, 0, reqPhase);
|
||||
|
||||
@@ -61,18 +61,15 @@ function selectCiv(civCode)
|
||||
"structures": [],
|
||||
"techs": []
|
||||
};
|
||||
g_ParsedData.techs[civCode] = {};
|
||||
|
||||
// get initial units
|
||||
var startStructs = [];
|
||||
for (let entity of g_CivData[civCode].StartEntities)
|
||||
{
|
||||
if (entity.Template.slice(0, 5) == "units")
|
||||
g_Lists.units.push(entity.Template);
|
||||
else if (entity.Template.slice(0, 6) == "struct")
|
||||
{
|
||||
g_Lists.structures.push(entity.Template);
|
||||
startStructs.push(entity.Template);
|
||||
}
|
||||
}
|
||||
|
||||
// Load units and structures
|
||||
@@ -95,44 +92,52 @@ function selectCiv(civCode)
|
||||
var techPairs = {};
|
||||
for (let techcode of g_Lists.techs)
|
||||
{
|
||||
let realcode = depath(techcode);
|
||||
let realcode = basename(techcode);
|
||||
|
||||
if (realcode.slice(0,4) == "pair" || realcode.indexOf("_pair") > -1)
|
||||
techPairs[techcode] = loadTechnologyPair(techcode);
|
||||
else if (realcode.slice(0,5) == "phase")
|
||||
g_ParsedData.phases[techcode] = loadPhase(techcode);
|
||||
else
|
||||
g_ParsedData.techs[techcode] = loadTechnology(techcode);
|
||||
g_ParsedData.techs[civCode][techcode] = loadTechnology(techcode);
|
||||
}
|
||||
|
||||
// Expand tech pairs
|
||||
for (let paircode in techPairs)
|
||||
{
|
||||
let pair = techPairs[paircode];
|
||||
if (pair.reqs === false)
|
||||
continue;
|
||||
|
||||
for (let techcode of pair.techs)
|
||||
{
|
||||
if (depath(techcode).slice(0, 5) === "phase")
|
||||
if (basename(techcode).slice(0, 5) === "phase")
|
||||
g_ParsedData.phases[techcode] = loadPhase(techcode);
|
||||
else
|
||||
{
|
||||
let newTech = loadTechnology(techcode);
|
||||
if (pair.req !== "")
|
||||
{
|
||||
if ("generic" in newTech.reqs)
|
||||
newTech.reqs.generic.concat(techPairs[pair.req].techs);
|
||||
else
|
||||
{
|
||||
for (let civkey of Object.keys(newTech.reqs))
|
||||
newTech.reqs[civkey].concat(techPairs[pair.req].techs);
|
||||
}
|
||||
}
|
||||
g_ParsedData.techs[techcode] = newTech;
|
||||
|
||||
if (!newTech.reqs)
|
||||
newTech.reqs = {};
|
||||
else if (newTech.reqs === false)
|
||||
continue;
|
||||
|
||||
for (let option of pair.reqs)
|
||||
for (let type in option)
|
||||
for (let opt in newTech.reqs)
|
||||
{
|
||||
if (!newTech.reqs[opt][type])
|
||||
newTech.reqs[opt][type] = [];
|
||||
newTech.reqs[opt][type] = newTech.reqs[opt][type].concat(option[type]);
|
||||
}
|
||||
|
||||
g_ParsedData.techs[civCode][techcode] = newTech;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Establish phase order
|
||||
g_ParsedData.phaseList = unravelPhases(g_ParsedData.techs);
|
||||
g_ParsedData.phaseList = unravelPhases(g_ParsedData.techs[civCode]);
|
||||
for (let phasecode of g_ParsedData.phaseList)
|
||||
{
|
||||
let phaseInfo = loadTechData(phasecode);
|
||||
@@ -170,6 +175,7 @@ function selectCiv(civCode)
|
||||
for (let structCode of g_Lists.structures)
|
||||
{
|
||||
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,
|
||||
@@ -181,43 +187,20 @@ function selectCiv(civCode)
|
||||
for (let prod of structInfo.production.technology)
|
||||
if (prod in techPairs)
|
||||
structInfo.production.technology.splice(
|
||||
structInfo.production.technology.indexOf(prod), 1,
|
||||
techPairs[prod].techs[0], techPairs[prod].techs[1]
|
||||
structInfo.production.technology.indexOf(prod),
|
||||
1, ...techPairs[prod].techs
|
||||
);
|
||||
|
||||
// Sort techs by phase
|
||||
let newProdTech = {};
|
||||
for (let prod of structInfo.production.technology)
|
||||
{
|
||||
let phase = "";
|
||||
let phase = GetPhaseOfTechnology(prod);
|
||||
if (phase === false)
|
||||
continue;
|
||||
|
||||
if (depath(prod).slice(0,5) === "phase")
|
||||
{
|
||||
phase = g_ParsedData.phaseList.indexOf(g_ParsedData.phases[prod].actualPhase);
|
||||
if (phase > 0)
|
||||
phase = g_ParsedData.phaseList[phase - 1];
|
||||
}
|
||||
else if (g_SelectedCiv in g_ParsedData.techs[prod].reqs)
|
||||
{
|
||||
for (let req of g_ParsedData.techs[prod].reqs[g_SelectedCiv])
|
||||
if (depath(req).slice(0,5) === "phase")
|
||||
phase = req;
|
||||
}
|
||||
else if ("generic" in g_ParsedData.techs[prod].reqs)
|
||||
{
|
||||
for (let req of g_ParsedData.techs[prod].reqs.generic)
|
||||
if (depath(req).slice(0,5) === "phase")
|
||||
phase = req;
|
||||
}
|
||||
|
||||
if (depath(phase).slice(0,5) !== "phase" ||
|
||||
g_ParsedData.phaseList.indexOf(phase) < structPhaseIdx)
|
||||
{
|
||||
if (structInfo.phase !== false)
|
||||
phase = structInfo.phase;
|
||||
else
|
||||
phase = g_ParsedData.phaseList[0];
|
||||
}
|
||||
if (g_ParsedData.phaseList.indexOf(phase) < structPhaseIdx)
|
||||
phase = structInfo.phase;
|
||||
|
||||
if (!(phase in newProdTech))
|
||||
newProdTech[phase] = [];
|
||||
@@ -225,46 +208,19 @@ function selectCiv(civCode)
|
||||
newProdTech[phase].push(prod);
|
||||
}
|
||||
|
||||
// Determine phase for units
|
||||
// Sort units by phase
|
||||
let newProdUnits = {};
|
||||
for (let prod of structInfo.production.units)
|
||||
{
|
||||
if (!g_ParsedData.units[prod])
|
||||
continue;
|
||||
|
||||
let unit = g_ParsedData.units[prod];
|
||||
let phase = "";
|
||||
let phase = GetPhaseOfTemplate(g_ParsedData.units[prod]);
|
||||
if (phase === false)
|
||||
continue;
|
||||
|
||||
if (unit.phase !== false)
|
||||
{
|
||||
if (g_ParsedData.phaseList.indexOf(unit.phase) < 0)
|
||||
phase = g_ParsedData.phases[unit.phase].actualPhase;
|
||||
else
|
||||
phase = unit.phase;
|
||||
}
|
||||
else if (unit.required !== undefined)
|
||||
{
|
||||
if (g_ParsedData.phases[unit.required])
|
||||
phase = g_ParsedData.phases[unit.required].actualPhase;
|
||||
else if (g_ParsedData.techs[unit.required])
|
||||
{
|
||||
let reqs = g_ParsedData.techs[unit.required].reqs;
|
||||
if (reqs[g_SelectedCiv])
|
||||
phase = reqs[g_SelectedCiv][0];
|
||||
else if (reqs.generic)
|
||||
phase = reqs.generic[0];
|
||||
else
|
||||
warn("Empty requirements found on technology " + unit.required);
|
||||
}
|
||||
else
|
||||
warn("Technology " + unit.required + " for " + prod + " not found.");
|
||||
}
|
||||
|
||||
if (depath(phase).slice(0,5) !== "phase" || g_ParsedData.phaseList.indexOf(phase) < structPhaseIdx)
|
||||
if (structInfo.phase !== false)
|
||||
phase = structInfo.phase;
|
||||
else
|
||||
phase = g_ParsedData.phaseList[0];
|
||||
if (g_ParsedData.phaseList.indexOf(phase) < structPhaseIdx)
|
||||
phase = structInfo.phase;
|
||||
|
||||
if (!(phase in newProdUnits))
|
||||
newProdUnits[phase] = [];
|
||||
@@ -279,20 +235,14 @@ function selectCiv(civCode)
|
||||
}
|
||||
|
||||
// Determine the buildList for the civ (grouped by phase)
|
||||
var buildList = {};
|
||||
var trainerList = [];
|
||||
let buildList = {};
|
||||
let trainerList = [];
|
||||
for (let pha of g_ParsedData.phaseList)
|
||||
buildList[pha] = [];
|
||||
for (let structCode of g_Lists.structures)
|
||||
{
|
||||
if (!g_ParsedData.structures[structCode].phase || startStructs.indexOf(structCode) > -1)
|
||||
g_ParsedData.structures[structCode].phase = g_ParsedData.phaseList[0];
|
||||
|
||||
let myPhase = g_ParsedData.structures[structCode].phase;
|
||||
if (g_ParsedData.phaseList.indexOf(myPhase) === -1)
|
||||
myPhase = g_ParsedData.phases[myPhase].actualPhase;
|
||||
|
||||
buildList[myPhase].push(structCode);
|
||||
let phase = g_ParsedData.structures[structCode].phase;
|
||||
buildList[phase].push(structCode);
|
||||
}
|
||||
for (let unitCode of g_Lists.units)
|
||||
if (g_ParsedData.units[unitCode] && g_ParsedData.units[unitCode].production)
|
||||
|
||||
@@ -36,7 +36,7 @@ m.GameState.prototype.init = function(SharedScript, state, player) {
|
||||
let k = techs.indexOf(this.phases[i].name);
|
||||
if (k !== -1)
|
||||
{
|
||||
this.phases[i].requirements = (this.getTemplate(techs[k]))._template.requirements;
|
||||
this.phases[i].requirements = DeriveTechnologyRequirements(this.getTemplate(techs[k])._template, this.getPlayerCiv());
|
||||
continue;
|
||||
}
|
||||
for (let tech of techs)
|
||||
@@ -45,7 +45,7 @@ m.GameState.prototype.init = function(SharedScript, state, player) {
|
||||
if (template.replaces && template.replaces.indexOf(this.phases[i].name) != -1)
|
||||
{
|
||||
this.phases[i].name = tech;
|
||||
this.phases[i].requirements = template.requirements;
|
||||
this.phases[i].requirements = DeriveTechnologyRequirements(template, this.getPlayerCiv());
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -169,21 +169,24 @@ m.GameState.prototype.cityPhase = function()
|
||||
return this.phases[2].name;
|
||||
};
|
||||
|
||||
m.GameState.prototype.getPhaseRequirements = function(i)
|
||||
m.GameState.prototype.getPhaseEntityRequirements = function(i)
|
||||
{
|
||||
if (!this.phases[i-1].requirements)
|
||||
return undefined;
|
||||
let requirements = this.phases[i-1].requirements;
|
||||
if (requirements.number)
|
||||
return requirements;
|
||||
else if (requirements.all)
|
||||
let entityReqs = [];
|
||||
|
||||
for (let requirement of this.phases[i-1].requirements)
|
||||
{
|
||||
for (let req of requirements.all)
|
||||
if (req.number)
|
||||
return req;
|
||||
if (!requirement.entities)
|
||||
continue;
|
||||
for (let entity of requirement.entities)
|
||||
if (entity.check == "count")
|
||||
entityReqs.push({
|
||||
"class": entity.class,
|
||||
"count": entity.number
|
||||
});
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
return entityReqs;
|
||||
}
|
||||
|
||||
m.GameState.prototype.isResearched = function(template)
|
||||
{
|
||||
@@ -213,14 +216,6 @@ m.GameState.prototype.canResearch = function(techTemplateName, noRequirementChec
|
||||
if (noRequirementCheck === true)
|
||||
return true;
|
||||
|
||||
// not already researched, check if we can.
|
||||
// basically a copy of the function in technologyManager since we can't use it.
|
||||
// Checks the requirements for a technology to see if it can be researched at the current time
|
||||
|
||||
// The technology which this technology supersedes is required
|
||||
if (template.supersedes() && !this.playerData.researchedTechs[template.supersedes()])
|
||||
return false;
|
||||
|
||||
// if this is a pair, we must check that the pair tech is not being researched
|
||||
if (template.pair())
|
||||
{
|
||||
@@ -231,39 +226,52 @@ m.GameState.prototype.canResearch = function(techTemplateName, noRequirementChec
|
||||
return false;
|
||||
}
|
||||
|
||||
return this.checkTechRequirements(template.requirements());
|
||||
return this.checkTechRequirements(template.requirements(this.playerData.civ));
|
||||
};
|
||||
|
||||
/**
|
||||
* Private function for checking a set of requirements is met
|
||||
* basically copies TechnologyManager
|
||||
* Private function for checking a set of requirements is met.
|
||||
* Basically copies TechnologyManager, but compares against
|
||||
* variables only available within the AI
|
||||
*/
|
||||
m.GameState.prototype.checkTechRequirements = function(reqs)
|
||||
{
|
||||
// If there are no requirements then all requirements are met
|
||||
if (!reqs)
|
||||
return false;
|
||||
|
||||
if (!reqs.length)
|
||||
return true;
|
||||
|
||||
if (reqs.all)
|
||||
return reqs.all.every(r => this.checkTechRequirements(r));
|
||||
if (reqs.any)
|
||||
return reqs.any.some(r => this.checkTechRequirements(r));
|
||||
if (reqs.civ)
|
||||
return this.playerData.civ == reqs.civ;
|
||||
if (reqs.notciv)
|
||||
return this.playerData.civ != reqs.notciv;
|
||||
if (reqs.tech)
|
||||
return this.playerData.researchedTechs[reqs.tech] !== undefined && this.playerData.researchedTechs[reqs.tech];
|
||||
if (reqs.class && reqs.numberOfTypes)
|
||||
return this.playerData.typeCountsByClass[reqs.class] &&
|
||||
Object.keys(this.playerData.typeCountsByClass[reqs.class]).length >= reqs.numberOfTypes;
|
||||
if (reqs.class && reqs.number)
|
||||
return this.playerData.classCounts[reqs.class] &&
|
||||
this.playerData.classCounts[reqs.class] >= reqs.number;
|
||||
function doesEntitySpecPass(entity)
|
||||
{
|
||||
switch (entity.check)
|
||||
{
|
||||
case "count":
|
||||
if (!this.playerData.classCounts[entity.class] || this.playerData.classCounts[entity.class] < entity.number)
|
||||
return false;
|
||||
break;
|
||||
|
||||
// The technologies requirements are not a recognised format
|
||||
error("Bad requirements " + uneval(reqs));
|
||||
return false;
|
||||
case "variants":
|
||||
if (!this.playerData.typeCountsByClass[entity.class] || Object.keys(this.playerData.typeCountsByClass[entity.class]).length < entity.number)
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
return reqs.some(req => {
|
||||
return Object.keys(req).every(type => {
|
||||
switch (type)
|
||||
{
|
||||
case "techs":
|
||||
return req[type].every(tech => !!this.playerData.researchedTechs[tech]);
|
||||
|
||||
case "entities":
|
||||
return req[type].every(doesEntitySpecPass, this);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
m.GameState.prototype.getMap = function()
|
||||
|
||||
@@ -97,11 +97,9 @@ m.Technology.prototype.researchTime = function()
|
||||
return this._template.researchTime;
|
||||
};
|
||||
|
||||
m.Technology.prototype.requirements = function()
|
||||
m.Technology.prototype.requirements = function(civ)
|
||||
{
|
||||
if (!this._template.requirements)
|
||||
return undefined;
|
||||
return this._template.requirements;
|
||||
return DeriveTechnologyRequirements(this._template, civ);
|
||||
};
|
||||
|
||||
m.Technology.prototype.autoResearch = function()
|
||||
|
||||
@@ -1274,11 +1274,12 @@ m.HQ.prototype.buildTemple = function(gameState, queues)
|
||||
{
|
||||
if (gameState.currentPhase() < 2 || this.econState !== "cityPhasing")
|
||||
return;
|
||||
let requirements = gameState.getPhaseRequirements(3);
|
||||
if (!requirements || !requirements.number)
|
||||
return;
|
||||
if (gameState.getOwnStructures().filter(API3.Filters.byClass(requirements["class"])).length >= requirements.number)
|
||||
let requirements = gameState.getPhaseEntityRequirements(3);
|
||||
if (!requirements.length)
|
||||
return;
|
||||
for (let entityReq of requirements)
|
||||
if (gameState.getOwnStructures().filter(API3.Filters.byClass(entityReq.class)).length >= entityReq.count)
|
||||
return;
|
||||
}
|
||||
if (!this.canBuild(gameState, "structures/{civ}_temple"))
|
||||
return;
|
||||
@@ -1388,7 +1389,7 @@ m.HQ.prototype.manageCorral = function(gameState, queues)
|
||||
* build more houses if needed.
|
||||
* kinda ugly, lots of special cases to both build enough houses but not tooo many…
|
||||
*/
|
||||
m.HQ.prototype.buildMoreHouses = function(gameState,queues)
|
||||
m.HQ.prototype.buildMoreHouses = function(gameState, queues)
|
||||
{
|
||||
if (gameState.getPopulationMax() <= gameState.getPopulationLimit())
|
||||
return;
|
||||
@@ -1418,35 +1419,45 @@ m.HQ.prototype.buildMoreHouses = function(gameState,queues)
|
||||
queues.house.addPlan(plan);
|
||||
}
|
||||
|
||||
if (numPlanned > 0 && this.econState == "townPhasing" && gameState.getPhaseRequirements(2))
|
||||
if (numPlanned > 0 && this.econState == "townPhasing" && gameState.getPhaseEntityRequirements(2).length)
|
||||
{
|
||||
let requirements = gameState.getPhaseRequirements(2);
|
||||
let count = gameState.getOwnStructures().filter(API3.Filters.byClass(requirements["class"])).length;
|
||||
if (requirements && count < requirements.number && this.stopBuilding.has(gameState.applyCiv("structures/{civ}_house")))
|
||||
let houseTemplateName = gameState.applyCiv("structures/{civ}_house");
|
||||
let houseTemplate = gameState.getTemplate(houseTemplateName);
|
||||
|
||||
let needed = 0;
|
||||
for (let entityReq of gameState.getPhaseEntityRequirements(2))
|
||||
{
|
||||
if (this.Config.debug > 1)
|
||||
API3.warn("no room to place a house ... try to be less restrictive");
|
||||
this.stopBuilding.delete(gameState.applyCiv("structures/{civ}_house"));
|
||||
this.requireHouses = true;
|
||||
if (!houseTemplate.hasClass(entityReq.class))
|
||||
continue;
|
||||
|
||||
let count = gameState.getOwnStructures().filter(API3.Filters.byClass(entityReq.class)).length;
|
||||
if (count < entityReq.count && this.stopBuilding.has(houseTemplateName))
|
||||
{
|
||||
if (this.Config.debug > 1)
|
||||
API3.warn("no room to place a house ... try to be less restrictive");
|
||||
this.stopBuilding.delete(houseTemplateName);
|
||||
this.requireHouses = true;
|
||||
}
|
||||
needed = Math.max(needed, entityReq.count - count);
|
||||
}
|
||||
|
||||
let houseQueue = queues.house.plans;
|
||||
for (let i = 0; i < numPlanned; ++i)
|
||||
{
|
||||
if (houseQueue[i].isGo(gameState))
|
||||
++count;
|
||||
else if (count < requirements.number)
|
||||
--needed;
|
||||
else if (needed > 0)
|
||||
{
|
||||
houseQueue[i].isGo = function () { return true; };
|
||||
++count;
|
||||
--needed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.requireHouses)
|
||||
{
|
||||
let requirements = gameState.getPhaseRequirements(2);
|
||||
if (gameState.getOwnStructures().filter(API3.Filters.byClass(requirements["class"])).length >= requirements.number)
|
||||
this.requireHouses = undefined;
|
||||
let houseTemplate = gameState.getTemplate(gameState.applyCiv("structures/{civ}_house"));
|
||||
if (gameState.getPhaseEntityRequirements(2).every(req =>
|
||||
!houseTemplate.hasClass(req.class) || gameState.getOwnStructures().filter(API3.Filters.byClass(req.class)).length >= req.count))
|
||||
this.requireHouses = undefined;
|
||||
}
|
||||
|
||||
// When population limit too tight
|
||||
|
||||
@@ -658,19 +658,19 @@ GuiInterface.prototype.GetTemplateData = function(player, extendedName)
|
||||
return GetTemplateDataHelper(template, player, aurasTemplate, Resources);
|
||||
};
|
||||
|
||||
GuiInterface.prototype.GetTechnologyData = function(player, name)
|
||||
GuiInterface.prototype.GetTechnologyData = function(player, data)
|
||||
{
|
||||
let cmpDataTemplateManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_DataTemplateManager);
|
||||
let template = cmpDataTemplateManager.GetTechnologyTemplate(name);
|
||||
let template = cmpDataTemplateManager.GetTechnologyTemplate(data.name);
|
||||
|
||||
if (!template)
|
||||
{
|
||||
warn("Tried to get data for invalid technology: " + name);
|
||||
warn("Tried to get data for invalid technology: " + data.name);
|
||||
return null;
|
||||
}
|
||||
|
||||
let cmpPlayer = QueryPlayerIDInterface(player, IID_Player);
|
||||
return GetTechnologyDataHelper(template, cmpPlayer.GetCiv(), Resources);
|
||||
return GetTechnologyDataHelper(template, data.civ || cmpPlayer.GetCiv(), Resources);
|
||||
};
|
||||
|
||||
GuiInterface.prototype.IsTechnologyResearched = function(player, data)
|
||||
|
||||
@@ -162,7 +162,7 @@ ProductionQueue.prototype.GetTechnologiesList = function()
|
||||
|
||||
// Remove any technologies that can't be researched by this civ
|
||||
techs = techs.filter(tech => {
|
||||
let reqs = cmpTechnologyManager.GetTechnologyTemplate(tech).requirements || null;
|
||||
let reqs = DeriveTechnologyRequirements(cmpTechnologyManager.GetTechnologyTemplate(tech), cmpPlayer.GetCiv());
|
||||
return cmpTechnologyManager.CheckTechnologyRequirements(reqs, true);
|
||||
});
|
||||
|
||||
|
||||
@@ -97,17 +97,14 @@ TechnologyManager.prototype.IsTechnologyResearched = function(tech)
|
||||
// Checks the requirements for a technology to see if it can be researched at the current time
|
||||
TechnologyManager.prototype.CanResearch = function(tech)
|
||||
{
|
||||
var template = this.GetTechnologyTemplate(tech);
|
||||
let template = this.GetTechnologyTemplate(tech);
|
||||
|
||||
if (!template)
|
||||
{
|
||||
warn("Technology \"" + tech + "\" does not exist");
|
||||
return false;
|
||||
}
|
||||
|
||||
// The technology which this technology supersedes is required
|
||||
if (template.supersedes && !this.IsTechnologyResearched(template.supersedes))
|
||||
return false;
|
||||
|
||||
if (template.top && this.IsInProgress(template.top) ||
|
||||
template.bottom && this.IsInProgress(template.bottom))
|
||||
return false;
|
||||
@@ -121,50 +118,56 @@ TechnologyManager.prototype.CanResearch = function(tech)
|
||||
if (this.IsTechnologyResearched(tech))
|
||||
return false;
|
||||
|
||||
return this.CheckTechnologyRequirements(template.requirements || null);
|
||||
return this.CheckTechnologyRequirements(DeriveTechnologyRequirements(template, Engine.QueryInterface(this.entity, IID_Player).GetCiv()));
|
||||
};
|
||||
|
||||
/**
|
||||
* Private function for checking a set of requirements is met
|
||||
* @param reqs Object of technology requirements as given by the technology template
|
||||
* @param civonly A boolean set to true if only the civ requirement is checked
|
||||
* @param {object} reqs - Technology requirements as derived from the technology template by globalscripts
|
||||
* @param {boolean} civonly - True if only the civ requirement is to be checked
|
||||
*
|
||||
* @return true if the requirements are checked
|
||||
* false otherwise
|
||||
* @return true if the requirements pass, false otherwise
|
||||
*/
|
||||
TechnologyManager.prototype.CheckTechnologyRequirements = function(reqs, civonly)
|
||||
TechnologyManager.prototype.CheckTechnologyRequirements = function(reqs, civonly = false)
|
||||
{
|
||||
// If there are no requirements then all requirements are met
|
||||
let cmpPlayer = Engine.QueryInterface(this.entity, IID_Player);
|
||||
|
||||
if (!reqs)
|
||||
return false;
|
||||
|
||||
if (civonly || !reqs.length)
|
||||
return true;
|
||||
|
||||
if (reqs.all)
|
||||
return reqs.all.every(r => this.CheckTechnologyRequirements(r, civonly));
|
||||
if (reqs.any)
|
||||
return reqs.any.some(r => this.CheckTechnologyRequirements(r, civonly));
|
||||
if (reqs.civ)
|
||||
return reqs.some(req => {
|
||||
return Object.keys(req).every(type => {
|
||||
switch (type)
|
||||
{
|
||||
case "techs":
|
||||
return req[type].every(this.IsTechnologyResearched, this);
|
||||
|
||||
case "entities":
|
||||
return req[type].every(this.DoesEntitySpecPass, this);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
TechnologyManager.prototype.DoesEntitySpecPass = function(entity)
|
||||
{
|
||||
switch (entity.check)
|
||||
{
|
||||
let cmpPlayer = Engine.QueryInterface(this.entity, IID_Player);
|
||||
return cmpPlayer && cmpPlayer.GetCiv() == reqs.civ;
|
||||
case "count":
|
||||
if (!this.classCounts[entity.class] || this.classCounts[entity.class] < entity.number)
|
||||
return false;
|
||||
break;
|
||||
|
||||
case "variants":
|
||||
if (!this.typeCountsByClass[entity.class] || Object.keys(this.typeCountsByClass[entity.class]).length < entity.number)
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
if (reqs.notciv)
|
||||
{
|
||||
let cmpPlayer = Engine.QueryInterface(this.entity, IID_Player);
|
||||
return cmpPlayer && cmpPlayer.GetCiv() != reqs.notciv;
|
||||
}
|
||||
if (civonly)
|
||||
return true;
|
||||
if (reqs.tech)
|
||||
return this.IsTechnologyResearched(reqs.tech);
|
||||
if (reqs.class && reqs.numberOfTypes)
|
||||
return this.typeCountsByClass[reqs.class] &&
|
||||
Object.keys(this.typeCountsByClass[reqs.class]).length >= reqs.numberOfTypes;
|
||||
if (reqs.class && reqs.number)
|
||||
return this.classCounts[reqs.class] &&
|
||||
this.classCounts[reqs.class] >= reqs.number;
|
||||
// The technologies requirements are not a recognised format
|
||||
error("Bad requirements " + uneval(reqs));
|
||||
return false;
|
||||
return true;
|
||||
};
|
||||
|
||||
TechnologyManager.prototype.OnGlobalOwnershipChanged = function(msg)
|
||||
|
||||
@@ -0,0 +1,510 @@
|
||||
// TODO: Move this to a folder of tests for GlobalScripts (once one is created)
|
||||
|
||||
// No requirements set in template
|
||||
let template = {};
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), []);
|
||||
|
||||
/**
|
||||
* First, the basics:
|
||||
*/
|
||||
|
||||
// Technology Requirement
|
||||
template.requirements = { "tech": "expected_tech" };
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [{ "techs": ["expected_tech"] }]);
|
||||
|
||||
// Entity Requirement: Count of entities matching given class
|
||||
template.requirements = { "entity": { "class": "Village", "number": 5 } };
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [{ "entities": [{ "class": "Village", "number": 5, "check": "count" }] }]);
|
||||
|
||||
// Entity Requirement: Count of entities matching given class
|
||||
template.requirements = { "entity": { "class": "Village", "numberOfTypes": 5 } };
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [{ "entities": [{ "class": "Village", "number": 5, "check": "variants" }] }]);
|
||||
|
||||
// Single `civ`
|
||||
template.requirements = { "civ": "athen" };
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), []);
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "spart"), false);
|
||||
|
||||
// Single `notciv`
|
||||
template.requirements = { "notciv": "athen" };
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), false);
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "spart"), []);
|
||||
|
||||
|
||||
/**
|
||||
* Basic `all`s:
|
||||
*/
|
||||
|
||||
// Multiple techs
|
||||
template.requirements = { "all": [{ "tech": "tech_A" }, { "tech": "tech_B" }, { "tech": "tech_C" }] };
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [{ "techs": ["tech_A", "tech_B", "tech_C"] }]);
|
||||
|
||||
// Multiple entity definitions
|
||||
template.requirements = {
|
||||
"all": [
|
||||
{ "entity": { "class": "class_A", "number": 5 } },
|
||||
{ "entity": { "class": "class_B", "number": 5 } },
|
||||
{ "entity": { "class": "class_C", "number": 5 } }
|
||||
]
|
||||
};
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"),
|
||||
[{ "entities": [{ "class": "class_A", "number": 5, "check": "count" }, { "class": "class_B", "number": 5, "check": "count" }, { "class": "class_C", "number": 5, "check": "count" }] }]);
|
||||
|
||||
// A `tech` and an `entity`
|
||||
template.requirements = { "all": [{ "tech": "tech_A" }, { "entity": { "class": "class_B", "number": 5, "check": "count" } }] };
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [{ "techs": ["tech_A"], "entities": [{ "class": "class_B", "number": 5, "check": "count" }] }]);
|
||||
|
||||
// Multiple `civ`s
|
||||
template.requirements = { "all": [{ "civ": "civ_A"}, { "civ": "civ_B"}, { "civ": "civ_C"}] };
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civ_A"), []);
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civ_B"), []);
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civ_C"), []);
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civ_D"), false);
|
||||
|
||||
// Multiple `notciv`s
|
||||
template.requirements = { "all": [{ "notciv": "civ_A"}, { "notciv": "civ_B"}, { "notciv": "civ_C"}] };
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civ_A"), false);
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civ_B"), false);
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civ_C"), false);
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civ_D"), []);
|
||||
|
||||
// A `civ` with a tech/entity
|
||||
template.requirements = { "all": [{ "civ": "athen" }, { "tech": "expected_tech" }] };
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [{ "techs": ["expected_tech"] }]);
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "spart"), false);
|
||||
|
||||
template.requirements = { "all": [{ "civ": "athen" }, { "entity": { "class": "Village", "number": 5 } }] };
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [{ "entities": [{ "class": "Village", "number": 5, "check": "count" }] }]);
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "spart"), false);
|
||||
|
||||
template.requirements = { "all": [{ "civ": "athen" }, { "entity": { "class": "Village", "numberOfTypes": 5 } }] };
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [{ "entities": [{ "class": "Village", "number": 5, "check": "variants" }] }]);
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "spart"), false);
|
||||
|
||||
// A `notciv` with a tech/entity
|
||||
template.requirements = { "all": [{ "notciv": "athen" }, { "tech": "expected_tech" }] };
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), false);
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "spart"), [{ "techs": ["expected_tech"] }]);
|
||||
|
||||
template.requirements = { "all": [{ "notciv": "athen" }, { "entity": { "class": "Village", "number": 5 } }] };
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), false);
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "spart"), [{ "entities": [{ "class": "Village", "number": 5, "check": "count" }] }]);
|
||||
|
||||
template.requirements = { "all": [{ "notciv": "athen" }, { "entity": { "class": "Village", "numberOfTypes": 5 } }] };
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), false);
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "spart"), [{ "entities": [{ "class": "Village", "number": 5, "check": "variants" }] }]);
|
||||
|
||||
|
||||
/**
|
||||
* Basic `any`s:
|
||||
*/
|
||||
|
||||
// Multiple techs
|
||||
template.requirements = { "any": [{ "tech": "tech_A" }, { "tech": "tech_B" }, { "tech": "tech_C" }] };
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [{ "techs": ["tech_A"] }, { "techs": ["tech_B"] }, { "techs": ["tech_C"] }]);
|
||||
|
||||
// Multiple entity definitions
|
||||
template.requirements = {
|
||||
"any": [
|
||||
{ "entity": { "class": "class_A", "number": 5 } },
|
||||
{ "entity": { "class": "class_B", "number": 5 } },
|
||||
{ "entity": { "class": "class_C", "number": 5 } }
|
||||
]
|
||||
};
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [
|
||||
{ "entities": [{ "class": "class_A", "number": 5, "check": "count" }] },
|
||||
{ "entities": [{ "class": "class_B", "number": 5, "check": "count" }] },
|
||||
{ "entities": [{ "class": "class_C", "number": 5, "check": "count" }] }
|
||||
]);
|
||||
|
||||
// A tech or an entity
|
||||
template.requirements = { "any": [{ "tech": "tech_A" }, { "entity": { "class": "class_B", "number": 5, "check": "count" } }] };
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [{ "techs": ["tech_A"] }, { "entities": [{ "class": "class_B", "number": 5, "check": "count" }] }]);
|
||||
|
||||
// Multiple `civ`s
|
||||
template.requirements = { "any": [{ "civ": "civ_A" }, { "civ": "civ_B" }, { "civ": "civ_C" }] };
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civ_A"), []);
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civ_B"), []);
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civ_C"), []);
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civ_D"), false);
|
||||
|
||||
// Multiple `notciv`s
|
||||
template.requirements = { "any": [{ "notciv": "civ_A" }, { "notciv": "civ_B" }, { "notciv": "civ_C" }] };
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civ_A"), false);
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civ_B"), false);
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civ_C"), false);
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civ_D"), []);
|
||||
|
||||
// A `civ` or a tech/entity
|
||||
template.requirements = { "any": [{ "civ": "athen" }, { "tech": "expected_tech" }] };
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), []);
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "spart"), [{ "techs": ["expected_tech"] }]);
|
||||
|
||||
template.requirements = { "any": [{ "civ": "athen" }, { "entity": { "class": "Village", "number": 5 } }] };
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), []);
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "spart"), [{ "entities": [{ "class": "Village", "number": 5, "check": "count" }] }]);
|
||||
|
||||
template.requirements = { "any": [{ "civ": "athen" }, { "entity": { "class": "Village", "numberOfTypes": 5 } }] };
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), []);
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "spart"), [{ "entities": [{ "class": "Village", "number": 5, "check": "variants" }] }]);
|
||||
|
||||
// A `notciv` or a tech
|
||||
template.requirements = { "any": [{ "notciv": "athen" }, { "tech": "expected_tech" }] };
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), false);
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "spart"), [{ "techs": ["expected_tech"] }]);
|
||||
|
||||
template.requirements = { "any": [{ "notciv": "athen" }, { "entity": { "class": "Village", "number": 5 } }] };
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), false);
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "spart"), [{ "entities": [{ "class": "Village", "number": 5, "check": "count" }] }]);
|
||||
|
||||
template.requirements = { "any": [{ "notciv": "athen" }, { "entity": { "class": "Village", "numberOfTypes": 5 } }] };
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), false);
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "spart"), [{ "entities": [{ "class": "Village", "number": 5, "check": "variants" }] }]);
|
||||
|
||||
|
||||
/**
|
||||
* Complicated `all`s, part 1 - an `all` inside an `all`:
|
||||
*/
|
||||
|
||||
// Techs
|
||||
template.requirements = {
|
||||
"all": [
|
||||
{ "all": [{ "tech": "tech_A" }, { "tech": "tech_B" }] },
|
||||
{ "all": [{ "tech": "tech_C" }, { "tech": "tech_D" }] }
|
||||
]
|
||||
};
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [{ "techs": ["tech_A", "tech_B", "tech_C", "tech_D"] }]);
|
||||
|
||||
// Techs and entities
|
||||
template.requirements = {
|
||||
"all": [
|
||||
{ "all": [{ "tech": "tech_A" }, { "entity": { "class": "class_A", "number": 5 } }] },
|
||||
{ "all": [{ "entity": { "class": "class_B", "numberOfTypes": 5 } }, { "tech": "tech_B" }] }
|
||||
]
|
||||
};
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [{
|
||||
"techs": ["tech_A", "tech_B"],
|
||||
"entities": [{ "class": "class_A", "number": 5, "check": "count" }, { "class": "class_B", "number": 5, "check": "variants" }]
|
||||
}]);
|
||||
|
||||
// Two `civ`s, without and with a tech
|
||||
template.requirements = {
|
||||
"all": [
|
||||
{ "all": [{ "civ": "athen" }, { "civ": "spart" }] }
|
||||
]
|
||||
};
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), []);
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "maur"), false);
|
||||
|
||||
template.requirements = {
|
||||
"all": [
|
||||
{ "tech": "required_tech" },
|
||||
{ "all": [{ "civ": "athen" }, { "civ": "spart" }] }
|
||||
]
|
||||
};
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [{ "techs": ["required_tech"] }]);
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "maur"), false);
|
||||
|
||||
// Two `notciv`s, without and with a tech
|
||||
template.requirements = {
|
||||
"all": [
|
||||
{ "all": [{ "notciv": "athen" }, { "notciv": "spart" }] }
|
||||
]
|
||||
};
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), false);
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "maur"), []);
|
||||
|
||||
template.requirements = {
|
||||
"all": [
|
||||
{ "tech": "required_tech" },
|
||||
{ "all": [{ "notciv": "athen" }, { "notciv": "spart" }] }
|
||||
]
|
||||
};
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), false);
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "maur"), [{ "techs": ["required_tech"] }]);
|
||||
|
||||
// Inner `all` has a tech and a `civ`/`notciv`
|
||||
template.requirements = {
|
||||
"all": [
|
||||
{ "all": [{ "tech": "tech_A" }, { "civ": "maur" }] },
|
||||
{ "tech": "tech_B" }
|
||||
]
|
||||
};
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [{ "techs": ["tech_B"] }]);
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "maur"), [{ "techs": ["tech_A", "tech_B"] }]);
|
||||
|
||||
template.requirements = {
|
||||
"all": [
|
||||
{ "all": [{ "tech": "tech_A" }, { "notciv": "maur" }] },
|
||||
{ "tech": "tech_B" }
|
||||
]
|
||||
}
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [{ "techs": ["tech_A", "tech_B"] }]);
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "maur"), [{ "techs": ["tech_B"] }]);
|
||||
|
||||
|
||||
/**
|
||||
* Complicated `all`s, part 2 - an `any` inside an `all`:
|
||||
*/
|
||||
|
||||
// Techs
|
||||
template.requirements = {
|
||||
"all": [
|
||||
{ "any": [{ "tech": "tech_A" }, { "tech": "tech_B" }] },
|
||||
{ "any": [{ "tech": "tech_C" }, { "tech": "tech_D" }] }
|
||||
]
|
||||
};
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [
|
||||
{ "techs": ["tech_A", "tech_C"] },
|
||||
{ "techs": ["tech_A", "tech_D"] },
|
||||
{ "techs": ["tech_B", "tech_C"] },
|
||||
{ "techs": ["tech_B", "tech_D"] }
|
||||
]);
|
||||
|
||||
// Techs and entities
|
||||
template.requirements = {
|
||||
"all": [
|
||||
{ "any": [{ "tech": "tech_A" }, { "entity": { "class": "class_A", "number": 5 } }] },
|
||||
{ "any": [{ "entity": { "class": "class_B", "numberOfTypes": 5 } }, { "tech": "tech_B" }] }
|
||||
]
|
||||
};
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [
|
||||
{ "techs": ["tech_A"], "entities": [{ "class": "class_B", "number": 5, "check": "variants" }] },
|
||||
{ "techs": ["tech_A", "tech_B"] },
|
||||
{ "entities": [{ "class": "class_A", "number": 5, "check": "count" }, { "class": "class_B", "number": 5, "check": "variants" }] },
|
||||
{ "entities": [{ "class": "class_A", "number": 5, "check": "count" }], "techs": ["tech_B"] }
|
||||
]);
|
||||
|
||||
// Two `civ`s, without and with a tech
|
||||
template.requirements = {
|
||||
"all": [
|
||||
{ "any": [{ "civ": "athen" }, { "civ": "spart" }] }
|
||||
]
|
||||
};
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), []);
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "maur"), false);
|
||||
|
||||
template.requirements = {
|
||||
"all": [
|
||||
{ "tech": "required_tech" },
|
||||
{ "any": [{ "civ": "athen" }, { "civ": "spart" }] }
|
||||
]
|
||||
};
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [{ "techs": ["required_tech"] }]);
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "maur"), false);
|
||||
|
||||
// Two `notciv`s, without and with a tech
|
||||
template.requirements = {
|
||||
"all": [
|
||||
{ "any": [{ "notciv": "athen" }, { "notciv": "spart" }] }
|
||||
]
|
||||
};
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), false);
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "maur"), []);
|
||||
|
||||
template.requirements = {
|
||||
"all": [
|
||||
{ "tech": "required_tech" },
|
||||
{ "any": [{ "notciv": "athen" }, { "notciv": "spart" }] }
|
||||
]
|
||||
};
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), false);
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "maur"), [{ "techs": ["required_tech"] }]);
|
||||
|
||||
|
||||
/**
|
||||
* Complicated `any`s, part 1 - an `all` inside an `any`:
|
||||
*/
|
||||
|
||||
// Techs
|
||||
template.requirements = {
|
||||
"any": [
|
||||
{ "all": [{ "tech": "tech_A" }, { "tech": "tech_B" }] },
|
||||
{ "all": [{ "tech": "tech_C" }, { "tech": "tech_D" }] }
|
||||
]
|
||||
};
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [
|
||||
{ "techs": ["tech_A", "tech_B"] },
|
||||
{ "techs": ["tech_C", "tech_D"] }
|
||||
]);
|
||||
|
||||
// Techs and entities
|
||||
template.requirements = {
|
||||
"any": [
|
||||
{ "all": [{ "tech": "tech_A" }, { "entity": { "class": "class_A", "number": 5 } }] },
|
||||
{ "all": [{ "entity": { "class": "class_B", "numberOfTypes": 5 } }, { "tech": "tech_B" }] }
|
||||
]
|
||||
};
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [
|
||||
{ "techs": ["tech_A"], "entities": [{ "class": "class_A", "number": 5, "check": "count" }] },
|
||||
{ "entities": [{ "class": "class_B", "number": 5, "check": "variants" }], "techs": ["tech_B"] }
|
||||
]);
|
||||
|
||||
// Two `civ`s, without and with a tech
|
||||
template.requirements = {
|
||||
"any": [
|
||||
{ "all": [{ "civ": "athen" }, { "civ": "spart" }] }
|
||||
]
|
||||
};
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), []);
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "maur"), false);
|
||||
|
||||
template.requirements = {
|
||||
"any": [
|
||||
{ "tech": "required_tech" },
|
||||
{ "all": [{ "civ": "athen" }, { "civ": "spart" }] }
|
||||
]
|
||||
};
|
||||
// Note: these requirements don't really make sense, as the `any` makes the `civ`s in the the inner `all` irrelevant.
|
||||
// We test it anyway as a precursor to later tests.
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [{ "techs": ["required_tech"] }]);
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "maur"), [{ "techs": ["required_tech"] }]);
|
||||
|
||||
// Two `notciv`s, without and with a tech
|
||||
template.requirements = {
|
||||
"any": [
|
||||
{ "all": [{ "notciv": "athen" }, { "notciv": "spart" }] }
|
||||
]
|
||||
};
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), false);
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "maur"), []);
|
||||
|
||||
template.requirements = {
|
||||
"any": [
|
||||
{ "tech": "required_tech" },
|
||||
{ "all": [{ "notciv": "athen" }, { "notciv": "spart" }] }
|
||||
]
|
||||
};
|
||||
// Note: these requirements have a result that might seen unexpected at first glance.
|
||||
// This is because the `notciv`s are rendered irrelevant by the `any`, and they have nothing else to operate on.
|
||||
// We test it anyway as a precursor for later tests.
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [{ "techs": ["required_tech"] }]);
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "maur"), [{ "techs": ["required_tech"] }]);
|
||||
|
||||
// Inner `all` has a tech and a `civ`/`notciv`
|
||||
template.requirements = {
|
||||
"any": [
|
||||
{ "all": [{ "civ": "civA" }, { "tech": "tech1" }] },
|
||||
{ "tech": "tech2" }
|
||||
]
|
||||
};
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civA"), [{ "techs": ["tech1"] }, { "techs": ["tech2"] }]);
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civB"), [{ "techs": ["tech2"] }]);
|
||||
|
||||
template.requirements = {
|
||||
"any": [
|
||||
{ "all": [{ "notciv": "civA" }, { "tech": "tech1" }] },
|
||||
{ "tech": "tech2" }
|
||||
]
|
||||
};
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civA"), [{ "techs": ["tech2"] }]);
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civB"), [{ "techs": ["tech1"] }, { "techs": ["tech2"] }]);
|
||||
|
||||
|
||||
/**
|
||||
* Complicated `any`s, part 2 - an `any` inside an `any`:
|
||||
*/
|
||||
|
||||
// Techs
|
||||
template.requirements = {
|
||||
"any": [
|
||||
{ "any": [{ "tech": "tech_A" }, { "tech": "tech_B" }] },
|
||||
{ "any": [{ "tech": "tech_C" }, { "tech": "tech_D" }] }
|
||||
]
|
||||
};
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [
|
||||
{ "techs": ["tech_A"] },
|
||||
{ "techs": ["tech_B"] },
|
||||
{ "techs": ["tech_C"] },
|
||||
{ "techs": ["tech_D"] }
|
||||
]);
|
||||
|
||||
// Techs and entities
|
||||
template.requirements = {
|
||||
"any": [
|
||||
{ "any": [{ "tech": "tech_A" }, { "entity": { "class": "class_A", "number": 5 } }] },
|
||||
{ "any": [{ "entity": { "class": "class_B", "numberOfTypes": 5 } }, { "tech": "tech_B" }] }
|
||||
]
|
||||
};
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [
|
||||
{ "techs": ["tech_A"] },
|
||||
{ "entities": [{ "class": "class_A", "number": 5, "check": "count" }] },
|
||||
{ "entities": [{ "class": "class_B", "number": 5, "check": "variants" }] },
|
||||
{ "techs": ["tech_B"] }
|
||||
]);
|
||||
|
||||
// Two `civ`s, without and with a tech
|
||||
template.requirements = {
|
||||
"any": [
|
||||
{ "any": [{ "civ": "athen" }, { "civ": "spart" }] }
|
||||
]
|
||||
};
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), []);
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "maur"), false);
|
||||
|
||||
template.requirements = {
|
||||
"any": [
|
||||
{ "tech": "required_tech" },
|
||||
{ "any": [{ "civ": "athen" }, { "civ": "spart" }] }
|
||||
]
|
||||
};
|
||||
// These requirements may not make sense, as the `civ`s are unable to restrict the requirements due to the outer `any`
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [{ "techs": ["required_tech"] }]);
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "maur"), [{ "techs": ["required_tech"] }]);
|
||||
|
||||
// Two `notciv`s, without and with a tech
|
||||
template.requirements = {
|
||||
"any": [
|
||||
{ "any": [{ "notciv": "athen" }, { "notciv": "spart" }] }
|
||||
]
|
||||
};
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), false);
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "maur"), []);
|
||||
|
||||
template.requirements = {
|
||||
"any": [
|
||||
{ "tech": "required_tech" },
|
||||
{ "any": [{ "notciv": "athen" }, { "notciv": "spart" }] }
|
||||
]
|
||||
};
|
||||
// These requirements may not make sense, as the `notciv`s are made irrelevant by the outer `any`
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "athen"), [{ "techs": ["required_tech"] }]);
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "maur"), [{ "techs": ["required_tech"] }]);
|
||||
|
||||
|
||||
/**
|
||||
* Further tests
|
||||
*/
|
||||
|
||||
template.requirements = {
|
||||
"all": [
|
||||
{ "tech": "tech1" },
|
||||
{ "any": [{ "civ": "civA" }, { "civ": "civB" }] },
|
||||
{ "notciv": "civC" }
|
||||
]
|
||||
};
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civA"), [{ "techs": ["tech1"] }]);
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civC"), false);
|
||||
|
||||
template.requirements = {
|
||||
"any": [
|
||||
{ "all": [{ "civ": "civA" }, { "tech": "tech1" }] },
|
||||
{ "all": [{ "civ": "civB" }, { "tech": "tech2" }] }
|
||||
]
|
||||
};
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civA"), [{ "techs": ["tech1"] }]);
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civB"), [{ "techs": ["tech2"] }]);
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civC"), false);
|
||||
|
||||
template.requirements = {
|
||||
"any": [
|
||||
{ "all": [{ "civ": "civA" }, { "tech": "tech1" }] },
|
||||
{ "all": [
|
||||
{ "any": [{ "civ": "civB" }, { "civ": "civC" }] },
|
||||
{ "tech": "tech2" }
|
||||
] }
|
||||
]
|
||||
};
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civA"), [{ "techs": ["tech1"] }]);
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civC"), [{ "techs": ["tech2"] }]);
|
||||
TS_ASSERT_UNEVAL_EQUALS(DeriveTechnologyRequirements(template, "civD"), false);
|
||||
@@ -6,7 +6,7 @@
|
||||
},
|
||||
"description": "Advances from a bustling town to a veritable metropolis, full of the wonders of modern technology.",
|
||||
"cost": { "food": 0, "wood": 0, "stone": 750, "metal": 750 },
|
||||
"requirements": { "all": [{ "class": "Town", "number": 4 }, { "notciv": "athen" }] },
|
||||
"requirements": { "all": [{ "entity": { "class": "Town", "number": 4 } }, { "notciv": "athen" }] },
|
||||
"requirementsTooltip": "Requires 4 new Town Phase structures (except Walls and Civic Centers).",
|
||||
"supersedes": "phase_town",
|
||||
"icon": "city_phase.png",
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
},
|
||||
"description": "Advances from a bustling town to a veritable metropolis, full of the wonders of modern technology. This is the Athenian city phase, where metal gathering rates are boosted because of the 'Silver Owls' bonus.",
|
||||
"cost": { "food": 0, "wood": 0, "stone": 750, "metal": 750 },
|
||||
"requirements": { "all": [{ "class": "Town", "number": 4 }, { "civ": "athen" }] },
|
||||
"requirements": { "all": [{ "entity": { "class": "Town", "number": 4 } }, { "civ": "athen" }] },
|
||||
"requirementsTooltip": "Requires 4 new Town Phase structures (except Walls and Civic Centers).",
|
||||
"supersedes": "phase_town_athen",
|
||||
"replaces": ["phase_city"],
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
},
|
||||
"description": "Advances from a small village to a bustling town, ready to expand rapidly.",
|
||||
"cost": { "food": 500, "wood": 500, "stone": 0, "metal": 0 },
|
||||
"requirements": { "all": [{ "class": "Village", "number": 5 }, { "notciv": "athen" }] },
|
||||
"requirements": { "all": [{ "entity": { "class": "Village", "number": 5 } }, { "notciv": "athen" }] },
|
||||
"requirementsTooltip": "Requires 5 Village Phase structures (except Palisades and Farm Fields).",
|
||||
"supersedes": "phase_village",
|
||||
"icon": "town_phase.png",
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
},
|
||||
"description": "Advances from a small village to a bustling town, ready to expand rapidly. This is the Athenian town phase, where metal gathering rates are boosted because of the 'Silver Owls' bonus.",
|
||||
"cost": { "food": 500, "wood": 500, "stone": 0, "metal": 0 },
|
||||
"requirements": { "all": [{ "class": "Village", "number": 5 }, { "civ": "athen" }] },
|
||||
"requirements": { "all": [{ "entity": { "class": "Village", "number": 5 } }, { "civ": "athen" }] },
|
||||
"requirementsTooltip": "Requires 5 Village Phase structures (except Palisades and Farm Fields).",
|
||||
"supersedes": "phase_village",
|
||||
"replaces": ["phase_town"],
|
||||
|
||||
@@ -5,16 +5,12 @@
|
||||
"requirements": {
|
||||
"all": [
|
||||
{ "tech": "phase_city" },
|
||||
{
|
||||
"all": [
|
||||
{ "notciv": "brit"},
|
||||
{ "notciv": "gaul" },
|
||||
{ "notciv": "iber" },
|
||||
{ "notciv": "maur" },
|
||||
{ "notciv": "pers" },
|
||||
{ "notciv": "sele" }
|
||||
]
|
||||
}
|
||||
{ "notciv": "brit"},
|
||||
{ "notciv": "gaul" },
|
||||
{ "notciv": "iber" },
|
||||
{ "notciv": "maur" },
|
||||
{ "notciv": "pers" },
|
||||
{ "notciv": "sele" }
|
||||
]
|
||||
},
|
||||
"requirementsTooltip": "Unlocked in City Phase.",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"genericName": "Diaspora",
|
||||
"description": "The extension of trade leads to the permanent establishment of storekeepers and their families in foreign countries, allowing them to exploit the wealth of these countries.",
|
||||
"cost": { "food": 200, "wood": 200, "stone": 100, "metal": 100 },
|
||||
"requirements": { "class": "Trader", "number": 3 },
|
||||
"requirements": { "entity": { "class": "Trader", "number": 3 } },
|
||||
"requirementsTooltip": "Requires 3 Traders",
|
||||
"supersedes": "unlock_shared_los",
|
||||
"icon": "diaspora.png",
|
||||
|
||||
Reference in New Issue
Block a user