1
0
forked from 0ad/0ad

Allow to specify subunits on creation.

This basically does two things:
- Allow turrets on moving entities (fixes serialisation in cmpPos).
- Allow an entity to be present on another when the other is created.

That makes it basically a boiled down version of a prior patch, omitting
the part where orders could be passed on.
This does allow e.g. ranged units on chariots that fire while the
chariot moves.

Original diff by: @sanderd17
Redone by: @Stan
Differential revision: D1958
Comments by: @Alexandermb, @Angen, @bb, @Langbart, @Nescio, @Stan,
@wraitii
Refs. #2577 by implementing the entity on platform case.

This was SVN commit r25192.
This commit is contained in:
Freagarach 2021-04-05 05:22:25 +00:00
parent e2f4ce0649
commit 93d22ef16b
12 changed files with 211 additions and 112 deletions

View File

@ -1321,7 +1321,7 @@ var g_EntityCommands =
continue;
if (allowedPlayersCheck([entState], ["Player"]))
count += entState.turretHolder.turretPoints.filter(turretPoint => turretPoint.entity).length;
count += entState.turretHolder.turretPoints.filter(turretPoint => turretPoint.entity && turretPoint.ejectable).length;
else
for (let turretPoint of entState.turretHolder.turretPoints)
if (turretPoint.entity && allowedPlayersCheck([GetEntityState(turretPoint.entity)], ["Player"]))
@ -1443,7 +1443,8 @@ var g_EntityCommands =
"getInfo": function(entStates)
{
if (entStates.every(entState => !entState.turretable ||
entState.turretable.holder == INVALID_ENTITY))
entState.turretable.holder == INVALID_ENTITY ||
!entState.turretable.ejectable))
return false;
return {
@ -1460,7 +1461,8 @@ var g_EntityCommands =
Engine.PostNetworkCommand({
"type": "leave-turret",
"entities": entStates.filter(entState => entState.turretable &&
entState.turretable.holder != INVALID_ENTITY).map(entState => entState.id)
entState.turretable.holder != INVALID_ENTITY ||
!entState.turretable.ejectable).map(entState => entState.id)
});
},
"allowedPlayers": ["Player"]

View File

@ -391,6 +391,7 @@ GuiInterface.prototype.GetEntityState = function(player, ent)
let cmpTurretable = Engine.QueryInterface(ent, IID_Turretable);
if (cmpTurretable)
ret.turretable = {
"ejectable": cmpTurretable.IsEjectable(),
"holder": cmpTurretable.HolderID()
};

View File

@ -134,8 +134,7 @@ Health.prototype.ExecuteRegeneration = function()
if (this.GetIdleRegenRate() != 0)
{
let cmpUnitAI = Engine.QueryInterface(this.entity, IID_UnitAI);
if (cmpUnitAI && (cmpUnitAI.IsIdle() ||
cmpUnitAI.GetGarrisonHolder() != INVALID_ENTITY && !cmpUnitAI.IsTurret()))
if (cmpUnitAI && cmpUnitAI.IsIdle())
regen += this.GetIdleRegenRate();
}

View File

