0ad/binaries/data/mods/public/simulation/components/tests/test_ModifiersManager.js

251 lines
10 KiB
JavaScript
Raw Normal View History

Engine.LoadComponentScript("interfaces/ModifiersManager.js");
Engine.LoadComponentScript("ModifiersManager.js");
Engine.LoadHelperScript("Player.js");
Engine.LoadHelperScript("ValueModification.js");
Engine.LoadComponentScript("interfaces/Health.js");
let cmpModifiersManager = ConstructComponent(SYSTEM_ENTITY, "ModifiersManager", {});
cmpModifiersManager.Init();
// These should be different as that is the general case.
const PLAYER_ID_FOR_TEST = 2;
const PLAYER_ENTITY_ID = 3;
const STRUCTURE_ENTITY_ID = 5;
AddMock(SYSTEM_ENTITY, IID_RangeManager, {
"GetEntitiesByPlayer": () => [],
});
AddMock(SYSTEM_ENTITY, IID_PlayerManager, {
"GetPlayerByID": (a) => PLAYER_ENTITY_ID
});
AddMock(PLAYER_ENTITY_ID, IID_Player, {
"GetPlayerID": () => PLAYER_ID_FOR_TEST
});
const entitiesToTest = [STRUCTURE_ENTITY_ID, 6, 7, 8];
for (const ent of entitiesToTest)
AddMock(ent, IID_Ownership, {
"GetOwner": () => PLAYER_ID_FOR_TEST
});
AddMock(PLAYER_ENTITY_ID, IID_Identity, {
"GetClassesList": () => "Player",
});
AddMock(STRUCTURE_ENTITY_ID, IID_Identity, {
"GetClassesList": () => "Structure",
});
AddMock(6, IID_Identity, {
"GetClassesList": () => "Infantry",
});
AddMock(7, IID_Identity, {
"GetClassesList": () => "Unit",
});
AddMock(8, IID_Identity, {
"GetClassesList": () => "Structure Unit",
});
// Sprinkle random serialisation cycles.
function SerializationCycle()
{
const data = cmpModifiersManager.Serialize();
cmpModifiersManager = ConstructComponent(SYSTEM_ENTITY, "ModifiersManager", {});
cmpModifiersManager.Deserialize(data);
}
cmpModifiersManager.OnGlobalPlayerEntityChanged({ "player": PLAYER_ID_FOR_TEST, "from": -1, "to": PLAYER_ENTITY_ID });
cmpModifiersManager.AddModifier("Test_A", "Test_A_0", [{ "affects": ["Structure"], "add": 10 }], 10, "testLol");
cmpModifiersManager.AddModifier("Test_A", "Test_A_0", [{ "affects": ["Structure"], "add": 10 }], PLAYER_ENTITY_ID);
cmpModifiersManager.AddModifier("Test_A", "Test_A_1", [{ "affects": ["Infantry"], "add": 5 }], PLAYER_ENTITY_ID);
cmpModifiersManager.AddModifier("Test_A", "Test_A_2", [{ "affects": ["Unit"], "add": 3 }], PLAYER_ENTITY_ID);
TS_ASSERT_EQUALS(ApplyValueModificationsToEntity("Test_A", 5, PLAYER_ENTITY_ID), 5);
cmpModifiersManager.AddModifier("Test_A", "Test_A_Player", [{ "affects": ["Player"], "add": 3 }], PLAYER_ENTITY_ID);
TS_ASSERT_EQUALS(ApplyValueModificationsToEntity("Test_A", 5, PLAYER_ENTITY_ID), 8);
TS_ASSERT_EQUALS(ApplyValueModificationsToEntity("Test_A", 5, 5), 15);
TS_ASSERT_EQUALS(ApplyValueModificationsToEntity("Test_A", 5, 6), 10);
SerializationCycle();
TS_ASSERT_EQUALS(ApplyValueModificationsToEntity("Test_A", 5, 5), 15);
TS_ASSERT_EQUALS(ApplyValueModificationsToEntity("Test_A", 5, 6), 10);
TS_ASSERT_EQUALS(ApplyValueModificationsToEntity("Test_A", 5, 7), 8);
TS_ASSERT_EQUALS(ApplyValueModificationsToEntity("Test_A", 5, 8), 18);
TS_ASSERT_EQUALS(ApplyValueModificationsToEntity("Test_B", 5, 8), 5);
cmpModifiersManager.RemoveAllModifiers("Test_A_0", PLAYER_ENTITY_ID);
TS_ASSERT_EQUALS(ApplyValueModificationsToEntity("Test_A", 5, 5), 5);
cmpModifiersManager.AddModifiers("Test_A_0", {
"Test_A": [{ "affects": ["Structure"], "add": 10 }],
"Test_B": [{ "affects": ["Structure"], "add": 8 }],
}, PLAYER_ENTITY_ID);
TS_ASSERT_EQUALS(ApplyValueModificationsToEntity("Test_A", 5, 5), 15);
TS_ASSERT_EQUALS(ApplyValueModificationsToEntity("Test_B", 5, 8), 13);
// Add two local modifications, only the first should stick.
cmpModifiersManager.AddModifier("Test_C", "Test_C_0", [{ "affects": ["Structure"], "add": 10 }], STRUCTURE_ENTITY_ID);
cmpModifiersManager.AddModifier("Test_C", "Test_C_invalid", [{ "affects": ["Unit"], "add": 5 }], STRUCTURE_ENTITY_ID);
SerializationCycle();
TS_ASSERT_EQUALS(ApplyValueModificationsToEntity("Test_C", 5, STRUCTURE_ENTITY_ID), 15);
// test that local modifications are indeed applied after global managers
cmpModifiersManager.AddModifier("Test_C", "Test_C_player", [{ "affects": ["Structure"], "replace": 2 }], PLAYER_ENTITY_ID);
TS_ASSERT_EQUALS(ApplyValueModificationsToEntity("Test_C", 5, STRUCTURE_ENTITY_ID), 12);
TS_ASSERT_EQUALS(ApplyValueModificationsToEntity("Test_C", 2, STRUCTURE_ENTITY_ID), 12);
SerializationCycle();
// test removal
cmpModifiersManager.RemoveModifier("Test_C", "Test_C_player", PLAYER_ENTITY_ID);
TS_ASSERT_EQUALS(ApplyValueModificationsToEntity("Test_C", 5, STRUCTURE_ENTITY_ID), 15);
// check that things still work properly if we change global modifications
cmpModifiersManager.AddModifier("Test_C", "Test_C_player", [{ "affects": ["Structure"], "add": 12 }], PLAYER_ENTITY_ID);
TS_ASSERT_EQUALS(ApplyValueModificationsToEntity("Test_C", 5, STRUCTURE_ENTITY_ID), 27);
TS_ASSERT(cmpModifiersManager.HasAnyModifier("Test_C_player", PLAYER_ENTITY_ID));
TS_ASSERT(cmpModifiersManager.HasModifier("Test_C", "Test_C_player", PLAYER_ENTITY_ID));
SerializationCycle();
TS_ASSERT(cmpModifiersManager.HasModifier("Test_C", "Test_C_player", PLAYER_ENTITY_ID));
TS_ASSERT(!cmpModifiersManager.HasModifier("Test_C", "Test_C_player", STRUCTURE_ENTITY_ID));
// Regression test for a caching issue
cmpModifiersManager.AddModifier("Test_E", "Test_E_player", [{ "affects": ["Structure"], "add": 1 }], PLAYER_ENTITY_ID);
TS_ASSERT_EQUALS(ApplyValueModificationsToEntity("Test_E", 3, STRUCTURE_ENTITY_ID), 4);
cmpModifiersManager.AddModifier("Test_E", "Test_E_1", [{ "affects": ["Structure"], "add": 1 }], STRUCTURE_ENTITY_ID);
TS_ASSERT_EQUALS(ApplyValueModificationsToEntity("Test_E", 4, STRUCTURE_ENTITY_ID), 6);
TS_ASSERT_EQUALS(ApplyValueModificationsToEntity("Test_E", 5, STRUCTURE_ENTITY_ID), 7);
// Test that entities keep local modifications but not global ones when changing owner.
AddMock(SYSTEM_ENTITY, IID_PlayerManager, {
"GetPlayerByID": (a) => a == PLAYER_ID_FOR_TEST ? PLAYER_ENTITY_ID : PLAYER_ENTITY_ID + 1
});
AddMock(PLAYER_ENTITY_ID + 1, IID_Player, {
"GetPlayerID": () => PLAYER_ID_FOR_TEST + 1
});
cmpModifiersManager = ConstructComponent(SYSTEM_ENTITY, "ModifiersManager", {});
cmpModifiersManager.Init();
cmpModifiersManager.AddModifier("Test_D", "Test_D_0", [{ "affects": ["Structure"], "add": 10 }], PLAYER_ENTITY_ID);
cmpModifiersManager.AddModifier("Test_D", "Test_D_1", [{ "affects": ["Structure"], "add": 1 }], PLAYER_ENTITY_ID + 1);
cmpModifiersManager.AddModifier("Test_D", "Test_D_2", [{ "affects": ["Structure"], "add": 5 }], STRUCTURE_ENTITY_ID);
cmpModifiersManager.OnGlobalPlayerEntityChanged({ "player": PLAYER_ID_FOR_TEST, "from": -1, "to": PLAYER_ENTITY_ID });
cmpModifiersManager.OnGlobalPlayerEntityChanged({ "player": PLAYER_ID_FOR_TEST + 1, "from": -1, "to": PLAYER_ENTITY_ID + 1 });
TS_ASSERT_EQUALS(ApplyValueModificationsToEntity("Test_D", 10, 5), 25);
cmpModifiersManager.OnGlobalOwnershipChanged({ "entity": 5, "from": PLAYER_ID_FOR_TEST, "to": PLAYER_ID_FOR_TEST + 1 });
AddMock(5, IID_Ownership, {
"GetOwner": () => PLAYER_ID_FOR_TEST + 1
});
TS_ASSERT_EQUALS(ApplyValueModificationsToEntity("Test_D", 10, 5), 16);
// Test: Entity changes owner from player 2 (HP modifier) to player 3 (Vision modifier)
(function Test_OwnerChange_ModifierSwitch()
{
const PLAYER2_ID = 2;
const PLAYER3_ID = 3;
const PLAYER2_ENTITY = 20;
const PLAYER3_ENTITY = 21;
const TEST_ENTITY = 30;
const baseHp = 100;
const baseVision = 20;
// Set up mocks for both players
AddMock(SYSTEM_ENTITY, IID_PlayerManager, {
"GetPlayerByID": (a) => a === PLAYER2_ID ? PLAYER2_ENTITY : PLAYER3_ENTITY
});
AddMock(PLAYER2_ENTITY, IID_Player, {
"GetPlayerID": () => PLAYER2_ID
});
AddMock(PLAYER3_ENTITY, IID_Player, {
"GetPlayerID": () => PLAYER3_ID
});
AddMock(TEST_ENTITY, IID_Ownership, {
"GetOwner": () => PLAYER2_ID
});
AddMock(TEST_ENTITY, IID_Identity, {
"GetClassesList": () => "Unit"
});
// These components cache the values, so we need to mock the message passing.
let cachedHp = baseHp;
AddMock(TEST_ENTITY, IID_Health, {
"GetHitPoints": () => cachedHp,
});
let cachedVision = baseVision;
AddMock(TEST_ENTITY, IID_Vision, {
"GetRange": () => cachedVision
});
const oldPostMessage = Engine.PostMessage;
const oldBroadcastMessage = Engine.BroadcastMessage;
Engine.PostMessage = function(ent, iid, message)
{
if (message.component === "HP")
cachedHp = ApplyValueModificationsToEntity("HP", baseHp, TEST_ENTITY);
else if (message.component === "Vision")
cachedVision = ApplyValueModificationsToEntity("Vision", baseVision, TEST_ENTITY);
else
throw new Error("Unexpected component: " + message.component);
};
Engine.BroadcastMessage = function(iid, message)
{
if (message.component === "HP")
cachedHp = ApplyValueModificationsToEntity("HP", baseHp, TEST_ENTITY);
else if (message.component === "Vision")
cachedVision = ApplyValueModificationsToEntity("Vision", baseVision, TEST_ENTITY);
else
throw new Error("Unexpected component: " + message.component);
};
// Initialize ModifiersManager
const cmp = ConstructComponent(SYSTEM_ENTITY, "ModifiersManager", {});
cmp.Init();
cmp.OnGlobalPlayerEntityChanged({ "player": PLAYER2_ID, "from": INVALID_PLAYER, "to": PLAYER2_ENTITY });
cmp.OnGlobalPlayerEntityChanged({ "player": PLAYER3_ID, "from": INVALID_PLAYER, "to": PLAYER3_ENTITY });
// Player 2 gets HP modifier
cmp.AddModifier("HP", "HP_mod", [{ "affects": ["Unit"], "add": 50 }], PLAYER2_ENTITY);
// Player 3 gets Vision modifier
cmp.AddModifier("Vision", "Vision_mod", [{ "affects": ["Unit"], "add": 10 }], PLAYER3_ENTITY);
// Should have HP modified, not Vision
TS_ASSERT_EQUALS(ApplyValueModificationsToEntity("HP", baseHp, TEST_ENTITY), 150);
TS_ASSERT_EQUALS(ApplyValueModificationsToEntity("Vision", baseVision, TEST_ENTITY), 20);
TS_ASSERT_EQUALS(Engine.QueryInterface(TEST_ENTITY, IID_Health).GetHitPoints(), 150);
TS_ASSERT_EQUALS(Engine.QueryInterface(TEST_ENTITY, IID_Vision).GetRange(), 20);
// Change owner to player 3
AddMock(TEST_ENTITY, IID_Ownership, {
"GetOwner": () => PLAYER3_ID
});
cmp.OnGlobalOwnershipChanged({ "entity": TEST_ENTITY, "from": PLAYER2_ID, "to": PLAYER3_ID });
// Now should have Vision modified, not HP
TS_ASSERT_EQUALS(ApplyValueModificationsToEntity("HP", baseHp, TEST_ENTITY), 100);
TS_ASSERT_EQUALS(ApplyValueModificationsToEntity("Vision", baseVision, TEST_ENTITY), 30);
TS_ASSERT_EQUALS(Engine.QueryInterface(TEST_ENTITY, IID_Health).GetHitPoints(), 100);
TS_ASSERT_EQUALS(Engine.QueryInterface(TEST_ENTITY, IID_Vision).GetRange(), 30);
// Cleanup
Engine.PostMessage = oldPostMessage;
Engine.BroadcastMessage = oldBroadcastMessage;
})();