0ad/binaries/data/mods/public/simulation/components/tests/test_TurretHolder.js
Atrik fb1c0d2a82 Fix renamed entities on TurretPoint
Some units would retain an undue HeightOffset after renaming
while being on a TurretPoint.

Fixes #8651
Fixes #2004
2026-02-04 11:05:07 +01:00

246 lines
8.3 KiB
JavaScript

Engine.LoadHelperScript("Player.js");
Engine.LoadComponentScript("interfaces/Diplomacy.js");
Engine.LoadComponentScript("interfaces/TurretHolder.js");
Engine.LoadComponentScript("interfaces/Turretable.js");
Engine.LoadComponentScript("interfaces/UnitAI.js");
Engine.LoadComponentScript("TurretHolder.js");
Engine.LoadComponentScript("Turretable.js");
AddMock(SYSTEM_ENTITY, IID_PlayerManager, {
"GetPlayerByID": id => id
});
const player = 1;
const enemyPlayer = 2;
const alliedPlayer = 3;
const turretHolderID = 9;
const entitiesToTest = [10, 11, 12, 13];
AddMock(turretHolderID, IID_Ownership, {
"GetOwner": () => player
});
AddMock(turretHolderID, IID_Position, {
"GetPosition": () => new Vector3D(4, 3, 25),
"GetRotation": () => new Vector3D(4, 0, 6),
"IsInWorld": () => true
});
for (const entity of entitiesToTest)
{
AddMock(entity, IID_Position, {
"GetPosition": () => new Vector3D(4, 3, 25),
"GetRotation": () => new Vector3D(4, 0, 6),
"SetTurretParent": (parent, offset) => {},
"IsInWorld": () => true
});
AddMock(entity, IID_Ownership, {
"GetOwner": () => player
});
}
AddMock(player, IID_Diplomacy, {
"IsAlly": id => id != enemyPlayer,
"IsMutualAlly": id => id != enemyPlayer,
});
AddMock(alliedPlayer, IID_Diplomacy, {
"IsAlly": id => true,
"IsMutualAlly": id => true,
});
let cmpTurretHolder = ConstructComponent(turretHolderID, "TurretHolder", {
"TurretPoints": {
"archer1": {
"X": "12.0",
"Y": "5.",
"Z": "6.0"
},
"archer2": {
"X": "15.0",
"Y": "5.0",
"Z": "6.0",
"AllowedClasses": { "_string": "Siege Trader" }
},
"archer3": {
"X": "15.0",
"Y": "5.0",
"Z": "6.0",
"AllowedClasses": { "_string": "Siege Infantry+Ranged Infantry+Cavalry" }
}
}
});
const siegeEngineID = entitiesToTest[0];
AddMock(siegeEngineID, IID_Identity, {
"GetClassesList": () => ["Siege"]
});
const archerID = entitiesToTest[1];
AddMock(archerID, IID_Identity, {
"GetClassesList": () => ["Infantry", "Ranged"]
});
const cavID = entitiesToTest[2];
AddMock(cavID, IID_Identity, {
"GetClassesList": () => ["Infantry", "Cavalry"]
});
const infID = entitiesToTest[3];
AddMock(infID, IID_Identity, {
"GetClassesList": () => ["Infantry"]
});
// Test visible garrisoning restrictions.
TS_ASSERT_EQUALS(cmpTurretHolder.AllowedToOccupyTurretPoint(siegeEngineID, cmpTurretHolder.turretPoints[0]), true);
TS_ASSERT_EQUALS(cmpTurretHolder.AllowedToOccupyTurretPoint(siegeEngineID, cmpTurretHolder.turretPoints[1]), true);
TS_ASSERT_EQUALS(cmpTurretHolder.AllowedToOccupyTurretPoint(siegeEngineID, cmpTurretHolder.turretPoints[2]), true);
TS_ASSERT_EQUALS(cmpTurretHolder.AllowedToOccupyTurretPoint(archerID, cmpTurretHolder.turretPoints[0]), true);
TS_ASSERT_EQUALS(cmpTurretHolder.AllowedToOccupyTurretPoint(archerID, cmpTurretHolder.turretPoints[1]), false);
TS_ASSERT_EQUALS(cmpTurretHolder.AllowedToOccupyTurretPoint(archerID, cmpTurretHolder.turretPoints[2]), true);
TS_ASSERT_EQUALS(cmpTurretHolder.AllowedToOccupyTurretPoint(cavID, cmpTurretHolder.turretPoints[0]), true);
TS_ASSERT_EQUALS(cmpTurretHolder.AllowedToOccupyTurretPoint(cavID, cmpTurretHolder.turretPoints[1]), false);
TS_ASSERT_EQUALS(cmpTurretHolder.AllowedToOccupyTurretPoint(cavID, cmpTurretHolder.turretPoints[2]), true);
TS_ASSERT_EQUALS(cmpTurretHolder.AllowedToOccupyTurretPoint(infID, cmpTurretHolder.turretPoints[0]), true);
TS_ASSERT_EQUALS(cmpTurretHolder.AllowedToOccupyTurretPoint(infID, cmpTurretHolder.turretPoints[1]), false);
TS_ASSERT_EQUALS(cmpTurretHolder.AllowedToOccupyTurretPoint(infID, cmpTurretHolder.turretPoints[2]), false);
// Test forReplacement parameter - empty turret points should allow both
TS_ASSERT_EQUALS(cmpTurretHolder.AllowedToOccupyTurretPoint(siegeEngineID, cmpTurretHolder.turretPoints[0], false), true);
TS_ASSERT_EQUALS(cmpTurretHolder.AllowedToOccupyTurretPoint(siegeEngineID, cmpTurretHolder.turretPoints[0], true), true);
// Now occupy some turret points to test replacement logic
TS_ASSERT(cmpTurretHolder.OccupyTurretPoint(archerID, cmpTurretHolder.turretPoints[0]));
TS_ASSERT(cmpTurretHolder.OccupyTurretPoint(cavID, cmpTurretHolder.turretPoints[2]));
// Test that occupied turrets block normal occupation but allow replacement
TS_ASSERT_EQUALS(cmpTurretHolder.AllowedToOccupyTurretPoint(siegeEngineID, cmpTurretHolder.turretPoints[0], false), false);
TS_ASSERT_EQUALS(cmpTurretHolder.AllowedToOccupyTurretPoint(siegeEngineID, cmpTurretHolder.turretPoints[0], true), true);
TS_ASSERT_EQUALS(cmpTurretHolder.AllowedToOccupyTurretPoint(infID, cmpTurretHolder.turretPoints[2], false), false);
TS_ASSERT_EQUALS(cmpTurretHolder.AllowedToOccupyTurretPoint(infID, cmpTurretHolder.turretPoints[2], true), false); // Still false due to class restriction
// Test that class restrictions still apply even for replacement
TS_ASSERT_EQUALS(cmpTurretHolder.AllowedToOccupyTurretPoint(infID, cmpTurretHolder.turretPoints[1], false), false);
TS_ASSERT_EQUALS(cmpTurretHolder.AllowedToOccupyTurretPoint(infID, cmpTurretHolder.turretPoints[1], true), false); // Still false due to class restriction
// Clean up for subsequent tests
TS_ASSERT(cmpTurretHolder.LeaveTurretPoint(archerID));
TS_ASSERT(cmpTurretHolder.LeaveTurretPoint(cavID));
// Test that one cannot leave a turret that is not occupied.
TS_ASSERT(!cmpTurretHolder.LeaveTurretPoint(archerID));
// Test occupying a turret.
TS_ASSERT(!cmpTurretHolder.OccupiesTurretPoint(archerID));
TS_ASSERT(cmpTurretHolder.OccupyTurretPoint(archerID));
TS_ASSERT(cmpTurretHolder.OccupiesTurretPoint(archerID));
// We're not occupying a turret that we can't occupy.
TS_ASSERT(!cmpTurretHolder.OccupiesTurretPoint(archerID, cmpTurretHolder.turretPoints[1]));
TS_ASSERT(!cmpTurretHolder.OccupyTurretPoint(cavID, cmpTurretHolder.turretPoints[1]));
TS_ASSERT(!cmpTurretHolder.OccupyTurretPoint(cavID, cmpTurretHolder.turretPoints[0]));
TS_ASSERT(cmpTurretHolder.OccupyTurretPoint(cavID, cmpTurretHolder.turretPoints[2]));
// Leave turrets.
TS_ASSERT(cmpTurretHolder.LeaveTurretPoint(archerID));
TS_ASSERT(!cmpTurretHolder.LeaveTurretPoint(cavID, false, cmpTurretHolder.turretPoints[1]));
TS_ASSERT(cmpTurretHolder.LeaveTurretPoint(cavID, false, cmpTurretHolder.turretPoints[2]));
// Incremental Turret creation.
cmpTurretHolder = ConstructComponent(turretHolderID, "TurretHolder", {
"TurretPoints": {
"Turret": {
"X": "15.0",
"Y": "5.0",
"Z": "6.0",
"Template": "units/iber/cavalry_javelineer_c"
}
}
});
let spawned = 100;
Engine.AddEntity = function()
{
++spawned;
if (spawned > 101)
{
ConstructComponent(spawned, "Turretable", {});
}
if (spawned > 102)
{
AddMock(spawned, IID_Ownership, {
"GetOwner": () => player,
"SetOwner": () => {}
});
}
if (spawned > 103)
{
AddMock(spawned, IID_Position, {
"GetPosition": () => new Vector3D(4, 3, 25),
"GetRotation": () => new Vector3D(4, 0, 6),
"SetTurretParent": () => {},
"IsInWorld": () => true
});
}
return spawned;
};
const GetUpgradedTemplate = (_, template) => template === "units/iber/cavalry_javelineer_b" ? "units/iber/cavalry_javelineer_a" : template;
Engine.RegisterGlobal("GetUpgradedTemplate", GetUpgradedTemplate);
cmpTurretHolder.OnOwnershipChanged({
"to": 1,
"from": INVALID_PLAYER
});
TS_ASSERT(!cmpTurretHolder.OccupiesTurretPoint(spawned));
cmpTurretHolder.OnOwnershipChanged({
"to": 1,
"from": INVALID_PLAYER
});
TS_ASSERT(!cmpTurretHolder.OccupiesTurretPoint(spawned));
cmpTurretHolder.OnOwnershipChanged({
"to": 1,
"from": INVALID_PLAYER
});
TS_ASSERT(!cmpTurretHolder.OccupiesTurretPoint(spawned));
cmpTurretHolder.OnOwnershipChanged({
"to": 1,
"from": INVALID_PLAYER
});
TS_ASSERT(cmpTurretHolder.OccupiesTurretPoint(spawned));
// Normal turret creation.
Engine.AddEntity = function(t)
{
++spawned;
// Check that we're using the upgraded template.
TS_ASSERT(t, "units/iber/cavalry_javelineer_a");
ConstructComponent(spawned, "Turretable", {});
AddMock(spawned, IID_Ownership, {
"GetOwner": () => player,
"SetOwner": () => {}
});
AddMock(spawned, IID_Position, {
"GetPosition": () => new Vector3D(4, 3, 25),
"GetRotation": () => new Vector3D(4, 0, 6),
"SetTurretParent": () => {},
"IsInWorld": () => true
});
return spawned;
};
cmpTurretHolder = ConstructComponent(turretHolderID, "TurretHolder", {
"TurretPoints": {
"Turret": {
"X": "15.0",
"Y": "5.0",
"Z": "6.0",
"Template": "units/iber/cavalry_javelineer_b"
}
}
});
cmpTurretHolder.OnOwnershipChanged({
"to": 1,
"from": INVALID_PLAYER
});
TS_ASSERT(cmpTurretHolder.OccupiesTurretPoint(spawned));