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:
parent
e2f4ce0649
commit
93d22ef16b
@ -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"]
|
||||
|
@ -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()
|
||||
};
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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'>" +
|
||||
|
@ -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)
|
||||
|
@ -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";
|
||||
|
@ -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]));
|
||||
|
@ -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());
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
));
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user