mirror of
https://gitea.wildfiregames.com/0ad/0ad
synced 2026-08-15 14:43:32 -07:00
Fix renamed entities on TurretPoint
Some units would retain an undue HeightOffset after renaming while being on a TurretPoint. Fixes #8651 Fixes #2004
This commit is contained in:
parent
87ed9c8092
commit
fb1c0d2a82
6 changed files with 93 additions and 3 deletions
|
|
@ -74,12 +74,14 @@ class TurretHolder
|
||||||
/**
|
/**
|
||||||
* @param {number} entity - The entity to check for.
|
* @param {number} entity - The entity to check for.
|
||||||
* @param {Object} turretPoint - The turret point to use.
|
* @param {Object} turretPoint - The turret point to use.
|
||||||
|
* @param {boolean} [forReplacement=false] - Whether this check is for replacement
|
||||||
|
* (if true, occupied turret points are allowed).
|
||||||
*
|
*
|
||||||
* @return {boolean} - Whether the entity is allowed to occupy the specified turret point.
|
* @return {boolean} - Whether the entity is allowed to occupy the specified turret point.
|
||||||
*/
|
*/
|
||||||
AllowedToOccupyTurretPoint(entity, turretPoint)
|
AllowedToOccupyTurretPoint(entity, turretPoint, forReplacement = false)
|
||||||
{
|
{
|
||||||
if (!turretPoint || turretPoint.entity)
|
if (!turretPoint || turretPoint.entity && !forReplacement)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!IsOwnedByMutualAllyOfEntity(entity, this.entity))
|
if (!IsOwnedByMutualAllyOfEntity(entity, this.entity))
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,14 @@ Turretable.prototype.GetRange = function(type, target)
|
||||||
return cmpTurretHolder ? cmpTurretHolder.LoadingRange() : { "min": 0, "max": 1 };
|
return cmpTurretHolder ? cmpTurretHolder.LoadingRange() : { "min": 0, "max": 1 };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {number} - The turret point name of the entity this entity is turreted on.
|
||||||
|
*/
|
||||||
|
Turretable.prototype.GetTurretPointName = function()
|
||||||
|
{
|
||||||
|
return this.turretPointName || "";
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return {number} - The entity ID of the entity this entity is turreted on.
|
* @return {number} - The entity ID of the entity this entity is turreted on.
|
||||||
*/
|
*/
|
||||||
|
|
@ -73,6 +81,7 @@ Turretable.prototype.OccupyTurret = function(target, turretPointName = "", eject
|
||||||
|
|
||||||
this.holder = target;
|
this.holder = target;
|
||||||
this.ejectable = ejectable;
|
this.ejectable = ejectable;
|
||||||
|
this.turretPointName = turretPointName;
|
||||||
|
|
||||||
const cmpUnitAI = Engine.QueryInterface(this.entity, IID_UnitAI);
|
const cmpUnitAI = Engine.QueryInterface(this.entity, IID_UnitAI);
|
||||||
if (cmpUnitAI)
|
if (cmpUnitAI)
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,8 @@ Engine.LoadComponentScript("interfaces/TerritoryDecay.js");
|
||||||
Engine.LoadComponentScript("interfaces/Trigger.js");
|
Engine.LoadComponentScript("interfaces/Trigger.js");
|
||||||
Engine.LoadComponentScript("interfaces/Timer.js");
|
Engine.LoadComponentScript("interfaces/Timer.js");
|
||||||
Engine.LoadComponentScript("interfaces/UnitAI.js");
|
Engine.LoadComponentScript("interfaces/UnitAI.js");
|
||||||
|
Engine.LoadComponentScript("interfaces/Turretable.js");
|
||||||
|
Engine.LoadComponentScript("interfaces/TurretHolder.js");
|
||||||
Engine.LoadComponentScript("AutoBuildable.js");
|
Engine.LoadComponentScript("AutoBuildable.js");
|
||||||
Engine.LoadComponentScript("Foundation.js");
|
Engine.LoadComponentScript("Foundation.js");
|
||||||
Engine.LoadComponentScript("Timer.js");
|
Engine.LoadComponentScript("Timer.js");
|
||||||
|
|
@ -101,6 +103,12 @@ function testFoundation(...mocks)
|
||||||
"MoveOutOfWorld": () => {}
|
"MoveOutOfWorld": () => {}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
AddMock(foundationEnt, IID_Turretable, {
|
||||||
|
"IsTurreted": () => false,
|
||||||
|
"HolderID": () => 0,
|
||||||
|
"LeaveTurret": () => true
|
||||||
|
});
|
||||||
|
|
||||||
AddMock(previewEnt, IID_Ownership, {
|
AddMock(previewEnt, IID_Ownership, {
|
||||||
"SetOwner": owner => { TS_ASSERT_EQUALS(owner, player); },
|
"SetOwner": owner => { TS_ASSERT_EQUALS(owner, player); },
|
||||||
});
|
});
|
||||||
|
|
@ -140,6 +148,13 @@ function testFoundation(...mocks)
|
||||||
"SetHeightOffset": () => {}
|
"SetHeightOffset": () => {}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
AddMock(newEnt, IID_Turretable, {
|
||||||
|
"IsTurreted": () => false,
|
||||||
|
"HolderID": () => 0,
|
||||||
|
"CanOccupy": (target) => true,
|
||||||
|
"LeaveTurret": () => true
|
||||||
|
});
|
||||||
|
|
||||||
for (const mock of mocks)
|
for (const mock of mocks)
|
||||||
AddMock(...mock);
|
AddMock(...mock);
|
||||||
|
|
||||||
|
|
@ -263,6 +278,12 @@ AddMock(foundationEnt2, IID_Health, {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
AddMock(foundationEnt2, IID_Turretable, {
|
||||||
|
"IsTurreted": () => false,
|
||||||
|
"HolderID": () => 0,
|
||||||
|
"LeaveTurret": () => true
|
||||||
|
});
|
||||||
|
|
||||||
const cmpBuildableAuto = ConstructComponent(foundationEnt2, "AutoBuildable", {
|
const cmpBuildableAuto = ConstructComponent(foundationEnt2, "AutoBuildable", {
|
||||||
"Rate": "1.0"
|
"Rate": "1.0"
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,8 @@ Engine.LoadComponentScript("interfaces/StatusEffectsReceiver.js");
|
||||||
Engine.LoadComponentScript("interfaces/TerritoryDecay.js");
|
Engine.LoadComponentScript("interfaces/TerritoryDecay.js");
|
||||||
Engine.LoadComponentScript("interfaces/Timer.js");
|
Engine.LoadComponentScript("interfaces/Timer.js");
|
||||||
Engine.LoadComponentScript("interfaces/UnitAI.js");
|
Engine.LoadComponentScript("interfaces/UnitAI.js");
|
||||||
|
Engine.LoadComponentScript("interfaces/Turretable.js");
|
||||||
|
Engine.LoadComponentScript("interfaces/TurretHolder.js");
|
||||||
Engine.LoadComponentScript("Pack.js");
|
Engine.LoadComponentScript("Pack.js");
|
||||||
Engine.RegisterGlobal("MT_EntityRenamed", "entityRenamed");
|
Engine.RegisterGlobal("MT_EntityRenamed", "entityRenamed");
|
||||||
|
|
||||||
|
|
@ -45,10 +47,23 @@ AddMock(SYSTEM_ENTITY, IID_Timer, {
|
||||||
"SetInterval": (entity, iid, funcname, time, repeattime, data) => { timerActivated = true; return 7; }
|
"SetInterval": (entity, iid, funcname, time, repeattime, data) => { timerActivated = true; return 7; }
|
||||||
});
|
});
|
||||||
|
|
||||||
|
AddMock(ent, IID_Turretable, {
|
||||||
|
"IsTurreted": () => false,
|
||||||
|
"HolderID": () => 0,
|
||||||
|
"LeaveTurret": () => true
|
||||||
|
});
|
||||||
|
|
||||||
|
AddMock(newEnt, IID_Turretable, {
|
||||||
|
"IsTurreted": () => false,
|
||||||
|
"HolderID": () => 0,
|
||||||
|
"CanOccupy": (target) => true,
|
||||||
|
"LeaveTurret": () => true
|
||||||
|
});
|
||||||
|
|
||||||
Engine.AddEntity = function(template)
|
Engine.AddEntity = function(template)
|
||||||
{
|
{
|
||||||
TS_ASSERT_EQUALS(template, "finalTemplate");
|
TS_ASSERT_EQUALS(template, "finalTemplate");
|
||||||
return true;
|
return newEnt;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Test Packing
|
// Test Packing
|
||||||
|
|
|
||||||
|
|
@ -105,6 +105,28 @@ TS_ASSERT_EQUALS(cmpTurretHolder.AllowedToOccupyTurretPoint(infID, cmpTurretHold
|
||||||
TS_ASSERT_EQUALS(cmpTurretHolder.AllowedToOccupyTurretPoint(infID, cmpTurretHolder.turretPoints[1]), false);
|
TS_ASSERT_EQUALS(cmpTurretHolder.AllowedToOccupyTurretPoint(infID, cmpTurretHolder.turretPoints[1]), false);
|
||||||
TS_ASSERT_EQUALS(cmpTurretHolder.AllowedToOccupyTurretPoint(infID, cmpTurretHolder.turretPoints[2]), 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.
|
// Test that one cannot leave a turret that is not occupied.
|
||||||
TS_ASSERT(!cmpTurretHolder.LeaveTurretPoint(archerID));
|
TS_ASSERT(!cmpTurretHolder.LeaveTurretPoint(archerID));
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,27 @@ function ChangeEntityTemplate(oldEnt, newTemplate)
|
||||||
if (cmpVisual && cmpNewVisual)
|
if (cmpVisual && cmpNewVisual)
|
||||||
cmpNewVisual.SetActorSeed(cmpVisual.GetActorSeed());
|
cmpNewVisual.SetActorSeed(cmpVisual.GetActorSeed());
|
||||||
|
|
||||||
|
const cmpOldTurretable = Engine.QueryInterface(oldEnt, IID_Turretable);
|
||||||
|
|
||||||
|
// If the old entity is turreted, we need to handle it before copying position
|
||||||
|
if (cmpOldTurretable && cmpOldTurretable.IsTurreted())
|
||||||
|
{
|
||||||
|
const cmpNewTurretable = Engine.QueryInterface(newEnt, IID_Turretable);
|
||||||
|
|
||||||
|
// Check if new entity doesn't have Turretable component
|
||||||
|
if (!cmpNewTurretable)
|
||||||
|
cmpOldTurretable.LeaveTurret(true);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Check if it's allowed to occupy the turret point
|
||||||
|
const cmpTurretHolderOfOldEnt = Engine.QueryInterface(cmpOldTurretable.HolderID(), IID_TurretHolder);
|
||||||
|
|
||||||
|
if (cmpTurretHolderOfNewEnt &&
|
||||||
|
!cmpTurretHolderOfOldEnt.AllowedToOccupyTurretPoint(newEnt, cmpOldTurretable.GetTurretPointName(), true))
|
||||||
|
cmpOldTurretable.LeaveTurret(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var cmpPosition = Engine.QueryInterface(oldEnt, IID_Position);
|
var cmpPosition = Engine.QueryInterface(oldEnt, IID_Position);
|
||||||
var cmpNewPosition = Engine.QueryInterface(newEnt, IID_Position);
|
var cmpNewPosition = Engine.QueryInterface(newEnt, IID_Position);
|
||||||
if (cmpPosition && cmpNewPosition)
|
if (cmpPosition && cmpNewPosition)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue