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
@@ -831,7 +831,7 @@
"getActionInfo": function(entState, targetState)
{
if (!entState.garrisonable || !targetState || !targetState.garrisonHolder ||
- !playerCheck(entState, targetState, ["Player", "MutualAlly"]))
+ !playerCheck(entState, targetState, targetState.garrisonHolder.allowedPlayers))
return false;
let tooltip = sprintf(translate("Current garrison: %(garrisoned)s/%(capacity)s"), {
@@ -1360,7 +1360,7 @@
{
unloadAll();
},
- "allowedPlayers": ["Player", "Ally"]
+ "allowedPlayers": ["Player", "Ally", "Neutral", "Enemy"]
},
"unload-all-turrets": {
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
@@ -31,6 +31,20 @@
"" +
"" +
"" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "Player" +
+ "MutualAlly" +
+ "Neutral" +
+ "Enemy" +
+ "" +
+ "" +
+ "
" +
+ "" +
"";
/**
@@ -47,6 +61,7 @@
{
this.entities = [];
this.allowedClasses = ApplyValueModificationsToEntity("GarrisonHolder/List/_string", this.template.List._string, this.entity);
+ this.allowedPlayers = this.template.AllowedPlayers ? this.template.AllowedPlayers.split(/\s+/) : ["Player", "MutualAlly"];
};
/**
@@ -80,6 +95,15 @@
};
/**
+ * What diplomatic stances are allowd to garrison.
+ * @return {string[]} An array containing the allowed diplomacies to garrison.
+ */
+GarrisonHolder.prototype.GetAllowedPlayers = function()
+{
+ return ApplyValueModificationsToEntity("GarrisonHolder/AllowedPlayers", this.allowedPlayers, this.entity);
+};
+
+/**
* @return {Array} unit classes which can be garrisoned inside this
* particular entity. Obtained from the entity's template.
*/
@@ -168,7 +192,7 @@
GarrisonHolder.prototype.IsAllowedToBeGarrisoned = function(entity)
{
- if (!IsOwnedByMutualAllyOfEntity(entity, this.entity))
+ if (!this.IsAllowedByDiplomacy(entity))
return false;
let cmpIdentity = Engine.QueryInterface(entity, IID_Identity);
@@ -325,6 +349,24 @@
return !cmpHealth || cmpHealth.GetHitpoints() > Math.floor(+this.template.EjectHealth * cmpHealth.GetMaxHitpoints());
};
+/**
+ * Used to check if the garrisoning entity's diplomatic status is right for garrisoning this entity.
+ * @param {number} ent - The entity ID to check for.
+ *
+ * @return {boolean} - Whether garrisoning is allowed purely on diplomatic grounds.
+ */
+GarrisonHolder.prototype.IsAllowedByDiplomacy = function(ent)
+{
+ if (!this.template.AllowedPlayers)
+ return IsOwnedByMutualAllyOfEntity(this.entity, ent);
+
+ const allowedPlayers = this.GetAllowedPlayers();
+ return allowedPlayers.indexOf("Player") != -1 && IsOwnedByPlayer(Engine.QueryInterface(this.entity, IID_Ownership)?.GetOwner() || INVALID_PLAYER, ent) ||
+ allowedPlayers.indexOf("MutualAlly") != -1 && IsOwnedByMutualAllyOfEntity(this.entity, ent) ||
+ allowedPlayers.indexOf("Neutral") != -1 && IsOwnedByNeutralOfEntity(this.entity, ent) ||
+ allowedPlayers.indexOf("Enemy") != -1 && IsOwnedByEnemyOfEntity(this.entity, ent);
+};
+
GarrisonHolder.prototype.StartTimer = function()
{
if (this.timer)
@@ -395,7 +437,7 @@
// The ownership change may be on the garrisonholder
if (this.entity == msg.entity)
{
- let entities = this.entities.filter(ent => msg.to == INVALID_PLAYER || !IsOwnedByMutualAllyOfEntity(this.entity, ent));
+ let entities = this.entities.filter(ent => msg.to == INVALID_PLAYER || !this.IsAllowedByDiplomacy(ent));
if (entities.length)
this.EjectOrKill(entities);
@@ -405,7 +447,7 @@
// or on some of its garrisoned units
let entityIndex = this.entities.indexOf(msg.entity);
- if (entityIndex != -1 && (msg.to == INVALID_PLAYER || !IsOwnedByMutualAllyOfEntity(this.entity, msg.entity)))
+ if (entityIndex != -1 && (msg.to == INVALID_PLAYER || !this.IsAllowedByDiplomacy(msg.entity)))
this.EjectOrKill([msg.entity]);
};
@@ -436,7 +478,7 @@
*/
GarrisonHolder.prototype.OnDiplomacyChanged = function()
{
- this.EjectOrKill(this.entities.filter(ent => !IsOwnedByMutualAllyOfEntity(this.entity, ent)));
+ this.EjectOrKill(this.entities.filter(ent => !this.IsAllowedByDiplomacy(ent)));
};
/**
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
@@ -401,7 +401,8 @@
"buffHeal": cmpGarrisonHolder.GetHealRate(),
"allowedClasses": cmpGarrisonHolder.GetAllowedClasses(),
"capacity": cmpGarrisonHolder.GetCapacity(),
- "occupiedSlots": cmpGarrisonHolder.OccupiedSlots()
+ "occupiedSlots": cmpGarrisonHolder.OccupiedSlots(),
+ "allowedPlayers": cmpGarrisonHolder.GetAllowedPlayers()
};
let cmpTurretHolder = Engine.QueryInterface(ent, IID_TurretHolder);
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
@@ -34,6 +34,7 @@
AddMock(player, IID_Player, {
"IsAlly": id => id != enemyPlayer,
"IsMutualAlly": id => id != enemyPlayer,
+ "IsEnemy": id => id == enemyPlayer,
"GetPlayerID": () => player
});
@@ -289,3 +290,29 @@
TS_ASSERT_UNEVAL_EQUALS(cmpGarrisonHolder.GetEntities(), []);
TS_ASSERT(!cmpGarrisonHolder.Garrison(siegeEngineId));
TS_ASSERT(cmpGarrisonHolder.Garrison(traderId));
+
+// Test allowed players.
+cmpGarrisonHolder = ConstructComponent(garrisonHolderId, "GarrisonHolder", {
+ "Max": 10,
+ "List": { "_string": "Infantry+Cavalry" },
+ "EjectHealth": 0.1,
+ "EjectClassesOnDestroy": { "_string": "Infantry" },
+ "BuffHeal": 1,
+ "LoadingRange": 2.1,
+ "Pickup": false,
+ "AllowedPlayers": "Enemy",
+ "VisibleGarrisonPoints": {
+ "archer1": {
+ "X": 12,
+ "Y": 5,
+ "Z": 6
+ },
+ "archer2": {
+ "X": 15,
+ "Y": 5,
+ "Z": 6
+ }
+ }
+});
+TS_ASSERT_EQUALS(cmpGarrisonHolder.IsAllowedToGarrison(enemyUnitId), true);
+TS_ASSERT_EQUALS(cmpGarrisonHolder.IsAllowedToGarrison(unitToGarrisonId), false);
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
@@ -482,13 +482,6 @@
"garrison": function(player, cmd, data)
{
- if (!CanPlayerOrAllyControlUnit(cmd.target, player, data.controlAllUnits))
- {
- if (g_DebugCommands)
- warn("Invalid command: garrison target cannot be controlled by player "+player+" (or ally): "+uneval(cmd));
- return;
- }
-
GetFormationUnitAIs(data.entities, player, cmd, data.formation).forEach(cmpUnitAI => {
cmpUnitAI.Garrison(cmd.target, cmd.queued, cmd.pushFront);
});
Index: binaries/data/mods/public/simulation/helpers/Player.js
===================================================================
--- binaries/data/mods/public/simulation/helpers/Player.js
+++ binaries/data/mods/public/simulation/helpers/Player.js
@@ -230,6 +230,16 @@
return IsOwnedByEntityHelper(entity, target, "IsMutualAlly");
}
+function IsOwnedByNeutralOfEntity(entity, target)
+{
+ return IsOwnedByEntityHelper(entity, target, "IsNeutral");
+}
+
+function IsOwnedByEnemyOfEntity(entity, target)
+{
+ return IsOwnedByEntityHelper(entity, target, "IsEnemy");
+}
+
function IsOwnedByEntityHelper(entity, target, check)
{
// Figure out which player controls us
@@ -306,6 +316,8 @@
Engine.RegisterGlobal("QueryBuilderListInterface", QueryBuilderListInterface);
Engine.RegisterGlobal("IsOwnedByAllyOfEntity", IsOwnedByAllyOfEntity);
Engine.RegisterGlobal("IsOwnedByMutualAllyOfEntity", IsOwnedByMutualAllyOfEntity);
+Engine.RegisterGlobal("IsOwnedByNeutralOfEntity", IsOwnedByNeutralOfEntity);
+Engine.RegisterGlobal("IsOwnedByEnemyOfEntity", IsOwnedByEnemyOfEntity);
Engine.RegisterGlobal("IsOwnedByPlayer", IsOwnedByPlayer);
Engine.RegisterGlobal("IsOwnedByGaia", IsOwnedByGaia);
Engine.RegisterGlobal("IsOwnedByAllyOfPlayer", IsOwnedByAllyOfPlayer);
Index: binaries/data/mods/public/simulation/templates/template_structure_civic_house.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_structure_civic_house.xml
+++ binaries/data/mods/public/simulation/templates/template_structure_civic_house.xml
@@ -15,8 +15,9 @@
0
0.1
Unit
- Support+!Elephant
+ Unit
1
+ Player Enemy
800