Index: binaries/data/mods/public/gui/session/utility_functions.js
===================================================================
--- binaries/data/mods/public/gui/session/utility_functions.js	(révision 13266)
+++ binaries/data/mods/public/gui/session/utility_functions.js	(copie de travail)
@@ -62,6 +62,7 @@
 		    "teamsLocked": playerState.teamsLocked,
 		    "state": playerState.state,
 		    "isAlly": playerState.isAlly,
+		    "isMutualAlly": playerState.isMutualAlly,
 		    "isNeutral": playerState.isNeutral,
 		    "isEnemy": playerState.isEnemy,
 		    "guid": undefined, // network guid for players controlled by hosts
Index: binaries/data/mods/public/gui/session/input.js
===================================================================
--- binaries/data/mods/public/gui/session/input.js	(révision 13266)
+++ binaries/data/mods/public/gui/session/input.js	(copie de travail)
@@ -217,6 +217,7 @@
 		var playerState = simState.players[entState.player];
 		var playerOwned = (targetState.player == entState.player);
 		var allyOwned = playerState.isAlly[targetState.player];
+		var mutualAllyOwned = playerState.isMutualAlly[targetState.player];
 		var enemyOwned = playerState.isEnemy[targetState.player];
 		var gaiaOwned = (targetState.player == 0);
 
@@ -225,7 +226,7 @@
 
 		// default to walking there
 		var data = {command: "walk"};
-		if (targetState.garrisonHolder && playerOwned)
+		if (targetState.garrisonHolder && (playerOwned || mutualAllyOwned))
 		{
 			data.command = "garrison";
 			data.target = target;
@@ -297,6 +298,7 @@
 		var playerState = simState.players[entState.player];
 		var playerOwned = (targetState.player == entState.player);
 		var allyOwned = playerState.isAlly[targetState.player];
+		var mutualAllyOwned = playerState.isMutualAlly[targetState.player];
 		var neutralOwned = playerState.isNeutral[targetState.player];
 		var enemyOwned = playerState.isEnemy[targetState.player];
 		var gaiaOwned = (targetState.player == 0);
@@ -308,7 +310,7 @@
 		switch (action)
 		{
 		case "garrison":
-			if (hasClass(entState, "Unit") && targetState.garrisonHolder && playerOwned)
+			if (hasClass(entState, "Unit") && targetState.garrisonHolder && (playerOwned || mutualAllyOwned))
 			{
 				var allowedClasses = targetState.garrisonHolder.allowedClasses;
 				for each (var unitClass in entState.identity.classes)
@@ -1732,6 +1734,8 @@
 		var unitName = getEntityName(template);
 
 		var playerID = Engine.GetPlayerID();
+		var simState = Engine.GuiInterfaceCall("GetSimulationState");
+
 		if (entState.player == playerID || g_DevSettings.controlAll)
 		{
 			switch (commandName)
@@ -1780,6 +1784,18 @@
 				break;
 			}
 		}
+		else if (simState.players[playerID].isMutualAlly[entState.player])
+		{
+			switch (commandName)
+			{
+			case "garrison":
+				inputState = INPUT_PRESELECTEDACTION;
+				preSelectedAction = ACTION_GARRISON;
+				break;
+			default:
+				break;
+			}
+		}
 	}
 }
 
Index: binaries/data/mods/public/gui/session/unit_commands.js
===================================================================
--- binaries/data/mods/public/gui/session/unit_commands.js	(révision 13266)
+++ binaries/data/mods/public/gui/session/unit_commands.js	(copie de travail)
@@ -689,6 +689,23 @@
                 affordableMask.hidden = false;
 				affordableMask.sprite = "colour: 0 0 0 127";
 			}
