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

324 lines
11 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];
let entityID = 100;
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));
// Test GetClosestApproachDistanceToTurretPoint
{
const holder = ++entityID;
// Mock the holder's obstruction
AddMock(holder, IID_Obstruction, {
"GetBlockMovementFlag": () => true,
"GetObstructionHalfSizes": () => ({ "x": 10, "y": 15 })
});
const cmpHolder = ConstructComponent(holder, "TurretHolder", {
"TurretPoints": {
"center": {
"X": "0",
"Y": "5.0",
"Z": "0"
},
"edge": {
"X": "8.0",
"Y": "5.0",
"Z": "0"
},
"corner": {
"X": "10.0",
"Y": "5.0",
"Z": "15.0"
},
"outside": {
"X": "15.0",
"Y": "5.0",
"Z": "0"
}
}
});
// Center point (0,0) in 20x30 building → min(10, 15) = 10
TS_ASSERT_EQUALS(cmpHolder.GetClosestApproachDistanceToTurretPoint("center"), 10);
// Edge point (8,0) in 20x30 building → min(10-8, 15-0) = 2
TS_ASSERT_EQUALS(cmpHolder.GetClosestApproachDistanceToTurretPoint("edge"), 2);
// Corner point (10,15) in 20x30 building → min(10-10, 15-15) = 0
TS_ASSERT_EQUALS(cmpHolder.GetClosestApproachDistanceToTurretPoint("corner"), 0);
// Outside point (15,0) in 20x30 building → min(10-15, 15-0) = -5 → clamped to 0
TS_ASSERT_EQUALS(cmpHolder.GetClosestApproachDistanceToTurretPoint("outside"), 0);
// Nonexistent turret point
TS_ASSERT_EQUALS(cmpHolder.GetClosestApproachDistanceToTurretPoint("nonexistent"), 0);
// Pass object directly
const turretPoint = cmpHolder.TurretPointByName("center");
TS_ASSERT_EQUALS(cmpHolder.GetClosestApproachDistanceToTurretPoint(turretPoint), 10);
// Passable building (no obstruction or doesn't block movement)
const passableHolder = ++entityID;
AddMock(passableHolder, IID_Obstruction, {
"GetBlockMovementFlag": () => false
});
const cmpHolderPassable = ConstructComponent(passableHolder, "TurretHolder", {
"TurretPoints": {
"center": { "X": "0", "Y": "5.0", "Z": "0" }
}
});
TS_ASSERT_EQUALS(cmpHolderPassable.GetClosestApproachDistanceToTurretPoint("center"), 0);
// No obstruction component at all
++entityID;
const cmpHolderNoObst = ConstructComponent(entityID, "TurretHolder", {
"TurretPoints": {
"center": { "X": "0", "Y": "5.0", "Z": "0" }
}
});
TS_ASSERT_EQUALS(cmpHolderNoObst.GetClosestApproachDistanceToTurretPoint("center"), 0);
}