@ -19,10 +19,51 @@ class TurretHolder
},
"allowedClasses": points[point].AllowedClasses?._string,
"angle": points[point].Angle ? +points[point].Angle * Math.PI / 180 : null,
"entity": null
"entity": null,
"template": points[point].Template,
"ejectable": "Ejectable" in points[point] ? points[point].Ejectable == "true" : true
});
}
/**
* Add a subunit as specified in the template.
* This function creates an entity and places it on the turret point.
*
* @param {Object} turretPoint - A turret point to (re)create the predefined subunit for.
*
* @return {boolean} - Whether the turret creation has succeeded.
*/
CreateSubunit(turretPointName)
{
let turretPoint = this.TurretPointByName(turretPointName);
if (!turretPoint || turretPoint.entity ||
this.initTurrets?.has(turretPointName) ||
this.reservedTurrets?.has(turretPointName))
return false;
let ent = Engine.AddEntity(turretPoint.template);
let cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership);
if (cmpOwnership)
{
let cmpEntOwnership = Engine.QueryInterface(ent, IID_Ownership);
cmpEntOwnership?.SetOwner(cmpOwnership.GetOwner());
}
let cmpTurretable = Engine.QueryInterface(ent, IID_Turretable);
return cmpTurretable?.OccupyTurret(this.entity, turretPoint.name, turretPoint.ejectable) || Engine.DestroyEntity(ent);
}
/**
* @param {string} name - The name of a turret point to reserve, e.g. for promotion.
*/
SetReservedTurretPoint(name)
{
if (!this.reservedTurrets)
this.reservedTurrets = new Set();
this.reservedTurrets.add(name);
}
/**
* @return {Object[]} - An array of the turret points this entity has.
*/
@ -37,7 +78,7 @@ class TurretHolder
*
* @return {boolean} - Whether the entity is allowed to occupy the specified turret point.
*/
AllowedToOccupyTurret(entity, turretPoint)
AllowedToOccupyTurretPoint(entity, turretPoint)
{
if (!turretPoint || turretPoint.entity)
return false;
@ -58,7 +99,7 @@ class TurretHolder
*/
CanOccupy(entity)
{
return !!this.turretPoints.find(turretPoint => this.AllowedToOccupyTurret(entity, turretPoint));
return !!this.turretPoints.find(turretPoint => this.AllowedToOccupyTurretPoint(entity, turretPoint));
}
/**
@ -68,7 +109,7 @@ class TurretHolder
*
* @return {boolean} - Whether the occupation was successful.
*/
OccupyTurret(entity, requestedTurretPoint)
OccupyTurretPoint(entity, requestedTurretPoint)
{
let cmpPositionOccupant = Engine.QueryInterface(entity, IID_Position);
if (!cmpPositionOccupant)
@ -78,17 +119,17 @@ class TurretHolder
if (!cmpPositionSelf)
return false;
if (this.OccupiesTurret(entity))
if (this.OccupiesTurretPoint(entity))
return false;
let turretPoint;
if (requestedTurretPoint)
{
if (this.AllowedToOccupyTurret(entity, requestedTurretPoint))
if (this.AllowedToOccupyTurretPoint(entity, requestedTurretPoint))
turretPoint = requestedTurretPoint;
}
else
turretPoint = this.turretPoints.find(turret => !turret.entity && this.AllowedToOccupyTurret(entity, turret));
turretPoint = this.turretPoints.find(turret => !turret.entity && this.AllowedToOccupyTurretPoint(entity, turret));
if (!turretPoint)
return false;
@ -121,19 +162,29 @@ class TurretHolder
* @param {String} turretName - The name of the turret point to occupy.
* @return {boolean} - Whether the occupation has succeeded.
*/
OccupyNamedTurret(entity, turretName)
OccupyNamedTurretPoint(entity, turretName)
{
return this.OccupyTurret(entity, this.turretPoints.find(turret => turret.name == turretName));
return this.OccupyTurretPoint(entity, this.TurretPointByName(turretName));
}
/**
* @param {string} turretPointName - The name of the requested turret point.
* @return {Object} - The requested turret point.
*/
TurretPointByName(turretPointName)
{
return this.turretPoints.find(turret => turret.name == turretPointName);
}
/**
* Remove the entity from a turret.
* @param {number} entity - The specific entity to eject.
* @param {boolean} forced - Whether ejection is forced (e.g. due to death or renaming).
* @param {Object} turret - Optionally the turret to abandon.
*
* @return {boolean} - Whether the entity was occupying a/the turret before.
* @return {boolean} - Whether the entity succesfully left us.
*/
LeaveTurret(entity, requestedTurretPoint)
LeaveTurretPoint(entity, forced, requestedTurretPoint)
{
let turretPoint;
if (requestedTurretPoint)
@ -142,17 +193,13 @@ class TurretHolder
turretPoint = requestedTurretPoint;
}
else
turretPoint = this.turretPoints.find(turret => turret.entity == entity);
turretPoint = this.GetOccupiedTurretPoint(entity);
if (!turretPoint)
if (!turretPoint || (!turretPoint.ejectable && !forced))
return false;
turretPoint.entity = null;
let cmpPositionEntity = Engine.QueryInterface(entity, IID_Position);
if (cmpPositionEntity)
cmpPositionEntity.SetTurretParent(INVALID_ENTITY, new Vector3D());
Engine.PostMessage(this.entity, MT_TurretsChanged, {
"added": [],
"removed": [entity]
@ -167,17 +214,17 @@ class TurretHolder
*
* @return {boolean} - Whether the entity is positioned on a turret of this entity.
*/
OccupiesTurret(entity, requestedTurretPoint)
OccupiesTurretPoint(entity, requestedTurretPoint)
{
return requestedTurretPoint ? requestedTurretPoint.entity == entity :
this.turretPoints.some(turretPoint => turretPoint.entity == entity);
!!this.GetOccupiedTurretPoint(entity);
}
/**
* @param {number} entity - The entity's id.
* @return {Object} - The turret this entity is positioned on, if applicable.
*/
GetOccupiedTurret(entity)
GetOccupiedTurretPoint(entity)
{
return this.turretPoints.find(turretPoint => turretPoint.entity == entity);
}
@ -186,9 +233,9 @@ class TurretHolder
* @param {number} entity - The entity's id.
* @return {Object} - The turret this entity is positioned on, if applicable.
*/
GetOccupiedTurretName(entity)
GetOccupiedTurretPointName(entity)
{
let turret = this.GetOccupiedTurret(entity);
let turret = this.GetOccupiedTurretPoint(entity);
return turret ? turret.name : "";
}
@ -306,9 +353,12 @@ class TurretHolder
return;
for (let [turretPointName, entity] of this.initTurrets)
if (!this.OccupyNamedTurret(entity, turretPointName))
{
let cmpTurretable = Engine.QueryInterface(entity, IID_Turretable);
if (!cmpTurretable || !cmpTurretable.OccupyTurret(this.entity, turretPointName, this.TurretPointByName(turretPointName).ejectable))
warn("Entity " + entity + " could not occupy the turret point " +
turretPointName + " of turret holder " + this.entity + ".");
}
delete this.initTurrets;
}
@ -323,7 +373,7 @@ class TurretHolder
let cmpTurretable = Engine.QueryInterface(entity, IID_Turretable);
if (!cmpTurretable)
continue;
let currentPoint = this.GetOccupiedTurretName(entity);
let currentPoint = this.GetOccupiedTurretPointName(entity);
cmpTurretable.LeaveTurret(true);
cmpTurretable.OccupyTurret(msg.newentity, currentPoint);
}
@ -334,19 +384,37 @@ class TurretHolder
*/
OnOwnershipChanged(msg)
{
let entities = this.GetEntities();
if (!entities.length)
if (msg.to === INVALID_PLAYER)
{
this.EjectOrKill(this.GetEntities());
return;
if (msg.to == INVALID_PLAYER)
this.EjectOrKill(entities);
else
for (let entity of entities.filter(entity => !IsOwnedByMutualAllyOfEntity(entity, this.entity)))
}
for (let point of this.turretPoints)
{
// If we were created, create any subunits now.
// This has to be done here (instead of on Init)
// for Ownership ought to be initialised.
if (point.template && msg.from === INVALID_PLAYER)
{
let cmpTurretable = Engine.QueryInterface(entity, IID_Turretable);
this.CreateSubunit(point.name);
continue;
}
if (!point.entity)
continue;
if (!point.ejectable)
{
let cmpTurretOwnership = Engine.QueryInterface(point.entity, IID_Ownership);
if (cmpTurretOwnership)
cmpTurretOwnership.SetOwner(msg.to);
}
else if (!IsOwnedByMutualAllyOfEntity(point.entity, this.entity))
{
let cmpTurretable = Engine.QueryInterface(point.entity, IID_Turretable);
if (cmpTurretable)
cmpTurretable.LeaveTurret();
}
}
delete this.reservedTurrets;
}
}
@ -365,6 +433,16 @@ TurretHolder.prototype.Schema =
"<element name='Z'>" +
"<data type='decimal'/>" +
"</element>" +
"<optional>" +
"<interleave>" +
"<element name='Template'>" +
"<text/>" +
"</element>" +
"<element name='Ejectable' a:help='Whether this template is tied to the turret position (i.e. not allowed to leave the turret point).'>" +
"<data type='boolean'/>" +
"</element>" +
"</interleave>" +
"</optional>" +
"<optional>" +
"<element name='AllowedClasses' a:help='If specified, only entities matching the given classes will be able to use this turret.'>" +
"<attribute name='datatype'>" +

View File

@ -34,6 +34,14 @@ Turretable.prototype.IsTurreted = function()
return !!this.holder;
};
/**
* @return {boolean} - Whether we can leave the turret point.
*/
Turretable.prototype.IsEjectable = function()
{
return this.ejectable;
};
/**
* @param {number} target - The entity ID to check.
* @return {boolean} - Whether we can occupy the turret.
@ -50,18 +58,21 @@ Turretable.prototype.CanOccupy = function(target)
/**
* @param {number} target - The entity ID of the entity this entity is being turreted on.
* @param {string} turretPointName - Optionally the turret point name to occupy.
* @param {boolean} ejectable - Whether we can leave this turret point (e.g. false for a tank turret).
*
* @return {boolean} - Whether occupying succeeded.
*/
Turretable.prototype.OccupyTurret = function(target, turretPointName = "")
Turretable.prototype.OccupyTurret = function(target, turretPointName = "", ejectable = true)
{
if (!this.CanOccupy(target))
return false;
let cmpTurretHolder = Engine.QueryInterface(target, IID_TurretHolder);
if (!cmpTurretHolder || !cmpTurretHolder.OccupyNamedTurret(this.entity, turretPointName))
if (!cmpTurretHolder || !cmpTurretHolder.OccupyNamedTurretPoint(this.entity, turretPointName))
return false;
this.holder = target;
this.ejectable = ejectable;
let cmpUnitAI = Engine.QueryInterface(this.entity, IID_UnitAI);
if (cmpUnitAI)
@ -96,12 +107,15 @@ Turretable.prototype.LeaveTurret = function(forced = false)
if (!this.holder)
return true;
if (!this.ejectable && !forced)
return false;
let pos = PositionHelper.GetSpawnPosition(this.holder, this.entity, forced);
if (!pos)
return false;
let cmpTurretHolder = Engine.QueryInterface(this.holder, IID_TurretHolder);
if (!cmpTurretHolder || !cmpTurretHolder.LeaveTurret(this.entity))
if (!cmpTurretHolder || !cmpTurretHolder.LeaveTurretPoint(this.entity, forced))
return false;
let cmpUnitMotionEntity = Engine.QueryInterface(this.entity, IID_UnitMotion);
@ -111,13 +125,14 @@ Turretable.prototype.LeaveTurret = function(forced = false)
let cmpPosition = Engine.QueryInterface(this.entity, IID_Position);
if (cmpPosition)
{
cmpPosition.SetTurretParent(INVALID_ENTITY, new Vector3D());
cmpPosition.JumpTo(pos.x, pos.z);
cmpPosition.SetHeightOffset(0);
}
let cmpHolderPosition = Engine.QueryInterface(this.holder, IID_Position);
if (cmpHolderPosition)
cmpPosition.SetYRotation(cmpHolderPosition.GetPosition().horizAngleTo(pos));
let cmpHolderPosition = Engine.QueryInterface(this.holder, IID_Position);
if (cmpHolderPosition)
cmpPosition.SetYRotation(cmpHolderPosition.GetPosition().horizAngleTo(pos));
}
let cmpUnitAI = Engine.QueryInterface(this.entity, IID_UnitAI);
if (cmpUnitAI)
@ -138,10 +153,15 @@ Turretable.prototype.LeaveTurret = function(forced = false)
});
let cmpRallyPoint = Engine.QueryInterface(this.holder, IID_RallyPoint);
// Need to delete this before ordering to a rally
// point else we may not occupy another turret point.
delete this.holder;
if (cmpRallyPoint)
cmpRallyPoint.OrderToRallyPoint(this.entity, ["occupy-turret"]);
delete this.holder;
delete this.ejectable;
return true;
};
@ -155,7 +175,7 @@ Turretable.prototype.OnEntityRenamed = function(msg)
return;
let holder = this.holder;
let currentPoint = cmpTurretHolder.GetOccupiedTurretName(this.entity);
let currentPoint = cmpTurretHolder.GetOccupiedTurretPointName(this.entity);
this.LeaveTurret(true);
let cmpTurretableNew = Engine.QueryInterface(msg.newentity, IID_Turretable);
if (cmpTurretableNew)