+
+			if (guiName == GARRISON)
+			{
+				var ents = garrisonGroups.getEntsByName(item);
+				var entplayer = GetEntityState(ents[0]).player;
+				button.sprite = "colour: " + g_Players[entplayer].color.r + " " + g_Players[entplayer].color.g + " " + g_Players[entplayer].color.b;
+
+				var player = Engine.GetPlayerID();
+				if(player != unitEntState.player && !g_DevSettings.controlAll)
+				{
+					if (entplayer != player) 
+					{
+						button.enabled = false;
+						grayscale = "grayscale:";
+					}
+				}
+			}
 			
 			icon.sprite = "stretched:" + grayscale + "session/portraits/" + template.icon;
 			
@@ -1003,12 +1020,13 @@
 
 	// If the selection is friendly units, add the command panels
 	var player = Engine.GetPlayerID();
+	// Get player state to check some constraints
+	// e.g. presence of a hero or build limits
+	var simState = Engine.GuiInterfaceCall("GetSimulationState");
+	var playerState = simState.players[player];
+
 	if (entState.player == player || g_DevSettings.controlAll)
 	{
-		// Get player state to check some constraints
-		// e.g. presence of a hero or build limits
-		var simState = Engine.GuiInterfaceCall("GetSimulationState");
-		var playerState = simState.players[player];
 
 		if (selection.length > 1)
 			setupUnitPanel(SELECTION, usedPanels, entState, playerState, g_Selection.groups.getTemplateNames(),
@@ -1024,7 +1042,7 @@
 			var groups = new EntityGroups();
 			for (var i in selection)
 			{
-				state = GetEntityState(selection[i]);
+				var state = GetEntityState(selection[i]);
 				if (state.garrisonHolder)
 					groups.add(state.garrisonHolder.entities)
 			}
@@ -1191,6 +1209,31 @@
 		supplementalDetailsPanel.hidden = false;
 		commandsPanel.hidden = false;
 	}
+	else if (playerState.isMutualAlly[entState.player]) // owned by allied player
+	{
+
+		if (entState.garrisonHolder)
+		{
+			var groups = new EntityGroups();
+			for (var i in selection)
+			{
+				var state = GetEntityState(selection[i]);
+				if (state.garrisonHolder)
+					groups.add(state.garrisonHolder.entities)
+			}
+
+			setupUnitPanel(GARRISON, usedPanels, entState, playerState, groups.getTemplateNames(),
+				function (item) { unloadTemplate(item); } );
+
+			supplementalDetailsPanel.hidden = false;
+		}
+		else
+		{
+			supplementalDetailsPanel.hidden = true;
+		}
+
+		commandsPanel.hidden = true;
+	}
 	else // owned by another player
 	{
 		supplementalDetailsPanel.hidden = true;
Index: binaries/data/mods/public/simulation/helpers/Player.js
===================================================================
--- binaries/data/mods/public/simulation/helpers/Player.js	(révision 13266)
+++ binaries/data/mods/public/simulation/helpers/Player.js	(copie de travail)
@@ -209,7 +209,7 @@
 	if (cmpOwnership)
 		owner = cmpOwnership.GetOwner();
 
-	// Figure out which player controls the foundation being built
+	// Figure out which player controls the target entity
 	var targetOwner = 0;
 	var cmpOwnershipTarget = Engine.QueryInterface(target, IID_Ownership);
 	if (cmpOwnershipTarget)
@@ -226,6 +226,34 @@
 }
 
 /**
+ * Returns true if the entity 'target' is owned by a mutual ally of
+ * the owner of 'entity'.
+ */
+function IsOwnedByMutualAllyOfEntity(entity, target)
+{
+	// Figure out which player controls us
+	var owner = 0;
+	var cmpOwnership = Engine.QueryInterface(entity, IID_Ownership);
+	if (cmpOwnership)
+		owner = cmpOwnership.GetOwner();
+
+	// Figure out which player controls the target entity
+	var targetOwner = 0;
+	var cmpOwnershipTarget = Engine.QueryInterface(target, IID_Ownership);
+	if (cmpOwnershipTarget)
+		targetOwner = cmpOwnershipTarget.GetOwner();
+
+	var cmpPlayerManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager);
+	var cmpPlayer = Engine.QueryInterface(cmpPlayerManager.GetPlayerByID(owner), IID_Player);
+
+	// Check for allied diplomacy status
+	if (cmpPlayer.IsMutualAlly(targetOwner))
+		return true;
+
+	return false;
+}
+
+/**
  * Returns true if the entity 'target' is owned by player
  */
 function IsOwnedByPlayer(player, target)
