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
@@ -1,4 +1,4 @@
-//Number of rounds of firing per 2 seconds
+// Number of rounds of firing per 2 seconds
const roundCount = 10;
const attackType = "Ranged";
@@ -36,18 +36,26 @@
for (let ent of msg.added)
{
+ if (msg.visible[ent])
+ continue;
+
let cmpIdentity = Engine.QueryInterface(ent, IID_Identity);
if (!cmpIdentity)
continue;
+
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;
}
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
@@ -45,6 +45,14 @@
"" +
"" +
"" +
+ "" +
+ "" +
+ "" +
+ "tokens" +
+ "" +
+ "" +
+ "" +
+ ""+
"" +
"" +
"" +
@@ -68,20 +76,21 @@
this.timer = undefined;
this.allowGarrisoning = new Map();
this.visibleGarrisonPoints = [];
- if (this.template.VisibleGarrisonPoints)
- {
- 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
- },
- "angle": points[point].Angle ? +points[point].Angle * Math.PI / 180 : null,
- "entity": null
- });
- }
+ 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 ? points[point].AllowedClasses : { "_string": "Infantry+Ranged" },
+ "angle": points[point].Angle ? +points[point].Angle * Math.PI / 180 : null,
+ "entity": null
+ });
};
/**
@@ -178,11 +187,25 @@
return MatchesClassList(entityClasses, this.template.List._string) && !!Engine.QueryInterface(ent, IID_Garrisonable);
};
+/**
+ * @param {number} entity - The entity's id.
+ * @param {Object} visibleGarrisonPoint - The vgp object
+ * @return {boolean} true if the unit is allowed be visible on that garrison point, false otherwise.
+ */
+GarrisonHolder.prototype.AllowedToVisibleGarrisoning = function(entity, visibleGarrisonPoint)
+{
+ let cmpIdentity = Engine.QueryInterface(entity, IID_Identity);
+ if (!cmpIdentity)
+ return false;
+
+ return MatchesClassList(cmpIdentity.GetClassesList(), visibleGarrisonPoint ? 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.
- * @return {boolean} Whether the entity was garrisonned.
+ * @return {boolean} Whether the entity was garrisoned.
*/
GarrisonHolder.prototype.Garrison = function(entity, vgpEntity)
{
@@ -194,14 +217,11 @@
return false;
let visibleGarrisonPoint = vgpEntity;
+ if (!vgpEntity || !this.AllowedToVisibleGarrisoning(entity, vgpEntity))
+ visibleGarrisonPoint = undefined;
+
if (!visibleGarrisonPoint)
- for (let vgp of this.visibleGarrisonPoints)
- {
- if (vgp.entity)
- continue;
- visibleGarrisonPoint = vgp;
- break;
- }
+ this.visibleGarrisonPoints.find(vgp => !vgp.entity && this.AllowedToVisibleGarrisoning(entity, vgp));
if (visibleGarrisonPoint)
{
@@ -228,6 +248,15 @@
else
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] : this.IsVisiblyGarrisoned(entity),
+ }
+ });
+
return true;
};
@@ -268,7 +297,6 @@
if (cmpAura && cmpAura.HasGarrisonAura())
cmpAura.ApplyGarrisonAura(this.entity);
- Engine.PostMessage(this.entity, MT_GarrisonedUnitsChanged, { "added": [entity], "removed": [] });
return true;
};
@@ -313,6 +341,11 @@
let cmpEntPosition = Engine.QueryInterface(entity, IID_Position);
let cmpEntUnitAI = Engine.QueryInterface(entity, IID_UnitAI);
+ // Needs to be called before the visible garrison points are cleared.
+ let visible = {
+ [entity] : this.IsVisiblyGarrisoned(entity)
+ };
+
for (let vgp of this.visibleGarrisonPoints)
{
if (vgp.entity != entity)
@@ -345,7 +378,11 @@
if (cmpPosition)
cmpEntPosition.SetYRotation(cmpPosition.GetPosition().horizAngleTo(pos));
- Engine.PostMessage(this.entity, MT_GarrisonedUnitsChanged, { "added": [], "removed": [entity] });
+ Engine.PostMessage(this.entity, MT_GarrisonedUnitsChanged, {
+ "added": [],
+ "removed": [entity],
+ "visible": visible
+ });
return true;
};
@@ -581,7 +618,13 @@
if (cmpHealth && cmpHealth.GetHitpoints() == 0)
{
this.entities.splice(entityIndex, 1);
- Engine.PostMessage(this.entity, MT_GarrisonedUnitsChanged, { "added": [], "removed": [msg.entity] });
+ Engine.PostMessage(this.entity, MT_GarrisonedUnitsChanged, {
+ "added": [],
+ "removed": [msg.entity],
+ "visible": {
+ [msg.entity]: this.IsVisiblyGarrisoned(msg.entity)
+ }
+ });
this.UpdateGarrisonFlag();
for (let point of this.visibleGarrisonPoints)
@@ -670,10 +713,29 @@
}
if (killedEntities.length)
- Engine.PostMessage(this.entity, MT_GarrisonedUnitsChanged, { "added": [], "removed": killedEntities });
+ {
+ let visibleEntitiesIds = {};
+ for (let ent of killedEntities)
+ visibleEntitiesIds[ent] = this.IsVisiblyGarrisoned(ent);
+ Engine.PostMessage(this.entity, MT_GarrisonedUnitsChanged, {
+ "added": [],
+ "removed": killedEntities,
+ "visible": visibleEntitiesIds
+ });
+ }
this.UpdateGarrisonFlag();
};
+/**
+* Gives insight about the unit type of garrisoning.
+* @param {number} entity - the entity's id.
+* @return {boolean} - Whether the unit is visible on the structure.
+*/
+GarrisonHolder.prototype.IsVisiblyGarrisoned = function(entity)
+{
+ return this.visibleGarrisonPoints.some(point => point.entity == entity);
+};
+
/**
* Whether an entity is ejectable.
* @param {number} entity - The entity-ID to be tested.
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
@@ -55,7 +55,7 @@
for (let i = 24; i <= 34; ++i)
{
AddMock(i, IID_Identity, {
- "GetClassesList": () => "Infantry+Cavalry",
+ "GetClassesList": () => ["Infantry", "Cavalry"],
"GetSelectionGroupName": () => "mace_infantry_archer_a"
});
@@ -88,7 +88,7 @@
}
AddMock(33, IID_Identity, {
- "GetClassesList": () => "Infantry+Cavalry",
+ "GetClassesList": () => ["Infantry", "Cavalry"],
"GetSelectionGroupName": () => "spart_infantry_archer_a"
});
@@ -168,3 +168,52 @@
TS_ASSERT_EQUALS(cmpGarrisonHolder.IsFull(), false);
TS_ASSERT_EQUALS(cmpGarrisonHolder.UnloadAll(), true);
TS_ASSERT_UNEVAL_EQUALS(cmpGarrisonHolder.GetEntities(), []);
+
+let siegeEngineId = 44;
+AddMock(siegeEngineId, IID_Identity, {
+ "GetClassesList": () => ["Siege"]
+});
+let archerId = 45;
+AddMock(archerId, IID_Identity, {
+ "GetClassesList": () => ["Infantry", "Ranged"]
+});
+
+// Test visible garrisoning restrictions.
+cmpGarrisonHolder = ConstructComponent(garrisonHolderId, "GarrisonHolder", {
+ "Max": 10,
+ "List": { "_string": "Infantry+Ranged Siege" },
+ "EjectHealth": 0.1,
+ "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" }
+ },
+ "archer3": {
+ "X": 15,
+ "Y": 5,
+ "Z": 6,
+ "AllowedClasses": { "_string": "Siege Infantry+Ranged Infantry+Cavalry" }
+ }
+ }
+});
+
+TS_ASSERT_EQUALS(cmpGarrisonHolder.AllowedToVisibleGarrisoning(siegeEngineId, cmpGarrisonHolder.visibleGarrisonPoints[0]), false);
+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]), false);
+TS_ASSERT_EQUALS(cmpGarrisonHolder.AllowedToVisibleGarrisoning(33, cmpGarrisonHolder.visibleGarrisonPoints[1]), false);
+TS_ASSERT_EQUALS(cmpGarrisonHolder.AllowedToVisibleGarrisoning(33, cmpGarrisonHolder.visibleGarrisonPoints[2]), true);