diff --git a/binaries/data/mods/public/simulation/components/Builder.js b/binaries/data/mods/public/simulation/components/Builder.js index 0b07dacbfb..6245ab76f9 100644 --- a/binaries/data/mods/public/simulation/components/Builder.js +++ b/binaries/data/mods/public/simulation/components/Builder.js @@ -75,7 +75,7 @@ Builder.prototype.CanRepair = function(target) { let cmpFoundation = QueryMiragedInterface(target, IID_Foundation); let cmpRepairable = QueryMiragedInterface(target, IID_Repairable); - if (!cmpFoundation && !cmpRepairable) + if (!cmpFoundation && (!cmpRepairable || !cmpRepairable.IsRepairable())) return false; let cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership); diff --git a/binaries/data/mods/public/simulation/components/Health.js b/binaries/data/mods/public/simulation/components/Health.js index bd0ed41e21..ca37e7f7ed 100644 --- a/binaries/data/mods/public/simulation/components/Health.js +++ b/binaries/data/mods/public/simulation/components/Health.js @@ -109,7 +109,8 @@ Health.prototype.SetHitpoints = function(value) Health.prototype.IsRepairable = function() { - return Engine.QueryInterface(this.entity, IID_Repairable) != null; + let cmpRepairable = Engine.QueryInterface(this.entity, IID_Repairable); + return cmpRepairable && cmpRepairable.IsRepairable(); }; Health.prototype.IsUnhealable = function() diff --git a/binaries/data/mods/public/simulation/components/Repairable.js b/binaries/data/mods/public/simulation/components/Repairable.js index 35cc6c8a14..b2f7f5e5bf 100644 --- a/binaries/data/mods/public/simulation/components/Repairable.js +++ b/binaries/data/mods/public/simulation/components/Repairable.js @@ -33,6 +33,19 @@ Repairable.prototype.GetBuildProgress = function() return hitpoints / maxHitpoints; }; +/** + * @return whether this entity can be repaired (this does not account for health). + */ +Repairable.prototype.IsRepairable = function() +{ + return !this.unrepairable; +}; + +Repairable.prototype.SetRepairability = function(repairable) +{ + this.unrepairable = !repairable; +}; + /** * Returns the current builders. * diff --git a/binaries/data/mods/public/simulation/components/tests/test_Builder.js b/binaries/data/mods/public/simulation/components/tests/test_Builder.js index 613a480ea0..0da3ed5b52 100644 --- a/binaries/data/mods/public/simulation/components/tests/test_Builder.js +++ b/binaries/data/mods/public/simulation/components/tests/test_Builder.js @@ -1,10 +1,14 @@ Engine.LoadHelperScript("Player.js"); Engine.LoadComponentScript("interfaces/Builder.js"); +Engine.LoadComponentScript("interfaces/Cost.js"); Engine.LoadComponentScript("interfaces/Foundation.js"); +Engine.LoadComponentScript("interfaces/Health.js"); Engine.LoadComponentScript("interfaces/Repairable.js"); Engine.LoadComponentScript("interfaces/Timer.js"); Engine.LoadComponentScript("interfaces/UnitAI.js"); Engine.LoadComponentScript("Builder.js"); +Engine.LoadComponentScript("Health.js"); +Engine.LoadComponentScript("Repairable.js"); Engine.LoadComponentScript("Timer.js"); const builderId = 6; @@ -22,99 +26,165 @@ AddMock(SYSTEM_ENTITY, IID_TemplateManager, { Engine.RegisterGlobal("ApplyValueModificationsToEntity", (prop, oVal, ent) => oVal); +function testEntitiesList() +{ + let cmpBuilder = ConstructComponent(builderId, "Builder", { + "Rate": "1.0", + "Entities": { "_string": "structures/{civ}/barracks structures/{civ}/civil_centre structures/{native}/house" } + }); -let cmpBuilder = ConstructComponent(builderId, "Builder", { - "Rate": "1.0", - "Entities": { "_string": "structures/{civ}/barracks structures/{civ}/civil_centre structures/{native}/house" } -}); + TS_ASSERT_UNEVAL_EQUALS(cmpBuilder.GetEntitiesList(), []); -TS_ASSERT_UNEVAL_EQUALS(cmpBuilder.GetEntitiesList(), []); + AddMock(SYSTEM_ENTITY, IID_PlayerManager, { + "GetPlayerByID": id => playerEntityID + }); -AddMock(SYSTEM_ENTITY, IID_PlayerManager, { - "GetPlayerByID": id => playerEntityID -}); + AddMock(playerEntityID, IID_Player, { + "GetCiv": () => "iber", + "GetDisabledTemplates": () => ({}), + "GetPlayerID": () => playerId + }); -AddMock(playerEntityID, IID_Player, { - "GetCiv": () => "iber", - "GetDisabledTemplates": () => ({}), - "GetPlayerID": () => playerId -}); + AddMock(builderId, IID_Ownership, { + "GetOwner": () => playerId + }); -AddMock(builderId, IID_Ownership, { - "GetOwner": () => playerId -}); + AddMock(builderId, IID_Identity, { + "GetCiv": () => "iber" + }); -AddMock(builderId, IID_Identity, { - "GetCiv": () => "iber" -}); + TS_ASSERT_UNEVAL_EQUALS(cmpBuilder.GetEntitiesList(), ["structures/iber/barracks", "structures/iber/civil_centre", "structures/iber/house"]); -TS_ASSERT_UNEVAL_EQUALS(cmpBuilder.GetEntitiesList(), ["structures/iber/barracks", "structures/iber/civil_centre", "structures/iber/house"]); + AddMock(SYSTEM_ENTITY, IID_TemplateManager, { + "TemplateExists": name => name == "structures/iber/civil_centre" + }); -AddMock(SYSTEM_ENTITY, IID_TemplateManager, { - "TemplateExists": name => name == "structures/iber/civil_centre" -}); + TS_ASSERT_UNEVAL_EQUALS(cmpBuilder.GetEntitiesList(), ["structures/iber/civil_centre"]); -TS_ASSERT_UNEVAL_EQUALS(cmpBuilder.GetEntitiesList(), ["structures/iber/civil_centre"]); + AddMock(SYSTEM_ENTITY, IID_TemplateManager, { + "TemplateExists": () => true + }); -AddMock(SYSTEM_ENTITY, IID_TemplateManager, { - "TemplateExists": () => true -}); + AddMock(playerEntityID, IID_Player, { + "GetCiv": () => "iber", + "GetDisabledTemplates": () => ({ "structures/athen/barracks": true }), + "GetPlayerID": () => playerId + }); -AddMock(playerEntityID, IID_Player, { - "GetCiv": () => "iber", - "GetDisabledTemplates": () => ({ "structures/athen/barracks": true }), - "GetPlayerID": () => playerId -}); + TS_ASSERT_UNEVAL_EQUALS(cmpBuilder.GetEntitiesList(), ["structures/iber/barracks", "structures/iber/civil_centre", "structures/iber/house"]); -TS_ASSERT_UNEVAL_EQUALS(cmpBuilder.GetEntitiesList(), ["structures/iber/barracks", "structures/iber/civil_centre", "structures/iber/house"]); + AddMock(playerEntityID, IID_Player, { + "GetCiv": () => "iber", + "GetDisabledTemplates": () => ({ "structures/iber/barracks": true }), + "GetPlayerID": () => playerId + }); -AddMock(playerEntityID, IID_Player, { - "GetCiv": () => "iber", - "GetDisabledTemplates": () => ({ "structures/iber/barracks": true }), - "GetPlayerID": () => playerId -}); + TS_ASSERT_UNEVAL_EQUALS(cmpBuilder.GetEntitiesList(), ["structures/iber/civil_centre", "structures/iber/house"]); -TS_ASSERT_UNEVAL_EQUALS(cmpBuilder.GetEntitiesList(), ["structures/iber/civil_centre", "structures/iber/house"]); + AddMock(playerEntityID, IID_Player, { + "GetCiv": () => "athen", + "GetDisabledTemplates": () => ({ "structures/athen/barracks": true }), + "GetPlayerID": () => playerId + }); -AddMock(playerEntityID, IID_Player, { - "GetCiv": () => "athen", - "GetDisabledTemplates": () => ({ "structures/athen/barracks": true }), - "GetPlayerID": () => playerId -}); + TS_ASSERT_UNEVAL_EQUALS(cmpBuilder.GetEntitiesList(), ["structures/athen/civil_centre", "structures/iber/house"]); -TS_ASSERT_UNEVAL_EQUALS(cmpBuilder.GetEntitiesList(), ["structures/athen/civil_centre", "structures/iber/house"]); + TS_ASSERT_UNEVAL_EQUALS(cmpBuilder.GetRange(), { "max": 2, "min": 0 }); -TS_ASSERT_UNEVAL_EQUALS(cmpBuilder.GetRange(), { "max": 2, "min": 0 }); + AddMock(builderId, IID_Obstruction, { + "GetSize": () => 1 + }); -AddMock(builderId, IID_Obstruction, { - "GetSize": () => 1 -}); + TS_ASSERT_UNEVAL_EQUALS(cmpBuilder.GetRange(), { "max": 3, "min": 0 }); +} +testEntitiesList(); -TS_ASSERT_UNEVAL_EQUALS(cmpBuilder.GetRange(), { "max": 3, "min": 0 }); +function testBuildingFoundation() +{ + let cmpBuilder = ConstructComponent(builderId, "Builder", { + "Rate": "1.0", + "Entities": { "_string": "" } + }); -// Test repairing. -AddMock(playerEntityID, IID_Player, { - "IsAlly": (p) => p == playerId -}); + AddMock(playerEntityID, IID_Player, { + "IsAlly": (p) => p == playerId + }); -AddMock(target, IID_Ownership, { - "GetOwner": () => playerId -}); + AddMock(target, IID_Ownership, { + "GetOwner": () => playerId + }); -let increased = false; -AddMock(target, IID_Foundation, { - "Build": (entity, amount) => { - increased = true; - TS_ASSERT_EQUALS(amount, 1); - }, - "AddBuilder": () => {} -}); + let increased = false; + AddMock(target, IID_Foundation, { + "Build": (entity, amount) => { + increased = true; + TS_ASSERT_EQUALS(amount, 1); + }, + "AddBuilder": () => {} + }); -let cmpTimer = ConstructComponent(SYSTEM_ENTITY, "Timer"); + let cmpTimer = ConstructComponent(SYSTEM_ENTITY, "Timer"); -TS_ASSERT(cmpBuilder.StartRepairing(target)); -cmpTimer.OnUpdate({ "turnLength": 1 }); -TS_ASSERT(increased); -increased = false; -cmpTimer.OnUpdate({ "turnLength": 2 }); -TS_ASSERT(increased); + TS_ASSERT(cmpBuilder.StartRepairing(target)); + cmpTimer.OnUpdate({ "turnLength": 1 }); + TS_ASSERT(increased); + increased = false; + cmpTimer.OnUpdate({ "turnLength": 2 }); + TS_ASSERT(increased); +} +testBuildingFoundation(); + +function testRepairing() +{ + AddMock(playerEntityID, IID_Player, { + "IsAlly": (p) => p == playerId + }); + + let cmpBuilder = ConstructComponent(builderId, "Builder", { + "Rate": "1.0", + "Entities": { "_string": "" } + }); + + AddMock(target, IID_Ownership, { + "GetOwner": () => playerId + }); + + AddMock(target, IID_Cost, { + "GetBuildTime": () => 100 + }); + + let cmpTargetHealth = ConstructComponent(target, "Health", { + "Max": 100, + "RegenRate": 0, + "IdleRegenRate": 0, + "DeathType": "vanish", + "Unhealable": false + }); + + cmpTargetHealth.SetHitpoints(50); + + DeleteMock(target, IID_Foundation); + let cmpTargetRepairable = ConstructComponent(target, "Repairable", { + "RepairTimeRatio": 1, + }); + + let cmpTimer = ConstructComponent(SYSTEM_ENTITY, "Timer"); + + TS_ASSERT(cmpTargetRepairable.IsRepairable()); + TS_ASSERT(cmpBuilder.StartRepairing(target)); + cmpTimer.OnUpdate({ "turnLength": 1 }); + TS_ASSERT_EQUALS(cmpTargetHealth.GetHitpoints(), 51); + cmpTimer.OnUpdate({ "turnLength": 1 }); + TS_ASSERT_EQUALS(cmpTargetHealth.GetHitpoints(), 52); + cmpTargetRepairable.SetRepairability(false); + cmpTimer.OnUpdate({ "turnLength": 1 }); + TS_ASSERT_EQUALS(cmpTargetHealth.GetHitpoints(), 52); + cmpTargetRepairable.SetRepairability(true); + // Check that we indeed stopped - shouldn't restart on its own. + cmpTimer.OnUpdate({ "turnLength": 1 }); + TS_ASSERT_EQUALS(cmpTargetHealth.GetHitpoints(), 52); + TS_ASSERT(cmpBuilder.StartRepairing(target)); + cmpTimer.OnUpdate({ "turnLength": 1 }); + TS_ASSERT_EQUALS(cmpTargetHealth.GetHitpoints(), 53); +} +testRepairing();