Index: binaries/data/mods/public/simulation/components/Trainer.js =================================================================== --- binaries/data/mods/public/simulation/components/Trainer.js +++ binaries/data/mods/public/simulation/components/Trainer.js @@ -624,7 +624,10 @@ */ Trainer.prototype.StopBatch = function(id) { - this.queue.get(id).Stop(); + const item = this.queue.get(id); + if (!item) + return; + item.Stop(); this.queue.delete(id); }; @@ -663,6 +666,8 @@ Trainer.prototype.Progress = function(id, allocatedTime) { const item = this.queue.get(id); + if (!item) + return 0; const usedTime = item.Progress(allocatedTime); if (item.IsFinished()) this.queue.delete(id); Index: binaries/data/mods/public/simulation/components/tests/test_Production.js =================================================================== --- /dev/null +++ binaries/data/mods/public/simulation/components/tests/test_Production.js @@ -0,0 +1,140 @@ +Engine.RegisterGlobal("Resources", { + "BuildSchema": (a, b) => {}, + "GetCodes": () => ["food"] +}); +Engine.LoadHelperScript("Player.js"); +Engine.LoadHelperScript("Sound.js"); +Engine.LoadHelperScript("Transform.js"); +Engine.LoadComponentScript("interfaces/BuildRestrictions.js"); +Engine.LoadComponentScript("interfaces/EntityLimits.js"); +Engine.LoadComponentScript("interfaces/Foundation.js"); +Engine.LoadComponentScript("interfaces/ProductionQueue.js"); +Engine.LoadComponentScript("interfaces/Researcher.js"); +Engine.LoadComponentScript("interfaces/StatisticsTracker.js"); +Engine.LoadComponentScript("interfaces/TechnologyManager.js"); +Engine.LoadComponentScript("interfaces/Timer.js"); +Engine.LoadComponentScript("interfaces/Trainer.js"); +Engine.LoadComponentScript("interfaces/TrainingRestrictions.js"); +Engine.LoadComponentScript("interfaces/Trigger.js"); +Engine.LoadComponentScript("interfaces/Upgrade.js"); +Engine.LoadComponentScript("EntityLimits.js"); +Engine.LoadComponentScript("ProductionQueue.js"); +Engine.LoadComponentScript("Researcher.js"); +Engine.LoadComponentScript("TechnologyManager.js"); +Engine.LoadComponentScript("Trainer.js"); +Engine.LoadComponentScript("TrainingRestrictions.js"); + +Engine.RegisterGlobal("ApplyValueModificationsToEntity", (_, value) => value); +Engine.RegisterGlobal("ApplyValueModificationsToTemplate", (_, value) => value); + +AddMock(SYSTEM_ENTITY, IID_Timer, { + "CancelTimer": (id) => {}, + "SetInterval": (ent, iid, func) => 1 +}); + +AddMock(SYSTEM_ENTITY, IID_Trigger, { + "CallEvent": () => {} +}); + +const playerID = 1; +const playerEntityID = 11; +const entityID = 21; + +const template = { + "Cost": { "BuildTime": 1000 } +}; + +AddMock(SYSTEM_ENTITY, IID_TemplateManager, { + "TemplateExists": () => true, + "GetTemplate": name => (template) +}); + +let cmpTrainer = ConstructComponent(entityID, "Trainer", { + "Entities": { "_string": "units/{civ}/cavalry_javelineer_b " + + "units/{native}/support_female_citizen" } +}); +cmpTrainer.GetUpgradedTemplate = (template) => template; + +AddMock(SYSTEM_ENTITY, IID_PlayerManager, { + "GetPlayerByID": id => playerEntityID +}); + +AddMock(playerEntityID, IID_Player, { + "GetDisabledTechnologies": () => {}, + "GetDisabledTemplates": () => ({}), + "GetPlayerID": () => playerID, + "RefundResources": () => {}, + "TryReservePopulationSlots": () => false, + "TrySubtractResources": () => true, + "UnReservePopulationSlots": () => {}, + "UnBlockTraining": () => {}, +}); + +AddMock(playerEntityID, IID_Identity, { + "GetCiv": () => "iber", +}); + +AddMock(entityID, IID_Ownership, { + "GetOwner": () => playerID +}); + +AddMock(entityID, IID_Identity, { + "GetCiv": () => "iber" +}); + +cmpTrainer.OnOwnershipChanged({ "to": playerID }); + +let cmpResearcher = ConstructComponent(entityID, "Researcher", { + "Technologies": { + "_string": "technologies/{civ}/will" + } +}); +let cmpProductionQueue = ConstructComponent(entityID, "ProductionQueue", null); + +TS_ASSERT(cmpProductionQueue.AddItem("units/iber/cavalry_javelineer_b", "unit", 1)); +TS_ASSERT_EQUALS(cmpProductionQueue.GetQueue().length, 1); +cmpProductionQueue.ProgressTimeout(null, 500); + +Engine.RegisterGlobal("TechnologyTemplates", { + "GetAll": () => [], + "Get": (tech) => { + return template; + }, + "Has": () => true, +}); +ConstructComponent(playerEntityID, "TechnologyManager", null); + +AddMock(SYSTEM_ENTITY, IID_GuiInterface, { + "SetSelectionDirty": () => {} +}); + +cmpProductionQueue.RemoveItem(cmpProductionQueue.nextID - 1); +TS_ASSERT(cmpProductionQueue.AddItem("technologies/iber/will", "technology")); + +cmpProductionQueue.RemoveItem(cmpProductionQueue.nextID - 1); + + +// #6763 (If the item is removed in cmpTrainer, don't error in cmpProductionQueue.) +TS_ASSERT(cmpProductionQueue.AddItem("units/iber/cavalry_javelineer_b", "unit", 1)); + +AddMock(SYSTEM_ENTITY, IID_TemplateManager, { + "TemplateExists": () => false, + "GetTemplate": name => (template) +}); + +cmpTrainer.OnValueModification({ "component": "Promotion", "entities": [entityID] }); +cmpProductionQueue.OnOwnershipChanged({ "to": INVALID_PLAYER }); + +AddMock(SYSTEM_ENTITY, IID_TemplateManager, { + "TemplateExists": () => true, + "GetTemplate": name => (template) +}); +cmpTrainer.CalculateEntitiesMap(); +TS_ASSERT(cmpProductionQueue.AddItem("units/iber/cavalry_javelineer_b", "unit", 1)); + +AddMock(SYSTEM_ENTITY, IID_TemplateManager, { + "TemplateExists": () => false, + "GetTemplate": name => (template) +}); +cmpTrainer.OnValueModification({ "component": "Promotion", "entities": [entityID] }); +cmpProductionQueue.ProgressTimeout(null, 500);