Index: binaries/data/config/default.cfg
===================================================================
--- binaries/data/config/default.cfg
+++ binaries/data/config/default.cfg
@@ -314,6 +314,7 @@
attackmove = Ctrl ; Modifier to attackmove when clicking on a point
attackmoveUnit = "Ctrl+Q" ; Modifier to attackmove targeting only units when clicking on a point
garrison = Ctrl ; Modifier to garrison when clicking on building
+occupyturret = Ctrl ; Modifier to occupy a turret when clicking on a turret holder.
autorallypoint = Ctrl ; Modifier to set the rally point on the building itself
guard = "G" ; Modifier to escort/guard when clicking on unit/building
patrol = "P" ; Modifier to patrol a unit
Index: binaries/data/mods/public/globalscripts/Templates.js
===================================================================
--- binaries/data/mods/public/globalscripts/Templates.js
+++ binaries/data/mods/public/globalscripts/Templates.js
@@ -491,6 +491,11 @@
ret.treasure.resources[resource] = getEntityValue("Treasure/Resources/" + resource);
}
+ if (template.TurretHolder)
+ ret.turretHolder = {
+ "turretPoints": template.TurretHolder.TurretPoints
+ };
+
if (template.WallSet)
{
ret.wallSet = {
Index: binaries/data/mods/public/gui/common/tooltips.js
===================================================================
--- binaries/data/mods/public/gui/common/tooltips.js
+++ binaries/data/mods/public/gui/common/tooltips.js
@@ -577,6 +577,16 @@
return tooltips.join("\n");
}
+function getTurretsTooltip(template)
+{
+ if (!template.turretHolder)
+ return "";
+ return sprintf(translate("%(label)s: %(turretsLimit)s"), {
+ "label": headerFont(translate("Turret Positions")),
+ "turretsLimit": Object.keys(template.turretHolder.turretPoints).length
+ });
+}
+
function getProjectilesTooltip(template)
{
if (!template.garrisonHolder || !template.buildingAI)
Index: binaries/data/mods/public/gui/reference/common/ReferencePage.js
===================================================================
--- binaries/data/mods/public/gui/reference/common/ReferencePage.js
+++ binaries/data/mods/public/gui/reference/common/ReferencePage.js
@@ -57,6 +57,7 @@
getHealerTooltip,
getResistanceTooltip,
getGarrisonTooltip,
+ getTurretsTooltip,
getProjectilesTooltip,
getSpeedTooltip,
getGatherTooltip,
Index: binaries/data/mods/public/gui/session/selection_details.js
===================================================================
--- binaries/data/mods/public/gui/session/selection_details.js
+++ binaries/data/mods/public/gui/session/selection_details.js
@@ -317,6 +317,7 @@
getGatherTooltip,
getSpeedTooltip,
getGarrisonTooltip,
+ getTurretsTooltip,
getPopulationBonusTooltip,
getProjectilesTooltip,
getResourceTrickleTooltip,
Index: binaries/data/mods/public/gui/session/selection_panels.js
===================================================================
--- binaries/data/mods/public/gui/session/selection_panels.js
+++ binaries/data/mods/public/gui/session/selection_panels.js
@@ -193,6 +193,7 @@
getEntityCostTooltip(template, data.player),
getResourceDropsiteTooltip(template),
getGarrisonTooltip(template),
+ getTurretsTooltip(template),
getPopulationBonusTooltip(template),
showTemplateViewerOnRightClickTooltip(template)
);
@@ -975,6 +976,7 @@
getHealerTooltip,
getResistanceTooltip,
getGarrisonTooltip,
+ getTurretsTooltip,
getProjectilesTooltip,
getSpeedTooltip,
getResourceDropsiteTooltip
Index: binaries/data/mods/public/gui/session/unit_actions.js
===================================================================
--- binaries/data/mods/public/gui/session/unit_actions.js
+++ binaries/data/mods/public/gui/session/unit_actions.js
@@ -684,6 +684,77 @@
"specificness": 0,
},
+ "occupy-turret":
+ {
+ "execute": function(target, action, selection, queued, pushFront)
+ {
+ Engine.PostNetworkCommand({
+ "type": "occupy-turret",
+ "entities": selection,
+ "target": action.target,
+ "queued": queued,
+ "pushFront": pushFront,
+ "formation": g_AutoFormation.getNull()
+ });
+
+ Engine.GuiInterfaceCall("PlaySound", {
+ "name": "order_garrison",
+ "entity": action.firstAbleEntity
+ });
+
+ return true;
+ },
+ "getActionInfo": function(entState, targetState)
+ {
+ if (!entState.turretable || !targetState || !targetState.turretHolder ||
+ !playerCheck(entState, targetState, ["Player", "MutualAlly"]))
+ return false;
+
+ if (!targetState.turretHolder.turretPoints.find(point =>
+ !point.allowedClasses || MatchesClassList(entState.identity.classes, point.allowedClasses)))
+ return false;
+
+ let occupiedTurrets = targetState.turretHolder.turretPoints.filter(point => point.entity != null);
+ let tooltip = sprintf(translate("Current turrets: %(occupied)s/%(capacity)s"), {
+ "occupied": occupiedTurrets.length,
+ "capacity": targetState.turretHolder.turretPoints.length
+ });
+
+ if (occupiedTurrets.length == targetState.turretHolder.turretPoints.length)
+ tooltip = coloredText(tooltip, "orange");
+
+ return {
+ "possible": true,
+ "tooltip": tooltip
+ };
+ },
+ "preSelectedActionCheck": function(target, selection)
+ {
+ return preSelectedAction == ACTION_GARRISON &&
+ (this.actionCheck(target, selection) || {
+ "type": "none",
+ "cursor": "action-garrison-disabled",
+ "target": null
+ });
+ },
+ "hotkeyActionCheck": function(target, selection)
+ {
+ return Engine.HotkeyIsPressed("session.occupyturret") &&
+ this.actionCheck(target, selection);
+ },
+ "actionCheck": function(target, selection)
+ {
+ let actionInfo = getActionInfo("occupy-turret", target, selection);
+ return actionInfo.possible && {
+ "type": "occupy-turret",
+ "cursor": "action-garrison",
+ "tooltip": actionInfo.tooltip,
+ "target": target
+ };
+ },
+ "specificness": 20,
+ },
+
"garrison":
{
"execute": function(target, action, selection, queued, pushFront)
@@ -968,6 +1039,22 @@
data.target = targetState.id;
cursor = "action-repair";
}
+ else if (targetState && targetState.turretHolder &&
+ playerCheck(entState, targetState, ["Player", "MutualAlly"]))
+ {
+ data.command = "occupy-turret";
+ data.target = targetState.id;
+ cursor = "action-garrison";
+
+ let occupiedTurrets = targetState.turretHolder.turretPoints.filter(point => point.entity != null);
+ tooltip = sprintf(translate("Current turrets: %(occupied)s/%(capacity)s"), {
+ "occupied": occupiedTurrets.length,
+ "capacity": targetState.turretHolder.turretPoints.length
+ });
+
+ if (occupiedTurrets.length >= targetState.turretHolder.turretPoints.length)
+ tooltip = coloredText(tooltip, "orange");
+ }
else if (targetState && targetState.garrisonHolder &&
playerCheck(entState, targetState, ["Player", "MutualAlly"]))
{
@@ -1341,6 +1428,32 @@
"allowedPlayers": ["Player"]
},
+ "leave-turret": {
+ "getInfo": function(entStates)
+ {
+ if (entStates.every(entState => !entState.turretable ||
+ entState.turretable.holder == INVALID_ENTITY))
+ return false;
+
+ return {
+ "tooltip": translate("Unload"),
+ "icon": "garrison-out.png",
+ "enabled": true
+ };
+ },
+ "execute": function(entStates)
+ {
+ if (!entStates.length)
+ return;
+
+ Engine.PostNetworkCommand({
+ "type": "leave-turret",
+ "entities": entStates.map(entState => entState.id)
+ });
+ },
+ "allowedPlayers": ["Player"]
+ },
+
"repair": {
"getInfo": function(entStates)
{
Index: binaries/data/mods/public/simulation/ai/common-api/entity.js
===================================================================
--- binaries/data/mods/public/simulation/ai/common-api/entity.js
+++ binaries/data/mods/public/simulation/ai/common-api/entity.js
@@ -543,6 +543,8 @@
"isGarrisonHolder": function() { return this.get("GarrisonHolder") !== undefined; },
+ "isTurretHolder": function() { return this.get("TurretHolder") !== undefined; },
+
/**
* returns true if the tempalte can capture the given target entity
* if no target is given, returns true if the template has the Capture attack
@@ -565,6 +567,8 @@
"canGarrison": function() { return "Garrisonable" in this._template; },
+ "canOccupyTurret": function() { return "Turretable" in this._template; },
+
"isTreasureCollecter": function() { return this.get("TreasureCollecter") !== undefined; },
});
@@ -840,23 +844,29 @@
return this;
},
- "garrison": function(target, queued = false) {
- Engine.PostCommand(PlayerID, { "type": "garrison", "entities": [this.id()], "target": target.id(), "queued": queued });
+ "garrison": function(target, queued = false, pushFront = false) {
+ Engine.PostCommand(PlayerID, { "type": "garrison", "entities": [this.id()], "target": target.id(), "queued": queued, "pushFront": pushFront });
+ return this;
+ },
+
+ "occupy-turret": function(target, queued = false, pushFront = false) {
+ Engine.PostCommand(PlayerID, { "type": "occupy-turret", "entities": [this.id()], "target": target.id(), "queued": queued, "pushFront": pushFront });
return this;
},
- "attack": function(unitId, allowCapture = true, queued = false) {
- Engine.PostCommand(PlayerID, { "type": "attack", "entities": [this.id()], "target": unitId, "allowCapture": allowCapture, "queued": queued });
+ "attack": function(unitId, allowCapture = true, queued = false, pushFront = false) {
+ Engine.PostCommand(PlayerID, { "type": "attack", "entities": [this.id()], "target": unitId, "allowCapture": allowCapture, "queued": queued, "pushFront": pushFront });
return this;
},
- "collectTreasure": function(target, autocontinue = false, queued = false) {
+ "collectTreasure": function(target, autocontinue = false, queued = false, pushFront = false) {
Engine.PostCommand(PlayerID, {
"type": "collect-treasure",
"entities": [this.id()],
"target": target.id(),
"autocontinue": autocontinue,
- "queued": queued
+ "queued": queued,
+ "pushFront": pushFront
});
return this;
},
Index: binaries/data/mods/public/simulation/ai/common-api/entitycollection.js
===================================================================
--- binaries/data/mods/public/simulation/ai/common-api/entitycollection.js
+++ binaries/data/mods/public/simulation/ai/common-api/entitycollection.js
@@ -175,6 +175,12 @@
return this;
};
+m.EntityCollection.prototype.occupyTurret = function(target, queued = false)
+{
+ Engine.PostCommand(PlayerID, { "type": "occupy-turret", "entities": this.toIdArray(), "target": target.id(), "queued": queued });
+ return this;
+};
+
m.EntityCollection.prototype.destroy = function()
{
Engine.PostCommand(PlayerID, { "type": "delete-entities", "entities": this.toIdArray() });
Index: binaries/data/mods/public/simulation/components/Auras.js
===================================================================
--- binaries/data/mods/public/simulation/components/Auras.js
+++ binaries/data/mods/public/simulation/components/Auras.js
@@ -180,6 +180,11 @@
return this.GetType(name) == "garrisonedUnits";
};
+Auras.prototype.IsTurretedUnitsAura = function(name)
+{
+ return this.GetType(name) == "turretedUnits";
+};
+
Auras.prototype.IsRangeAura = function(name)
{
return this.GetType(name) == "range";
@@ -321,6 +326,15 @@
}
};
+Auras.prototype.OnTurretsChanged = function(msg)
+{
+ for (let name of this.GetAuraNames().filter(n => this.IsTurretedUnitsAura(n)))
+ {
+ this.ApplyAura(name, msg.added);
+ this.RemoveAura(name, msg.removed);
+ }
+};
+
Auras.prototype.ApplyFormationAura = function(memberList)
{
for (let name of this.GetAuraNames().filter(n => this.IsFormationAura(n)))
@@ -512,7 +526,7 @@
if (msg.holderID != INVALID_ENTITY)
this.ApplyGarrisonAura(msg.holderID);
- if (msg.olderHolder != INVALID_ENTITY)
+ if (msg.oldHolder != INVALID_ENTITY)
this.RemoveGarrisonAura(msg.oldHolder);
};
Index: binaries/data/mods/public/simulation/components/BuildingAI.js
===================================================================
--- binaries/data/mods/public/simulation/components/BuildingAI.js
+++ binaries/data/mods/public/simulation/components/BuildingAI.js
@@ -30,36 +30,21 @@
this.targetUnits = [];
};
-BuildingAI.prototype.OnGarrisonedUnitsChanged = function()
+BuildingAI.prototype.OnGarrisonedUnitsChanged = function(msg)
{
- this.RecalculateProjectileCount();
-};
-
-BuildingAI.prototype.OnTurretsChanged = function()
-{
- this.RecalculateProjectileCount();
-};
-
-BuildingAI.prototype.RecalculateProjectileCount = function()
-{
- this.archersGarrisoned = 0;
let classes = this.template.GarrisonArrowClasses;
-
- let cmpTurretHolder = Engine.QueryInterface(this.entity, IID_TurretHolder);
- let cmpGarrisonHolder = Engine.QueryInterface(this.entity, IID_GarrisonHolder);
- for (let ent of cmpGarrisonHolder.GetEntities())
- {
- // Only count non-visible garrisoned entities towards extra arrows.
- if (cmpTurretHolder && cmpTurretHolder.OccupiesTurret(ent))
- continue;
-
+ for (let ent of msg.added)
+ {
let cmpIdentity = Engine.QueryInterface(ent, IID_Identity);
- if (!cmpIdentity)
- continue;
-
- if (MatchesClassList(cmpIdentity.GetClassesList(), classes))
+ if (cmpIdentity && MatchesClassList(cmpIdentity.GetClassesList(), classes))
++this.archersGarrisoned;
}
+ for (let ent of msg.removed)
+ {
+ let cmpIdentity = Engine.QueryInterface(ent, IID_Identity);
+ if (cmpIdentity && MatchesClassList(cmpIdentity.GetClassesList(), classes))
+ --this.archersGarrisoned;
+ }
};
BuildingAI.prototype.OnOwnershipChanged = function(msg)
Index: binaries/data/mods/public/simulation/components/GarrisonHolder.js
===================================================================
--- binaries/data/mods/public/simulation/components/GarrisonHolder.js
+++ binaries/data/mods/public/simulation/components/GarrisonHolder.js
@@ -200,38 +200,6 @@
};
/**
- * @param {number} entity - EntityID to find the spawn position for.
- * @param {boolean} forced - Optionally whether the spawning is forced.
- * @return {Vector3D} - An appropriate spawning position.
- */
-GarrisonHolder.prototype.GetSpawnPosition = function(entity, forced)
-{
- let cmpFootprint = Engine.QueryInterface(this.entity, IID_Footprint);
- let cmpHealth = Engine.QueryInterface(this.entity, IID_Health);
- let cmpIdentity = Engine.QueryInterface(this.entity, IID_Identity);
-
- // If the garrisonHolder is a sinking ship, restrict the location to the intersection of both passabilities
- // TODO: should use passability classes to be more generic
- let pos;
- if ((!cmpHealth || cmpHealth.GetHitpoints() == 0) && cmpIdentity && cmpIdentity.HasClass("Ship"))
- pos = cmpFootprint.PickSpawnPointBothPass(entity);
- else
- pos = cmpFootprint.PickSpawnPoint(entity);
-
- if (pos.y < 0)
- {
- // Error: couldn't find space satisfying the unit's passability criteria
- if (!forced)
- return null;
-
- // If ejection is forced, we need to continue, so use center of the building
- let cmpPosition = Engine.QueryInterface(this.entity, IID_Position);
- pos = cmpPosition.GetPosition();
- }
- return pos;
-};
-
-/**
* @param {number} entity - The entity ID of the entity to eject.
* @param {boolean} forced - Whether eject is forced (e.g. if building is destroyed).
* @return {boolean} Whether the entity was ejected.
@@ -257,32 +225,6 @@
};
/**
- * @param {number} entity - The entity ID of the entity to order to the rally point.
- */
-GarrisonHolder.prototype.OrderToRallyPoint = function(entity)
-{
- let cmpRallyPoint = Engine.QueryInterface(this.entity, IID_RallyPoint);
- if (!cmpRallyPoint || !cmpRallyPoint.GetPositions()[0])
- return;
-
- let cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership);
- if (!cmpOwnership)
- return;
-
- let cmpEntOwnership = Engine.QueryInterface(entity, IID_Ownership);
- if (!cmpEntOwnership || cmpOwnership.GetOwner() != cmpEntOwnership.GetOwner())
- return;
-
- let commands = GetRallyPointCommands(cmpRallyPoint, [entity]);
- // Ignore the rally point if it is autogarrison
- if (commands[0].type == "garrison" && commands[0].target == this.entity)
- return;
-
- for (let command of commands)
- ProcessCommand(cmpOwnership.GetOwner(), command);
-};
-
-/**
* Tell unit to unload from this entity.
* @param {number} entity - The entity to unload.
* @return {boolean} Whether the command was successful.
Index: binaries/data/mods/public/simulation/components/Garrisonable.js
===================================================================
--- binaries/data/mods/public/simulation/components/Garrisonable.js
+++ binaries/data/mods/public/simulation/components/Garrisonable.js
@@ -69,7 +69,7 @@
* @param {number} target - The entity ID of the entity this entity is being garrisoned in.
* @return {boolean} - Whether garrisoning succeeded.
*/
-Garrisonable.prototype.Garrison = function(target, renamed = false)
+Garrisonable.prototype.Garrison = function(target)
{
if (!this.CanGarrison(target))
return false;
@@ -93,13 +93,6 @@
"holderID": target
});
- if (renamed)
- return true;
-
- let cmpTurretHolder = Engine.QueryInterface(target, IID_TurretHolder);
- if (cmpTurretHolder)
- cmpTurretHolder.OccupyTurret(this.entity);
-
return true;
};
@@ -113,15 +106,12 @@
if (!this.holder)
return true;
- let cmpGarrisonHolder = Engine.QueryInterface(this.holder, IID_GarrisonHolder);
- if (!cmpGarrisonHolder)
- return false;
-
- let pos = cmpGarrisonHolder.GetSpawnPosition(this.entity, forced);
+ let pos = PositionHelper.GetSpawnPosition(this.holder, this.entity, forced);
if (!pos)
return false;
- if (!cmpGarrisonHolder.Eject(this.entity, forced))
+ let cmpGarrisonHolder = Engine.QueryInterface(this.holder, IID_GarrisonHolder);
+ if (!cmpGarrisonHolder || !cmpGarrisonHolder.Eject(this.entity, forced))
return false;
let cmpPosition = Engine.QueryInterface(this.entity, IID_Position);
@@ -150,11 +140,9 @@
if (renamed)
return true;
- let cmpTurretHolder = Engine.QueryInterface(this.holder, IID_TurretHolder);
- if (cmpTurretHolder)
- cmpTurretHolder.LeaveTurret(this.entity);
-
- cmpGarrisonHolder.OrderToRallyPoint(this.entity);
+ let cmpRallyPoint = Engine.QueryInterface(this.holder, IID_RallyPoint);
+ if (cmpRallyPoint)
+ cmpRallyPoint.OrderToRallyPoint(this.entity, ["garrison"]);
delete this.holder;
return true;
@@ -174,12 +162,6 @@
cmpGarrisonable.Garrison(this.holder, true);
}
- // We process EntityRenamed of turrets seperately since we
- // want to occupy the same position after being renamed.
- let cmpTurretHolder = Engine.QueryInterface(this.holder, IID_TurretHolder);
- if (cmpTurretHolder)
- cmpTurretHolder.SwapEntities(msg.entity, msg.newentity);
-
delete this.holder;
};
Index: binaries/data/mods/public/simulation/components/GuiInterface.js
===================================================================
--- binaries/data/mods/public/simulation/components/GuiInterface.js
+++ binaries/data/mods/public/simulation/components/GuiInterface.js
@@ -388,6 +388,12 @@
"turretPoints": cmpTurretHolder.GetTurretPoints()
};
+ let cmpTurretable = Engine.QueryInterface(ent, IID_Turretable);
+ if (cmpTurretable)
+ ret.turretable = {
+ "holder": cmpTurretable.HolderID()
+ };
+
let cmpGarrisonable = Engine.QueryInterface(ent, IID_Garrisonable);
if (cmpGarrisonable)
ret.garrisonable = {
Index: binaries/data/mods/public/simulation/components/RallyPoint.js
===================================================================
--- binaries/data/mods/public/simulation/components/RallyPoint.js
+++ binaries/data/mods/public/simulation/components/RallyPoint.js
@@ -100,6 +100,31 @@
cmpRallyPointRenderer.Reset();
};
+/**
+ * @param {number} entity - The entity ID of the entity to order to the rally point.
+ * @param {string[]} ignore - The commands to ignore when performed on this.entity.
+ * E.g. "garrison" when unloading.
+ */
+RallyPoint.prototype.OrderToRallyPoint = function(entity, ignore = [])
+{
+ let cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership);
+ if (!cmpOwnership)
+ return;
+ let owner = cmpOwnership.GetOwner();
+
+ let cmpEntOwnership = Engine.QueryInterface(entity, IID_Ownership);
+ if (!cmpEntOwnership || cmpEntOwnership.GetOwner() != owner)
+ return;
+
+ let commands = GetRallyPointCommands(this, [entity]);
+ if (!commands.length ||
+ commands[0].target == this.entity && ignore.includes(commands[0].type))
+ return;
+
+ for (let command of commands)
+ ProcessCommand(owner, command);
+};
+
RallyPoint.prototype.OnGlobalEntityRenamed = function(msg)
{
for (var data of this.data)
Index: binaries/data/mods/public/simulation/components/TurretHolder.js
===================================================================
--- binaries/data/mods/public/simulation/components/TurretHolder.js
+++ binaries/data/mods/public/simulation/components/TurretHolder.js
@@ -1,8 +1,6 @@
/**
* 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
{
@@ -55,6 +53,15 @@
}
/**
+ * @param {number} entity - The entity to check for.
+ * @return {boolean} - Whether the entity is allowed to occupy any turret point.
+ */
+ CanOccupy(entity)
+ {
+ return !!this.turretPoints.find(turretPoint => this.AllowedToOccupyTurret(entity, turretPoint));
+ }
+
+ /**
* Occupy a turret point with the given entity.
* @param {number} entity - The entity to use.
* @param {Object} requestedTurretPoint - Optionally the specific turret point to occupy.
@@ -87,6 +94,7 @@
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.
@@ -100,19 +108,6 @@
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();
-
- // Remove the unit's obstruction to avoid interfering with pathing.
- let cmpObstruction = Engine.QueryInterface(entity, IID_Obstruction);
- if (cmpObstruction)
- cmpObstruction.SetActive(false);
-
Engine.PostMessage(this.entity, MT_TurretsChanged, {
"added": [entity],
"removed": []
@@ -152,23 +147,11 @@
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;
- // Reset the obstruction flags to template defaults.
- let cmpObstruction = Engine.QueryInterface(entity, IID_Obstruction);
- if (cmpObstruction)
- cmpObstruction.SetActive(true);
+ let cmpPositionEntity = Engine.QueryInterface(entity, IID_Position);
+ if (cmpPositionEntity)
+ cmpPositionEntity.SetTurretParent(INVALID_ENTITY, new Vector3D());
Engine.PostMessage(this.entity, MT_TurretsChanged, {
"added": [],
@@ -205,7 +188,8 @@
*/
GetOccupiedTurretName(entity)
{
- return this.GetOccupiedTurret(entity).name || "";
+ let turret = this.GetOccupiedTurret(entity);
+ return turret ? turret.name : "";
}
/**
@@ -221,6 +205,60 @@
}
/**
+ * @return {boolean} - Whether all the turret points are occupied.
+ */
+ IsFull()
+ {
+ return !!this.turretPoints.find(turretPoint => turretPoint.entity == null);
+ }
+
+ /**
+ * @return {Object} - Max and min ranges at which entities can occupy any turret.
+ */
+ GetLoadingRange()
+ {
+ return { "min": 0, "max": +(this.template.LoadingRange || 2) };
+ }
+
+ /**
+ * @param {number} ent - The entity ID of the turret to be potentially picked up.
+ * @return {boolean} - Whether this entity can pick the specified entity up.
+ */
+ CanPickup(ent)
+ {
+ if (!this.template.Pickup || this.IsFull())
+ return false;
+ let cmpOwner = Engine.QueryInterface(this.entity, IID_Ownership);
+ return !!cmpOwner && IsOwnedByPlayer(cmpOwner.GetOwner(), ent);
+ }
+
+ /**
+ * @param {number[]} entities - The entities to ask to leave or to kill.
+ */
+ EjectOrKill(entities)
+ {
+ let removedEntities = [];
+ for (let entity of entities)
+ {
+ let cmpTurretable = Engine.QueryInterface(entity, IID_Turretable);
+ if (!cmpTurretable || !cmpTurretable.LeaveTurret(true))
+ {
+ let cmpHealth = Engine.QueryInterface(entity, IID_Health);
+ if (cmpHealth)
+ cmpHealth.Kill();
+ else
+ Engine.DestroyEntity(entity);
+ removedEntities.push(entity);
+ }
+ }
+ if (removedEntities.length)
+ Engine.PostMessage(this.entity, MT_TurretsChanged, {
+ "added": [],
+ "removed": removedEntities
+ });
+ }
+
+ /**
* Sets an init turret, present from game start. (E.g. set in Atlas.)
* @param {String} turretName - The name of the turret point to be used.
* @param {number} entity - The entity-ID to be placed.
@@ -238,20 +276,6 @@
}
/**
- * @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);
- this.OccupyTurret(to, turretPoint);
- }
- }
-
- /**
* Update list of turreted entities when a game inits.
*/
OnGlobalSkirmishReplacerReplaced(msg)
@@ -274,10 +298,7 @@
}
/**
- * Initialise the turreted units.
- * Really ugly, but because GarrisonHolder is processed earlier, and also turrets
- * entities on init, we can find an entity that already is present.
- * In that case we reject and occupy.
+ * Initialise turreted units.
*/
OnGlobalInitGame(msg)
{
@@ -285,16 +306,48 @@
return;
for (let [turretPointName, entity] of this.initTurrets)
- {
- if (this.OccupiesTurret(entity))
- this.LeaveTurret(entity);
if (!this.OccupyNamedTurret(entity, turretPointName))
warn("Entity " + entity + " could not occupy the turret point " +
turretPointName + " of turret holder " + this.entity + ".");
- }
delete this.initTurrets;
}
+
+ /**
+ * @param {Object} msg - { "entity": number, "newentity": number }.
+ */
+ OnEntityRenamed(msg)
+ {
+ for (let entity of this.GetEntities())
+ {
+ let cmpTurretable = Engine.QueryInterface(entity, IID_Turretable);
+ if (!cmpTurretable)
+ continue;
+ let currentPoint = this.GetOccupiedTurretName(entity);
+ cmpTurretable.LeaveTurret(true);
+ cmpTurretable.OccupyTurret(msg.newentity, currentPoint);
+ }
+ }
+
+ /**
+ * @param {Object} msg - { "entity": number, "from": number, "to": number }.
+ */
+ OnOwnershipChanged(msg)
+ {
+ let entities = this.GetEntities();
+ if (!entities.length)
+ return;
+
+ if (msg.to == INVALID_PLAYER)
+ this.EjectOrKill(entities);
+ else
+ for (let entity of entities.filter(entity => !IsOwnedByMutualAllyOfEntity(entity, this.entity)))
+ {
+ let cmpTurretable = Engine.QueryInterface(entity, IID_Turretable);
+ if (cmpTurretable)
+ cmpTurretable.LeaveTurret();
+ }
+ }
}
TurretHolder.prototype.Schema =
@@ -328,6 +381,16 @@
"" +
"" +
"" +
- "";
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ ""
+ "" +
+ "" +
+ "" +
+ "" +
+ "";
Engine.RegisterComponentType(IID_TurretHolder, "TurretHolder", TurretHolder);
Index: binaries/data/mods/public/simulation/components/Turretable.js
===================================================================
--- binaries/data/mods/public/simulation/components/Turretable.js
+++ binaries/data/mods/public/simulation/components/Turretable.js
@@ -1,129 +1,102 @@
-function Garrisonable() {}
+function Turretable() {}
-Garrisonable.prototype.Schema =
- "Controls the garrisonability of an entity." +
- "" +
- "10" +
- "" +
- "" +
- "" +
- "";
+Turretable.prototype.Schema =
+ "";
-Garrisonable.prototype.Init = function()
+Turretable.prototype.Init = function()
{
};
/**
- * @return {number} - The number of slots this unit takes in a garrisonHolder.
+ * @return {number} - The entity ID of the entity this entity is turreted on.
*/
-Garrisonable.prototype.UnitSize = function()
-{
- return ApplyValueModificationsToEntity("Garrisonable/Size", +this.template.Size, this.entity);
-};
-
-/**
- * Calculates the number of slots this unit takes in a garrisonHolder by
- * adding the number of garrisoned slots to the equation.
- *
- * @return {number} - The number of slots this unit and its garrison takes in a garrisonHolder.
- */
-Garrisonable.prototype.TotalSize = function()
-{
- let size = this.UnitSize();
- let cmpGarrisonHolder = Engine.QueryInterface(this.entity, IID_GarrisonHolder);
- if (cmpGarrisonHolder)
- size += cmpGarrisonHolder.OccupiedSlots();
- return size;
-};
-
-/**
- * @return {number} - The entity ID of the entity this entity is garrisoned in.
- */
-Garrisonable.prototype.HolderID = function()
+Turretable.prototype.HolderID = function()
{
return this.holder || INVALID_ENTITY;
};
/**
- * @return {boolean} - Whether we're garrisoned.
+ * @return {boolean} - Whether we're turreted.
*/
-Garrisonable.prototype.IsGarrisoned = function()
+Turretable.prototype.IsTurreted = function()
{
return !!this.holder;
};
/**
* @param {number} target - The entity ID to check.
- * @return {boolean} - Whether we can garrison.
+ * @return {boolean} - Whether we can occupy the turret.
*/
-Garrisonable.prototype.CanGarrison = function(target)
+Turretable.prototype.CanOccupy = function(target)
{
if (this.holder)
return false;
- let cmpGarrisonHolder = Engine.QueryInterface(target, IID_GarrisonHolder);
- return cmpGarrisonHolder && cmpGarrisonHolder.IsAllowedToGarrison(this.entity);
+ let cmpTurretHolder = Engine.QueryInterface(target, IID_TurretHolder);
+ return cmpTurretHolder && cmpTurretHolder.CanOccupy(this.entity);
};
/**
- * @param {number} target - The entity ID of the entity this entity is being garrisoned in.
- * @return {boolean} - Whether garrisoning succeeded.
+ * @param {number} target - The entity ID of the entity this entity is being turreted on.
+ * @param {string} turretPointName - Optionally the turret point name to occupy.
+ * @return {boolean} - Whether occupying succeeded.
*/
-Garrisonable.prototype.Garrison = function(target, renamed = false)
+Turretable.prototype.OccupyTurret = function(target, turretPointName = "")
{
- if (!this.CanGarrison(target))
+ if (!this.CanOccupy(target))
return false;
- let cmpGarrisonHolder = Engine.QueryInterface(target, IID_GarrisonHolder);
- if (!cmpGarrisonHolder || !cmpGarrisonHolder.Garrison(this.entity))
+ let cmpTurretHolder = Engine.QueryInterface(target, IID_TurretHolder);
+ if (!cmpTurretHolder || !cmpTurretHolder.OccupyNamedTurret(this.entity, turretPointName))
return false;
this.holder = target;
let cmpUnitAI = Engine.QueryInterface(this.entity, IID_UnitAI);
if (cmpUnitAI)
+ {
cmpUnitAI.SetGarrisoned();
+ cmpUnitAI.SetTurretStance();
+ }
- let cmpPosition = Engine.QueryInterface(this.entity, IID_Position);
- if (cmpPosition)
- cmpPosition.MoveOutOfWorld();
+ let cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
+ if (cmpUnitMotion)
+ cmpUnitMotion.SetFacePointAfterMove(false);
+
+ // Remove the unit's obstruction to avoid interfering with pathing.
+ let cmpObstruction = Engine.QueryInterface(this.entity, IID_Obstruction);
+ if (cmpObstruction)
+ cmpObstruction.SetActive(false);
- Engine.PostMessage(this.entity, MT_GarrisonedStateChanged, {
+ Engine.PostMessage(this.entity, MT_TurretedStateChanged, {
"oldHolder": INVALID_ENTITY,
"holderID": target
});
- if (renamed)
- return true;
-
- let cmpTurretHolder = Engine.QueryInterface(target, IID_TurretHolder);
- if (cmpTurretHolder)
- cmpTurretHolder.OccupyTurret(this.entity);
-
return true;
};
/**
- * @param {boolean} forced - Optionally whether the spawning is forced.
- * @param {boolean} renamed - Optionally whether the ungarrisoning is due to renaming.
- * @return {boolean} - Whether the ungarrisoning succeeded.
+ * @param {boolean} forced - Optionally whether the leaving the turret is forced.
+ * @return {boolean} - Whether leaving the turret succeeded.
*/
-Garrisonable.prototype.UnGarrison = function(forced = false, renamed = false)
+Turretable.prototype.LeaveTurret = function(forced = false)
{
if (!this.holder)
return true;
- let cmpGarrisonHolder = Engine.QueryInterface(this.holder, IID_GarrisonHolder);
- if (!cmpGarrisonHolder)
- return false;
-
- let pos = cmpGarrisonHolder.GetSpawnPosition(this.entity, forced);
+ let pos = PositionHelper.GetSpawnPosition(this.holder, this.entity, forced);
if (!pos)
return false;
- if (!cmpGarrisonHolder.Eject(this.entity, forced))
+ let cmpTurretHolder = Engine.QueryInterface(this.holder, IID_TurretHolder);
+ if (!cmpTurretHolder || !cmpTurretHolder.LeaveTurret(this.entity))
return false;
+ let cmpUnitMotionEntity = Engine.QueryInterface(this.entity, IID_UnitMotion);
+ if (cmpUnitMotionEntity)
+ cmpUnitMotionEntity.SetFacePointAfterMove(true);
+
let cmpPosition = Engine.QueryInterface(this.entity, IID_Position);
if (cmpPosition)
{
@@ -140,47 +113,53 @@
{
cmpUnitAI.Ungarrison();
cmpUnitAI.UnsetGarrisoned();
+ cmpUnitAI.ResetTurretStance();
}
- Engine.PostMessage(this.entity, MT_GarrisonedStateChanged, {
+ // Reset the obstruction flags to template defaults.
+ let cmpObstruction = Engine.QueryInterface(this.entity, IID_Obstruction);
+ if (cmpObstruction)
+ cmpObstruction.SetActive(true);
+
+ Engine.PostMessage(this.entity, MT_TurretedStateChanged, {
"oldHolder": this.holder,
"holderID": INVALID_ENTITY
});
- if (renamed)
- return true;
-
- let cmpTurretHolder = Engine.QueryInterface(this.holder, IID_TurretHolder);
- if (cmpTurretHolder)
- cmpTurretHolder.LeaveTurret(this.entity);
-
- cmpGarrisonHolder.OrderToRallyPoint(this.entity);
+ let cmpRallyPoint = Engine.QueryInterface(this.holder, IID_RallyPoint);
+ if (cmpRallyPoint)
+ cmpRallyPoint.OrderToRallyPoint(this.entity, ["occupy-turret"]);
delete this.holder;
return true;
};
-Garrisonable.prototype.OnEntityRenamed = function(msg)
+Turretable.prototype.OnEntityRenamed = function(msg)
{
if (!this.holder)
return;
- let cmpGarrisonHolder = Engine.QueryInterface(this.holder, IID_GarrisonHolder);
- if (cmpGarrisonHolder)
- {
- this.UnGarrison(true, true);
- let cmpGarrisonable = Engine.QueryInterface(msg.newentity, IID_Garrisonable);
- if (cmpGarrisonable)
- cmpGarrisonable.Garrison(this.holder, true);
- }
-
- // We process EntityRenamed of turrets seperately since we
- // want to occupy the same position after being renamed.
let cmpTurretHolder = Engine.QueryInterface(this.holder, IID_TurretHolder);
- if (cmpTurretHolder)
- cmpTurretHolder.SwapEntities(msg.entity, msg.newentity);
+ if (!cmpTurretHolder)
+ return;
- delete this.holder;
+ let holder = this.holder;
+ let currentPoint = cmpTurretHolder.GetOccupiedTurretName(this.entity);
+ this.LeaveTurret(true);
+ let cmpTurretableNew = Engine.QueryInterface(msg.newentity, IID_Turretable);
+ if (cmpTurretableNew)
+ cmpTurretableNew.OccupyTurret(holder, currentPoint);
+};
+
+Turretable.prototype.OnOwnershipChanged = function(msg)
+{
+ if (!this.holder)
+ return;
+
+ if (msg.to == INVALID_PLAYER)
+ this.LeaveTurret(true);
+ else if (!IsOwnedByMutualAllyOfEntity(this.entity, this.holder))
+ this.LeaveTurret();
};
-Engine.RegisterComponentType(IID_Garrisonable, "Garrisonable", Garrisonable);
+Engine.RegisterComponentType(IID_Turretable, "Turretable", Turretable);
Index: binaries/data/mods/public/simulation/components/UnitAI.js
===================================================================
--- binaries/data/mods/public/simulation/components/UnitAI.js
+++ binaries/data/mods/public/simulation/components/UnitAI.js
@@ -592,7 +592,8 @@
return ACCEPT_ORDER;
}
- if (this.CheckGarrisonRange(msg.data.target))
+ if (msg.data.garrison ? this.CheckGarrisonRange(msg.data.target) :
+ this.CheckOccupyTurretRange(msg.data.target))
this.SetNextState("INDIVIDUAL.GARRISON.GARRISONING");
else
this.SetNextState("INDIVIDUAL.GARRISON.APPROACHING");
@@ -744,9 +745,11 @@
},
"Order.Garrison": function(msg) {
- if (!Engine.QueryInterface(msg.data.target, IID_GarrisonHolder))
+ if (!Engine.QueryInterface(msg.data.target,
+ msg.data.garrison ? IID_GarrisonHolder : IID_TurretHolder))
return this.FinishOrder();
- if (!this.CheckGarrisonRange(msg.data.target))
+ if (!(msg.data.garrison ? this.CheckGarrisonRange(msg.data.target) :
+ this.CheckOccupyTurretRange(msg.data.target)))
{
if (!this.CheckTargetVisible(msg.data.target))
return this.FinishOrder();
@@ -1100,19 +1103,20 @@
"GARRISON": {
"APPROACHING": {
"enter": function() {
- let cmpFormation = Engine.QueryInterface(this.entity, IID_Formation);
- cmpFormation.SetRearrange(true);
- cmpFormation.MoveMembersIntoFormation(true, true);
-
- if (!this.MoveToGarrisonRange(this.order.data.target))
+ if (!(this.order.data.garrison ? this.MoveToGarrisonRange(this.order.data.target) :
+ this.MoveToOccupyTurretRange(this.order.data.target)))
{
this.FinishOrder();
return true;
}
- // If the garrisonholder should pickup, warn it so it can take needed action.
- let cmpGarrisonHolder = Engine.QueryInterface(this.order.data.target, IID_GarrisonHolder);
- if (cmpGarrisonHolder && cmpGarrisonHolder.CanPickup(this.entity))
+ let cmpFormation = Engine.QueryInterface(this.entity, IID_Formation);
+ cmpFormation.SetRearrange(true);
+ cmpFormation.MoveMembersIntoFormation(true, true);
+
+ // If the holder should pickup, warn it so it can take needed action.
+ let cmpHolder = Engine.QueryInterface(this.order.data.target, this.order.data.garrison ? IID_GarrisonHolder : IID_TurretHolder);
+ if (cmpHolder && cmpHolder.CanPickup(this.entity))
{
this.pickup = this.order.data.target; // temporary, deleted in "leave"
Engine.PostMessage(this.pickup, MT_PickupRequested, { "entity": this.entity });
@@ -1137,7 +1141,7 @@
"GARRISONING": {
"enter": function() {
- this.CallMemberFunction("Garrison", [this.order.data.target, false]);
+ this.CallMemberFunction(this.order.data.garrison ? "Garrison" : "OccupyTurret", [this.order.data.target, false]);
// We might have been disbanded due to the lack of members.
if (Engine.QueryInterface(this.entity, IID_Formation).GetMemberCount())
this.SetNextState("MEMBER");
@@ -3209,13 +3213,15 @@
"GARRISON": {
"APPROACHING": {
"enter": function() {
- if (!this.CanGarrison(this.order.data.target))
+ if (this.order.data.garrison ? !this.CanGarrison(this.order.data.target) :
+ !this.CanOccupyTurret(this.order.data.target))
{
this.FinishOrder();
return true;
}
- if (!this.MoveToGarrisonRange(this.order.data.target))
+ if (this.order.data.garrison ? !this.MoveToGarrisonRange(this.order.data.target) :
+ !this.MoveToOccupyTurretRange(this.order.data.target))
{
this.FinishOrder();
return true;
@@ -3224,8 +3230,8 @@
if (this.pickup)
Engine.PostMessage(this.pickup, MT_PickupCanceled, { "entity": this.entity });
- let cmpGarrisonHolder = Engine.QueryInterface(this.order.data.target, IID_GarrisonHolder);
- if (cmpGarrisonHolder && cmpGarrisonHolder.CanPickup(this.entity))
+ let cmpHolder = Engine.QueryInterface(this.order.data.target, this.order.data.garrison ? IID_GarrisonHolder : IID_TurretHolder);
+ if (cmpHolder && cmpHolder.CanPickup(this.entity))
{
this.pickup = this.order.data.target;
Engine.PostMessage(this.pickup, MT_PickupRequested, { "entity": this.entity });
@@ -3246,7 +3252,8 @@
if (!msg.likelyFailure && !msg.likelySuccess)
return;
- if (this.CheckGarrisonRange(this.order.data.target))
+ if (this.order.data.garrison ? this.CheckGarrisonRange(this.order.data.target) :
+ this.CheckOccupyTurretRange(this.order.data.target))
this.SetNextState("GARRISONING");
else
{
@@ -3265,11 +3272,23 @@
"GARRISONING": {
"enter": function() {
let target = this.order.data.target;
- let cmpGarrisonable = Engine.QueryInterface(this.entity, IID_Garrisonable);
- if (!cmpGarrisonable || !cmpGarrisonable.Garrison(target))
+ if (this.order.data.garrison)
{
- this.FinishOrder();
- return true;
+ let cmpGarrisonable = Engine.QueryInterface(this.entity, IID_Garrisonable);
+ if (!cmpGarrisonable || !cmpGarrisonable.Garrison(target))
+ {
+ this.FinishOrder();
+ return true;
+ }
+ }
+ else
+ {
+ let cmpTurretable = Engine.QueryInterface(this.entity, IID_Turretable);
+ if (!cmpTurretable || !cmpTurretable.OccupyTurret(target))
+ {
+ this.FinishOrder();
+ return true;
+ }
}
if (this.formationController)
@@ -3459,8 +3478,10 @@
UnitAI.prototype.IsTurret = function()
{
- let cmpPosition = Engine.QueryInterface(this.entity, IID_Position);
- return cmpPosition && cmpPosition.GetTurretParent() != INVALID_ENTITY;
+ if (!this.isGarrisoned)
+ return false;
+ let cmpTurretable = Engine.QueryInterface(this.entity, IID_Turretable);
+ return cmpTurretable && cmpTurretable.HolderID() != INVALID_ENTITY;
};
UnitAI.prototype.IsFormationController = function()
@@ -4795,6 +4816,20 @@
return this.AbleToMove(cmpUnitMotion) && cmpUnitMotion.MoveToTargetRange(target, range.min, range.max);
};
+UnitAI.prototype.MoveToOccupyTurretRange = function(target)
+{
+ if (!this.CheckTargetVisible(target))
+ return false;
+
+ let cmpTurretHolder = Engine.QueryInterface(target, IID_TurretHolder);
+ if (!cmpTurretHolder)
+ return false;
+ let range = cmpTurretHolder.GetLoadingRange();
+
+ let cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
+ return this.AbleToMove(cmpUnitMotion) && cmpUnitMotion.MoveToTargetRange(target, range.min, range.max);
+};
+
/**
* Generic dispatcher for other Check...Range functions.
* @param iid - Interface ID (optional) implementing GetRange
@@ -4920,6 +4955,16 @@
return this.CheckTargetRangeExplicit(target, range.min, range.max);
};
+UnitAI.prototype.CheckOccupyTurretRange = function(target)
+{
+ let cmpTurretHolder = Engine.QueryInterface(target, IID_TurretHolder);
+ if (!cmpTurretHolder)
+ return false;
+
+ let range = cmpTurretHolder.GetLoadingRange();
+ return this.CheckTargetRangeExplicit(target, range.min, range.max);
+};
+
/**
* Returns true if the target entity is visible through the FoW/SoD.
*/
@@ -5353,7 +5398,7 @@
// May happen if an order arrives on the same turn the unit is garrisoned
// in that case, just forget the order as this will lead to an infinite loop.
// ToDo: Fix that by checking for the ability to move on orders that need that.
- if (this.isGarrisoned && !this.IsTurret() && type != "Ungarrison")
+ if (this.isGarrisoned && type != "Ungarrison")
return;
this.ReplaceOrder(type, data);
}
@@ -5591,7 +5636,7 @@
this.WalkToTarget(target, queued);
return;
}
- this.AddOrder("Garrison", { "target": target, "force": true }, queued, pushFront);
+ this.AddOrder("Garrison", { "target": target, "force": true, "garrison": true }, queued, pushFront);
};
/**
@@ -5605,6 +5650,21 @@
};
/**
+ * Adds garrison order to the queue, forced by the player.
+ */
+UnitAI.prototype.OccupyTurret = function(target, queued, pushFront)
+{
+ if (target == this.entity)
+ return;
+ if (!this.CanOccupyTurret(target))
+ {
+ this.WalkToTarget(target, queued);
+ return;
+ }
+ this.AddOrder("Garrison", { "target": target, "force": true, "garrison": false }, queued, pushFront);
+};
+
+/**
* Adds gather order to the queue, forced by the player
* until the target is reached
*/
@@ -6443,6 +6503,17 @@
return cmpOwnership && IsOwnedByAllyOfPlayer(cmpOwnership.GetOwner(), target);
};
+UnitAI.prototype.CanOccupyTurret = function(target)
+{
+ // Formation controllers should always respond to commands
+ // (then the individual units can make up their own minds).
+ if (this.IsFormationController())
+ return true;
+
+ let cmpTurretable = Engine.QueryInterface(this.entity, IID_Turretable);
+ return cmpTurretable && cmpTurretable.CanOccupy(target);
+};
+
UnitAI.prototype.CanPack = function()
{
var cmpPack = Engine.QueryInterface(this.entity, IID_Pack);
Index: binaries/data/mods/public/simulation/components/interfaces/Turretable.js
===================================================================
--- binaries/data/mods/public/simulation/components/interfaces/Turretable.js
+++ binaries/data/mods/public/simulation/components/interfaces/Turretable.js
@@ -1,7 +1,7 @@
-Engine.RegisterInterface("Garrisonable");
+Engine.RegisterInterface("Turretable");
/**
* Message of the form { "holderID": number }
- * sent from the Garrisonable component whenever the garrisoned state changes.
+ * sent from the Turretable component whenever the turreted state changes.
*/
-Engine.RegisterMessageType("GarrisonedStateChanged");
+Engine.RegisterMessageType("TurretedStateChanged");
Index: binaries/data/mods/public/simulation/components/tests/test_GarrisonHolder.js
===================================================================
--- binaries/data/mods/public/simulation/components/tests/test_GarrisonHolder.js
+++ binaries/data/mods/public/simulation/components/tests/test_GarrisonHolder.js
@@ -2,7 +2,6 @@
Engine.LoadHelperScript("Player.js");
Engine.LoadComponentScript("interfaces/Garrisonable.js");
Engine.LoadComponentScript("interfaces/GarrisonHolder.js");
-Engine.LoadComponentScript("interfaces/TurretHolder.js");
Engine.LoadComponentScript("interfaces/Health.js");
Engine.LoadComponentScript("interfaces/ModifiersManager.js");
Engine.LoadComponentScript("interfaces/Timer.js");
Index: binaries/data/mods/public/simulation/components/tests/test_Garrisonable.js
===================================================================
--- binaries/data/mods/public/simulation/components/tests/test_Garrisonable.js
+++ binaries/data/mods/public/simulation/components/tests/test_Garrisonable.js
@@ -1,5 +1,7 @@
+Engine.LoadHelperScript("Position.js");
Engine.LoadComponentScript("interfaces/Garrisonable.js");
Engine.LoadComponentScript("interfaces/GarrisonHolder.js");
+Engine.LoadComponentScript("interfaces/Health.js");
Engine.LoadComponentScript("interfaces/UnitAI.js");
Engine.LoadComponentScript("Garrisonable.js");
@@ -9,12 +11,15 @@
const garrisonableID = 2;
AddMock(garrisonHolderID, IID_GarrisonHolder, {
"Garrison": () => true,
- "GetSpawnPosition": () => new Vector3D(0, 0, 0),
"IsAllowedToGarrison": () => true,
- "OrderToRallyPoint": () => {},
"Eject": () => true
});
+AddMock(garrisonHolderID, IID_Footprint, {
+ "PickSpawnPointBothPass": entity => new Vector3D(4, 3, 30),
+ "PickSpawnPoint": entity => new Vector3D(4, 3, 30)
+});
+
let size = 1;
let cmpGarrisonable = ConstructComponent(garrisonableID, "Garrisonable", {
"Size": size
@@ -39,7 +44,7 @@
TS_ASSERT(!cmpGarrisonable.Garrison(garrisonHolderID));
TS_ASSERT_UNEVAL_EQUALS(cmpGarrisonable.HolderID(), garrisonHolderID);
-cmpGarrisonable.UnGarrison();
+TS_ASSERT(cmpGarrisonable.UnGarrison());
TS_ASSERT_UNEVAL_EQUALS(cmpGarrisonable.HolderID(), INVALID_ENTITY);
// Test renaming.
Index: binaries/data/mods/public/simulation/components/tests/test_Garrisoning.js
===================================================================
--- binaries/data/mods/public/simulation/components/tests/test_Garrisoning.js
+++ binaries/data/mods/public/simulation/components/tests/test_Garrisoning.js
@@ -1,15 +1,14 @@
Engine.LoadHelperScript("ValueModification.js");
Engine.LoadHelperScript("Player.js");
+Engine.LoadHelperScript("Position.js");
Engine.LoadComponentScript("interfaces/Garrisonable.js");
Engine.LoadComponentScript("interfaces/GarrisonHolder.js");
Engine.LoadComponentScript("interfaces/Health.js");
Engine.LoadComponentScript("interfaces/ModifiersManager.js");
Engine.LoadComponentScript("interfaces/Timer.js");
-Engine.LoadComponentScript("interfaces/TurretHolder.js");
Engine.LoadComponentScript("interfaces/UnitAI.js");
Engine.LoadComponentScript("Garrisonable.js");
Engine.LoadComponentScript("GarrisonHolder.js");
-Engine.LoadComponentScript("TurretHolder.js");
const player = 1;
const enemyPlayer = 2;
@@ -34,7 +33,6 @@
"JumpTo": (posX, posZ) => {},
"MoveOutOfWorld": () => {},
"SetHeightOffset": height => {},
- "SetTurretParent": ent => {},
"SetYRotation": angle => {}
});
@@ -88,7 +86,6 @@
"JumpTo": (posX, posZ) => {},
"MoveOutOfWorld": () => {},
"SetHeightOffset": height => {},
- "SetTurretParent": entity => {},
"SetYRotation": angle => {}
});
@@ -143,52 +140,3 @@
TS_ASSERT(cmpGarrisonHolder.UnloadAll());
TS_ASSERT_UNEVAL_EQUALS(cmpGarrisonHolder.GetEntities(), []);
-// Turrets!
-AddMock(holder, IID_Position, {
- "GetPosition": () => new Vector3D(4, 3, 25),
- "GetRotation": () => new Vector3D(4, 0, 6)
-});
-
-let cmpTurretHolder = ConstructComponent(holder, "TurretHolder", {
- "TurretPoints": {
- "archer1": {
- "X": "12.0",
- "Y": "5.",
- "Z": "6.0"
- },
- "archer2": {
- "X": "15.0",
- "Y": "5.0",
- "Z": "6.0"
- }
- }
-});
-
-TS_ASSERT(cmpGarrisonable.Garrison(holder));
-TS_ASSERT_UNEVAL_EQUALS(cmpGarrisonHolder.GetEntities(), [garrison]);
-TS_ASSERT(cmpTurretHolder.OccupiesTurret(garrison));
-TS_ASSERT(cmpGarrisonable.UnGarrison());
-TS_ASSERT_UNEVAL_EQUALS(cmpGarrisonHolder.GetEntities(), []);
-TS_ASSERT_UNEVAL_EQUALS(cmpTurretHolder.GetEntities(), []);
-
-// Test renaming on a turret.
-// Ensure we test renaming from the second spot, not the first.
-const newGarrison = 31;
-let cmpGarrisonableNew = createGarrisonCmp(newGarrison);
-TS_ASSERT(cmpGarrisonableNew.Garrison(holder));
-TS_ASSERT(cmpGarrisonable.Garrison(holder));
-TS_ASSERT(cmpGarrisonableNew.UnGarrison());
-let previousTurret = cmpTurretHolder.GetOccupiedTurretName(garrison);
-cmpGarrisonable.OnEntityRenamed({
- "entity": garrison,
- "newentity": newGarrison
-});
-let newTurret = cmpTurretHolder.GetOccupiedTurretName(newGarrison);
-TS_ASSERT_UNEVAL_EQUALS(newTurret, previousTurret);
-TS_ASSERT(cmpGarrisonableNew.UnGarrison());
-
-// Test initTurrets.
-cmpTurretHolder.SetInitEntity("archer1", garrison);
-cmpTurretHolder.SetInitEntity("archer2", newGarrison);
-cmpTurretHolder.OnGlobalInitGame();
-TS_ASSERT_UNEVAL_EQUALS(cmpTurretHolder.GetEntities(), [garrison, newGarrison]);
Index: binaries/data/mods/public/simulation/components/tests/test_GuiInterface.js
===================================================================
--- binaries/data/mods/public/simulation/components/tests/test_GuiInterface.js
+++ binaries/data/mods/public/simulation/components/tests/test_GuiInterface.js
@@ -34,6 +34,7 @@
Engine.LoadComponentScript("interfaces/Timer.js");
Engine.LoadComponentScript("interfaces/Treasure.js");
Engine.LoadComponentScript("interfaces/TreasureCollecter.js");
+Engine.LoadComponentScript("interfaces/Turretable.js");
Engine.LoadComponentScript("interfaces/StatisticsTracker.js");
Engine.LoadComponentScript("interfaces/StatusEffectsReceiver.js");
Engine.LoadComponentScript("interfaces/UnitAI.js");
Index: binaries/data/mods/public/simulation/components/tests/test_TurretHolder.js
===================================================================
--- binaries/data/mods/public/simulation/components/tests/test_TurretHolder.js
+++ binaries/data/mods/public/simulation/components/tests/test_TurretHolder.js
@@ -122,20 +122,3 @@
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.
Index: binaries/data/mods/public/simulation/components/tests/test_Turrets.js
===================================================================
--- /dev/null
+++ binaries/data/mods/public/simulation/components/tests/test_Turrets.js
@@ -0,0 +1,114 @@
+Engine.LoadHelperScript("ValueModification.js");
+Engine.LoadHelperScript("Player.js");
+Engine.LoadHelperScript("Position.js");
+Engine.LoadComponentScript("interfaces/Health.js");
+Engine.LoadComponentScript("interfaces/Turretable.js");
+Engine.LoadComponentScript("interfaces/TurretHolder.js");
+Engine.LoadComponentScript("interfaces/UnitAI.js");
+Engine.LoadComponentScript("Turretable.js");
+Engine.LoadComponentScript("TurretHolder.js");
+
+const player = 1;
+const enemyPlayer = 2;
+const friendlyPlayer = 3;
+const turret = 10;
+const holder = 11;
+
+let createTurretCmp = entity => {
+ AddMock(entity, IID_Identity, {
+ "GetClassesList": () => ["Ranged"],
+ "GetSelectionGroupName": () => "mace_infantry_archer_a"
+ });
+
+ AddMock(entity, IID_Ownership, {
+ "GetOwner": () => player
+ });
+
+ AddMock(entity, IID_Position, {
+ "GetHeightOffset": () => 0,
+ "GetPosition": () => new Vector3D(4, 3, 25),
+ "GetRotation": () => new Vector3D(4, 0, 6),
+ "JumpTo": (posX, posZ) => {},
+ "MoveOutOfWorld": () => {},
+ "SetHeightOffset": height => {},
+ "SetTurretParent": entity => {},
+ "SetYRotation": angle => {}
+ });
+
+ return ConstructComponent(entity, "Turretable", null);
+};
+
+AddMock(holder, IID_Footprint, {
+ "PickSpawnPointBothPass": entity => new Vector3D(4, 3, 30),
+ "PickSpawnPoint": entity => new Vector3D(4, 3, 30)
+});
+
+AddMock(holder, IID_Ownership, {
+ "GetOwner": () => player
+});
+
+AddMock(player, IID_Player, {
+ "IsAlly": id => id != enemyPlayer,
+ "IsMutualAlly": id => id != enemyPlayer,
+ "GetPlayerID": () => player
+});
+
+AddMock(friendlyPlayer, IID_Player, {
+ "IsAlly": id => true,
+ "IsMutualAlly": id => true,
+ "GetPlayerID": () => friendlyPlayer
+});
+
+AddMock(SYSTEM_ENTITY, IID_PlayerManager, {
+ "GetPlayerByID": id => id
+});
+
+AddMock(holder, IID_Position, {
+ "GetPosition": () => new Vector3D(4, 3, 25),
+ "GetRotation": () => new Vector3D(4, 0, 6)
+});
+
+let cmpTurretable = createTurretCmp(turret);
+
+let cmpTurretHolder = ConstructComponent(holder, "TurretHolder", {
+ "TurretPoints": {
+ "archer1": {
+ "X": "12.0",
+ "Y": "5.",
+ "Z": "6.0"
+ },
+ "archer2": {
+ "X": "15.0",
+ "Y": "5.0",
+ "Z": "6.0"
+ }
+ }
+});
+
+TS_ASSERT(cmpTurretable.OccupyTurret(holder));
+TS_ASSERT_UNEVAL_EQUALS(cmpTurretHolder.GetEntities(), [turret]);
+TS_ASSERT(cmpTurretHolder.OccupiesTurret(turret));
+TS_ASSERT(cmpTurretable.LeaveTurret());
+TS_ASSERT_UNEVAL_EQUALS(cmpTurretHolder.GetEntities(), []);
+
+// Test renaming on a turret.
+// Ensure we test renaming from the second spot, not the first.
+const newTurret = 31;
+let cmpTurretableNew = createTurretCmp(newTurret);
+TS_ASSERT(cmpTurretableNew.OccupyTurret(holder));
+TS_ASSERT(cmpTurretable.OccupyTurret(holder));
+TS_ASSERT(cmpTurretableNew.LeaveTurret());
+let previousTurret = cmpTurretHolder.GetOccupiedTurretName(turret);
+cmpTurretable.OnEntityRenamed({
+ "entity": turret,
+ "newentity": newTurret
+});
+let newTurretPos = cmpTurretHolder.GetOccupiedTurretName(newTurret);
+TS_ASSERT_UNEVAL_EQUALS(newTurretPos, previousTurret);
+TS_ASSERT(cmpTurretableNew.LeaveTurret());
+
+// Test initTurrets.
+cmpTurretHolder.SetInitEntity("archer1", turret);
+cmpTurretHolder.SetInitEntity("archer2", newTurret);
+cmpTurretHolder.OnGlobalInitGame();
+TS_ASSERT_UNEVAL_EQUALS(cmpTurretHolder.GetEntities(), [turret, newTurret]);
Index: binaries/data/mods/public/simulation/data/auras/structures/wall_garrisoned.json
===================================================================
--- binaries/data/mods/public/simulation/data/auras/structures/wall_garrisoned.json
+++ binaries/data/mods/public/simulation/data/auras/structures/wall_garrisoned.json
@@ -1,5 +1,5 @@
{
- "type": "garrisonedUnits",
+ "type": "turretedUnits",
"affects": ["Soldier"],
"modifications": [
{ "value": "Resistance/Entity/Damage/Hack", "add": 3 },
@@ -8,5 +8,5 @@
{ "value": "Vision/Range", "add": 20 }
],
"auraName": "Wall Protection",
- "auraDescription": "Garrisoned Soldiers +3 crush, hack, pierce resistance and +20 vision range."
+ "auraDescription": "Turreted Soldiers +3 crush, hack, pierce resistance and +20 vision range."
}
Index: binaries/data/mods/public/simulation/helpers/Commands.js
===================================================================
--- binaries/data/mods/public/simulation/helpers/Commands.js
+++ binaries/data/mods/public/simulation/helpers/Commands.js
@@ -462,6 +462,13 @@
data.cmpPlayer.SetState("defeated", markForTranslation("%(player)s has resigned."));
},
+ "occupy-turret": function(player, cmd, data)
+ {
+ GetFormationUnitAIs(data.entities, player).forEach(cmpUnitAI => {
+ cmpUnitAI.OccupyTurret(cmd.target, cmd.queued);
+ });
+ },
+
"garrison": function(player, cmd, data)
{
if (!CanPlayerOrAllyControlUnit(cmd.target, player, data.controlAllUnits))
@@ -497,6 +504,20 @@
});
},
+ "leave-turret": function(player, cmd, data)
+ {
+ let notUnloaded = 0;
+ for (let ent of data.entities)
+ {
+ let cmpTurretable = Engine.QueryInterface(ent, IID_Turretable);
+ if (!cmpTurretable || !cmpTurretable.LeaveTurret())
+ ++notUnloaded;
+ }
+
+ if (notUnloaded)
+ notifyUnloadFailure(player);
+ },
+
"unload": function(player, cmd, data)
{
if (!CanPlayerOrAllyControlUnit(cmd.garrisonHolder, player, data.controlAllUnits))
@@ -843,13 +864,13 @@
/**
* Sends a GUI notification about unit(s) that failed to ungarrison.
*/
-function notifyUnloadFailure(player, garrisonHolder)
+function notifyUnloadFailure(player)
{
- var cmpGUIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
+ let cmpGUIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
cmpGUIInterface.PushNotification({
"type": "text",
"players": [player],
- "message": markForTranslation("Unable to ungarrison unit(s)"),
+ "message": markForTranslation("Unable to unload unit(s)."),
"translateMessage": true
});
}
Index: binaries/data/mods/public/simulation/helpers/Position.js
===================================================================
--- binaries/data/mods/public/simulation/helpers/Position.js
+++ binaries/data/mods/public/simulation/helpers/Position.js
@@ -133,4 +133,39 @@
return false;
};
+/**
+ * @param {number} target - EntityID to find the spawn position for.
+ * @param {number} entity - EntityID to find the spawn position for.
+ * @param {boolean} forced - Optionally whether the spawning is forced.
+ * @return {Vector3D} - An appropriate spawning position.
+ */
+PositionHelper.prototype.GetSpawnPosition = function(target, entity, forced)
+{
+ let cmpFootprint = Engine.QueryInterface(target, IID_Footprint);
+ let cmpHealth = Engine.QueryInterface(target, IID_Health);
+ let cmpIdentity = Engine.QueryInterface(target, IID_Identity);
+
+ if (!cmpFootprint)
+ return null;
+
+ // If the spawner is a sinking ship, restrict the location to the intersection of both passabilities.
+ // TODO: should use passability classes to be more generic.
+ let pos;
+ if ((!cmpHealth || cmpHealth.GetHitpoints() == 0) && cmpIdentity && cmpIdentity.HasClass("Ship"))
+ pos = cmpFootprint.PickSpawnPointBothPass(entity);
+ else
+ pos = cmpFootprint.PickSpawnPoint(entity);
+
+ if (pos.y < 0)
+ {
+ if (!forced)
+ return null;
+
+ // If ejection is forced, we need to continue, so use center of the entity.
+ let cmpPosition = Engine.QueryInterface(target, IID_Position);
+ pos = cmpPosition.GetPosition();
+ }
+ return pos;
+};
+
Engine.RegisterGlobal("PositionHelper", new PositionHelper());
Index: binaries/data/mods/public/simulation/helpers/RallyPointCommands.js
===================================================================
--- binaries/data/mods/public/simulation/helpers/RallyPointCommands.js
+++ binaries/data/mods/public/simulation/helpers/RallyPointCommands.js
@@ -57,6 +57,14 @@
"autocontinue": i == rallyPos.length - 1
});
break;
+ case "occupy-turret":
+ ret.push({
+ "type": "occupy-turret",
+ "entities": spawnedEnts,
+ "target": data[i].target,
+ "queued": true
+ });
+ break;
case "garrison":
ret.push({
"type": "garrison",
Index: binaries/data/mods/public/simulation/templates/structures/athen/wall_gate.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/structures/athen/wall_gate.xml
+++ binaries/data/mods/public/simulation/templates/structures/athen/wall_gate.xml
@@ -4,9 +4,6 @@
15.5
-
- 8
-
athen
Pylai
Index: binaries/data/mods/public/simulation/templates/structures/brit/wall_gate.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/structures/brit/wall_gate.xml
+++ binaries/data/mods/public/simulation/templates/structures/brit/wall_gate.xml
@@ -4,9 +4,6 @@
18.0
-
- 4
-
brit
Duoricos
Index: binaries/data/mods/public/simulation/templates/structures/cart/wall_gate.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/structures/cart/wall_gate.xml
+++ binaries/data/mods/public/simulation/templates/structures/cart/wall_gate.xml
@@ -4,9 +4,6 @@
16.5
-
- 8
-
cart
Mijdil-šaʿar
Index: binaries/data/mods/public/simulation/templates/structures/gaul/wall_gate.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/structures/gaul/wall_gate.xml
+++ binaries/data/mods/public/simulation/templates/structures/gaul/wall_gate.xml
@@ -4,9 +4,6 @@
12.0
-
- 4
-
gaul
Duoricos
Index: binaries/data/mods/public/simulation/templates/structures/iber/wall_gate.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/structures/iber/wall_gate.xml
+++ binaries/data/mods/public/simulation/templates/structures/iber/wall_gate.xml
@@ -4,9 +4,6 @@
12.7
-
- 8
-
iber
Biko Sarbide
Index: binaries/data/mods/public/simulation/templates/structures/kush/wall_gate.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/structures/kush/wall_gate.xml
+++ binaries/data/mods/public/simulation/templates/structures/kush/wall_gate.xml
@@ -4,9 +4,6 @@
12.6
-
- 6
-
kush
ʿryt
Index: binaries/data/mods/public/simulation/templates/structures/mace/wall_gate.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/structures/mace/wall_gate.xml
+++ binaries/data/mods/public/simulation/templates/structures/mace/wall_gate.xml
@@ -4,9 +4,6 @@
15.5
-
- 8
-
mace
Pylai
Index: binaries/data/mods/public/simulation/templates/structures/maur/tower_double.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/structures/maur/tower_double.xml
+++ binaries/data/mods/public/simulation/templates/structures/maur/tower_double.xml
@@ -9,10 +9,6 @@
200
-
- 20
- Infantry+Archer
-
1200
Index: binaries/data/mods/public/simulation/templates/structures/maur/wall_gate.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/structures/maur/wall_gate.xml
+++ binaries/data/mods/public/simulation/templates/structures/maur/wall_gate.xml
@@ -10,9 +10,6 @@
22.0
-
- 4
-
maur
Dwara
Index: binaries/data/mods/public/simulation/templates/structures/pers/wall_gate.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/structures/pers/wall_gate.xml
+++ binaries/data/mods/public/simulation/templates/structures/pers/wall_gate.xml
@@ -4,9 +4,6 @@
13.8
-
- 6
-
pers
Duvarθiš
Index: binaries/data/mods/public/simulation/templates/structures/ptol/wall_gate.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/structures/ptol/wall_gate.xml
+++ binaries/data/mods/public/simulation/templates/structures/ptol/wall_gate.xml
@@ -4,9 +4,6 @@
17.8
-
- 10
-
ptol
Pylai
Index: binaries/data/mods/public/simulation/templates/structures/rome/siege_wall_gate.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/structures/rome/siege_wall_gate.xml
+++ binaries/data/mods/public/simulation/templates/structures/rome/siege_wall_gate.xml
@@ -14,9 +14,6 @@
12.5
-
- 8
-
0.75
Index: binaries/data/mods/public/simulation/templates/structures/rome/siege_wall_long.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/structures/rome/siege_wall_long.xml
+++ binaries/data/mods/public/simulation/templates/structures/rome/siege_wall_long.xml
@@ -14,9 +14,6 @@
6.7
-
- 7
-
0.75
Index: binaries/data/mods/public/simulation/templates/structures/rome/siege_wall_tower.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/structures/rome/siege_wall_tower.xml
+++ binaries/data/mods/public/simulation/templates/structures/rome/siege_wall_tower.xml
@@ -19,10 +19,6 @@
12.5
-
- 4
- -Support -Infantry
-
0.75
Index: binaries/data/mods/public/simulation/templates/structures/rome/wall_gate.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/structures/rome/wall_gate.xml
+++ binaries/data/mods/public/simulation/templates/structures/rome/wall_gate.xml
@@ -4,9 +4,6 @@
11.9
-
- 10
-
rome
Porta
Index: binaries/data/mods/public/simulation/templates/structures/sele/wall_gate.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/structures/sele/wall_gate.xml
+++ binaries/data/mods/public/simulation/templates/structures/sele/wall_gate.xml
@@ -4,9 +4,6 @@
11.6
-
- 5
-
sele
Pylai
Index: binaries/data/mods/public/simulation/templates/structures/spart/wall_gate.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/structures/spart/wall_gate.xml
+++ binaries/data/mods/public/simulation/templates/structures/spart/wall_gate.xml
@@ -4,9 +4,6 @@
15.5
-
- 8
-
spart
Pylai
Index: binaries/data/mods/public/simulation/templates/template_structure_defensive_outpost.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_structure_defensive_outpost.xml
+++ binaries/data/mods/public/simulation/templates/template_structure_defensive_outpost.xml
@@ -20,14 +20,6 @@
13.0
-
- 1
- 0.1
- Unit
- Infantry
- 0
- 2
-
400
decay|rubble/rubble_stone_2x2
Index: binaries/data/mods/public/simulation/templates/template_structure_defensive_wall.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_structure_defensive_wall.xml
+++ binaries/data/mods/public/simulation/templates/template_structure_defensive_wall.xml
@@ -11,14 +11,6 @@
8.0
-
- 1
- Ranged+Infantry
- 0.1
- Unit
- 0
- 2
-
Wall
template_structure_defensive_wall
Index: binaries/data/mods/public/simulation/templates/template_structure_defensive_wall_long.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_structure_defensive_wall_long.xml
+++ binaries/data/mods/public/simulation/templates/template_structure_defensive_wall_long.xml
@@ -9,9 +9,6 @@
36
-
- 8
-
3000
decay|rubble/rubble_stone_wall_long
Index: binaries/data/mods/public/simulation/templates/template_structure_defensive_wall_medium.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_structure_defensive_wall_medium.xml
+++ binaries/data/mods/public/simulation/templates/template_structure_defensive_wall_medium.xml
@@ -9,9 +9,6 @@
24
-
- 4
-
2000
decay|rubble/rubble_stone_wall_medium
Index: binaries/data/mods/public/simulation/templates/template_structure_defensive_wall_short.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_structure_defensive_wall_short.xml
+++ binaries/data/mods/public/simulation/templates/template_structure_defensive_wall_short.xml
@@ -6,7 +6,6 @@
12
-
1000
decay|rubble/rubble_stone_wall_short
Index: binaries/data/mods/public/simulation/templates/template_structure_defensive_wall_tower.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_structure_defensive_wall_tower.xml
+++ binaries/data/mods/public/simulation/templates/template_structure_defensive_wall_tower.xml
@@ -40,6 +40,10 @@
2
Support Infantry
+ 0.1
+ Unit
+ 0
+ 2
4000
Index: binaries/data/mods/public/simulation/templates/template_unit_champion_infantry.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_champion_infantry.xml
+++ binaries/data/mods/public/simulation/templates/template_unit_champion_infantry.xml
@@ -49,6 +49,7 @@
actor/human/death/{phenotype}_death.xml
+
80
Index: binaries/data/mods/public/simulation/templates/template_unit_hero_healer.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_hero_healer.xml
+++ binaries/data/mods/public/simulation/templates/template_unit_hero_healer.xml
@@ -54,4 +54,5 @@
voice/{lang}/civ/civ_{phenotype}_heal.xml
+
Index: binaries/data/mods/public/simulation/templates/template_unit_hero_infantry.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_hero_infantry.xml
+++ binaries/data/mods/public/simulation/templates/template_unit_hero_infantry.xml
@@ -37,4 +37,5 @@
+
Index: binaries/data/mods/public/simulation/templates/template_unit_infantry.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_infantry.xml
+++ binaries/data/mods/public/simulation/templates/template_unit_infantry.xml
@@ -132,6 +132,7 @@
interface/alarm/alarm_invalid_building_placement.xml
+
80
Index: binaries/data/mods/public/simulation/templates/template_unit_support_healer.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_support_healer.xml
+++ binaries/data/mods/public/simulation/templates/template_unit_support_healer.xml
@@ -54,4 +54,5 @@
30
+