Changeset View
Changeset View
Standalone View
Standalone View
binaries/data/mods/public/simulation/components/ProductionQueue.js
Show First 20 Lines • Show All 207 Lines • ▼ Show 20 Lines | if (requiredXp > 0) | ||||
break; | break; | ||||
templateName = template.Promotion.Entity; | templateName = template.Promotion.Entity; | ||||
template = cmpTemplateManager.GetTemplate(templateName); | template = cmpTemplateManager.GetTemplate(templateName); | ||||
} | } | ||||
return templateName; | return templateName; | ||||
}; | }; | ||||
/* | /* | ||||
* Returns list of technologies that can be researched by this building. | |||||
*/ | |||||
ProductionQueue.prototype.GetTechnologiesList = function() | |||||
{ | |||||
if (!this.template.Technologies) | |||||
return []; | |||||
let string = this.template.Technologies._string; | |||||
string = ApplyValueModificationsToEntity("ProductionQueue/Technologies/_string", string, this.entity); | |||||
if (!string) | |||||
return []; | |||||
let cmpTechnologyManager = QueryOwnerInterface(this.entity, IID_TechnologyManager); | |||||
if (!cmpTechnologyManager) | |||||
return []; | |||||
let cmpPlayer = QueryOwnerInterface(this.entity); | |||||
if (!cmpPlayer) | |||||
return []; | |||||
let techs = string.split(/\s+/); | |||||
// Replace the civ specific technologies. | |||||
for (let i = 0; i < techs.length; ++i) | |||||
{ | |||||
let tech = techs[i]; | |||||
if (tech.indexOf("{civ}") == -1) | |||||
continue; | |||||
let civTech = tech.replace("{civ}", cmpPlayer.GetCiv()); | |||||
techs[i] = TechnologyTemplates.Has(civTech) ? civTech : tech.replace("{civ}", "generic"); | |||||
} | |||||
// Remove any technologies that can't be researched by this civ. | |||||
techs = techs.filter(tech => | |||||
cmpTechnologyManager.CheckTechnologyRequirements( | |||||
DeriveTechnologyRequirements(TechnologyTemplates.Get(tech), cmpPlayer.GetCiv()), | |||||
true)); | |||||
let techList = []; | |||||
// Stores the tech which supersedes the key. | |||||
let superseded = {}; | |||||
let disabledTechnologies = cmpPlayer.GetDisabledTechnologies(); | |||||
// Add any top level technologies to an array which corresponds to the displayed icons. | |||||
// Also store what technology is superseded in the superseded object { "tech1":"techWhichSupercedesTech1", ... }. | |||||
for (let tech of techs) | |||||
{ | |||||
if (disabledTechnologies && disabledTechnologies[tech]) | |||||
continue; | |||||
let template = TechnologyTemplates.Get(tech); | |||||
if (!template.supersedes || techs.indexOf(template.supersedes) === -1) | |||||
techList.push(tech); | |||||
else | |||||
superseded[template.supersedes] = tech; | |||||
} | |||||
// Now make researched/in progress techs invisible. | |||||
for (let i in techList) | |||||
{ | |||||
let tech = techList[i]; | |||||
while (this.IsTechnologyResearchedOrInProgress(tech)) | |||||
tech = superseded[tech]; | |||||
techList[i] = tech; | |||||
} | |||||
let ret = []; | |||||
// This inserts the techs into the correct positions to line up the technology pairs. | |||||
for (let i = 0; i < techList.length; ++i) | |||||
{ | |||||
let tech = techList[i]; | |||||
if (!tech) | |||||
{ | |||||
ret[i] = undefined; | |||||
continue; | |||||
} | |||||
let template = TechnologyTemplates.Get(tech); | |||||
if (template.top) | |||||
ret[i] = { "pair": true, "top": template.top, "bottom": template.bottom }; | |||||
else | |||||
ret[i] = tech; | |||||
} | |||||
return ret; | |||||
}; | |||||
ProductionQueue.prototype.GetTechCostMultiplier = function() | |||||
{ | |||||
let techCostMultiplier = {}; | |||||
for (let res in this.template.TechCostMultiplier) | |||||
techCostMultiplier[res] = ApplyValueModificationsToEntity( | |||||
"ProductionQueue/TechCostMultiplier/" + res, | |||||
+this.template.TechCostMultiplier[res], | |||||
this.entity); | |||||
return techCostMultiplier; | |||||
}; | |||||
ProductionQueue.prototype.IsTechnologyResearchedOrInProgress = function(tech) | |||||
{ | |||||
if (!tech) | |||||
return false; | |||||
let cmpTechnologyManager = QueryOwnerInterface(this.entity, IID_TechnologyManager); | |||||
if (!cmpTechnologyManager) | |||||
return false; | |||||
let template = TechnologyTemplates.Get(tech); | |||||
if (template.top) | |||||
return cmpTechnologyManager.IsTechnologyResearched(template.top) || | |||||
cmpTechnologyManager.IsInProgress(template.top) || | |||||
cmpTechnologyManager.IsTechnologyResearched(template.bottom) || | |||||
cmpTechnologyManager.IsInProgress(template.bottom); | |||||
return cmpTechnologyManager.IsTechnologyResearched(tech) || cmpTechnologyManager.IsInProgress(tech); | |||||
}; | |||||
/* | |||||
* Adds a new batch of identical units to train or a technology to research to the production queue. | * Adds a new batch of identical units to train or a technology to research to the production queue. | ||||
* @param {string} templateName - The template to start production on. | * @param {string} templateName - The template to start production on. | ||||
* @param {string} type - The type of production (i.e. "unit" or "technology"). | * @param {string} type - The type of production (i.e. "unit" or "technology"). | ||||
* @param {number} count - The amount of units to be produced. Ignored for a tech. | * @param {number} count - The amount of units to be produced. Ignored for a tech. | ||||
* @param {any} metadata - Optionally any metadata to be attached to the item. | * @param {any} metadata - Optionally any metadata to be attached to the item. | ||||
* @param {boolean} pushFront - Whether to push the item to the front of the queue and pause any item(s) currently in progress. | * @param {boolean} pushFront - Whether to push the item to the front of the queue and pause any item(s) currently in progress. | ||||
* | * | ||||
* @return {boolean} - Whether the addition of the item has succeeded. | * @return {boolean} - Whether the addition of the item has succeeded. | ||||
▲ Show 20 Lines • Show All 95 Lines • ▼ Show 20 Lines | if (type == "unit") | ||||
const buildTime = ApplyValueModificationsToTemplate( | const buildTime = ApplyValueModificationsToTemplate( | ||||
"Cost/BuildTime", | "Cost/BuildTime", | ||||
+template.Cost.BuildTime, | +template.Cost.BuildTime, | ||||
player, | player, | ||||
template); | template); | ||||
const time = this.GetBatchTime(count) * buildTime * 1000; | const time = this.GetBatchTime(count) * buildTime * 1000; | ||||
item.timeTotal = time; | item.timeTotal = time; | ||||
item.timeRemaining = time; | item.timeRemaining = time; | ||||
// TrySubtractResources should report error to player (they ran out of resources). | |||||
if (!cmpPlayer.TrySubtractResources(item.resources)) | |||||
return false; | |||||
} | } | ||||
else if (type == "technology") | else if (type == "technology") | ||||
{ | { | ||||
if (!TechnologyTemplates.Has(templateName)) | const cmpResearcher = Engine.QueryInterface(this.entity, IID_Researcher); | ||||
if (!cmpResearcher) | |||||
return false; | return false; | ||||
item.technology = cmpResearcher.QueueTechnology(templateName, metadata); | |||||
if (!this.GetTechnologiesList().some(tech => | if (item.technology == -1) | ||||
tech && | |||||
(tech == templateName || | |||||
tech.pair && | |||||
(tech.top == templateName || tech.bottom == templateName)))) | |||||
{ | |||||
error("This entity cannot research " + templateName); | |||||
return false; | return false; | ||||
} | } | ||||
item.technology = { | |||||
"template": templateName, | |||||
"resources": {} | |||||
}; | |||||
let template = TechnologyTemplates.Get(templateName); | |||||
let techCostMultiplier = this.GetTechCostMultiplier(); | |||||
if (template.cost) | |||||
for (const res in template.cost) | |||||
{ | |||||
item.technology.resources[res] = Math.floor((techCostMultiplier[res] || 1) * template.cost[res]); | |||||
item.resources[res] = item.technology.resources[res]; | |||||
} | |||||
const time = techCostMultiplier.time * (template.researchTime || 0) * 1000; | |||||
item.timeTotal = time; | |||||
item.timeRemaining = time; | |||||
} | |||||
else | else | ||||
{ | { | ||||
warn("Tried to add invalid item of type \"" + type + "\" and template \"" + templateName + "\" to a production queue"); | warn("Tried to add invalid item of type \"" + type + "\" and template \"" + templateName + "\" to a production queue"); | ||||
return false; | return false; | ||||
} | } | ||||
// TrySubtractResources should report error to player (they ran out of resources). | |||||
if (!cmpPlayer.TrySubtractResources(item.resources)) | |||||
return false; | |||||
item.id = this.nextID++; | item.id = this.nextID++; | ||||
if (pushFront) | if (pushFront) | ||||
{ | { | ||||
if (this.queue[0]) | if (this.queue[0]) | ||||
this.queue[0].paused = true; | this.queue[0].paused = true; | ||||
this.queue.unshift(item); | this.queue.unshift(item); | ||||
} | } | ||||
else | else | ||||
this.queue.push(item); | this.queue.push(item); | ||||
const cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); | const cmpTrigger = Engine.QueryInterface(SYSTEM_ENTITY, IID_Trigger); | ||||
if (item.entity) | if (item.entity) | ||||
cmpTrigger.CallEvent("OnTrainingQueued", { | cmpTrigger.CallEvent("OnTrainingQueued", { | ||||
"playerid": player, | "playerid": player, | ||||
"unitTemplate": item.entity.template, | "unitTemplate": item.entity.template, | ||||
"count": count, | "count": count, | ||||
"metadata": metadata, | "metadata": metadata, | ||||
"trainerEntity": this.entity | "trainerEntity": this.entity | ||||
}); | }); | ||||
if (item.technology) | |||||
{ | |||||
// Tell the technology manager that we have started researching this | |||||
// such that players can't research the same thing twice. | |||||
const cmpTechnologyManager = QueryOwnerInterface(this.entity, IID_TechnologyManager); | |||||
cmpTechnologyManager.QueuedResearch(templateName, this.entity); | |||||
cmpTrigger.CallEvent("OnResearchQueued", { | |||||
"playerid": player, | |||||
"technologyTemplate": item.technology.template, | |||||
"researcherEntity": this.entity | |||||
}); | |||||
} | |||||
Engine.PostMessage(this.entity, MT_ProductionQueueChanged, null); | Engine.PostMessage(this.entity, MT_ProductionQueueChanged, null); | ||||
if (!this.timer) | if (!this.timer) | ||||
this.StartTimer(); | this.StartTimer(); | ||||
return true; | return true; | ||||
}; | }; | ||||
▲ Show 20 Lines • Show All 45 Lines • ▼ Show 20 Lines | ProductionQueue.prototype.RemoveItem = function(id) | ||||
let cmpStatisticsTracker = QueryPlayerIDInterface(item.player, IID_StatisticsTracker); | let cmpStatisticsTracker = QueryPlayerIDInterface(item.player, IID_StatisticsTracker); | ||||
const totalCosts = {}; | const totalCosts = {}; | ||||
for (let resource in item.resources) | for (let resource in item.resources) | ||||
{ | { | ||||
totalCosts[resource] = 0; | totalCosts[resource] = 0; | ||||
if (item.entity) | if (item.entity) | ||||
totalCosts[resource] += Math.floor(item.entity.count * item.entity.resources[resource]); | totalCosts[resource] += Math.floor(item.entity.count * item.entity.resources[resource]); | ||||
if (item.technology) | |||||
totalCosts[resource] += item.technology.resources[resource]; | |||||
if (cmpStatisticsTracker) | if (cmpStatisticsTracker) | ||||
cmpStatisticsTracker.IncreaseResourceUsedCounter(resource, -totalCosts[resource]); | cmpStatisticsTracker.IncreaseResourceUsedCounter(resource, -totalCosts[resource]); | ||||
} | } | ||||
if (cmpPlayer) | if (cmpPlayer) | ||||
cmpPlayer.AddResources(totalCosts); | cmpPlayer.AddResources(totalCosts); | ||||
if (item.technology) | if (item.technology) | ||||
{ | { | ||||
let cmpTechnologyManager = QueryPlayerIDInterface(item.player, IID_TechnologyManager); | let cmpResearcher = QueryInterface(this.entity, IID_Researcher); | ||||
Lint: prefer-const: 'cmpResearcher' is never reassigned. Use 'const' instead. | |||||
if (cmpTechnologyManager) | if (cmpResearcher) | ||||
cmpTechnologyManager.StoppedResearch(item.technology.template, true); | cmpResearcher.StopResearching(item.technology); | ||||
} | } | ||||
this.queue.splice(itemIndex, 1); | this.queue.splice(itemIndex, 1); | ||||
Engine.PostMessage(this.entity, MT_ProductionQueueChanged, null); | Engine.PostMessage(this.entity, MT_ProductionQueueChanged, null); | ||||
if (!this.queue.length) | if (!this.queue.length) | ||||
this.StopTimer(); | this.StopTimer(); | ||||
}; | }; | ||||
ProductionQueue.prototype.SetAnimation = function(name) | ProductionQueue.prototype.SetAnimation = function(name) | ||||
{ | { | ||||
let cmpVisual = Engine.QueryInterface(this.entity, IID_Visual); | let cmpVisual = Engine.QueryInterface(this.entity, IID_Visual); | ||||
if (cmpVisual) | if (cmpVisual) | ||||
cmpVisual.SelectAnimation(name, false, 1); | cmpVisual.SelectAnimation(name, false, 1); | ||||
}; | }; | ||||
/* | /* | ||||
* Returns basic data from all batches in the production queue. | * Returns basic data from all batches in the production queue. | ||||
*/ | */ | ||||
ProductionQueue.prototype.GetQueue = function() | ProductionQueue.prototype.GetQueue = function() | ||||
{ | { | ||||
return this.queue.map(item => ({ | return this.queue.map(item => ({ | ||||
"id": item.id, | "id": item.id, | ||||
"unitTemplate": item.entity?.template, | "unitTemplate": item.entity?.template, | ||||
"technologyTemplate": item.technology?.template, | "technology": item.technology, | ||||
"count": item.entity?.count, | "count": item.entity?.count, | ||||
"neededSlots": item.entity?.neededSlots, | "neededSlots": item.entity?.neededSlots, | ||||
"progress": 1 - (item.timeRemaining / (item.timeTotal || 1)), | "progress": 1 - (item.timeRemaining / (item.timeTotal || 1)), | ||||
"timeRemaining": item.timeRemaining, | "timeRemaining": item.timeRemaining, | ||||
"paused": item.paused, | "paused": item.paused, | ||||
"metadata": item.metadata | "metadata": item.metadata | ||||
})); | })); | ||||
}; | }; | ||||
▲ Show 20 Lines • Show All 194 Lines • ▼ Show 20 Lines | if (!item.productionStarted) | ||||
} | } | ||||
this.SetAnimation("training"); | this.SetAnimation("training"); | ||||
cmpPlayer.UnBlockTraining(); | cmpPlayer.UnBlockTraining(); | ||||
Engine.PostMessage(this.entity, MT_TrainingStarted, { "entity": this.entity }); | Engine.PostMessage(this.entity, MT_TrainingStarted, { "entity": this.entity }); | ||||
} | } | ||||
if (item.technology) | if (item.technology) | ||||
{ | |||||
let cmpTechnologyManager = QueryOwnerInterface(this.entity, IID_TechnologyManager); | |||||
if (cmpTechnologyManager) | |||||
cmpTechnologyManager.StartedResearch(item.technology.template, true); | |||||
else | |||||
warn("Failed to start researching " + item.technology.template + ": No TechnologyManager available."); | |||||
this.SetAnimation("researching"); | this.SetAnimation("researching"); | ||||
} | |||||
item.productionStarted = true; | item.productionStarted = true; | ||||
} | } | ||||
if (item.technology) | |||||
time = Engine.QueryInterface(this.entity, IID_Researcher).ProgressTimeout(id, time); | |||||
if (item.timeRemaining > time) | if (item.timeRemaining > time) | ||||
{ | { | ||||
item.timeRemaining -= time; | item.timeRemaining -= time; | ||||
Engine.PostMessage(this.entity, MT_ProductionQueueChanged, null); | Engine.PostMessage(this.entity, MT_ProductionQueueChanged, null); | ||||
return; | return; | ||||
} | } | ||||
Show All 25 Lines | if (item.entity) | ||||
"message": markForTranslation("Can't find free space to spawn trained units"), | "message": markForTranslation("Can't find free space to spawn trained units"), | ||||
"translateMessage": true | "translateMessage": true | ||||
}); | }); | ||||
this.spawnNotified = true; | this.spawnNotified = true; | ||||
} | } | ||||
return; | return; | ||||
} | } | ||||
} | } | ||||
if (item.technology) | |||||
{ | |||||
let cmpTechnologyManager = QueryOwnerInterface(this.entity, IID_TechnologyManager); | |||||
if (cmpTechnologyManager) | |||||
cmpTechnologyManager.ResearchTechnology(item.technology.template); | |||||
else | |||||
warn("Failed to finish researching " + item.technology.template + ": No TechnologyManager available."); | |||||
const template = TechnologyTemplates.Get(item.technology.template); | |||||
if (template && template.soundComplete) | |||||
{ | |||||
let cmpSoundManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_SoundManager); | |||||
if (cmpSoundManager) | |||||
cmpSoundManager.PlaySoundGroup(template.soundComplete, this.entity); | |||||
} | |||||
} | |||||
time -= item.timeRemaining; | time -= item.timeRemaining; | ||||
this.queue.shift(); | this.queue.shift(); | ||||
Engine.PostMessage(this.entity, MT_ProductionQueueChanged, null); | Engine.PostMessage(this.entity, MT_ProductionQueueChanged, null); | ||||
// If autoqueuing, push a new unit on the queue immediately, | // If autoqueuing, push a new unit on the queue immediately, | ||||
// but don't start right away. This 'wastes' some time, making | // but don't start right away. This 'wastes' some time, making | ||||
// autoqueue slightly worse than regular queuing, and also ensures | // autoqueue slightly worse than regular queuing, and also ensures | ||||
▲ Show 20 Lines • Show All 105 Lines • Show Last 20 Lines |
Wildfire Games · Phabricator
'cmpResearcher' is never reassigned. Use 'const' instead.