Move the ability to hold a turret to a separate file.
The logic concerning visible garrison points (i.e. turrets) is moved from `GarrisonHolder` to a separate file. This is logical because garrisoned != turreted, so this allows for turrets that cannot be garrisoned (refs. D1958). Also references #3488. Differential Revision: D2367 Reviewed by: @wraitii This was SVN commit r23856.
This commit is contained in:
parent
e07f12bea6
commit
215c503e30
@ -30,13 +30,27 @@ BuildingAI.prototype.Init = function()
|
||||
this.targetUnits = [];
|
||||
};
|
||||
|
||||
BuildingAI.prototype.OnGarrisonedUnitsChanged = function(msg)
|
||||
BuildingAI.prototype.OnGarrisonedUnitsChanged = function()
|
||||
{
|
||||
this.RecalculateProjectileCount();
|
||||
};
|
||||
|
||||
BuildingAI.prototype.OnTurretsChanged = function()
|
||||
{
|
||||
this.RecalculateProjectileCount();
|
||||
};
|
||||
|
||||
BuildingAI.prototype.RecalculateProjectileCount = function()
|
||||
{
|
||||
this.archersGarrisoned = 0;
|
||||
let classes = this.template.GarrisonArrowClasses;
|
||||
|
||||
for (let ent of msg.added)
|
||||
let cmpTurretHolder = Engine.QueryInterface(this.entity, IID_TurretHolder);
|
||||
let cmpGarrisonHolder = Engine.QueryInterface(this.entity, IID_GarrisonHolder);
|
||||
for (let ent of cmpGarrisonHolder.GetEntities())
|
||||
{
|
||||
if (msg.visible[ent])
|
||||
// Only count non-visible garrisoned entities towards extra arrows.
|
||||
if (cmpTurretHolder && cmpTurretHolder.OccupiesTurret(ent))
|
||||
continue;
|
||||
|
||||
let cmpIdentity = Engine.QueryInterface(ent, IID_Identity);
|
||||
@ -46,19 +60,6 @@ BuildingAI.prototype.OnGarrisonedUnitsChanged = function(msg)
|
||||
if (MatchesClassList(cmpIdentity.GetClassesList(), classes))
|
||||
++this.archersGarrisoned;
|
||||
}
|
||||
|
||||
for (let ent of msg.removed)
|
||||
{
|
||||
if (msg.visible[ent])
|
||||
continue;
|
||||
|
||||
let cmpIdentity = Engine.QueryInterface(ent, IID_Identity);
|
||||
if (!cmpIdentity)
|
||||
continue;
|
||||
|
||||
if (MatchesClassList(cmpIdentity.GetClassesList(), classes))
|
||||
--this.archersGarrisoned;
|
||||
}
|
||||
};
|
||||
|
||||
BuildingAI.prototype.OnOwnershipChanged = function(msg)
|
||||
|
@ -31,39 +31,6 @@ GarrisonHolder.prototype.Schema =
|
||||
"<element name='Pickup' a:help='This garrisonHolder will move to pick up units to be garrisoned'>" +
|
||||
"<data type='boolean'/>" +
|
||||
"</element>" +
|
||||
"</optional>" +
|
||||
"<optional>" +
|
||||
"<element name='VisibleGarrisonPoints' a:help='Points that will be used to visibly garrison a unit'>" +
|
||||
"<zeroOrMore>" +
|
||||
"<element a:help='Element containing the offset coordinates'>" +
|
||||
"<anyName/>" +
|
||||
"<interleave>" +
|
||||
"<element name='X'>" +
|
||||
"<data type='decimal'/>" +
|
||||
"</element>" +
|
||||
"<element name='Y'>" +
|
||||
"<data type='decimal'/>" +
|
||||
"</element>" +
|
||||
"<element name='Z'>" +
|
||||
"<data type='decimal'/>" +
|
||||
"</element>" +
|
||||
"<optional>" +
|
||||
"<element name='AllowedClasses' a:help='If specified, only entities matching the given classes will be able to use this visible garrison point.'>" +
|
||||
"<attribute name='datatype'>" +
|
||||
"<value>tokens</value>" +
|
||||
"</attribute>" +
|
||||
"<text/>" +
|
||||
"</element>" +
|
||||
"</optional>"+
|
||||
"<optional>" +
|
||||
"<element name='Angle' a:help='Angle in degrees relative to the garrisonHolder direction'>" +
|
||||
"<data type='decimal'/>" +
|
||||
"</element>" +
|
||||
"</optional>" +
|
||||
"</interleave>" +
|
||||
"</element>" +
|
||||
"</zeroOrMore>" +
|
||||
"</element>" +
|
||||
"</optional>";
|
||||
|
||||
/**
|
||||
@ -77,22 +44,15 @@ GarrisonHolder.prototype.Init = function()
|
||||
this.entities = [];
|
||||
this.timer = undefined;
|
||||
this.allowGarrisoning = new Map();
|
||||
this.visibleGarrisonPoints = [];
|
||||
if (!this.template.VisibleGarrisonPoints)
|
||||
return;
|
||||
};
|
||||
|
||||
let points = this.template.VisibleGarrisonPoints;
|
||||
for (let point in points)
|
||||
this.visibleGarrisonPoints.push({
|
||||
"offset": {
|
||||
"x": +points[point].X,
|
||||
"y": +points[point].Y,
|
||||
"z": +points[point].Z
|
||||
},
|
||||
"allowedClasses": points[point].AllowedClasses,
|
||||
"angle": points[point].Angle ? +points[point].Angle * Math.PI / 180 : null,
|
||||
"entity": null
|
||||
});
|
||||
/**
|
||||
* @param {number} entity - The entity to verify.
|
||||
* @return {boolean} - Whether the given entity is garrisoned in this GarrisonHolder.
|
||||
*/
|
||||
GarrisonHolder.prototype.IsGarrisoned = function(entity)
|
||||
{
|
||||
return this.entities.indexOf(entity) != -1;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -197,29 +157,12 @@ GarrisonHolder.prototype.IsAllowedToGarrison = function(entity)
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {number} entity - The entity's id.
|
||||
* @param {Object|undefined} visibleGarrisonPoint - The vgp object.
|
||||
* @return {boolean} - Whether the unit is allowed be visible on that garrison point.
|
||||
*/
|
||||
GarrisonHolder.prototype.AllowedToVisibleGarrisoning = function(entity, visibleGarrisonPoint)
|
||||
{
|
||||
if (!visibleGarrisonPoint)
|
||||
return false;
|
||||
|
||||
if (!visibleGarrisonPoint.allowedClasses)
|
||||
return true;
|
||||
|
||||
let cmpIdentity = Engine.QueryInterface(entity, IID_Identity);
|
||||
return cmpIdentity && MatchesClassList(cmpIdentity.GetClassesList(), visibleGarrisonPoint.allowedClasses._string);
|
||||
};
|
||||
|
||||
/**
|
||||
* Garrison a unit inside. The timer for AutoHeal is started here.
|
||||
* @param {number} vgpEntity - The visual garrison point that will be used.
|
||||
* If vgpEntity is given, this visualGarrisonPoint will be used for the entity.
|
||||
* @param {number} entity - The entityID to garrison.
|
||||
* @param {boolean} renamed - Whether the entity was renamed.
|
||||
*
|
||||
* @return {boolean} - Whether the entity was garrisoned.
|
||||
*/
|
||||
GarrisonHolder.prototype.Garrison = function(entity, vgpEntity)
|
||||
GarrisonHolder.prototype.Garrison = function(entity, renamed = false)
|
||||
{
|
||||
if (!this.IsAllowedToGarrison(entity))
|
||||
return false;
|
||||
@ -227,10 +170,6 @@ GarrisonHolder.prototype.Garrison = function(entity, vgpEntity)
|
||||
if (!this.HasEnoughHealth())
|
||||
return false;
|
||||
|
||||
let cmpPosition = Engine.QueryInterface(entity, IID_Position);
|
||||
if (!cmpPosition)
|
||||
return false;
|
||||
|
||||
if (!this.timer && this.GetHealRate() > 0)
|
||||
{
|
||||
let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
|
||||
@ -247,53 +186,14 @@ GarrisonHolder.prototype.Garrison = function(entity, vgpEntity)
|
||||
if (cmpAura && cmpAura.HasGarrisonAura())
|
||||
cmpAura.ApplyGarrisonAura(this.entity);
|
||||
|
||||
let visibleGarrisonPoint;
|
||||
if (vgpEntity && this.AllowedToVisibleGarrisoning(entity, vgpEntity))
|
||||
visibleGarrisonPoint = vgpEntity;
|
||||
|
||||
if (!visibleGarrisonPoint)
|
||||
visibleGarrisonPoint = this.visibleGarrisonPoints.find(vgp => !vgp.entity && this.AllowedToVisibleGarrisoning(entity, vgp));
|
||||
|
||||
let isVisiblyGarrisoned = false;
|
||||
if (visibleGarrisonPoint)
|
||||
{
|
||||
visibleGarrisonPoint.entity = entity;
|
||||
// Angle of turrets:
|
||||
// Renamed entities (vgpEntity != undefined) should keep their angle.
|
||||
// Otherwise if an angle is given in the visibleGarrisonPoint, use it.
|
||||
// If no such angle given (usually walls for which outside/inside not well defined), we keep
|
||||
// the current angle as it was used for garrisoning and thus quite often was from inside to
|
||||
// outside, except when garrisoning from outWorld where we take as default PI.
|
||||
let cmpTurretPosition = Engine.QueryInterface(this.entity, IID_Position);
|
||||
if (!vgpEntity && visibleGarrisonPoint.angle != null)
|
||||
cmpPosition.SetYRotation(cmpTurretPosition.GetRotation().y + visibleGarrisonPoint.angle);
|
||||
else if (!vgpEntity && !cmpPosition.IsInWorld())
|
||||
cmpPosition.SetYRotation(cmpTurretPosition.GetRotation().y + Math.PI);
|
||||
let cmpUnitMotion = Engine.QueryInterface(entity, IID_UnitMotion);
|
||||
if (cmpUnitMotion)
|
||||
cmpUnitMotion.SetFacePointAfterMove(false);
|
||||
cmpPosition.SetTurretParent(this.entity, visibleGarrisonPoint.offset);
|
||||
let cmpUnitAI = Engine.QueryInterface(entity, IID_UnitAI);
|
||||
if (cmpUnitAI)
|
||||
cmpUnitAI.SetTurretStance();
|
||||
|
||||
// Remove the unit's obstruction to avoid interfering with pathing.
|
||||
let cmpObstruction = Engine.QueryInterface(entity, IID_Obstruction);
|
||||
if (cmpObstruction)
|
||||
cmpObstruction.SetActive(false);
|
||||
|
||||
isVisiblyGarrisoned = true;
|
||||
}
|
||||
else
|
||||
let cmpPosition = Engine.QueryInterface(entity, IID_Position);
|
||||
if (cmpPosition)
|
||||
cmpPosition.MoveOutOfWorld();
|
||||
|
||||
// Should only be called after the garrison has been performed else the visible Garrison Points are not updated yet.
|
||||
Engine.PostMessage(this.entity, MT_GarrisonedUnitsChanged, {
|
||||
"added": [entity],
|
||||
"removed": [],
|
||||
"visible": {
|
||||
[entity]: isVisiblyGarrisoned,
|
||||
}
|
||||
"renamed": renamed
|
||||
});
|
||||
|
||||
return true;
|
||||
@ -303,9 +203,11 @@ GarrisonHolder.prototype.Garrison = function(entity, vgpEntity)
|
||||
* Simply eject the unit from the garrisoning entity without moving it
|
||||
* @param {number} entity - Id of the entity to be ejected.
|
||||
* @param {boolean} forced - Whether eject is forced (i.e. if building is destroyed).
|
||||
* @param {boolean} renamed - Whether eject was due to entity renaming.
|
||||
*
|
||||
* @return {boolean} Whether the entity was ejected.
|
||||
*/
|
||||
GarrisonHolder.prototype.Eject = function(entity, forced)
|
||||
GarrisonHolder.prototype.Eject = function(entity, forced, renamed = false)
|
||||
{
|
||||
let entityIndex = this.entities.indexOf(entity);
|
||||
// Error: invalid entity ID, usually it's already been ejected
|
||||
@ -337,33 +239,13 @@ GarrisonHolder.prototype.Eject = function(entity, forced)
|
||||
}
|
||||
|
||||
this.entities.splice(entityIndex, 1);
|
||||
let cmpEntPosition = Engine.QueryInterface(entity, IID_Position);
|
||||
let cmpEntUnitAI = Engine.QueryInterface(entity, IID_UnitAI);
|
||||
|
||||
// Needs to be set before the visible garrison points are cleared.
|
||||
let visible = {
|
||||
[entity]: this.IsVisiblyGarrisoned(entity)
|
||||
};
|
||||
|
||||
for (let vgp of this.visibleGarrisonPoints)
|
||||
{
|
||||
if (vgp.entity != entity)
|
||||
continue;
|
||||
cmpEntPosition.SetTurretParent(INVALID_ENTITY, new Vector3D());
|
||||
let cmpEntUnitMotion = Engine.QueryInterface(entity, IID_UnitMotion);
|
||||
if (cmpEntUnitMotion)
|
||||
cmpEntUnitMotion.SetFacePointAfterMove(true);
|
||||
if (cmpEntUnitAI)
|
||||
cmpEntUnitAI.ResetTurretStance();
|
||||
vgp.entity = null;
|
||||
break;
|
||||
}
|
||||
|
||||
// Reset the obstruction flags to template defaults.
|
||||
let cmpObstruction = Engine.QueryInterface(entity, IID_Obstruction);
|
||||
if (cmpObstruction)
|
||||
cmpObstruction.SetActive(true);
|
||||
|
||||
let cmpEntUnitAI = Engine.QueryInterface(entity, IID_UnitAI);
|
||||
if (cmpEntUnitAI)
|
||||
cmpEntUnitAI.Ungarrison();
|
||||
|
||||
@ -375,17 +257,21 @@ GarrisonHolder.prototype.Eject = function(entity, forced)
|
||||
if (cmpEntAura && cmpEntAura.HasGarrisonAura())
|
||||
cmpEntAura.RemoveGarrisonAura(this.entity);
|
||||
|
||||
cmpEntPosition.JumpTo(pos.x, pos.z);
|
||||
cmpEntPosition.SetHeightOffset(0);
|
||||
let cmpEntPosition = Engine.QueryInterface(entity, IID_Position);
|
||||
if (cmpEntPosition)
|
||||
{
|
||||
cmpEntPosition.JumpTo(pos.x, pos.z);
|
||||
cmpEntPosition.SetHeightOffset(0);
|
||||
|
||||
let cmpPosition = Engine.QueryInterface(this.entity, IID_Position);
|
||||
if (cmpPosition)
|
||||
cmpEntPosition.SetYRotation(cmpPosition.GetPosition().horizAngleTo(pos));
|
||||
let cmpPosition = Engine.QueryInterface(this.entity, IID_Position);
|
||||
if (cmpPosition)
|
||||
cmpEntPosition.SetYRotation(cmpPosition.GetPosition().horizAngleTo(pos));
|
||||
}
|
||||
|
||||
Engine.PostMessage(this.entity, MT_GarrisonedUnitsChanged, {
|
||||
"added": [],
|
||||
"removed": [entity],
|
||||
"visible": visible
|
||||
"renamed": renamed
|
||||
});
|
||||
|
||||
return true;
|
||||
@ -628,16 +514,9 @@ GarrisonHolder.prototype.OnGlobalOwnershipChanged = function(msg)
|
||||
this.entities.splice(entityIndex, 1);
|
||||
Engine.PostMessage(this.entity, MT_GarrisonedUnitsChanged, {
|
||||
"added": [],
|
||||
"removed": [msg.entity],
|
||||
"visible": {
|
||||
[msg.entity]: this.IsVisiblyGarrisoned(msg.entity)
|
||||
}
|
||||
"removed": [msg.entity]
|
||||
});
|
||||
this.UpdateGarrisonFlag();
|
||||
|
||||
for (let point of this.visibleGarrisonPoints)
|
||||
if (point.entity == msg.entity)
|
||||
point.entity = null;
|
||||
}
|
||||
else if (msg.to == INVALID_PLAYER || !IsOwnedByMutualAllyOfEntity(this.entity, msg.entity))
|
||||
this.EjectOrKill([msg.entity]);
|
||||
@ -652,16 +531,19 @@ GarrisonHolder.prototype.OnGlobalEntityRenamed = function(msg)
|
||||
let entityIndex = this.entities.indexOf(msg.entity);
|
||||
if (entityIndex != -1)
|
||||
{
|
||||
let vgpRenamed;
|
||||
for (let vgp of this.visibleGarrisonPoints)
|
||||
{
|
||||
if (vgp.entity != msg.entity)
|
||||
continue;
|
||||
vgpRenamed = vgp;
|
||||
break;
|
||||
}
|
||||
this.Eject(msg.entity, true);
|
||||
this.Garrison(msg.newentity, vgpRenamed);
|
||||
this.Eject(msg.entity, true, true);
|
||||
this.Garrison(msg.newentity, true);
|
||||
|
||||
// TurretHolder is not subscribed to GarrisonChanged, so we must inform it explicitly.
|
||||
// Otherwise a renaming entity may re-occupy another turret instead of its previous one,
|
||||
// since the message does not know what turret point whas used, which is not wanted.
|
||||
// Also ensure the TurretHolder receives the message after we process it.
|
||||
// If it processes it before us we garrison a turret and subsequently
|
||||
// are hidden by GarrisonHolder again.
|
||||
// This could be fixed by not requiring a turret to be 'garrisoned'.
|
||||
let cmpTurretHolder = Engine.QueryInterface(this.entity, IID_TurretHolder);
|
||||
if (cmpTurretHolder)
|
||||
cmpTurretHolder.SwapEntities(msg.entity, msg.newentity);
|
||||
}
|
||||
|
||||
if (!this.initGarrison)
|
||||
@ -722,26 +604,12 @@ GarrisonHolder.prototype.EjectOrKill = function(entities)
|
||||
|
||||
if (killedEntities.length)
|
||||
{
|
||||
let visibleEntitiesIds = {};
|
||||
for (let ent of killedEntities)
|
||||
visibleEntitiesIds[ent] = this.IsVisiblyGarrisoned(ent);
|
||||
Engine.PostMessage(this.entity, MT_GarrisonedUnitsChanged, {
|
||||
"added": [],
|
||||
"removed": killedEntities,
|
||||
"visible": visibleEntitiesIds
|
||||
"removed": killedEntities
|
||||
});
|
||||
this.UpdateGarrisonFlag();
|
||||
}
|
||||
this.UpdateGarrisonFlag();
|
||||
};
|
||||
|
||||
/**
|
||||
* Gives insight about the unit type of garrisoning.
|
||||
* @param {number} entity - The entity's id.
|
||||
* @return {boolean} - Whether the entity is visible on the garrison holder.
|
||||
*/
|
||||
GarrisonHolder.prototype.IsVisiblyGarrisoned = function(entity)
|
||||
{
|
||||
return this.visibleGarrisonPoints.some(point => point.entity == entity);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -359,6 +359,12 @@ GuiInterface.prototype.GetEntityState = function(player, ent)
|
||||
"garrisonedEntitiesCount": cmpGarrisonHolder.GetGarrisonedEntitiesCount()
|
||||
};
|
||||
|
||||
let cmpTurretHolder = Engine.QueryInterface(ent, IID_TurretHolder);
|
||||
if (cmpTurretHolder)
|
||||
ret.turretHolder = {
|
||||
"turretPoints": cmpTurretHolder.GetTurretPoints()
|
||||
};
|
||||
|
||||
ret.canGarrison = !!Engine.QueryInterface(ent, IID_Garrisonable);
|
||||
|
||||
let cmpUnitAI = Engine.QueryInterface(ent, IID_UnitAI);
|
||||
|
249
binaries/data/mods/public/simulation/components/TurretHolder.js
Normal file
249
binaries/data/mods/public/simulation/components/TurretHolder.js
Normal file
@ -0,0 +1,249 @@
|
||||
/**
|
||||
* This class holds the functions regarding entities being visible on
|
||||
* another entity, but tied to their parents location.
|
||||
* Currently renaming and changing ownership are still managed by GarrisonHolder.js,
|
||||
* but in the future these components should be independent.
|
||||
*/
|
||||
class TurretHolder
|
||||
{
|
||||
Init()
|
||||
{
|
||||
this.turretPoints = [];
|
||||
|
||||
let points = this.template.TurretPoints;
|
||||
for (let point in points)
|
||||
this.turretPoints.push({
|
||||
"offset": {
|
||||
"x": +points[point].X,
|
||||
"y": +points[point].Y,
|
||||
"z": +points[point].Z
|
||||
},
|
||||
"allowedClasses": points[point].AllowedClasses,
|
||||
"angle": points[point].Angle ? +points[point].Angle * Math.PI / 180 : null,
|
||||
"entity": null
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {Object[]} - An array of the turret points this entity has.
|
||||
*/
|
||||
GetTurretPoints()
|
||||
{
|
||||
return this.turretPoints;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} entity - The entity to check for.
|
||||
* @param {Object} turretPoint - The turret point to use.
|
||||
*
|
||||
* @return {boolean} - Whether the entity is allowed to occupy the specified turret point.
|
||||
*/
|
||||
AllowedToOccupyTurret(entity, turretPoint)
|
||||
{
|
||||
if (!turretPoint || turretPoint.entity)
|
||||
return false;
|
||||
|
||||
if (!IsOwnedByMutualAllyOfEntity(entity, this.entity))
|
||||
return false;
|
||||
|
||||
if (!turretPoint.allowedClasses)
|
||||
return true;
|
||||
|
||||
let cmpIdentity = Engine.QueryInterface(entity, IID_Identity);
|
||||
return cmpIdentity && MatchesClassList(cmpIdentity.GetClassesList(), turretPoint.allowedClasses._string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Occupy a turret point with the given entity.
|
||||
* @param {number} entity - The entity to use.
|
||||
* @param {Object} turretPoint - Optionally the specific turret point to occupy.
|
||||
*
|
||||
* @return {boolean} - Whether the occupation was successful.
|
||||
*/
|
||||
OccupyTurret(entity, requestedTurretPoint)
|
||||
{
|
||||
let cmpPositionOccupant = Engine.QueryInterface(entity, IID_Position);
|
||||
if (!cmpPositionOccupant)
|
||||
return false;
|
||||
|
||||
let cmpPositionSelf = Engine.QueryInterface(this.entity, IID_Position);
|
||||
if (!cmpPositionSelf)
|
||||
return false;
|
||||
|
||||
if (this.OccupiesTurret(entity))
|
||||
return false;
|
||||
|
||||
let turretPoint;
|
||||
if (requestedTurretPoint)
|
||||
{
|
||||
if (this.AllowedToOccupyTurret(entity, requestedTurretPoint))
|
||||
turretPoint = requestedTurretPoint;
|
||||
}
|
||||
else
|
||||
turretPoint = this.turretPoints.find(turret => !turret.entity && this.AllowedToOccupyTurret(entity, turret));
|
||||
|
||||
if (!turretPoint)
|
||||
return false;
|
||||
|
||||
turretPoint.entity = entity;
|
||||
// Angle of turrets:
|
||||
// Renamed entities (turretPoint != undefined) should keep their angle.
|
||||
// Otherwise if an angle is given in the turretPoint, use it.
|
||||
// If no such angle given (usually walls for which outside/inside not well defined), we keep
|
||||
// the current angle as it was used for garrisoning and thus quite often was from inside to
|
||||
// outside, except when garrisoning from outWorld where we take as default PI.
|
||||
if (!turretPoint && turretPoint.angle != null)
|
||||
cmpPositionOccupant.SetYRotation(cmpPositionSelf.GetRotation().y + turretPoint.angle);
|
||||
else if (!turretPoint && !cmpPosition.IsInWorld())
|
||||
cmpPositionOccupant.SetYRotation(cmpPositionSelf.GetRotation().y + Math.PI);
|
||||
|
||||
cmpPositionOccupant.SetTurretParent(this.entity, turretPoint.offset);
|
||||
|
||||
let cmpUnitMotion = Engine.QueryInterface(entity, IID_UnitMotion);
|
||||
if (cmpUnitMotion)
|
||||
cmpUnitMotion.SetFacePointAfterMove(false);
|
||||
|
||||
let cmpUnitAI = Engine.QueryInterface(entity, IID_UnitAI);
|
||||
if (cmpUnitAI)
|
||||
cmpUnitAI.SetTurretStance();
|
||||
|
||||
Engine.PostMessage(this.entity, MT_TurretsChanged, {
|
||||
"added": [entity],
|
||||
"removed": []
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the entity from a turret.
|
||||
* @param {number} entity - The specific entity to eject.
|
||||
* @param {Object} turret - Optionally the turret to abandon.
|
||||
*
|
||||
* @return {boolean} - Whether the entity was occupying a/the turret before.
|
||||
*/
|
||||
LeaveTurret(entity, requestedTurretPoint)
|
||||
{
|
||||
let turretPoint;
|
||||
if (requestedTurretPoint)
|
||||
{
|
||||
if (requestedTurretPoint.entity == entity)
|
||||
turretPoint = requestedTurretPoint;
|
||||
}
|
||||
else
|
||||
turretPoint = this.turretPoints.find(turret => turret.entity == entity);
|
||||
|
||||
if (!turretPoint)
|
||||
return false;
|
||||
|
||||
let cmpPositionEntity = Engine.QueryInterface(entity, IID_Position);
|
||||
cmpPositionEntity.SetTurretParent(INVALID_ENTITY, new Vector3D());
|
||||
|
||||
let cmpUnitMotionEntity = Engine.QueryInterface(entity, IID_UnitMotion);
|
||||
if (cmpUnitMotionEntity)
|
||||
cmpUnitMotionEntity.SetFacePointAfterMove(true);
|
||||
|
||||
let cmpUnitAIEntity = Engine.QueryInterface(entity, IID_UnitAI);
|
||||
if (cmpUnitAIEntity)
|
||||
cmpUnitAIEntity.ResetTurretStance();
|
||||
|
||||
turretPoint.entity = null;
|
||||
|
||||
Engine.PostMessage(this.entity, MT_TurretsChanged, {
|
||||
"added": [],
|
||||
"removed": [entity]
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} entity - The entity's id.
|
||||
* @param {Object} turret - Optionally the turret to check.
|
||||
*
|
||||
* @return {boolean} - Whether the entity is positioned on a turret of this entity.
|
||||
*/
|
||||
OccupiesTurret(entity, requestedTurretPoint)
|
||||
{
|
||||
return requestedTurretPoint ? requestedTurretPoint.entity == entity :
|
||||
this.turretPoints.some(turretPoint => turretPoint.entity == entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} entity - The entity's id.
|
||||
* @return {Object} - The turret this entity is positioned on, if applicable.
|
||||
*/
|
||||
GetOccupiedTurret(entity)
|
||||
{
|
||||
return this.turretPoints.find(turretPoint => turretPoint.entity == entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* We process EntityRenamed here because we need to be sure that we receive
|
||||
* it after it is processed by GarrisonHolder.js.
|
||||
* ToDo: Make this not needed by fully separating TurretHolder from GarrisonHolder.
|
||||
* That means an entity with TurretHolder should not need a GarrisonHolder
|
||||
* for e.g. the garrisoning logic.
|
||||
*
|
||||
* @param {number} from - The entity to substitute.
|
||||
* @param {number} to - The entity to subtitute with.
|
||||
*/
|
||||
SwapEntities(from, to)
|
||||
{
|
||||
let turretPoint = this.GetOccupiedTurret(from);
|
||||
if (turretPoint)
|
||||
this.LeaveTurret(from, turretPoint);
|
||||
|
||||
let cmpGarrisonHolder = Engine.QueryInterface(this.entity, IID_GarrisonHolder);
|
||||
if (cmpGarrisonHolder && cmpGarrisonHolder.IsGarrisoned(to))
|
||||
this.OccupyTurret(to, turretPoint);
|
||||
}
|
||||
|
||||
OnGarrisonedUnitsChanged(msg)
|
||||
{
|
||||
// Ignore renaming for that is handled seperately
|
||||
// (i.e. called directly from GarrisonHolder.js).
|
||||
if (msg.renamed)
|
||||
return;
|
||||
|
||||
for (let entity of msg.removed)
|
||||
this.LeaveTurret(entity);
|
||||
for (let entity of msg.added)
|
||||
this.OccupyTurret(entity);
|
||||
}
|
||||
}
|
||||
|
||||
TurretHolder.prototype.Schema =
|
||||
"<element name='TurretPoints' a:help='Points that will be used to visibly garrison a unit.'>" +
|
||||
"<oneOrMore>" +
|
||||
"<element a:help='Element containing the offset coordinates.'>" +
|
||||
"<anyName/>" +
|
||||
"<interleave>" +
|
||||
"<element name='X'>" +
|
||||
"<data type='decimal'/>" +
|
||||
"</element>" +
|
||||
"<element name='Y'>" +
|
||||
"<data type='decimal'/>" +
|
||||
"</element>" +
|
||||
"<element name='Z'>" +
|
||||
"<data type='decimal'/>" +
|
||||
"</element>" +
|
||||
"<optional>" +
|
||||
"<element name='AllowedClasses' a:help='If specified, only entities matching the given classes will be able to use this turret.'>" +
|
||||
"<attribute name='datatype'>" +
|
||||
"<value>tokens</value>" +
|
||||
"</attribute>" +
|
||||
"<text/>" +
|
||||
"</element>" +
|
||||
"</optional>"+
|
||||
"<optional>" +
|
||||
"<element name='Angle' a:help='Angle in degrees relative to the turretHolder direction.'>" +
|
||||
"<data type='decimal'/>" +
|
||||
"</element>" +
|
||||
"</optional>" +
|
||||
"</interleave>" +
|
||||
"</element>" +
|
||||
"</oneOrMore>" +
|
||||
"</element>";
|
||||
|
||||
Engine.RegisterComponentType(IID_TurretHolder, "TurretHolder", TurretHolder);
|
@ -0,0 +1,7 @@
|
||||
Engine.RegisterInterface("TurretHolder");
|
||||
|
||||
/**
|
||||
* Message of the form { "added": number[], "removed": number[] }
|
||||
* sent from the TurretHolder component to the current entity whenever the turrets change.
|
||||
*/
|
||||
Engine.RegisterMessageType("TurretsChanged");
|
@ -2,6 +2,7 @@ Engine.LoadHelperScript("ValueModification.js");
|
||||
Engine.LoadHelperScript("Player.js");
|
||||
Engine.LoadComponentScript("interfaces/GarrisonHolder.js");
|
||||
Engine.LoadComponentScript("interfaces/Garrisonable.js");
|
||||
Engine.LoadComponentScript("interfaces/TurretHolder.js");
|
||||
Engine.LoadComponentScript("GarrisonHolder.js");
|
||||
Engine.LoadComponentScript("interfaces/Auras.js");
|
||||
Engine.LoadComponentScript("interfaces/Health.js");
|
||||
@ -73,11 +74,8 @@ for (let i = 24; i <= 34; ++i)
|
||||
"GetHeightOffset": () => 0,
|
||||
"GetPosition": () => new Vector3D(4, 3, 25),
|
||||
"GetRotation": () => new Vector3D(4, 0, 6),
|
||||
"GetTurretParent": () => INVALID_ENTITY,
|
||||
"IsInWorld": () => true,
|
||||
"JumpTo": (posX, posZ) => {},
|
||||
"MoveOutOfWorld": () => {},
|
||||
"SetTurretParent": (entity, offset) => {},
|
||||
"SetHeightOffset": height => {}
|
||||
});
|
||||
}
|
||||
@ -94,19 +92,7 @@ let cmpGarrisonHolder = ConstructComponent(garrisonHolderId, "GarrisonHolder", {
|
||||
"EjectClassesOnDestroy": { "_string": "Infantry" },
|
||||
"BuffHeal": 1,
|
||||
"LoadingRange": 2.1,
|
||||
"Pickup": false,
|
||||
"VisibleGarrisonPoints": {
|
||||
"archer1": {
|
||||
"X": 12,
|
||||
"Y": 5,
|
||||
"Z": 6
|
||||
},
|
||||
"archer2": {
|
||||
"X": 15,
|
||||
"Y": 5,
|
||||
"Z": 6
|
||||
}
|
||||
}
|
||||
"Pickup": false
|
||||
});
|
||||
|
||||
let testGarrisonAllowed = function()
|
||||
@ -182,23 +168,12 @@ cmpGarrisonHolder = ConstructComponent(garrisonHolderId, "GarrisonHolder", {
|
||||
"EjectClassesOnDestroy": { "_string": "Infantry" },
|
||||
"BuffHeal": 1,
|
||||
"LoadingRange": 2.1,
|
||||
"Pickup": false,
|
||||
"VisibleGarrisonPoints": {
|
||||
"archer1": {
|
||||
"X": 12,
|
||||
"Y": 5,
|
||||
"Z": 6
|
||||
},
|
||||
"archer2": {
|
||||
"X": 15,
|
||||
"Y": 5,
|
||||
"Z": 6
|
||||
}
|
||||
}
|
||||
"Pickup": false
|
||||
});
|
||||
|
||||
testGarrisonAllowed();
|
||||
|
||||
// Test entity renaming.
|
||||
let siegeEngineId = 44;
|
||||
AddMock(siegeEngineId, IID_Identity, {
|
||||
"GetClassesList": () => ["Siege"]
|
||||
@ -208,7 +183,6 @@ AddMock(archerId, IID_Identity, {
|
||||
"GetClassesList": () => ["Infantry", "Ranged"]
|
||||
});
|
||||
|
||||
// Test visible garrisoning restrictions.
|
||||
cmpGarrisonHolder = ConstructComponent(garrisonHolderId, "GarrisonHolder", {
|
||||
"Max": 10,
|
||||
"List": { "_string": "Infantry+Ranged Siege Cavalry" },
|
||||
@ -216,55 +190,19 @@ cmpGarrisonHolder = ConstructComponent(garrisonHolderId, "GarrisonHolder", {
|
||||
"EjectClassesOnDestroy": { "_string": "Infantry" },
|
||||
"BuffHeal": 1,
|
||||
"LoadingRange": 2.1,
|
||||
"Pickup": false,
|
||||
"VisibleGarrisonPoints": {
|
||||
"archer1": {
|
||||
"X": 12,
|
||||
"Y": 5,
|
||||
"Z": 6
|
||||
},
|
||||
"archer2": {
|
||||
"X": 15,
|
||||
"Y": 5,
|
||||
"Z": 6,
|
||||
"AllowedClasses": { "_string": "Siege Trader" }
|
||||
},
|
||||
"archer3": {
|
||||
"X": 15,
|
||||
"Y": 5,
|
||||
"Z": 6,
|
||||
"AllowedClasses": { "_string": "Siege Infantry+Ranged Infantry+Cavalry" }
|
||||
}
|
||||
}
|
||||
"Pickup": false
|
||||
});
|
||||
|
||||
AddMock(32, IID_Identity, {
|
||||
"GetClassesList": () => ["Trader"]
|
||||
});
|
||||
|
||||
TS_ASSERT_EQUALS(cmpGarrisonHolder.Garrison(32), false);
|
||||
TS_ASSERT_EQUALS(cmpGarrisonHolder.AllowedToVisibleGarrisoning(siegeEngineId, cmpGarrisonHolder.visibleGarrisonPoints[0]), true);
|
||||
TS_ASSERT_EQUALS(cmpGarrisonHolder.AllowedToVisibleGarrisoning(siegeEngineId, cmpGarrisonHolder.visibleGarrisonPoints[1]), true);
|
||||
TS_ASSERT_EQUALS(cmpGarrisonHolder.AllowedToVisibleGarrisoning(siegeEngineId, cmpGarrisonHolder.visibleGarrisonPoints[2]), true);
|
||||
TS_ASSERT_EQUALS(cmpGarrisonHolder.AllowedToVisibleGarrisoning(archerId, cmpGarrisonHolder.visibleGarrisonPoints[0]), true);
|
||||
TS_ASSERT_EQUALS(cmpGarrisonHolder.AllowedToVisibleGarrisoning(archerId, cmpGarrisonHolder.visibleGarrisonPoints[1]), false);
|
||||
TS_ASSERT_EQUALS(cmpGarrisonHolder.AllowedToVisibleGarrisoning(archerId, cmpGarrisonHolder.visibleGarrisonPoints[2]), true);
|
||||
TS_ASSERT_EQUALS(cmpGarrisonHolder.AllowedToVisibleGarrisoning(33, cmpGarrisonHolder.visibleGarrisonPoints[0]), true);
|
||||
TS_ASSERT_EQUALS(cmpGarrisonHolder.AllowedToVisibleGarrisoning(33, cmpGarrisonHolder.visibleGarrisonPoints[1]), false);
|
||||
TS_ASSERT_EQUALS(cmpGarrisonHolder.AllowedToVisibleGarrisoning(33, cmpGarrisonHolder.visibleGarrisonPoints[2]), true);
|
||||
|
||||
// If an entity gets renamed (e.g. promotion, upgrade)
|
||||
// and is no longer able to be visibly garrisoned it
|
||||
// should be garisoned instead or ejected.
|
||||
AddMock(siegeEngineId, IID_Position, {
|
||||
"GetHeightOffset": () => 0,
|
||||
"GetPosition": () => new Vector3D(4, 3, 25),
|
||||
"GetRotation": () => new Vector3D(4, 0, 6),
|
||||
"GetTurretParent": () => INVALID_ENTITY,
|
||||
"IsInWorld": () => true,
|
||||
"JumpTo": (posX, posZ) => {},
|
||||
"MoveOutOfWorld": () => {},
|
||||
"SetTurretParent": (entity, offset) => {},
|
||||
"SetHeightOffset": height => {}
|
||||
});
|
||||
let currentSiegePlayer = player;
|
||||
@ -280,11 +218,8 @@ AddMock(cavalryId, IID_Position, {
|
||||
"GetHeightOffset": () => 0,
|
||||
"GetPosition": () => new Vector3D(4, 3, 25),
|
||||
"GetRotation": () => new Vector3D(4, 0, 6),
|
||||
"GetTurretParent": () => INVALID_ENTITY,
|
||||
"IsInWorld": () => true,
|
||||
"JumpTo": (posX, posZ) => {},
|
||||
"MoveOutOfWorld": () => {},
|
||||
"SetTurretParent": (entity, offset) => {},
|
||||
"SetHeightOffset": height => {}
|
||||
});
|
||||
|
||||
@ -295,14 +230,11 @@ AddMock(cavalryId, IID_Ownership, {
|
||||
AddMock(cavalryId, IID_Garrisonable, {});
|
||||
TS_ASSERT(cmpGarrisonHolder.Garrison(siegeEngineId));
|
||||
TS_ASSERT_EQUALS(cmpGarrisonHolder.GetGarrisonedEntitiesCount(), 1);
|
||||
TS_ASSERT(cmpGarrisonHolder.IsVisiblyGarrisoned(siegeEngineId));
|
||||
cmpGarrisonHolder.OnGlobalEntityRenamed({
|
||||
"entity": siegeEngineId,
|
||||
"newentity": cavalryId
|
||||
});
|
||||
TS_ASSERT_EQUALS(cmpGarrisonHolder.GetGarrisonedEntitiesCount(), 1);
|
||||
TS_ASSERT(!cmpGarrisonHolder.IsVisiblyGarrisoned(siegeEngineId));
|
||||
TS_ASSERT(!cmpGarrisonHolder.IsVisiblyGarrisoned(archerId));
|
||||
|
||||
// Eject enemy units.
|
||||
currentCavalryPlayer = enemyPlayer;
|
||||
@ -311,14 +243,3 @@ cmpGarrisonHolder.OnGlobalOwnershipChanged({
|
||||
"to": enemyPlayer
|
||||
});
|
||||
TS_ASSERT_EQUALS(cmpGarrisonHolder.GetGarrisonedEntitiesCount(), 0);
|
||||
|
||||
// Visibly garrisoned units should get ejected if they change players.
|
||||
TS_ASSERT(cmpGarrisonHolder.Garrison(siegeEngineId));
|
||||
TS_ASSERT(cmpGarrisonHolder.IsVisiblyGarrisoned(siegeEngineId));
|
||||
TS_ASSERT_EQUALS(cmpGarrisonHolder.GetGarrisonedEntitiesCount(), 1);
|
||||
currentSiegePlayer = enemyPlayer;
|
||||
cmpGarrisonHolder.OnGlobalOwnershipChanged({
|
||||
"entity": siegeEngineId,
|
||||
"to": enemyPlayer
|
||||
});
|
||||
TS_ASSERT_EQUALS(cmpGarrisonHolder.GetGarrisonedEntitiesCount(), 0);
|
||||
|
@ -30,6 +30,7 @@ Engine.LoadComponentScript("interfaces/ResourceTrickle.js");
|
||||
Engine.LoadComponentScript("interfaces/ResourceSupply.js");
|
||||
Engine.LoadComponentScript("interfaces/TechnologyManager.js");
|
||||
Engine.LoadComponentScript("interfaces/Trader.js");
|
||||
Engine.LoadComponentScript("interfaces/TurretHolder.js");
|
||||
Engine.LoadComponentScript("interfaces/Timer.js");
|
||||
Engine.LoadComponentScript("interfaces/StatisticsTracker.js");
|
||||
Engine.LoadComponentScript("interfaces/StatusEffectsReceiver.js");
|
||||
|
@ -0,0 +1,141 @@
|
||||
Engine.LoadHelperScript("Player.js");
|
||||
Engine.LoadComponentScript("interfaces/TurretHolder.js");
|
||||
Engine.LoadComponentScript("interfaces/UnitAI.js");
|
||||
Engine.LoadComponentScript("TurretHolder.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];
|
||||
|
||||
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 (let 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_Player, {
|
||||
"IsAlly": id => id != enemyPlayer,
|
||||
"IsMutualAlly": id => id != enemyPlayer,
|
||||
"GetPlayerID": () => player
|
||||
});
|
||||
|
||||
AddMock(alliedPlayer, IID_Player, {
|
||||
"IsAlly": id => true,
|
||||
"IsMutualAlly": id => true,
|
||||
"GetPlayerID": () => alliedPlayer
|
||||
});
|
||||
|
||||
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" }
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let siegeEngineID = entitiesToTest[0];
|
||||
AddMock(siegeEngineID, IID_Identity, {
|
||||
"GetClassesList": () => ["Siege"]
|
||||
});
|
||||
|
||||
let archerID = entitiesToTest[1];
|
||||
AddMock(archerID, IID_Identity, {
|
||||
"GetClassesList": () => ["Infantry", "Ranged"]
|
||||
});
|
||||
|
||||
let cavID = entitiesToTest[2];
|
||||
AddMock(cavID, IID_Identity, {
|
||||
"GetClassesList": () => ["Infantry", "Cavalry"]
|
||||
});
|
||||
|
||||
let infID = entitiesToTest[3];
|
||||
AddMock(infID, IID_Identity, {
|
||||
"GetClassesList": () => ["Infantry"]
|
||||
});
|
||||
|
||||
// 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);
|
||||
|
||||
// Test that one cannot leave a turret that is not occupied.
|
||||
TS_ASSERT(!cmpTurretHolder.LeaveTurret(archerID));
|
||||
|
||||
// Test occupying a turret.
|
||||
TS_ASSERT(!cmpTurretHolder.OccupiesTurret(archerID));
|
||||
TS_ASSERT(cmpTurretHolder.OccupyTurret(archerID));
|
||||
TS_ASSERT(cmpTurretHolder.OccupiesTurret(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]));
|
||||
|
||||
// Leave turrets.
|
||||
TS_ASSERT(cmpTurretHolder.LeaveTurret(archerID));
|
||||
TS_ASSERT(!cmpTurretHolder.LeaveTurret(cavID, cmpTurretHolder.turretPoints[1]));
|
||||
TS_ASSERT(cmpTurretHolder.LeaveTurret(cavID, cmpTurretHolder.turretPoints[2]));
|
||||
|
||||
// Test renaming.
|
||||
AddMock(turretHolderID, IID_GarrisonHolder, {
|
||||
"IsGarrisoned": () => true
|
||||
});
|
||||
TS_ASSERT(cmpTurretHolder.OccupyTurret(siegeEngineID, cmpTurretHolder.turretPoints[2]));
|
||||
cmpTurretHolder.SwapEntities(siegeEngineID, archerID);
|
||||
TS_ASSERT(!cmpTurretHolder.OccupiesTurret(siegeEngineID));
|
||||
TS_ASSERT(cmpTurretHolder.LeaveTurret(archerID));
|
||||
|
||||
// Renaming into an entity not allowed on the same turret point hides us.
|
||||
TS_ASSERT(cmpTurretHolder.OccupyTurret(siegeEngineID, cmpTurretHolder.turretPoints[1]));
|
||||
cmpTurretHolder.SwapEntities(siegeEngineID, archerID);
|
||||
TS_ASSERT(!cmpTurretHolder.OccupiesTurret(siegeEngineID));
|
||||
TS_ASSERT(!cmpTurretHolder.OccupiesTurret(archerID));
|
||||
|
||||
// ToDo: Ownership changes are handled by GarrisonHolder.js.
|
@ -4,8 +4,8 @@
|
||||
<Square width="37" depth="6"/>
|
||||
<Height>9.0</Height>
|
||||
</Footprint>
|
||||
<GarrisonHolder>
|
||||
<VisibleGarrisonPoints>
|
||||
<TurretHolder>
|
||||
<TurretPoints>
|
||||
<Archer1>
|
||||
<X>0</X>
|
||||
<Y>11.5</Y>
|
||||
@ -31,8 +31,8 @@
|
||||
<Y>11.5</Y>
|
||||
<Z>0</Z>
|
||||
</Archer5>
|
||||
</VisibleGarrisonPoints>
|
||||
</GarrisonHolder>
|
||||
</TurretPoints>
|
||||
</TurretHolder>
|
||||
<Identity>
|
||||
<Civ>athen</Civ>
|
||||
<SpecificName>Teichos</SpecificName>
|
||||
|
@ -4,8 +4,8 @@
|
||||
<Square width="25" depth="6"/>
|
||||
<Height>12.5</Height>
|
||||
</Footprint>
|
||||
<GarrisonHolder>
|
||||
<VisibleGarrisonPoints>
|
||||
<TurretHolder>
|
||||
<TurretPoints>
|
||||
<Archer1>
|
||||
<X>0</X>
|
||||
<Y>11.5</Y>
|
||||
@ -21,8 +21,8 @@
|
||||
<Y>11.5</Y>
|
||||
<Z>0</Z>
|
||||
</Archer3>
|
||||
</VisibleGarrisonPoints>
|
||||
</GarrisonHolder>
|
||||
</TurretPoints>
|
||||
</TurretHolder>
|
||||
<Identity>
|
||||
<Civ>athen</Civ>
|
||||
<SpecificName>Teichos</SpecificName>
|
||||
|
@ -4,8 +4,8 @@
|
||||
<Square width="37" depth="7"/>
|
||||
<Height>10.3</Height>
|
||||
</Footprint>
|
||||
<GarrisonHolder>
|
||||
<VisibleGarrisonPoints>
|
||||
<TurretHolder>
|
||||
<TurretPoints>
|
||||
<Archer1>
|
||||
<X>0</X>
|
||||
<Y>9.3</Y>
|
||||
@ -31,8 +31,8 @@
|
||||
<Y>9.3</Y>
|
||||
<Z>0</Z>
|
||||
</Archer5>
|
||||
</VisibleGarrisonPoints>
|
||||
</GarrisonHolder>
|
||||
</TurretPoints>
|
||||
</TurretHolder>
|
||||
<Identity>
|
||||
<Civ>brit</Civ>
|
||||
<SpecificName>Rate</SpecificName>
|
||||
|
@ -4,8 +4,8 @@
|
||||
<Square width="25" depth="7"/>
|
||||
<Height>10.3</Height>
|
||||
</Footprint>
|
||||
<GarrisonHolder>
|
||||
<VisibleGarrisonPoints>
|
||||
<TurretHolder>
|
||||
<TurretPoints>
|
||||
<Archer1>
|
||||
<X>0</X>
|
||||
<Y>9.3</Y>
|
||||
@ -21,8 +21,8 @@
|
||||
<Y>9.3</Y>
|
||||
<Z>0</Z>
|
||||
</Archer3>
|
||||
</VisibleGarrisonPoints>
|
||||
</GarrisonHolder>
|
||||
</TurretPoints>
|
||||
</TurretHolder>
|
||||
<Identity>
|
||||
<Civ>brit</Civ>
|
||||
<SpecificName>Rate</SpecificName>
|
||||
|
@ -4,8 +4,8 @@
|
||||
<Square width="37" depth="8"/>
|
||||
<Height>13</Height>
|
||||
</Footprint>
|
||||
<GarrisonHolder>
|
||||
<VisibleGarrisonPoints>
|
||||
<TurretHolder>
|
||||
<TurretPoints>
|
||||
<Archer1>
|
||||
<X>0</X>
|
||||
<Y>12</Y>
|
||||
@ -31,8 +31,8 @@
|
||||
<Y>12</Y>
|
||||
<Z>0</Z>
|
||||
</Archer5>
|
||||
</VisibleGarrisonPoints>
|
||||
</GarrisonHolder>
|
||||
</TurretPoints>
|
||||
</TurretHolder>
|
||||
<Identity>
|
||||
<Civ>cart</Civ>
|
||||
<SpecificName>Homah</SpecificName>
|
||||
|
@ -4,8 +4,8 @@
|
||||
<Square width="25" depth="8"/>
|
||||
<Height>13</Height>
|
||||
</Footprint>
|
||||
<GarrisonHolder>
|
||||
<VisibleGarrisonPoints>
|
||||
<TurretHolder>
|
||||
<TurretPoints>
|
||||
<Archer1>
|
||||
<X>0</X>
|
||||
<Y>12</Y>
|
||||
@ -21,8 +21,8 @@
|
||||
<Y>12</Y>
|
||||
<Z>0</Z>
|
||||
</Archer3>
|
||||
</VisibleGarrisonPoints>
|
||||
</GarrisonHolder>
|
||||
</TurretPoints>
|
||||
</TurretHolder>
|
||||
<Identity>
|
||||
<Civ>cart</Civ>
|
||||
<SpecificName>Homah</SpecificName>
|
||||
|
@ -4,8 +4,8 @@
|
||||
<Square width="37" depth="7"/>
|
||||
<Height>10.3</Height>
|
||||
</Footprint>
|
||||
<GarrisonHolder>
|
||||
<VisibleGarrisonPoints>
|
||||
<TurretHolder>
|
||||
<TurretPoints>
|
||||
<Archer1>
|
||||
<X>0</X>
|
||||
<Y>9.3</Y>
|
||||
@ -31,8 +31,8 @@
|
||||
<Y>9.3</Y>
|
||||
<Z>0</Z>
|
||||
</Archer5>
|
||||
</VisibleGarrisonPoints>
|
||||
</GarrisonHolder>
|
||||
</TurretPoints>
|
||||
</TurretHolder>
|
||||
<Identity>
|
||||
<Civ>gaul</Civ>
|
||||
<SpecificName>Rate</SpecificName>
|
||||
|
@ -4,8 +4,8 @@
|
||||
<Square width="25" depth="7"/>
|
||||
<Height>10.3</Height>
|
||||
</Footprint>
|
||||
<GarrisonHolder>
|
||||
<VisibleGarrisonPoints>
|
||||
<TurretHolder>
|
||||
<TurretPoints>
|
||||
<Archer1>
|
||||
<X>0</X>
|
||||
<Y>9.3</Y>
|
||||
@ -21,8 +21,8 @@
|
||||
<Y>9.3</Y>
|
||||
<Z>0</Z>
|
||||
</Archer3>
|
||||
</VisibleGarrisonPoints>
|
||||
</GarrisonHolder>
|
||||
</TurretPoints>
|
||||
</TurretHolder>
|
||||
<Identity>
|
||||
<Civ>gaul</Civ>
|
||||
<SpecificName>Rate</SpecificName>
|
||||
|
@ -4,8 +4,8 @@
|
||||
<Square width="37" depth="8"/>
|
||||
<Height>10</Height>
|
||||
</Footprint>
|
||||
<GarrisonHolder>
|
||||
<VisibleGarrisonPoints>
|
||||
<TurretHolder>
|
||||
<TurretPoints>
|
||||
<Archer1>
|
||||
<X>0</X>
|
||||
<Y>9</Y>
|
||||
@ -31,8 +31,8 @@
|
||||
<Y>9</Y>
|
||||
<Z>0</Z>
|
||||
</Archer5>
|
||||
</VisibleGarrisonPoints>
|
||||
</GarrisonHolder>
|
||||
</TurretPoints>
|
||||
</TurretHolder>
|
||||
<Identity>
|
||||
<Civ>iber</Civ>
|
||||
<SpecificName>Zabal Horma</SpecificName>
|
||||
|
@ -4,8 +4,8 @@
|
||||
<Square width="25" depth="8"/>
|
||||
<Height>10</Height>
|
||||
</Footprint>
|
||||
<GarrisonHolder>
|
||||
<VisibleGarrisonPoints>
|
||||
<TurretHolder>
|
||||
<TurretPoints>
|
||||
<Archer1>
|
||||
<X>0</X>
|
||||
<Y>9</Y>
|
||||
@ -21,8 +21,8 @@
|
||||
<Y>9</Y>
|
||||
<Z>0</Z>
|
||||
</Archer3>
|
||||
</VisibleGarrisonPoints>
|
||||
</GarrisonHolder>
|
||||
</TurretPoints>
|
||||
</TurretHolder>
|
||||
<Identity>
|
||||
<Civ>iber</Civ>
|
||||
<SpecificName>Zabal Horma</SpecificName>
|
||||
|
@ -4,8 +4,8 @@
|
||||
<Square width="37" depth="6"/>
|
||||
<Height>12.6</Height>
|
||||
</Footprint>
|
||||
<GarrisonHolder>
|
||||
<VisibleGarrisonPoints>
|
||||
<TurretHolder>
|
||||
<TurretPoints>
|
||||
<Archer1>
|
||||
<X>0</X>
|
||||
<Y>11.6</Y>
|
||||
@ -31,8 +31,8 @@
|
||||
<Y>11.6</Y>
|
||||
<Z>0</Z>
|
||||
</Archer5>
|
||||
</VisibleGarrisonPoints>
|
||||
</GarrisonHolder>
|
||||
</TurretPoints>
|
||||
</TurretHolder>
|
||||
<Identity>
|
||||
<Civ>kush</Civ>
|
||||
<SpecificName>sbty</SpecificName>
|
||||
|
@ -4,8 +4,8 @@
|
||||
<Square width="25" depth="6"/>
|
||||
<Height>12.6</Height>
|
||||
</Footprint>
|
||||
<GarrisonHolder>
|
||||
<VisibleGarrisonPoints>
|
||||
<TurretHolder>
|
||||
<TurretPoints>
|
||||
<Archer1>
|
||||
<X>0</X>
|
||||
<Y>11.6</Y>
|
||||
@ -21,8 +21,8 @@
|
||||
<Y>11.6</Y>
|
||||
<Z>0</Z>
|
||||
</Archer3>
|
||||
</VisibleGarrisonPoints>
|
||||
</GarrisonHolder>
|
||||
</TurretPoints>
|
||||
</TurretHolder>
|
||||
<Identity>
|
||||
<Civ>kush</Civ>
|
||||
<SpecificName>sbty</SpecificName>
|
||||
|
@ -4,8 +4,8 @@
|
||||
<Square width="37" depth="6"/>
|
||||
<Height>12.5</Height>
|
||||
</Footprint>
|
||||
<GarrisonHolder>
|
||||
<VisibleGarrisonPoints>
|
||||
<TurretHolder>
|
||||
<TurretPoints>
|
||||
<Archer1>
|
||||
<X>0</X>
|
||||
<Y>11.5</Y>
|
||||
@ -31,8 +31,8 @@
|
||||
<Y>11.5</Y>
|
||||
<Z>0</Z>
|
||||
</Archer5>
|
||||
</VisibleGarrisonPoints>
|
||||
</GarrisonHolder>
|
||||
</TurretPoints>
|
||||
</TurretHolder>
|
||||
<Identity>
|
||||
<Civ>mace</Civ>
|
||||
<SpecificName>Teichos</SpecificName>
|
||||
|
@ -4,8 +4,8 @@
|
||||
<Square width="25" depth="6"/>
|
||||
<Height>12.5</Height>
|
||||
</Footprint>
|
||||
<GarrisonHolder>
|
||||
<VisibleGarrisonPoints>
|
||||
<TurretHolder>
|
||||
<TurretPoints>
|
||||
<Archer1>
|
||||
<X>0</X>
|
||||
<Y>11.5</Y>
|
||||
@ -21,8 +21,8 @@
|
||||
<Y>11.5</Y>
|
||||
<Z>0</Z>
|
||||
</Archer3>
|
||||
</VisibleGarrisonPoints>
|
||||
</GarrisonHolder>
|
||||
</TurretPoints>
|
||||
</TurretHolder>
|
||||
<Identity>
|
||||
<Civ>mace</Civ>
|
||||
<SpecificName>Teichos</SpecificName>
|
||||
|
@ -23,7 +23,9 @@
|
||||
<GarrisonHolder>
|
||||
<List>Infantry+Archer</List>
|
||||
<Max>20</Max>
|
||||
<VisibleGarrisonPoints>
|
||||
</GarrisonHolder>
|
||||
<TurretHolder>
|
||||
<TurretPoints>
|
||||
<Archer1>
|
||||
<X>2</X><Y>12.5</Y><Z>0</Z>
|
||||
</Archer1>
|
||||
@ -72,8 +74,8 @@
|
||||
<Archer16>
|
||||
<X>-2.1</X><Y>18.0</Y><Z>-2.1</Z>
|
||||
</Archer16>
|
||||
</VisibleGarrisonPoints>
|
||||
</GarrisonHolder>
|
||||
</TurretPoints>
|
||||
</TurretHolder>
|
||||
<Health>
|
||||
<Max>1200</Max>
|
||||
</Health>
|
||||
|
@ -10,8 +10,8 @@
|
||||
<Square width="37" depth="5"/>
|
||||
<Height>10.5</Height>
|
||||
</Footprint>
|
||||
<GarrisonHolder>
|
||||
<VisibleGarrisonPoints>
|
||||
<TurretHolder>
|
||||
<TurretPoints>
|
||||
<Archer1>
|
||||
<X>0</X>
|
||||
<Y>9.5</Y>
|
||||
@ -37,8 +37,8 @@
|
||||
<Y>9.5</Y>
|
||||
<Z>0</Z>
|
||||
</Archer5>
|
||||
</VisibleGarrisonPoints>
|
||||
</GarrisonHolder>
|
||||
</TurretPoints>
|
||||
</TurretHolder>
|
||||
<Identity>
|
||||
<Civ>maur</Civ>
|
||||
<SpecificName>Shilabanda</SpecificName>
|
||||
|
@ -10,8 +10,8 @@
|
||||
<Square width="25" depth="5"/>
|
||||
<Height>10.5</Height>
|
||||
</Footprint>
|
||||
<GarrisonHolder>
|
||||
<VisibleGarrisonPoints>
|
||||
<TurretHolder>
|
||||
<TurretPoints>
|
||||
<Archer1>
|
||||
<X>0</X>
|
||||
<Y>9.5</Y>
|
||||
@ -27,8 +27,8 @@
|
||||
<Y>9.5</Y>
|
||||
<Z>0</Z>
|
||||
</Archer3>
|
||||
</VisibleGarrisonPoints>
|
||||
</GarrisonHolder>
|
||||
</TurretPoints>
|
||||
</TurretHolder>
|
||||
<Identity>
|
||||
<Civ>maur</Civ>
|
||||
<SpecificName>Shilabanda</SpecificName>
|
||||
|
@ -4,8 +4,8 @@
|
||||
<Square width="37" depth="7"/>
|
||||
<Height>11.6</Height>
|
||||
</Footprint>
|
||||
<GarrisonHolder>
|
||||
<VisibleGarrisonPoints>
|
||||
<TurretHolder>
|
||||
<TurretPoints>
|
||||
<Archer1>
|
||||
<X>0</X>
|
||||
<Y>10.6</Y>
|
||||
@ -31,8 +31,8 @@
|
||||
<Y>10.6</Y>
|
||||
<Z>0</Z>
|
||||
</Archer5>
|
||||
</VisibleGarrisonPoints>
|
||||
</GarrisonHolder>
|
||||
</TurretPoints>
|
||||
</TurretHolder>
|
||||
<Identity>
|
||||
<Civ>pers</Civ>
|
||||
<SpecificName>Para</SpecificName>
|
||||
|
@ -4,8 +4,8 @@
|
||||
<Square width="25" depth="7"/>
|
||||
<Height>11.6</Height>
|
||||
</Footprint>
|
||||
<GarrisonHolder>
|
||||
<VisibleGarrisonPoints>
|
||||
<TurretHolder>
|
||||
<TurretPoints>
|
||||
<Archer1>
|
||||
<X>0</X>
|
||||
<Y>10.6</Y>
|
||||
@ -21,8 +21,8 @@
|
||||
<Y>10.6</Y>
|
||||
<Z>0</Z>
|
||||
</Archer3>
|
||||
</VisibleGarrisonPoints>
|
||||
</GarrisonHolder>
|
||||
</TurretPoints>
|
||||
</TurretHolder>
|
||||
<Identity>
|
||||
<Civ>pers</Civ>
|
||||
<SpecificName>Para</SpecificName>
|
||||
|
@ -4,8 +4,8 @@
|
||||
<Square width="39" depth="6"/>
|
||||
<Height>10.8</Height>
|
||||
</Footprint>
|
||||
<GarrisonHolder>
|
||||
<VisibleGarrisonPoints>
|
||||
<TurretHolder>
|
||||
<TurretPoints>
|
||||
<Archer1>
|
||||
<X>0</X>
|
||||
<Y>9.8</Y>
|
||||
@ -31,8 +31,8 @@
|
||||
<Y>9.8</Y>
|
||||
<Z>0</Z>
|
||||
</Archer5>
|
||||
</VisibleGarrisonPoints>
|
||||
</GarrisonHolder>
|
||||
</TurretPoints>
|
||||
</TurretHolder>
|
||||
<Identity>
|
||||
<Civ>ptol</Civ>
|
||||
<SpecificName>Teichos</SpecificName>
|
||||
|
@ -4,8 +4,8 @@
|
||||
<Square width="26" depth="6"/>
|
||||
<Height>10.8</Height>
|
||||
</Footprint>
|
||||
<GarrisonHolder>
|
||||
<VisibleGarrisonPoints>
|
||||
<TurretHolder>
|
||||
<TurretPoints>
|
||||
<Archer1>
|
||||
<X>0</X>
|
||||
<Y>9.8</Y>
|
||||
@ -21,8 +21,8 @@
|
||||
<Y>9.8</Y>
|
||||
<Z>0</Z>
|
||||
</Archer3>
|
||||
</VisibleGarrisonPoints>
|
||||
</GarrisonHolder>
|
||||
</TurretPoints>
|
||||
</TurretHolder>
|
||||
<Identity>
|
||||
<Civ>ptol</Civ>
|
||||
<SpecificName>Teichos</SpecificName>
|
||||
|
@ -23,8 +23,8 @@
|
||||
<Square width="37" depth="5"/>
|
||||
<Height>6.7</Height>
|
||||
</Footprint>
|
||||
<GarrisonHolder>
|
||||
<VisibleGarrisonPoints>
|
||||
<TurretHolder>
|
||||
<TurretPoints>
|
||||
<Archer1>
|
||||
<X>0</X>
|
||||
<Y>5.7</Y>
|
||||
@ -50,8 +50,8 @@
|
||||
<Y>5.7</Y>
|
||||
<Z>0</Z>
|
||||
</Archer5>
|
||||
</VisibleGarrisonPoints>
|
||||
</GarrisonHolder>
|
||||
</TurretPoints>
|
||||
</TurretHolder>
|
||||
<Health>
|
||||
<Max op="mul">0.75</Max>
|
||||
</Health>
|
||||
|
@ -23,8 +23,8 @@
|
||||
<Square width="25" depth="5"/>
|
||||
<Height>6.7</Height>
|
||||
</Footprint>
|
||||
<GarrisonHolder>
|
||||
<VisibleGarrisonPoints>
|
||||
<TurretHolder>
|
||||
<TurretPoints>
|
||||
<Archer1>
|
||||
<X>0</X>
|
||||
<Y>5.7</Y>
|
||||
@ -40,8 +40,8 @@
|
||||
<Y>5.7</Y>
|
||||
<Z>0</Z>
|
||||
</Archer3>
|
||||
</VisibleGarrisonPoints>
|
||||
</GarrisonHolder>
|
||||
</TurretPoints>
|
||||
</TurretHolder>
|
||||
<Health>
|
||||
<Max op="mul">0.75</Max>
|
||||
</Health>
|
||||
|
@ -4,8 +4,8 @@
|
||||
<Square width="37" depth="9"/>
|
||||
<Height>9.9</Height>
|
||||
</Footprint>
|
||||
<GarrisonHolder>
|
||||
<VisibleGarrisonPoints>
|
||||
<TurretHolder>
|
||||
<TurretPoints>
|
||||
<Archer1>
|
||||
<X>0</X>
|
||||
<Y>8.9</Y>
|
||||
@ -31,8 +31,8 @@
|
||||
<Y>8.9</Y>
|
||||
<Z>0</Z>
|
||||
</Archer5>
|
||||
</VisibleGarrisonPoints>
|
||||
</GarrisonHolder>
|
||||
</TurretPoints>
|
||||
</TurretHolder>
|
||||
<Identity>
|
||||
<Civ>rome</Civ>
|
||||
<SpecificName>Moenia</SpecificName>
|
||||
|
@ -4,8 +4,8 @@
|
||||
<Square width="25" depth="9"/>
|
||||
<Height>9.9</Height>
|
||||
</Footprint>
|
||||
<GarrisonHolder>
|
||||
<VisibleGarrisonPoints>
|
||||
<TurretHolder>
|
||||
<TurretPoints>
|
||||
<Archer1>
|
||||
<X>0</X>
|
||||
<Y>8.9</Y>
|
||||
@ -21,8 +21,8 @@
|
||||
<Y>8.9</Y>
|
||||
<Z>0</Z>
|
||||
</Archer3>
|
||||
</VisibleGarrisonPoints>
|
||||
</GarrisonHolder>
|
||||
</TurretPoints>
|
||||
</TurretHolder>
|
||||
<Identity>
|
||||
<Civ>rome</Civ>
|
||||
<SpecificName>Moenia</SpecificName>
|
||||
|
@ -4,8 +4,8 @@
|
||||
<Square width="35" depth="6"/>
|
||||
<Height>11.4</Height>
|
||||
</Footprint>
|
||||
<GarrisonHolder>
|
||||
<VisibleGarrisonPoints>
|
||||
<TurretHolder>
|
||||
<TurretPoints>
|
||||
<Archer1>
|
||||
<X>0</X>
|
||||
<Y>10.4</Y>
|
||||
@ -31,8 +31,8 @@
|
||||
<Y>10.4</Y>
|
||||
<Z>0</Z>
|
||||
</Archer5>
|
||||
</VisibleGarrisonPoints>
|
||||
</GarrisonHolder>
|
||||
</TurretPoints>
|
||||
</TurretHolder>
|
||||
<Identity>
|
||||
<Civ>sele</Civ>
|
||||
<SpecificName>Teichos</SpecificName>
|
||||
|
@ -4,8 +4,8 @@
|
||||
<Square width="22" depth="6"/>
|
||||
<Height>11.4</Height>
|
||||
</Footprint>
|
||||
<GarrisonHolder>
|
||||
<VisibleGarrisonPoints>
|
||||
<TurretHolder>
|
||||
<TurretPoints>
|
||||
<Archer1>
|
||||
<X>0</X>
|
||||
<Y>10.4</Y>
|
||||
@ -21,8 +21,8 @@
|
||||
<Y>10.4</Y>
|
||||
<Z>0</Z>
|
||||
</Archer3>
|
||||
</VisibleGarrisonPoints>
|
||||
</GarrisonHolder>
|
||||
</TurretPoints>
|
||||
</TurretHolder>
|
||||
<Identity>
|
||||
<Civ>sele</Civ>
|
||||
<SpecificName>Teichos</SpecificName>
|
||||
|
@ -4,8 +4,8 @@
|
||||
<Square width="37" depth="6"/>
|
||||
<Height>12.5</Height>
|
||||
</Footprint>
|
||||
<GarrisonHolder>
|
||||
<VisibleGarrisonPoints>
|
||||
<TurretHolder>
|
||||
<TurretPoints>
|
||||
<Archer1>
|
||||
<X>0</X>
|
||||
<Y>11.5</Y>
|
||||
@ -31,8 +31,8 @@
|
||||
<Y>11.5</Y>
|
||||
<Z>0</Z>
|
||||
</Archer5>
|
||||
</VisibleGarrisonPoints>
|
||||
</GarrisonHolder>
|
||||
</TurretPoints>
|
||||
</TurretHolder>
|
||||
<Identity>
|
||||
<Civ>spart</Civ>
|
||||
<SpecificName>Teichos</SpecificName>
|
||||
|
@ -4,8 +4,8 @@
|
||||
<Square width="25" depth="6"/>
|
||||
<Height>12.5</Height>
|
||||
</Footprint>
|
||||
<GarrisonHolder>
|
||||
<VisibleGarrisonPoints>
|
||||
<TurretHolder>
|
||||
<TurretPoints>
|
||||
<Archer1>
|
||||
<X>0</X>
|
||||
<Y>11.5</Y>
|
||||
@ -21,8 +21,8 @@
|
||||
<Y>11.5</Y>
|
||||
<Z>0</Z>
|
||||
</Archer3>
|
||||
</VisibleGarrisonPoints>
|
||||
</GarrisonHolder>
|
||||
</TurretPoints>
|
||||
</TurretHolder>
|
||||
<Identity>
|
||||
<Civ>spart</Civ>
|
||||
<SpecificName>Teichos</SpecificName>
|
||||
|
Loading…
Reference in New Issue
Block a user