View File

@ -3574,15 +3574,6 @@ UnitAI.prototype.UnsetGarrisoned = function()
this.SetMobile();
};
UnitAI.prototype.GetGarrisonHolder = function()
{
if (!this.isGarrisoned)
return INVALID_ENTITY;
let cmpGarrisonable = Engine.QueryInterface(this.entity, IID_Garrisonable);
return cmpGarrisonable ? cmpGarrisonable.HolderID() : INVALID_ENTITY;
};
UnitAI.prototype.ShouldRespondToEndOfAlert = function()
{
return !this.orderQueue.length || this.orderQueue[0].type == "Garrison";

View File

@ -91,34 +91,34 @@ AddMock(infID, IID_Identity, {
});
// Test visible garrisoning restrictions.
TS_ASSERT_EQUALS(cmpTurretHolder.AllowedToOccupyTurret(siegeEngineID, cmpTurretHolder.turretPoints[0]), true);
TS_ASSERT_EQUALS(cmpTurretHolder.AllowedToOccupyTurret(siegeEngineID, cmpTurretHolder.turretPoints[1]), true);
TS_ASSERT_EQUALS(cmpTurretHolder.AllowedToOccupyTurret(siegeEngineID, cmpTurretHolder.turretPoints[2]), true);
TS_ASSERT_EQUALS(cmpTurretHolder.AllowedToOccupyTurret(archerID, cmpTurretHolder.turretPoints[0]), true);
TS_ASSERT_EQUALS(cmpTurretHolder.AllowedToOccupyTurret(archerID, cmpTurretHolder.turretPoints[1]), false);
TS_ASSERT_EQUALS(cmpTurretHolder.AllowedToOccupyTurret(archerID, cmpTurretHolder.turretPoints[2]), true);
TS_ASSERT_EQUALS(cmpTurretHolder.AllowedToOccupyTurret(cavID, cmpTurretHolder.turretPoints[0]), true);
TS_ASSERT_EQUALS(cmpTurretHolder.AllowedToOccupyTurret(cavID, cmpTurretHolder.turretPoints[1]), false);
TS_ASSERT_EQUALS(cmpTurretHolder.AllowedToOccupyTurret(cavID, cmpTurretHolder.turretPoints[2]), true);
TS_ASSERT_EQUALS(cmpTurretHolder.AllowedToOccupyTurret(infID, cmpTurretHolder.turretPoints[0]), true);
TS_ASSERT_EQUALS(cmpTurretHolder.AllowedToOccupyTurret(infID, cmpTurretHolder.turretPoints[1]), false);
TS_ASSERT_EQUALS(cmpTurretHolder.AllowedToOccupyTurret(infID, cmpTurretHolder.turretPoints[2]), false);
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 that one cannot leave a turret that is not occupied.
TS_ASSERT(!cmpTurretHolder.LeaveTurret(archerID));
TS_ASSERT(!cmpTurretHolder.LeaveTurretPoint(archerID));
// Test occupying a turret.
TS_ASSERT(!cmpTurretHolder.OccupiesTurret(archerID));
TS_ASSERT(cmpTurretHolder.OccupyTurret(archerID));
TS_ASSERT(cmpTurretHolder.OccupiesTurret(archerID));
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.OccupiesTurret(archerID, cmpTurretHolder.turretPoints[1]));
TS_ASSERT(!cmpTurretHolder.OccupyTurret(cavID, cmpTurretHolder.turretPoints[1]));
TS_ASSERT(!cmpTurretHolder.OccupyTurret(cavID, cmpTurretHolder.turretPoints[0]));
TS_ASSERT(cmpTurretHolder.OccupyTurret(cavID, cmpTurretHolder.turretPoints[2]));
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.LeaveTurret(archerID));
TS_ASSERT(!cmpTurretHolder.LeaveTurret(cavID, cmpTurretHolder.turretPoints[1]));
TS_ASSERT(cmpTurretHolder.LeaveTurret(cavID, cmpTurretHolder.turretPoints[2]));
TS_ASSERT(cmpTurretHolder.LeaveTurretPoint(archerID));
TS_ASSERT(!cmpTurretHolder.LeaveTurretPoint(cavID, false, cmpTurretHolder.turretPoints[1]));
TS_ASSERT(cmpTurretHolder.LeaveTurretPoint(cavID, false, cmpTurretHolder.turretPoints[2]));