@@ -264,6 +292,26 @@
 }
 
 /**
+ * Returns true if the entity 'target' is owned by a mutual ally of player
+ */
+function IsOwnedByMutualAllyOfPlayer(player, target)
+{
+	var targetOwner = 0;
+	var cmpOwnershipTarget = Engine.QueryInterface(target, IID_Ownership);
+	if (cmpOwnershipTarget)
+		targetOwner = cmpOwnershipTarget.GetOwner();
+
+	var cmpPlayerManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager);
+	var cmpPlayer = Engine.QueryInterface(cmpPlayerManager.GetPlayerByID(player), IID_Player);
+
+	// Check for allied diplomacy status
+	if (cmpPlayer.IsMutualAlly(targetOwner))
+		return true;
+
+	return false;
+}
+
+/**
  * Returns true if the entity 'target' is owned by someone neutral to player
  */
 function IsOwnedByNeutralOfPlayer(player,target)
@@ -307,8 +355,10 @@
 Engine.RegisterGlobal("QueryOwnerInterface", QueryOwnerInterface);
 Engine.RegisterGlobal("QueryPlayerIDInterface", QueryPlayerIDInterface);
 Engine.RegisterGlobal("IsOwnedByAllyOfEntity", IsOwnedByAllyOfEntity);
+Engine.RegisterGlobal("IsOwnedByMutualAllyOfEntity", IsOwnedByMutualAllyOfEntity);
 Engine.RegisterGlobal("IsOwnedByPlayer", IsOwnedByPlayer);
 Engine.RegisterGlobal("IsOwnedByGaia", IsOwnedByGaia);
 Engine.RegisterGlobal("IsOwnedByAllyOfPlayer", IsOwnedByAllyOfPlayer);
+Engine.RegisterGlobal("IsOwnedByMutualAllyOfPlayer", IsOwnedByMutualAllyOfPlayer);
 Engine.RegisterGlobal("IsOwnedByNeutralOfPlayer", IsOwnedByNeutralOfPlayer);
 Engine.RegisterGlobal("IsOwnedByEnemyOfPlayer", IsOwnedByEnemyOfPlayer);
Index: binaries/data/mods/public/simulation/helpers/Commands.js
===================================================================
--- binaries/data/mods/public/simulation/helpers/Commands.js	(révision 13266)
+++ binaries/data/mods/public/simulation/helpers/Commands.js	(copie de travail)
@@ -314,8 +314,8 @@
 		break;
 
 	case "garrison":
