1
0
forked from 0ad/0ad

Health.js cleanup: add tests, add an "IsInjured" function, use this.hitpoints everywhere

Continuation of D1769.

Differential Revision: https://code.wildfiregames.com/D1828
This was SVN commit r22298.
This commit is contained in:
wraitii 2019-05-25 13:11:46 +00:00
parent 01a8138780
commit ea208f19a3
4 changed files with 170 additions and 13 deletions

View File

@ -254,7 +254,7 @@ GuiInterface.prototype.GetEntityState = function(player, ent)
{
ret.hitpoints = cmpHealth.GetHitpoints();
ret.maxHitpoints = cmpHealth.GetMaxHitpoints();
ret.needsRepair = cmpHealth.IsRepairable() && cmpHealth.GetHitpoints() < cmpHealth.GetMaxHitpoints();
ret.needsRepair = cmpHealth.IsRepairable() && cmpHealth.IsInjured();
ret.needsHeal = !cmpHealth.IsUnhealable();
}

View File

@ -78,6 +78,14 @@ Health.prototype.GetMaxHitpoints = function()
return this.maxHitpoints;
};
/**
* @return {boolean} Whether the units are injured. Dead units are not considered injured.
*/
Health.prototype.IsInjured = function()
{
return this.hitpoints > 0 && this.hitpoints < this.GetMaxHitpoints();
};
Health.prototype.SetHitpoints = function(value)
{
// If we're already dead, don't allow resurrection
@ -94,7 +102,7 @@ Health.prototype.SetHitpoints = function(value)
let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
if (cmpRangeManager)
cmpRangeManager.SetEntityFlag(this.entity, "injured", this.hitpoints < this.GetMaxHitpoints());
cmpRangeManager.SetEntityFlag(this.entity, "injured", this.IsInjured());
this.RegisterHealthChanged(old);
};
@ -107,8 +115,7 @@ Health.prototype.IsRepairable = function()
Health.prototype.IsUnhealable = function()
{
return this.template.Unhealable == "true" ||
this.GetHitpoints() <= 0 ||
this.GetHitpoints() >= this.GetMaxHitpoints();
this.hitpoints <= 0 || !this.IsInjured();
};
Health.prototype.GetIdleRegenRate = function()
@ -144,8 +151,8 @@ Health.prototype.CheckRegenTimer = function()
{
// check if we need a timer
if (this.GetRegenRate() == 0 && this.GetIdleRegenRate() == 0 ||
this.GetHitpoints() == this.GetMaxHitpoints() && this.GetRegenRate() >= 0 && this.GetIdleRegenRate() >= 0 ||
this.GetHitpoints() == 0)
!this.IsInjured() && this.GetRegenRate() >= 0 && this.GetIdleRegenRate() >= 0 ||
this.hitpoints == 0)
{
// we don't need a timer, disable if one exists
if (this.regenTimer)
@ -199,7 +206,7 @@ Health.prototype.Reduce = function(amount)
}
// If we are not marked as injured, do it now
if (this.hitpoints == this.GetMaxHitpoints())
if (!this.IsInjured())
{
let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
if (cmpRangeManager)
@ -256,7 +263,7 @@ Health.prototype.Increase = function(amount)
if (cmpFogging)
cmpFogging.Activate();
if (this.hitpoints == this.GetMaxHitpoints())
if (!this.IsInjured())
return { "old": this.hitpoints, "new": this.hitpoints };
// If we're already dead, don't allow resurrection
@ -266,7 +273,7 @@ Health.prototype.Increase = function(amount)
let old = this.hitpoints;
this.hitpoints = Math.min(this.hitpoints + amount, this.GetMaxHitpoints());
if (this.hitpoints == this.GetMaxHitpoints())
if (!this.IsInjured())
{
let cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
if (cmpRangeManager)
@ -350,7 +357,7 @@ Health.prototype.UpdateActor = function()
{
if (!this.template.DamageVariants)
return;
let ratio = this.GetHitpoints() / this.GetMaxHitpoints();
let ratio = this.hitpoints / this.GetMaxHitpoints();
let newDamageVariant = "alive";
if (ratio > 0)
{
@ -386,7 +393,7 @@ Health.prototype.OnValueModification = function(msg)
let newMaxHitpoints = ApplyValueModificationsToEntity("Health/Max", +this.template.Max, this.entity);
if (oldMaxHitpoints != newMaxHitpoints)
{
let newHitpoints = this.GetHitpoints() * newMaxHitpoints/oldMaxHitpoints;
let newHitpoints = this.hitpoints * newMaxHitpoints/oldMaxHitpoints;
this.maxHitpoints = newMaxHitpoints;
this.SetHitpoints(newHitpoints);
}

View File

@ -1426,7 +1426,7 @@ UnitAI.prototype.UnitFsmSpec = {
var cmpIdentity = Engine.QueryInterface(this.entity, IID_Identity);
var cmpHealth = Engine.QueryInterface(this.isGuardOf, IID_Health);
if (cmpIdentity && cmpIdentity.HasClass("Support") &&
cmpHealth && cmpHealth.GetHitpoints() < cmpHealth.GetMaxHitpoints())
cmpHealth && cmpHealth.IsInjured())
{
if (this.CanHeal(this.isGuardOf))
this.PushOrderFront("Heal", { "target": this.isGuardOf, "force": false });
@ -1716,7 +1716,7 @@ UnitAI.prototype.UnitFsmSpec = {
{
// if nothing better to do, check if the guarded needs to be healed or repaired
var cmpHealth = Engine.QueryInterface(this.isGuardOf, IID_Health);
if (cmpHealth && (cmpHealth.GetHitpoints() < cmpHealth.GetMaxHitpoints()))
if (cmpHealth && cmpHealth.IsInjured())
{
if (this.CanHeal(this.isGuardOf))
this.PushOrderFront("Heal", { "target": this.isGuardOf, "force": false });

View File

@ -0,0 +1,150 @@
Engine.LoadComponentScript("interfaces/TechnologyManager.js");
Engine.LoadComponentScript("interfaces/AuraManager.js");
Engine.LoadHelperScript("Player.js");
Engine.LoadHelperScript("ValueModification.js");
Engine.LoadHelperScript("Sound.js");
Engine.LoadComponentScript("interfaces/DeathDamage.js");
Engine.LoadComponentScript("interfaces/Health.js");
Engine.LoadComponentScript("Health.js");
const entity_id = 5;
const corpse_id = entity_id + 1;
const health_template = {
"Max": 50,
"RegenRate": 0,
"IdleRegenRate": 0,
"DeathType": "corpse",
"Unhealable": false
};
var injured_flag = false;
var corpse_entity;
function setEntityUp()
{
let cmpHealth = ConstructComponent(entity_id, "Health", health_template);
AddMock(entity_id, IID_DeathDamage, {
"CauseDeathDamage": () => {}
});
AddMock(entity_id, IID_Position, {
"IsInWorld": () => true,
"GetPosition": () => ({ "x": 0, "z": 0 }),
"GetRotation": () => ({ "x": 0, "y": 0, "z": 0 })
});
AddMock(entity_id, IID_Ownership, {
"GetOwner": () => 1
});
AddMock(entity_id, IID_Visual, {
"GetActorSeed": () => 1
});
AddMock(SYSTEM_ENTITY, IID_TemplateManager, {
"GetCurrentTemplateName": () => "test"
});
AddMock(SYSTEM_ENTITY, IID_RangeManager, {
"SetEntityFlag": (ent, flag, value) => (injured_flag = value)
});
return cmpHealth;
}
var cmpHealth = setEntityUp();
TS_ASSERT_EQUALS(cmpHealth.GetHitpoints(), 50);
TS_ASSERT_EQUALS(cmpHealth.GetMaxHitpoints(), 50);
TS_ASSERT_EQUALS(cmpHealth.IsInjured(), false);
TS_ASSERT_EQUALS(cmpHealth.IsUnhealable(), true);
var change = cmpHealth.Reduce(25);
TS_ASSERT_EQUALS(injured_flag, true);
TS_ASSERT_EQUALS(change.killed, false);
TS_ASSERT_EQUALS(change.change, -25);
TS_ASSERT_EQUALS(cmpHealth.GetHitpoints(), 25);
TS_ASSERT_EQUALS(cmpHealth.GetMaxHitpoints(), 50);
TS_ASSERT_EQUALS(cmpHealth.IsInjured(), true);
TS_ASSERT_EQUALS(cmpHealth.IsUnhealable(), false);
change = cmpHealth.Increase(25);
TS_ASSERT_EQUALS(injured_flag, false);
TS_ASSERT_EQUALS(change.new, 50);
TS_ASSERT_EQUALS(cmpHealth.GetHitpoints(), 50);
TS_ASSERT_EQUALS(cmpHealth.GetMaxHitpoints(), 50);
TS_ASSERT_EQUALS(cmpHealth.IsInjured(), false);
TS_ASSERT_EQUALS(cmpHealth.IsUnhealable(), true);
// Check death.
Engine.AddLocalEntity = function(template) {
corpse_entity = template;
AddMock(corpse_id, IID_Position, {
"JumpTo": () => {},
"SetYRotation": () => {},
"SetXZRotation": () => {},
});
AddMock(corpse_id, IID_Ownership, {
"SetOwner": () => {},
});
AddMock(corpse_id, IID_Visual, {
"SetActorSeed": () => {},
"SelectAnimation": () => {},
});
return corpse_id;
};
change = cmpHealth.Reduce(50);
// Assert we create a corpse with the proper template.
TS_ASSERT_EQUALS(corpse_entity, "corpse|test");
// Check that we are not marked as injured.
TS_ASSERT_EQUALS(injured_flag, false);
TS_ASSERT_EQUALS(change.killed, true);
TS_ASSERT_EQUALS(change.change, -50);
TS_ASSERT_EQUALS(cmpHealth.GetHitpoints(), 0);
TS_ASSERT_EQUALS(cmpHealth.GetMaxHitpoints(), 50);
TS_ASSERT_EQUALS(cmpHealth.IsInjured(), false);
// Check that we can't be revived once dead.
change = cmpHealth.Increase(25);
TS_ASSERT_EQUALS(change.new, 0);
TS_ASSERT_EQUALS(cmpHealth.GetHitpoints(), 0);
TS_ASSERT_EQUALS(cmpHealth.GetMaxHitpoints(), 50);
TS_ASSERT_EQUALS(cmpHealth.IsInjured(), false);
// Check that we can't die twice.
change = cmpHealth.Reduce(50);
TS_ASSERT_EQUALS(change.killed, false);
TS_ASSERT_EQUALS(change.change, 0);
TS_ASSERT_EQUALS(cmpHealth.GetHitpoints(), 0);
TS_ASSERT_EQUALS(cmpHealth.GetMaxHitpoints(), 50);
TS_ASSERT_EQUALS(cmpHealth.IsInjured(), false);
cmpHealth = setEntityUp();
// Check that we still die with > Max HP of damage.
change = cmpHealth.Reduce(60);
TS_ASSERT_EQUALS(change.killed, true);
TS_ASSERT_EQUALS(change.change, -50);
TS_ASSERT_EQUALS(cmpHealth.GetHitpoints(), 0);
TS_ASSERT_EQUALS(cmpHealth.GetMaxHitpoints(), 50);
TS_ASSERT_EQUALS(cmpHealth.IsInjured(), false);
cmpHealth = setEntityUp();
// Check that increasing by more than required puts us at the max HP
change = cmpHealth.Reduce(30);
change = cmpHealth.Increase(30);
TS_ASSERT_EQUALS(injured_flag, false);
TS_ASSERT_EQUALS(change.new, 50);
TS_ASSERT_EQUALS(cmpHealth.GetHitpoints(), 50);
TS_ASSERT_EQUALS(cmpHealth.GetMaxHitpoints(), 50);
TS_ASSERT_EQUALS(cmpHealth.IsInjured(), false);
TS_ASSERT_EQUALS(cmpHealth.IsUnhealable(), true);