View File

@ -87,7 +87,7 @@ let cmpTurretHolder = ConstructComponent(holder, "TurretHolder", {
TS_ASSERT(cmpTurretable.OccupyTurret(holder));
TS_ASSERT_UNEVAL_EQUALS(cmpTurretHolder.GetEntities(), [turret]);
TS_ASSERT(cmpTurretHolder.OccupiesTurret(turret));
TS_ASSERT(cmpTurretHolder.OccupiesTurretPoint(turret));
TS_ASSERT(cmpTurretable.LeaveTurret());
TS_ASSERT_UNEVAL_EQUALS(cmpTurretHolder.GetEntities(), []);
@ -98,12 +98,12 @@ let cmpTurretableNew = createTurretCmp(newTurret);
TS_ASSERT(cmpTurretableNew.OccupyTurret(holder));
TS_ASSERT(cmpTurretable.OccupyTurret(holder));
TS_ASSERT(cmpTurretableNew.LeaveTurret());
let previousTurret = cmpTurretHolder.GetOccupiedTurretName(turret);
let previousTurret = cmpTurretHolder.GetOccupiedTurretPointName(turret);
cmpTurretable.OnEntityRenamed({
"entity": turret,
"newentity": newTurret
});
let newTurretPos = cmpTurretHolder.GetOccupiedTurretName(newTurret);
let newTurretPos = cmpTurretHolder.GetOccupiedTurretPointName(newTurret);
TS_ASSERT_UNEVAL_EQUALS(newTurretPos, previousTurret);
TS_ASSERT(cmpTurretableNew.LeaveTurret());

View File

@ -26,6 +26,13 @@ function ChangeEntityTemplate(oldEnt, newTemplate)
cmpNewPosition.SetHeightOffset(cmpPosition.GetHeightOffset());
}
// Prevent spawning subunits on occupied positions.
let cmpTurretHolder = Engine.QueryInterface(oldEnt, IID_TurretHolder);
let cmpNewTurretHolder = Engine.QueryInterface(newEnt, IID_TurretHolder);
if (cmpTurretHolder && cmpNewTurretHolder)
for (let entity of cmpTurretHolder.GetEntities())
cmpNewTurretHolder.SetReservedTurretPoint(cmpTurretHolder.GetOccupiedTurretPointName(entity));
var cmpOwnership = Engine.QueryInterface(oldEnt, IID_Ownership);
var cmpNewOwnership = Engine.QueryInterface(newEnt, IID_Ownership);
if (cmpOwnership && cmpNewOwnership)
@ -58,30 +65,6 @@ function ChangeEntityTemplate(oldEnt, newTemplate)
if (cmpBuilderList && cmpNewBuilderList)
cmpNewBuilderList.AddBuilders(cmpBuilderList.GetBuilders());
var cmpUnitAI = Engine.QueryInterface(oldEnt, IID_UnitAI);
var cmpNewUnitAI = Engine.QueryInterface(newEnt, IID_UnitAI);
if (cmpUnitAI && cmpNewUnitAI)
{
let pos = cmpUnitAI.GetHeldPosition();
if (pos)
cmpNewUnitAI.SetHeldPosition(pos.x, pos.z);
if (cmpUnitAI.GetStanceName())
cmpNewUnitAI.SwitchToStance(cmpUnitAI.GetStanceName());
if (cmpUnitAI.GetGarrisonHolder() != INVALID_ENTITY)
cmpNewUnitAI.SetGarrisoned();
cmpNewUnitAI.AddOrders(cmpUnitAI.GetOrders());
if (cmpUnitAI.IsGuardOf())
{
let guarded = cmpUnitAI.IsGuardOf();
let cmpGuard = Engine.QueryInterface(guarded, IID_Guard);
if (cmpGuard)
{
cmpGuard.RenameGuard(oldEnt, newEnt);
cmpNewUnitAI.SetGuardOf(guarded);
}
}
}
let cmpPromotion = Engine.QueryInterface(oldEnt, IID_Promotion);
let cmpNewPromotion = Engine.QueryInterface(newEnt, IID_Promotion);
if (cmpPromotion && cmpNewPromotion)
@ -96,7 +79,6 @@ function ChangeEntityTemplate(oldEnt, newTemplate)
cmpNewResGatherer.SetLastCarriedType(cmpResGatherer.GetLastCarriedType());
}
// Maintain the list of guards
let cmpGuard = Engine.QueryInterface(oldEnt, IID_Guard);
let cmpNewGuard = Engine.QueryInterface(newEnt, IID_Guard);
@ -133,6 +115,27 @@ function ChangeEntityTemplate(oldEnt, newTemplate)
Engine.PostMessage(oldEnt, MT_EntityRenamed, { "entity": oldEnt, "newentity": newEnt });
// UnitAI generally needs other components to be properly initialised.
let cmpUnitAI = Engine.QueryInterface(oldEnt, IID_UnitAI);
let cmpNewUnitAI = Engine.QueryInterface(newEnt, IID_UnitAI);
if (cmpUnitAI && cmpNewUnitAI)
{
let pos = cmpUnitAI.GetHeldPosition();
if (pos)
cmpNewUnitAI.SetHeldPosition(pos.x, pos.z);
cmpNewUnitAI.AddOrders(cmpUnitAI.GetOrders());
let guarded = cmpUnitAI.IsGuardOf();
if (guarded)
{
let cmpGuarded = Engine.QueryInterface(guarded, IID_Guard);
if (cmpGuarded)
{
cmpGuarded.RenameGuard(oldEnt, newEnt);
cmpNewUnitAI.SetGuardOf(guarded);
}
}
}
if (cmpPosition && cmpPosition.IsInWorld())
cmpPosition.MoveOutOfWorld();
Engine.DestroyEntity(oldEnt);

