Index: ps/trunk/binaries/data/mods/public/simulation/components/Heal.js =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/components/Heal.js +++ ps/trunk/binaries/data/mods/public/simulation/components/Heal.js @@ -87,6 +87,33 @@ return this.template.HealableClasses._string || ""; }; +/** + * Whether this entity can heal the target. + * + * @param {number} target - The target's entity ID. + * @return {boolean} - Whether the target can be healed. + */ +Heal.prototype.CanHeal = function(target) +{ + let cmpHealth = Engine.QueryInterface(target, IID_Health); + if (!cmpHealth || cmpHealth.IsUnhealable()) + return false; + + // Verify that the target is owned by an ally or the player self. + let cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership); + if (!cmpOwnership || !IsOwnedByAllyOfPlayer(cmpOwnership.GetOwner(), target)) + return false; + + // Verify that the target has the right class. + let cmpIdentity = Engine.QueryInterface(target, IID_Identity); + if (!cmpIdentity) + return false; + + let targetClasses = cmpIdentity.GetClassesList(); + return !MatchesClassList(targetClasses, this.GetUnhealableClasses()) && + MatchesClassList(targetClasses, this.GetHealableClasses()); +}; + Heal.prototype.GetRangeOverlays = function() { if (!this.template.RangeOverlay) @@ -121,7 +148,7 @@ // HP healed * XP per HP cmpPromotion.IncreaseXp((targetState.new - targetState.old) / cmpHealth.GetMaxHitpoints() * cmpLoot.GetXp()); } - //TODO we need a sound file + // TODO we need a sound file // PlaySound("heal_impact", this.entity); }; Index: ps/trunk/binaries/data/mods/public/simulation/components/UnitAI.js =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/components/UnitAI.js +++ ps/trunk/binaries/data/mods/public/simulation/components/UnitAI.js @@ -6114,38 +6114,8 @@ if (this.IsFormationController()) return true; - // Verify that we're able to respond to Heal commands - var cmpHeal = Engine.QueryInterface(this.entity, IID_Heal); - if (!cmpHeal) - return false; - - // Verify that the target is alive - if (!this.TargetIsAlive(target)) - return false; - - // Verify that the target is owned by the same player as the entity or of an ally - var cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership); - if (!cmpOwnership || !(IsOwnedByPlayer(cmpOwnership.GetOwner(), target) || IsOwnedByAllyOfPlayer(cmpOwnership.GetOwner(), target))) - return false; - - // Verify that the target is not unhealable (or at max health) - var cmpHealth = Engine.QueryInterface(target, IID_Health); - if (!cmpHealth || cmpHealth.IsUnhealable()) - return false; - - // Verify that the target has no unhealable class - var cmpIdentity = Engine.QueryInterface(target, IID_Identity); - if (!cmpIdentity) - return false; - - if (MatchesClassList(cmpIdentity.GetClassesList(), cmpHeal.GetUnhealableClasses())) - return false; - - // Verify that the target is a healable class - if (MatchesClassList(cmpIdentity.GetClassesList(), cmpHeal.GetHealableClasses())) - return true; - - return false; + let cmpHeal = Engine.QueryInterface(this.entity, IID_Heal); + return cmpHeal && cmpHeal.CanHeal(target); }; UnitAI.prototype.CanReturnResource = function(target, checkCarriedResource) Index: ps/trunk/binaries/data/mods/public/simulation/components/tests/test_Heal.js =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/components/tests/test_Heal.js +++ ps/trunk/binaries/data/mods/public/simulation/components/tests/test_Heal.js @@ -1,4 +1,6 @@ Engine.LoadHelperScript("ValueModification.js"); +Engine.LoadHelperScript("Player.js"); +Engine.LoadComponentScript("interfaces/Formation.js"); Engine.LoadComponentScript("interfaces/Heal.js"); Engine.LoadComponentScript("interfaces/Health.js"); Engine.LoadComponentScript("interfaces/Loot.js"); @@ -6,11 +8,13 @@ Engine.LoadComponentScript("interfaces/UnitAI.js"); Engine.LoadComponentScript("Heal.js"); -const entity= 60; +const entity = 60; +const player = 1; +const otherPlayer = 2 let template = { "Range": 20, - "RangeOverlay" : { + "RangeOverlay": { "LineTexture": "heal_overlay_range.png", "LineTextureMask": "heal_overlay_range_mask.png", "LineThickness": 0.35 @@ -21,6 +25,22 @@ "HealableClasses": { "_string": "Support Infantry" }, }; +AddMock(entity, IID_Ownership, { + "GetOwner": () => player +}); + +AddMock(SYSTEM_ENTITY, IID_PlayerManager, { + "GetPlayerByID": () => player +}); + +AddMock(player, IID_Player, { + "IsAlly": () => true +}); + +AddMock(otherPlayer, IID_Player, { + "IsAlly": () => false +}); + ApplyValueModificationsToEntity = function(value, stat, ent) { if (ent != entity) @@ -47,7 +67,7 @@ TS_ASSERT_EQUALS(cmpHeal.GetHP(), 5 + 100); -TS_ASSERT_UNEVAL_EQUALS(cmpHeal.GetRange(), {"min":0, "max": 20 + 300 }); +TS_ASSERT_UNEVAL_EQUALS(cmpHeal.GetRange(), { "min": 0, "max": 20 + 300 }); TS_ASSERT_EQUALS(cmpHeal.GetHealableClasses(), "Support Infantry"); @@ -63,14 +83,25 @@ // Test PerformHeal let target = 70; +AddMock(target, IID_Ownership, { + "GetOwner": () => player +}); + +let targetClasses; +AddMock(target, IID_Identity, { + "GetClassesList": () => targetClasses +}); + let increased; +let unhealable = false; AddMock(target, IID_Health, { "GetMaxHitpoints": () => 700, "Increase": amount => { increased = true; TS_ASSERT_EQUALS(amount, 5 + 100); return { "old": 600, "new": 600 + 5 + 100 }; - } + }, + "IsUnhealable": () => unhealable }); cmpHeal.PerformHeal(target); @@ -108,3 +139,32 @@ cmpHeal.OnValueModification({ "component": "Heal", "valueNames": ["Heal/Range"] }); TS_ASSERT(updated); + +// Test CanHeal. +targetClasses = ["Infantry", "Hero"]; +TS_ASSERT_UNEVAL_EQUALS(cmpHeal.CanHeal(target), true); + +targetClasses = ["Hero"]; +TS_ASSERT_UNEVAL_EQUALS(cmpHeal.CanHeal(target), false); + +targetClasses = ["Infantry", "Cavalry"]; +TS_ASSERT_UNEVAL_EQUALS(cmpHeal.CanHeal(target), false); + +targetClasses = ["Cavalry"]; +TS_ASSERT_UNEVAL_EQUALS(cmpHeal.CanHeal(target), false); + +targetClasses = ["Infantry"]; +TS_ASSERT_UNEVAL_EQUALS(cmpHeal.CanHeal(target), true); + +unhealable = true; +TS_ASSERT_UNEVAL_EQUALS(cmpHeal.CanHeal(target), false); + +let otherTarget = 71; +AddMock(otherTarget, IID_Ownership, { + "GetOwner": () => player +}); + +AddMock(otherTarget, IID_Health, { + "IsUnhealable": () => false +}); +TS_ASSERT_UNEVAL_EQUALS(cmpHeal.CanHeal(otherTarget), false);