-		// Verify that the building can be controlled by the player
-		if (CanControlUnit(cmd.target, player, controlAllUnits))
+		// Verify that the building can be controlled by the player or is mutualAlly
+		if (CanControlUnitOrIsAlly(cmd.target, player, controlAllUnits))
 		{
 			var entities = FilterEntityList(cmd.entities, player, controlAllUnits);
 			GetFormationUnitAIs(entities, player).forEach(function(cmpUnitAI) {
@@ -336,12 +336,17 @@
 		break;
 
 	case "unload":
-		// Verify that the building can be controlled by the player
-		if (CanControlUnit(cmd.garrisonHolder, player, controlAllUnits))
+		// Verify that the building can be controlled by the player or is mutualAlly
+		if (CanControlUnitOrIsAlly(cmd.garrisonHolder, player, controlAllUnits))
 		{
 			var cmpGarrisonHolder = Engine.QueryInterface(cmd.garrisonHolder, IID_GarrisonHolder);
 			var notUngarrisoned = 0;
-			for each (ent in cmd.entities)
+			if (IsOwnedByPlayer(player, cmd.garrisonHolder))
+				var entities = cmd.entities;
+			else
+				var entities = FilterEntityList(cmd.entities, player, controlAllUnits);
+
+			for each (var ent in entities)
 				if (!cmpGarrisonHolder || !cmpGarrisonHolder.Unload(ent))
 					notUngarrisoned++;
 
@@ -355,12 +360,34 @@
 		break;
 
 	case "unload-template":
-		var selected = FilterEntityList(cmd.garrisonHolders, player, controlAllUnits);
+		var selected = FilterEntityListWithAllies(cmd.garrisonHolders, player, controlAllUnits);
+		var cmpTemplateManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager);
 		for each (var garrisonHolder in selected)
 		{
 			var cmpGarrisonHolder = Engine.QueryInterface(garrisonHolder, IID_GarrisonHolder);
-			if (!cmpGarrisonHolder || !cmpGarrisonHolder.UnloadTemplate(cmd.template, cmd.all))
-				notifyUnloadFailure(player, garrisonHolder)
+			if (cmpGarrisonHolder)
+			{
+				// check that we are allowed to unload entities corresponding to this template
+				if (IsOwnedByPlayer(player, garrisonHolder))
+					var entities = cmpGarrisonHolder.entities;
+				else
+					var entities = FilterEntityList(cmpGarrisonHolder.entities, player, controlAllUnits);
+
+				for each (var ent in entities)
+				{
+					// Units with multiple ranks are grouped together.
+					var cmpIdentity = Engine.QueryInterface(ent, IID_Identity);
+					var name = cmpIdentity.GetSelectionGroupName();
+					if (!name)
+						name = cmpTemplateManager.GetCurrentTemplateName(ent);
+					if (name == cmd.template)
+					{
+						if (!cmpGarrisonHolder.UnloadTemplate(cmd.template, cmd.all))
+							notifyUnloadFailure(player, garrisonHolder);
+						break;
+					}
+				}
+			}
 		}
 		break;
 
@@ -1272,8 +1299,8 @@
 
 /**
  * Check if player can control this entity
- * returns: true if the entity is valid and owned by the player if
- *		or control all units is activated for the player, else false
+ * returns: true if the entity is valid and owned by the player
+ *		or control all units is activated, else false
  */
 function CanControlUnit(entity, player, controlAll)
 {
@@ -1281,6 +1308,17 @@
 }
 
 /**
+ * Check if player can control this entity
+ * returns: true if the entity is valid and owned by the player
+ *              or  the entity is owned by an mutualAlly
+ *		or control all units is activated, else false
+ */
+function CanControlUnitOrIsAlly(entity, player, controlAll)
+{
+	return (IsOwnedByPlayer(player, entity) || IsOwnedByMutualAllyOfPlayer(player, entity) || controlAll);
+}
+
+/**
  * Filter entities which the player can control
  */
 function FilterEntityList(entities, player, controlAll)
@@ -1289,6 +1327,14 @@
 }
 
 /**
+ * Filter entities which the player can control or are mutualAlly
+ */
+function FilterEntityListWithAllies(entities, player, controlAll)
+{
+	return entities.filter(function(ent) { return CanControlUnitOrIsAlly(ent, player, controlAll);} );
+}
+
+/**
  * Try to transform a wall to a gate 
  */
 function TryTransformWallToGate(ent, cmpPlayer, template)
Index: binaries/data/mods/public/simulation/components/UnitAI.js
===================================================================
--- binaries/data/mods/public/simulation/components/UnitAI.js	(révision 13266)
+++ binaries/data/mods/public/simulation/components/UnitAI.js	(copie de travail)
@@ -4142,9 +4142,9 @@
 	if (!cmpGarrisonHolder)
 		return false;
 
-	// Verify that the target is owned by this entity's player
+	// Verify that the target is owned by this entity's player or is mutual allied
 	var cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership);
-	if (!cmpOwnership || !IsOwnedByPlayer(cmpOwnership.GetOwner(), target))
+	if (!cmpOwnership || !(IsOwnedByPlayer(cmpOwnership.GetOwner(), target) || IsOwnedByMutualAllyOfPlayer(cmpOwnership.GetOwner(), target)))
 		return false;
 
 	// Don't let animals garrison for now
Index: binaries/data/mods/public/simulation/components/Player.js
===================================================================
--- binaries/data/mods/public/simulation/components/Player.js	(révision 13266)
+++ binaries/data/mods/public/simulation/components/Player.js	(copie de travail)
@@ -334,12 +334,8 @@
 
 	var sharedLos = [];
 	for (var i = 0; i < cmpPlayerManager.GetNumPlayers(); ++i)
-		if (this.IsAlly(i))
-		{
-			var cmpPlayer = Engine.QueryInterface(cmpPlayerManager.GetPlayerByID(i), IID_Player);
-			if (cmpPlayer && cmpPlayer.IsAlly(this.playerID))
-				sharedLos.push(i);
-		}
+		if (this.IsMutualAlly(i))
+			sharedLos.push(i);
 
 	cmpRangeManager.SetSharedLos(this.playerID, sharedLos);
 };