View File

@ -1058,10 +1058,6 @@ int CXMLReader::ReadEntities(XMBElement parent, double end_time)
// TODO: other parts of the position
}
CmpPtr<ICmpOwnership> cmpOwnership(sim, ent);
if (cmpOwnership)
cmpOwnership->SetOwner(PlayerID);
if (!Garrison.empty())
{
CmpPtr<ICmpGarrisonHolder> cmpGarrisonHolder(sim, ent);
@ -1071,6 +1067,8 @@ int CXMLReader::ReadEntities(XMBElement parent, double end_time)
LOGERROR("CXMLMapReader::ReadEntities() entity '%d' of player '%d' has no GarrisonHolder component and thus cannot garrison units.", ent, PlayerID);
}
// Needs to be before ownership changes to prevent initialising
// subunits too soon.
if (!Turrets.empty())
{
CmpPtr<ICmpTurretHolder> cmpTurretHolder(sim, ent);
@ -1080,6 +1078,10 @@ int CXMLReader::ReadEntities(XMBElement parent, double end_time)
LOGERROR("CXMLMapReader::ReadEntities() entity '%d' of player '%d' has no TurretHolder component and thus cannot use turrets.", ent, PlayerID);
}
CmpPtr<ICmpOwnership> cmpOwnership(sim, ent);
if (cmpOwnership)
cmpOwnership->SetOwner(PlayerID);
CmpPtr<ICmpObstruction> cmpObstruction(sim, ent);
if (cmpObstruction)
{

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2020 Wildfire Games.
/* Copyright (C) 2021 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -21,6 +21,7 @@
#include "ICmpPosition.h"
#include "simulation2/MessageTypes.h"
#include "simulation2/serialization/SerializedTypes.h"
#include "ICmpTerrain.h"
#include "ICmpTerritoryManager.h"
@ -228,6 +229,7 @@ public:
serialize.NumberFixed_Unbounded("y", m_TurretPosition.Y);
serialize.NumberFixed_Unbounded("z", m_TurretPosition.Z);
}
Serializer(serialize, "turrets", m_Turrets);
}
virtual void Deserialize(const CParamNode& paramNode, IDeserializer& deserialize)
@ -265,6 +267,7 @@ public:
deserialize.NumberFixed_Unbounded("y", m_TurretPosition.Y);
deserialize.NumberFixed_Unbounded("z", m_TurretPosition.Z);
}
Serializer(deserialize, "turrets", m_Turrets);
if (m_InWorld)
UpdateXZRotation();

View File

@ -40,7 +40,7 @@ public:
std::vector<entity_id_t> entities = m_Script.Call<std::vector<entity_id_t>>("GetEntities");
for (entity_id_t entity : entities)
turrets.push_back(std::make_pair(
m_Script.Call<std::string>("GetOccupiedTurretName", entity),
m_Script.Call<std::string>("GetOccupiedTurretPointName", entity),
entity
));