mirror of
https://gitea.wildfiregames.com/0ad/0ad
synced 2026-06-16 05:13:58 -07:00
324 lines
11 KiB
JavaScript
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);
|
|
}
|
|
|