@@ -407,6 +403,18 @@
 	return this.diplomacy[id] > 0;
 };
 
+/**
+ * Check if given player is our ally, and we are its ally
+ */
+Player.prototype.IsMutualAlly = function(id)
+{
+	var cmpPlayerManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager);
+	if (!cmpPlayerManager)
+		return false;
+	var cmpPlayer = Engine.QueryInterface(cmpPlayerManager.GetPlayerByID(id), IID_Player);
+	return this.IsAlly(id) && cmpPlayer && cmpPlayer.IsAlly(this.playerID);
+};
+
 Player.prototype.SetEnemy = function(id)
 {
 	this.SetDiplomacyIndex(id, -1);
Index: binaries/data/mods/public/simulation/components/GuiInterface.js
===================================================================
--- binaries/data/mods/public/simulation/components/GuiInterface.js	(révision 13266)
+++ binaries/data/mods/public/simulation/components/GuiInterface.js	(copie de travail)
@@ -64,11 +64,13 @@
 		
 		// store player ally/neutral/enemy data as arrays
 		var allies = [];
+		var mutualAllies = [];
 		var neutrals = [];
 		var enemies = [];
 		for (var j = 0; j < n; ++j)
 		{
 			allies[j] = cmpPlayer.IsAlly(j);
+			mutualAllies[j] = cmpPlayer.IsMutualAlly(j);
 			neutrals[j] = cmpPlayer.IsNeutral(j);
 			enemies[j] = cmpPlayer.IsEnemy(j);
 		}
@@ -86,6 +88,7 @@
 			"teamsLocked": cmpPlayer.GetLockTeams(),
 			"phase": phase,
 			"isAlly": allies,
+			"isMutualAlly": mutualAllies,
 			"isNeutral": neutrals,
 			"isEnemy": enemies,
 			"entityLimits": cmpPlayerEntityLimits.GetLimits(),
Index: binaries/data/mods/public/simulation/components/GarrisonHolder.js
===================================================================
--- binaries/data/mods/public/simulation/components/GarrisonHolder.js	(révision 13266)
+++ binaries/data/mods/public/simulation/components/GarrisonHolder.js	(copie de travail)
@@ -469,5 +469,19 @@
 	}
 };
 
+
+/**
+ * Eject all foreign garrisoned entities which are no more allied
+ */
+GarrisonHolder.prototype.OnDiplomacyChanged = function()
+{
+	for (var i = this.entities.length; i > 0; --i)
+	{
+		if (!IsOwnedByMutualAllyOfEntity(this.entity, this.entities[i-1]))
+			this.Eject(this.entities[i-1], true);
+	}
+	this.UpdateGarrisonFlag();
+};
+
 Engine.RegisterComponentType(IID_GarrisonHolder, "GarrisonHolder", GarrisonHolder);
 
