0ad/binaries/data/mods/public/simulation/components/tests/test_StatusEffectsReceiver.js
Dunedan 93ce94655d
Use @stylistic/brace-style for eslint
Up to now `eslint-plugin-brace-rules` was used to enforce a common brace
style for JavaScript code. This plugin was however updated the last time
over 9 years ago and will be incompatible with ESLint v10, as that
[removes `context.getSourceCode()`][1], the plugin relies on.

To keep the eslint config working with ESLint v10, this replaces
`eslint-plugin-brace-rules` with the [`@stylistic/brace-style`][2] rule
from `@stylistic/eslint-plugin`, a package we already use.

While `@stylistic/brace-style` doesn't offer an option to format braces
in exactly the same way as before, the "allman" style seems to be the
one closest to the existing code.

[1]: https://eslint.org/blog/2025/11/eslint-v10.0.0-alpha.0-released/#removed-deprecated-rule-context-members
[2]: https://eslint.style/rules/brace-style
2026-01-12 21:33:52 +01:00

340 lines
7.9 KiB
JavaScript

Engine.LoadHelperScript("Player.js");
Engine.LoadHelperScript("ValueModification.js");
Engine.LoadComponentScript("interfaces/Health.js");
Engine.LoadComponentScript("interfaces/ModifiersManager.js");
Engine.LoadComponentScript("interfaces/StatusEffectsReceiver.js");
Engine.LoadComponentScript("interfaces/Timer.js");
Engine.LoadComponentScript("Health.js");
Engine.LoadComponentScript("ModifiersManager.js");
Engine.LoadComponentScript("StatusEffectsReceiver.js");
Engine.LoadComponentScript("Timer.js");
const target = 42;
const cmpStatusReceiver = ConstructComponent(target, "StatusEffectsReceiver");
const cmpTimer = ConstructComponent(SYSTEM_ENTITY, "Timer");
let dealtDamage = 0;
const enemyEntity = 4;
const enemy = 2;
let statusName;
const AttackHelper = {
"HandleAttackEffects": (_, data) =>
{
for (const type in data.attackData.Damage)
dealtDamage += data.attackData.Damage[type];
}
};
Engine.RegisterGlobal("AttackHelper", AttackHelper);
function reset()
{
for (const status in cmpStatusReceiver.GetActiveStatuses())
cmpStatusReceiver.RemoveStatus(status);
dealtDamage = 0;
}
// Test adding a single effect.
statusName = "Burn";
// Damage scheduled: 0, 10, 20 seconds.
cmpStatusReceiver.AddStatus(statusName, {
"Duration": 20000,
"Interval": 10000,
"Damage": {
[statusName]: 1
}
},
enemyEntity,
enemy
);
cmpTimer.OnUpdate({ "turnLength": 1 });
TS_ASSERT_EQUALS(dealtDamage, 1); // 1 sec
cmpTimer.OnUpdate({ "turnLength": 8 });
TS_ASSERT_EQUALS(dealtDamage, 1); // 9 sec
cmpTimer.OnUpdate({ "turnLength": 1 });
TS_ASSERT_EQUALS(dealtDamage, 2); // 10 sec
cmpTimer.OnUpdate({ "turnLength": 10 });
TS_ASSERT_EQUALS(dealtDamage, 3); // 20 sec
cmpTimer.OnUpdate({ "turnLength": 10 });
TS_ASSERT_EQUALS(dealtDamage, 3); // 30 sec
// Test adding multiple effects.
reset();
// Damage scheduled: 0, 1, 2, 10 seconds.
cmpStatusReceiver.ApplyStatus({
"Burn": {
"Duration": 20000,
"Interval": 10000,
"Damage": {
"Burn": 10
}
},
"Poison": {
"Duration": 3000,
"Interval": 1000,
"Damage": {
"Poison": 1
}
}
});
cmpTimer.OnUpdate({ "turnLength": 1 });
TS_ASSERT_EQUALS(dealtDamage, 12); // 1 sec
cmpTimer.OnUpdate({ "turnLength": 1 });
TS_ASSERT_EQUALS(dealtDamage, 13); // 2 sec
cmpTimer.OnUpdate({ "turnLength": 1 });
TS_ASSERT_EQUALS(dealtDamage, 13); // 3 sec
cmpTimer.OnUpdate({ "turnLength": 7 });
TS_ASSERT_EQUALS(dealtDamage, 23); // 10 sec
// Test removing a status removes effects.
reset();
statusName = "Poison";
// Damage scheduled: 0, 10, 20 seconds.
cmpStatusReceiver.AddStatus(statusName, {
"Duration": 20000,
"Interval": 10000,
"Damage": {
[statusName]: 1
}
},
enemyEntity,
enemy
);
cmpTimer.OnUpdate({ "turnLength": 1 });
TS_ASSERT_EQUALS(dealtDamage, 1); // 1 sec
cmpStatusReceiver.RemoveStatus(statusName);
cmpTimer.OnUpdate({ "turnLength": 10 });
TS_ASSERT_EQUALS(dealtDamage, 1); // 11 sec
// Test that a status effect with modifications modifies.
reset();
AddMock(target, IID_Identity, {
"GetClassesList": () => ["AffectedClass"]
});
const cmpModifiersManager = ConstructComponent(SYSTEM_ENTITY, "ModifiersManager");
const maxHealth = 100;
AddMock(target, IID_Health, {
"GetMaxHitpoints": () => ApplyValueModificationsToEntity("Health/Max", maxHealth, target)
});
statusName = "Haste";
const factor = 0.5;
cmpStatusReceiver.AddStatus(statusName, {
"Duration": 5000,
"Modifiers": {
[statusName]: {
"Paths": {
"_string": "Health/Max"
},
"Affects": {
"_string": "AffectedClass"
},
"Multiply": factor
}
}
},
enemyEntity,
enemy
);
const cmpHealth = Engine.QueryInterface(target, IID_Health);
// Test that the modification is applied.
TS_ASSERT_EQUALS(cmpHealth.GetMaxHitpoints(), maxHealth * factor);
cmpTimer.OnUpdate({ "turnLength": 1 });
TS_ASSERT_EQUALS(cmpHealth.GetMaxHitpoints(), maxHealth * factor);
// Test that the modification is removed after the appropriate time.
cmpTimer.OnUpdate({ "turnLength": 4 });
TS_ASSERT_EQUALS(cmpHealth.GetMaxHitpoints(), maxHealth);
// Test addition.
const addition = 50;
cmpStatusReceiver.AddStatus(statusName, {
"Duration": 5000,
"Modifiers": {
[statusName]: {
"Paths": {
"_string": "Health/Max"
},
"Affects": {
"_string": "AffectedClass"
},
"Add": addition
}
}
},
enemyEntity,
enemy
);
// Test that the addition modification is applied.
TS_ASSERT_EQUALS(cmpHealth.GetMaxHitpoints(), maxHealth + addition);
cmpTimer.OnUpdate({ "turnLength": 1 });
TS_ASSERT_EQUALS(cmpHealth.GetMaxHitpoints(), maxHealth + addition);
// Test that the modification is removed after the appropriate time.
cmpTimer.OnUpdate({ "turnLength": 4 });
TS_ASSERT_EQUALS(cmpHealth.GetMaxHitpoints(), maxHealth);
// Test replacement.
const newValue = 50;
cmpStatusReceiver.AddStatus(statusName, {
"Duration": 5000,
"Modifiers": {
[statusName]: {
"Paths": {
"_string": "Health/Max"
},
"Affects": {
"_string": "AffectedClass"
},
"Replace": newValue
}
}
},
enemyEntity,
enemy
);
// Test that the replacement modification is applied.
TS_ASSERT_EQUALS(cmpHealth.GetMaxHitpoints(), newValue);
cmpTimer.OnUpdate({ "turnLength": 1 });
TS_ASSERT_EQUALS(cmpHealth.GetMaxHitpoints(), newValue);
// Test that the modification is removed after the appropriate time.
cmpTimer.OnUpdate({ "turnLength": 4 });
TS_ASSERT_EQUALS(cmpHealth.GetMaxHitpoints(), maxHealth);
function applyStatus(stackability)
{
cmpStatusReceiver.ApplyStatus({
"randomName": {
"Duration": 3000,
"Interval": 1000,
"Damage": {
"randomName": 1
},
"Stackability": stackability
}
});
}
// Test different stackabilities.
// First ignoring, i.e. next time the same status is added it is just ignored.
reset();
applyStatus("Ignore");
// 1 Second: 1 update and lateness.
cmpTimer.OnUpdate({ "turnLength": 1 });
TS_ASSERT_EQUALS(dealtDamage, 2);
applyStatus("Ignore");
// 2 Seconds.
cmpTimer.OnUpdate({ "turnLength": 1 });
TS_ASSERT_EQUALS(dealtDamage, 3);
// 3 Seconds: finished in previous turn.
cmpTimer.OnUpdate({ "turnLength": 1 });
TS_ASSERT_EQUALS(dealtDamage, 3);
// Extending, i.e. next time the same status is applied the times are added.
reset();
applyStatus("Extend");
// 1 Second: 1 update and lateness.
cmpTimer.OnUpdate({ "turnLength": 1 });
TS_ASSERT_EQUALS(dealtDamage, 2);
// Add 3 seconds.
applyStatus("Extend");
// 2 Seconds.
cmpTimer.OnUpdate({ "turnLength": 1 });
TS_ASSERT_EQUALS(dealtDamage, 3);
// 3 Seconds: extended in previous turn.
cmpTimer.OnUpdate({ "turnLength": 1 });
TS_ASSERT_EQUALS(dealtDamage, 4);
// 4 Seconds.
cmpTimer.OnUpdate({ "turnLength": 1 });
TS_ASSERT_EQUALS(dealtDamage, 5);
// 5 Seconds.
cmpTimer.OnUpdate({ "turnLength": 1 });
TS_ASSERT_EQUALS(dealtDamage, 6);
// 6 Seconds: finished in previous turn (3 + 3).
cmpTimer.OnUpdate({ "turnLength": 1 });
TS_ASSERT_EQUALS(dealtDamage, 6);
// Replacing, i.e. the next applied status replaces the former.
reset();
applyStatus("Replace");
// 1 Second: 1 update and lateness.
cmpTimer.OnUpdate({ "turnLength": 1 });
TS_ASSERT_EQUALS(dealtDamage, 2);
applyStatus("Replace");
// 2 Seconds: 1 update and lateness of the new status.
cmpTimer.OnUpdate({ "turnLength": 1 });
TS_ASSERT_EQUALS(dealtDamage, 4);
// 3 Seconds.
cmpTimer.OnUpdate({ "turnLength": 1 });
TS_ASSERT_EQUALS(dealtDamage, 5);
// 4 Seconds: finished in previous turn.
cmpTimer.OnUpdate({ "turnLength": 1 });
TS_ASSERT_EQUALS(dealtDamage, 5);
// Stacking, every new status just applies besides the rest.
reset();
applyStatus("Stack");
// 1 Second: 1 update and lateness.
cmpTimer.OnUpdate({ "turnLength": 1 });
TS_ASSERT_EQUALS(dealtDamage, 2);
applyStatus("Stack");
// 2 Seconds: 1 damage from the previous status + 2 from the new (1 turn + lateness).
cmpTimer.OnUpdate({ "turnLength": 1 });
TS_ASSERT_EQUALS(dealtDamage, 5);
// 3 Seconds: first one finished in the previous turn, +1 from the new.
cmpTimer.OnUpdate({ "turnLength": 1 });
TS_ASSERT_EQUALS(dealtDamage, 6);
// 4 Seconds: new status finished in previous turn.
cmpTimer.OnUpdate({ "turnLength": 1 });
TS_ASSERT_EQUALS(dealtDamage, 6);