diff --git a/binaries/data/mods/public/simulation/components/BuildingAI.js b/binaries/data/mods/public/simulation/components/BuildingAI.js
index 1e5eb70ff6..df47ebff62 100644
--- a/binaries/data/mods/public/simulation/components/BuildingAI.js
+++ b/binaries/data/mods/public/simulation/components/BuildingAI.js
@@ -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)
diff --git a/binaries/data/mods/public/simulation/components/GarrisonHolder.js b/binaries/data/mods/public/simulation/components/GarrisonHolder.js
index 84145e9259..6878ad53d0 100644
--- a/binaries/data/mods/public/simulation/components/GarrisonHolder.js
+++ b/binaries/data/mods/public/simulation/components/GarrisonHolder.js
@@ -31,39 +31,6 @@ GarrisonHolder.prototype.Schema =
"" +
"" +
"" +
- "" +
- "" +
- "" +
- "" +
- "" +
- "" +
- "" +
- "" +
- "" +
- "" +
- "" +
- "" +
- "" +
- "" +
- "" +
- "" +
- "" +
- "" +
- "" +
- "tokens" +
- "" +
- "" +
- "" +
- ""+
- "" +
- "" +
- "" +
- "" +
- "" +
- "" +
- "" +
- "" +
- "" +
"";
/**
@@ -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);
};
/**
diff --git a/binaries/data/mods/public/simulation/components/GuiInterface.js b/binaries/data/mods/public/simulation/components/GuiInterface.js
index cbec6134ab..8f800cad0a 100644
--- a/binaries/data/mods/public/simulation/components/GuiInterface.js
+++ b/binaries/data/mods/public/simulation/components/GuiInterface.js
@@ -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);
diff --git a/binaries/data/mods/public/simulation/components/TurretHolder.js b/binaries/data/mods/public/simulation/components/TurretHolder.js
new file mode 100644
index 0000000000..72431f9e3c
--- /dev/null
+++ b/binaries/data/mods/public/simulation/components/TurretHolder.js
@@ -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 =
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "tokens" +
+ "" +
+ "" +
+ "" +
+ ""+
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "";
+
+Engine.RegisterComponentType(IID_TurretHolder, "TurretHolder", TurretHolder);
diff --git a/binaries/data/mods/public/simulation/components/interfaces/TurretHolder.js b/binaries/data/mods/public/simulation/components/interfaces/TurretHolder.js
new file mode 100644
index 0000000000..576e9827ff
--- /dev/null
+++ b/binaries/data/mods/public/simulation/components/interfaces/TurretHolder.js
@@ -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");
diff --git a/binaries/data/mods/public/simulation/components/tests/test_GarrisonHolder.js b/binaries/data/mods/public/simulation/components/tests/test_GarrisonHolder.js
index 3984882bf3..8f2f36bfd5 100644
--- a/binaries/data/mods/public/simulation/components/tests/test_GarrisonHolder.js
+++ b/binaries/data/mods/public/simulation/components/tests/test_GarrisonHolder.js
@@ -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);
diff --git a/binaries/data/mods/public/simulation/components/tests/test_GuiInterface.js b/binaries/data/mods/public/simulation/components/tests/test_GuiInterface.js
index 2b7ec9a72b..1de32a202d 100644
--- a/binaries/data/mods/public/simulation/components/tests/test_GuiInterface.js
+++ b/binaries/data/mods/public/simulation/components/tests/test_GuiInterface.js
@@ -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");
diff --git a/binaries/data/mods/public/simulation/components/tests/test_TurretHolder.js b/binaries/data/mods/public/simulation/components/tests/test_TurretHolder.js
new file mode 100644
index 0000000000..7b5862d82d
--- /dev/null
+++ b/binaries/data/mods/public/simulation/components/tests/test_TurretHolder.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.
diff --git a/binaries/data/mods/public/simulation/templates/structures/athen_wall_long.xml b/binaries/data/mods/public/simulation/templates/structures/athen_wall_long.xml
index 543fe24506..453dbccc21 100644
--- a/binaries/data/mods/public/simulation/templates/structures/athen_wall_long.xml
+++ b/binaries/data/mods/public/simulation/templates/structures/athen_wall_long.xml
@@ -4,8 +4,8 @@
9.0
-
-
+
+
0
11.5
@@ -31,8 +31,8 @@
11.5
0
-
-
+
+
athen
Teichos
diff --git a/binaries/data/mods/public/simulation/templates/structures/athen_wall_medium.xml b/binaries/data/mods/public/simulation/templates/structures/athen_wall_medium.xml
index 80b4403bb1..dae23ee397 100644
--- a/binaries/data/mods/public/simulation/templates/structures/athen_wall_medium.xml
+++ b/binaries/data/mods/public/simulation/templates/structures/athen_wall_medium.xml
@@ -4,8 +4,8 @@
12.5
-
-
+
+
0
11.5
@@ -21,8 +21,8 @@
11.5
0
-
-
+
+
athen
Teichos
diff --git a/binaries/data/mods/public/simulation/templates/structures/brit_wall_long.xml b/binaries/data/mods/public/simulation/templates/structures/brit_wall_long.xml
index 13d98058bf..4902c82e23 100644
--- a/binaries/data/mods/public/simulation/templates/structures/brit_wall_long.xml
+++ b/binaries/data/mods/public/simulation/templates/structures/brit_wall_long.xml
@@ -4,8 +4,8 @@
10.3
-
-
+
+
0
9.3
@@ -31,8 +31,8 @@
9.3
0
-
-
+
+
brit
Rate
diff --git a/binaries/data/mods/public/simulation/templates/structures/brit_wall_medium.xml b/binaries/data/mods/public/simulation/templates/structures/brit_wall_medium.xml
index e31fcae6e5..a92f4b0a60 100644
--- a/binaries/data/mods/public/simulation/templates/structures/brit_wall_medium.xml
+++ b/binaries/data/mods/public/simulation/templates/structures/brit_wall_medium.xml
@@ -4,8 +4,8 @@
10.3
-
-
+
+
0
9.3
@@ -21,8 +21,8 @@
9.3
0
-
-
+
+
brit
Rate
diff --git a/binaries/data/mods/public/simulation/templates/structures/cart_wall_long.xml b/binaries/data/mods/public/simulation/templates/structures/cart_wall_long.xml
index 92b1468362..634e29c0f0 100644
--- a/binaries/data/mods/public/simulation/templates/structures/cart_wall_long.xml
+++ b/binaries/data/mods/public/simulation/templates/structures/cart_wall_long.xml
@@ -4,8 +4,8 @@
13
-
-
+
+
0
12
@@ -31,8 +31,8 @@
12
0
-
-
+
+
cart
Homah
diff --git a/binaries/data/mods/public/simulation/templates/structures/cart_wall_medium.xml b/binaries/data/mods/public/simulation/templates/structures/cart_wall_medium.xml
index 3c63e0b72e..9463325f01 100644
--- a/binaries/data/mods/public/simulation/templates/structures/cart_wall_medium.xml
+++ b/binaries/data/mods/public/simulation/templates/structures/cart_wall_medium.xml
@@ -4,8 +4,8 @@
13
-
-
+
+
0
12
@@ -21,8 +21,8 @@
12
0
-
-
+
+
cart
Homah
diff --git a/binaries/data/mods/public/simulation/templates/structures/gaul_wall_long.xml b/binaries/data/mods/public/simulation/templates/structures/gaul_wall_long.xml
index a2959b9f39..8b6d05a9b7 100644
--- a/binaries/data/mods/public/simulation/templates/structures/gaul_wall_long.xml
+++ b/binaries/data/mods/public/simulation/templates/structures/gaul_wall_long.xml
@@ -4,8 +4,8 @@
10.3
-
-
+
+
0
9.3
@@ -31,8 +31,8 @@
9.3
0
-
-
+
+
gaul
Rate
diff --git a/binaries/data/mods/public/simulation/templates/structures/gaul_wall_medium.xml b/binaries/data/mods/public/simulation/templates/structures/gaul_wall_medium.xml
index 707986c348..415fdf0af4 100644
--- a/binaries/data/mods/public/simulation/templates/structures/gaul_wall_medium.xml
+++ b/binaries/data/mods/public/simulation/templates/structures/gaul_wall_medium.xml
@@ -4,8 +4,8 @@
10.3
-
-
+
+
0
9.3
@@ -21,8 +21,8 @@
9.3
0
-
-
+
+
gaul
Rate
diff --git a/binaries/data/mods/public/simulation/templates/structures/iber_wall_long.xml b/binaries/data/mods/public/simulation/templates/structures/iber_wall_long.xml
index dbc6ff7f95..96765ff649 100644
--- a/binaries/data/mods/public/simulation/templates/structures/iber_wall_long.xml
+++ b/binaries/data/mods/public/simulation/templates/structures/iber_wall_long.xml
@@ -4,8 +4,8 @@
10
-
-
+
+
0
9
@@ -31,8 +31,8 @@
9
0
-
-
+
+
iber
Zabal Horma
diff --git a/binaries/data/mods/public/simulation/templates/structures/iber_wall_medium.xml b/binaries/data/mods/public/simulation/templates/structures/iber_wall_medium.xml
index 66ddf8136b..b340bddf61 100644
--- a/binaries/data/mods/public/simulation/templates/structures/iber_wall_medium.xml
+++ b/binaries/data/mods/public/simulation/templates/structures/iber_wall_medium.xml
@@ -4,8 +4,8 @@
10
-
-
+
+
0
9
@@ -21,8 +21,8 @@
9
0
-
-
+
+
iber
Zabal Horma
diff --git a/binaries/data/mods/public/simulation/templates/structures/kush_wall_long.xml b/binaries/data/mods/public/simulation/templates/structures/kush_wall_long.xml
index b2d031ed43..f60b0632ab 100644
--- a/binaries/data/mods/public/simulation/templates/structures/kush_wall_long.xml
+++ b/binaries/data/mods/public/simulation/templates/structures/kush_wall_long.xml
@@ -4,8 +4,8 @@
12.6
-
-
+
+
0
11.6
@@ -31,8 +31,8 @@
11.6
0
-
-
+
+
kush
sbty
diff --git a/binaries/data/mods/public/simulation/templates/structures/kush_wall_medium.xml b/binaries/data/mods/public/simulation/templates/structures/kush_wall_medium.xml
index bdedea0d5d..4e4e3204e5 100644
--- a/binaries/data/mods/public/simulation/templates/structures/kush_wall_medium.xml
+++ b/binaries/data/mods/public/simulation/templates/structures/kush_wall_medium.xml
@@ -4,8 +4,8 @@
12.6
-
-
+
+
0
11.6
@@ -21,8 +21,8 @@
11.6
0
-
-
+
+
kush
sbty
diff --git a/binaries/data/mods/public/simulation/templates/structures/mace_wall_long.xml b/binaries/data/mods/public/simulation/templates/structures/mace_wall_long.xml
index f406789d69..62da3645a5 100644
--- a/binaries/data/mods/public/simulation/templates/structures/mace_wall_long.xml
+++ b/binaries/data/mods/public/simulation/templates/structures/mace_wall_long.xml
@@ -4,8 +4,8 @@
12.5
-
-
+
+
0
11.5
@@ -31,8 +31,8 @@
11.5
0
-
-
+
+
mace
Teichos
diff --git a/binaries/data/mods/public/simulation/templates/structures/mace_wall_medium.xml b/binaries/data/mods/public/simulation/templates/structures/mace_wall_medium.xml
index 49638a7e85..683b30ec81 100644
--- a/binaries/data/mods/public/simulation/templates/structures/mace_wall_medium.xml
+++ b/binaries/data/mods/public/simulation/templates/structures/mace_wall_medium.xml
@@ -4,8 +4,8 @@
12.5
-
-
+
+
0
11.5
@@ -21,8 +21,8 @@
11.5
0
-
-
+
+
mace
Teichos
diff --git a/binaries/data/mods/public/simulation/templates/structures/maur_tower_double.xml b/binaries/data/mods/public/simulation/templates/structures/maur_tower_double.xml
index d4baa6dde6..3a8a7465e8 100644
--- a/binaries/data/mods/public/simulation/templates/structures/maur_tower_double.xml
+++ b/binaries/data/mods/public/simulation/templates/structures/maur_tower_double.xml
@@ -23,7 +23,9 @@
Infantry+Archer
20
-
+
+
+
212.50
@@ -72,8 +74,8 @@
-2.118.0-2.1
-
-
+
+
1200
diff --git a/binaries/data/mods/public/simulation/templates/structures/maur_wall_long.xml b/binaries/data/mods/public/simulation/templates/structures/maur_wall_long.xml
index e8b6743da2..e6a0bb5077 100644
--- a/binaries/data/mods/public/simulation/templates/structures/maur_wall_long.xml
+++ b/binaries/data/mods/public/simulation/templates/structures/maur_wall_long.xml
@@ -10,8 +10,8 @@
10.5
-
-
+
+
0
9.5
@@ -37,8 +37,8 @@
9.5
0
-
-
+
+
maur
Shilabanda
diff --git a/binaries/data/mods/public/simulation/templates/structures/maur_wall_medium.xml b/binaries/data/mods/public/simulation/templates/structures/maur_wall_medium.xml
index 28b6428388..bf01860f91 100644
--- a/binaries/data/mods/public/simulation/templates/structures/maur_wall_medium.xml
+++ b/binaries/data/mods/public/simulation/templates/structures/maur_wall_medium.xml
@@ -10,8 +10,8 @@
10.5
-
-
+
+
0
9.5
@@ -27,8 +27,8 @@
9.5
0
-
-
+
+
maur
Shilabanda
diff --git a/binaries/data/mods/public/simulation/templates/structures/pers_wall_long.xml b/binaries/data/mods/public/simulation/templates/structures/pers_wall_long.xml
index ab8e021e69..b596cef62c 100644
--- a/binaries/data/mods/public/simulation/templates/structures/pers_wall_long.xml
+++ b/binaries/data/mods/public/simulation/templates/structures/pers_wall_long.xml
@@ -4,8 +4,8 @@
11.6
-
-
+
+
0
10.6
@@ -31,8 +31,8 @@
10.6
0
-
-
+
+
pers
Para
diff --git a/binaries/data/mods/public/simulation/templates/structures/pers_wall_medium.xml b/binaries/data/mods/public/simulation/templates/structures/pers_wall_medium.xml
index 1304915323..b092064469 100644
--- a/binaries/data/mods/public/simulation/templates/structures/pers_wall_medium.xml
+++ b/binaries/data/mods/public/simulation/templates/structures/pers_wall_medium.xml
@@ -4,8 +4,8 @@
11.6
-
-
+
+
0
10.6
@@ -21,8 +21,8 @@
10.6
0
-
-
+
+
pers
Para
diff --git a/binaries/data/mods/public/simulation/templates/structures/ptol_wall_long.xml b/binaries/data/mods/public/simulation/templates/structures/ptol_wall_long.xml
index 99d40ed221..87c3b22af2 100644
--- a/binaries/data/mods/public/simulation/templates/structures/ptol_wall_long.xml
+++ b/binaries/data/mods/public/simulation/templates/structures/ptol_wall_long.xml
@@ -4,8 +4,8 @@
10.8
-
-
+
+
0
9.8
@@ -31,8 +31,8 @@
9.8
0
-
-
+
+
ptol
Teichos
diff --git a/binaries/data/mods/public/simulation/templates/structures/ptol_wall_medium.xml b/binaries/data/mods/public/simulation/templates/structures/ptol_wall_medium.xml
index 5cd445ec4f..4b998bf208 100644
--- a/binaries/data/mods/public/simulation/templates/structures/ptol_wall_medium.xml
+++ b/binaries/data/mods/public/simulation/templates/structures/ptol_wall_medium.xml
@@ -4,8 +4,8 @@
10.8
-
-
+
+
0
9.8
@@ -21,8 +21,8 @@
9.8
0
-
-
+
+
ptol
Teichos
diff --git a/binaries/data/mods/public/simulation/templates/structures/rome_siege_wall_long.xml b/binaries/data/mods/public/simulation/templates/structures/rome_siege_wall_long.xml
index a32427ab09..35ca63f55b 100644
--- a/binaries/data/mods/public/simulation/templates/structures/rome_siege_wall_long.xml
+++ b/binaries/data/mods/public/simulation/templates/structures/rome_siege_wall_long.xml
@@ -23,8 +23,8 @@
6.7
-
-
+
+
0
5.7
@@ -50,8 +50,8 @@
5.7
0
-
-
+
+
0.75
diff --git a/binaries/data/mods/public/simulation/templates/structures/rome_siege_wall_medium.xml b/binaries/data/mods/public/simulation/templates/structures/rome_siege_wall_medium.xml
index a628fc86c0..0f316122be 100644
--- a/binaries/data/mods/public/simulation/templates/structures/rome_siege_wall_medium.xml
+++ b/binaries/data/mods/public/simulation/templates/structures/rome_siege_wall_medium.xml
@@ -23,8 +23,8 @@
6.7
-
-
+
+
0
5.7
@@ -40,8 +40,8 @@
5.7
0
-
-
+
+
0.75
diff --git a/binaries/data/mods/public/simulation/templates/structures/rome_wall_long.xml b/binaries/data/mods/public/simulation/templates/structures/rome_wall_long.xml
index f6e957633d..c3f7ad12a7 100644
--- a/binaries/data/mods/public/simulation/templates/structures/rome_wall_long.xml
+++ b/binaries/data/mods/public/simulation/templates/structures/rome_wall_long.xml
@@ -4,8 +4,8 @@
9.9
-
-
+
+
0
8.9
@@ -31,8 +31,8 @@
8.9
0
-
-
+
+
rome
Moenia
diff --git a/binaries/data/mods/public/simulation/templates/structures/rome_wall_medium.xml b/binaries/data/mods/public/simulation/templates/structures/rome_wall_medium.xml
index c67e72505f..0b78d6fd77 100644
--- a/binaries/data/mods/public/simulation/templates/structures/rome_wall_medium.xml
+++ b/binaries/data/mods/public/simulation/templates/structures/rome_wall_medium.xml
@@ -4,8 +4,8 @@
9.9
-
-
+
+
0
8.9
@@ -21,8 +21,8 @@
8.9
0
-
-
+
+
rome
Moenia
diff --git a/binaries/data/mods/public/simulation/templates/structures/sele_wall_long.xml b/binaries/data/mods/public/simulation/templates/structures/sele_wall_long.xml
index 3c04f9d7e2..5302db9eab 100644
--- a/binaries/data/mods/public/simulation/templates/structures/sele_wall_long.xml
+++ b/binaries/data/mods/public/simulation/templates/structures/sele_wall_long.xml
@@ -4,8 +4,8 @@
11.4
-
-
+
+
0
10.4
@@ -31,8 +31,8 @@
10.4
0
-
-
+
+
sele
Teichos
diff --git a/binaries/data/mods/public/simulation/templates/structures/sele_wall_medium.xml b/binaries/data/mods/public/simulation/templates/structures/sele_wall_medium.xml
index 977d85b319..3a16b408bc 100644
--- a/binaries/data/mods/public/simulation/templates/structures/sele_wall_medium.xml
+++ b/binaries/data/mods/public/simulation/templates/structures/sele_wall_medium.xml
@@ -4,8 +4,8 @@
11.4
-
-
+
+
0
10.4
@@ -21,8 +21,8 @@
10.4
0
-
-
+
+
sele
Teichos
diff --git a/binaries/data/mods/public/simulation/templates/structures/spart_wall_long.xml b/binaries/data/mods/public/simulation/templates/structures/spart_wall_long.xml
index 495d2f42dc..533829fea5 100644
--- a/binaries/data/mods/public/simulation/templates/structures/spart_wall_long.xml
+++ b/binaries/data/mods/public/simulation/templates/structures/spart_wall_long.xml
@@ -4,8 +4,8 @@
12.5
-
-
+
+
0
11.5
@@ -31,8 +31,8 @@
11.5
0
-
-
+
+
spart
Teichos
diff --git a/binaries/data/mods/public/simulation/templates/structures/spart_wall_medium.xml b/binaries/data/mods/public/simulation/templates/structures/spart_wall_medium.xml
index 5ccb2d3578..f3360d3527 100644
--- a/binaries/data/mods/public/simulation/templates/structures/spart_wall_medium.xml
+++ b/binaries/data/mods/public/simulation/templates/structures/spart_wall_medium.xml
@@ -4,8 +4,8 @@
12.5
-
-
+
+
0
11.5
@@ -21,8 +21,8 @@
11.5
0
-
-
+
+
spart
Teichos