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
@@ -631,7 +631,7 @@
"getActionInfo": function(entState, targetState)
{
if (!entState.canGarrison || !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"), {
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" +
+ "" +
+ "" +
+ "
" +
+ "" +
+ "" +
+ "" +
"" +
"" +
"" +
@@ -65,6 +79,7 @@
{
// Garrisoned Units
this.entities = [];
+ this.allowedPlayers = this.template.AllowedPlayers ? this.template.AllowedPlayers.split(/\s+/) : ["Player", "MutualAlly"];
this.timer = undefined;
this.allowGarrisoning = new Map();
this.visibleGarrisonPoints = [];
@@ -106,6 +121,15 @@
};
/**
+ * What diplomatic stances are allowd to join.
+ * @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.
*/
@@ -167,7 +191,7 @@
if (!this.IsGarrisoningAllowed())
return false;
- if (!IsOwnedByMutualAllyOfEntity(ent, this.entity))
+ if (!this.IsAllowedByDiplomacy(ent))
return false;
let cmpIdentity = Engine.QueryInterface(ent, IID_Identity);
@@ -509,6 +533,28 @@
};
/**
+ * 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);
+
+ let owner = INVALID_PLAYER;
+ let cmpOwner = Engine.QueryInterface(this.entity, IID_Ownership);
+ if (cmpOwner)
+ owner = cmpOwner.GetOwner();
+ let allowedPlayers = this.GetAllowedPlayers();
+ return allowedPlayers.indexOf("Player") != -1 && IsOwnedByPlayer(owner, 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);
+};
+
+/**
* Called every second. Heals garrisoned units.
*/
GarrisonHolder.prototype.HealTimeout = function(data)
@@ -564,7 +610,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);
@@ -588,7 +634,7 @@
if (point.entity == msg.entity)
point.entity = null;
}
- else if (msg.to == INVALID_PLAYER || !IsOwnedByMutualAllyOfEntity(this.entity, msg.entity))
+ else if (msg.to == INVALID_PLAYER || !this.IsAllowedByDiplomacy(msg.entity))
this.EjectOrKill([msg.entity]);
}
};
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
@@ -360,7 +360,8 @@
"buffHeal": cmpGarrisonHolder.GetHealRate(),
"allowedClasses": cmpGarrisonHolder.GetAllowedClasses(),
"capacity": cmpGarrisonHolder.GetCapacity(),
- "garrisonedEntitiesCount": cmpGarrisonHolder.GetGarrisonedEntitiesCount()
+ "garrisonedEntitiesCount": cmpGarrisonHolder.GetGarrisonedEntitiesCount(),
+ "allowedPlayers": cmpGarrisonHolder.GetAllowedPlayers()
};
ret.canGarrison = !!Engine.QueryInterface(ent, IID_Garrisonable);
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
@@ -5767,7 +5767,7 @@
// Verify that the target is owned by this entity's player or a mutual ally of this player
var cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership);
- if (!cmpOwnership || !(IsOwnedByPlayer(cmpOwnership.GetOwner(), target) || IsOwnedByMutualAllyOfPlayer(cmpOwnership.GetOwner(), target)))
+ if (!cmpOwnership || !cmpGarrisonHolder.IsAllowedByDiplomacy(this.entity))
return false;
// Don't let animals garrison for now
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
@@ -33,14 +33,15 @@
});
AddMock(player, IID_Player, {
- "IsAlly": id => true,
- "IsMutualAlly": id => true,
+ "IsAlly": id => id != enemyPlayer,
+ "IsMutualAlly": id => id != enemyPlayer,
+ "IsEnemy": id => id == enemyPlayer,
"GetPlayerID": () => player
});
AddMock(friendlyPlayer, IID_Player, {
- "IsAlly": id => true,
- "IsMutualAlly": id => true,
+ "IsAlly": id => id != enemyPlayer,
+ "IsMutualAlly": id => id != enemyPlayer,
"GetPlayerID": () => friendlyPlayer
});
@@ -168,3 +169,29 @@
TS_ASSERT_EQUALS(cmpGarrisonHolder.IsFull(), false);
TS_ASSERT_EQUALS(cmpGarrisonHolder.UnloadAll(), true);
TS_ASSERT_UNEVAL_EQUALS(cmpGarrisonHolder.GetEntities(), []);
+
+// 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
@@ -468,14 +468,6 @@
"garrison": function(player, cmd, data)
{
- // Verify that the building can be controlled by the player or is mutualAlly
- if (!CanControlUnitOrIsAlly(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).forEach(cmpUnitAI => {
cmpUnitAI.Garrison(cmd.target, cmd.queued);
});
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
@@ -277,6 +277,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
@@ -353,6 +363,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);