Index: binaries/data/mods/public/gui/session/input.js
===================================================================
--- binaries/data/mods/public/gui/session/input.js	(révision 13817)
+++ binaries/data/mods/public/gui/session/input.js	(copie de travail)
@@ -203,7 +203,7 @@
 
 	if (!target)
 	{
-		if (action == "set-rallypoint" && haveRallyPoints)
+		if (action == "set-rallypoint" && haveRallyPoints && !Engine.HotkeyIsPressed("session.autorallypoint"))
 			return {"possible": true};
 		else if (action == "move" || action == "attack-move")
 			return {"possible": true};
@@ -302,9 +302,20 @@
 		}
 
 		// Don't allow the rally point to be set on any of the currently selected entities
-		for (var i = 0; i < selection.length; i++)
-			if (target === selection[i])
-				return {"possible": false};
+		// (except if garrisonHolder or corral and the corresponding hotkey is pressed)
+		if (Engine.HotkeyIsPressed("session.autorallypoint"))
+		{
+			for (var i = 0; i < selection.length; ++i)
+				if (target === selection[i] && (targetState.garrisonHolder || targetState.corral))
+					return {"possible": true, "data": data, "position": targetState.position, "cursor": cursor, "tooltip": tooltip};
+			return {"possible": false};
+		}
+		else
+		{
+			for (var i = 0; i < selection.length; ++i)
+				if (target === selection[i])
+					return {"possible": false};
+		}
 
 		return {"possible": true, "data": data, "position": targetState.position, "cursor": cursor, "tooltip": tooltip};
 	}
@@ -515,9 +526,13 @@
 	{
 		return {"type": "garrison", "cursor": "action-garrison", "tooltip": actionInfo.tooltip, "target": target};
 	}
+	else if (Engine.HotkeyIsPressed("session.autorallypoint") && (actionInfo = getActionInfo("set-rallypoint", target)).possible)
+	{
+		return {"type": "set-rallypoint", "cursor": actionInfo.cursor, "data": actionInfo.data, "tooltip": actionInfo.tooltip, "position": actionInfo.position};
+	}
 	else if (Engine.HotkeyIsPressed("session.attackmove") && getActionInfo("attack-move", target).possible)
 	{
-			return {"type": "attack-move", "cursor": "action-attack-move"};
+		return {"type": "attack-move", "cursor": "action-attack-move"};
 	}
 	else
 	{
Index: binaries/data/mods/public/simulation/components/GarrisonHolder.js
===================================================================
--- binaries/data/mods/public/simulation/components/GarrisonHolder.js	(révision 13817)
+++ binaries/data/mods/public/simulation/components/GarrisonHolder.js	(copie de travail)
@@ -158,6 +158,19 @@
  */
 GarrisonHolder.prototype.Garrison = function(entity)
 {
+	var cmpPosition = Engine.QueryInterface(entity, IID_Position);
+	if (!cmpPosition)
+		return false;
+
+	if (!this.PerformGarrison(entity))
+		return false;
+
+	cmpPosition.MoveOutOfWorld();
+	return true;
+};
+
+GarrisonHolder.prototype.PerformGarrison = function(entity)
+{
 	if (!this.HasEnoughHealth())
 		return false;
 
@@ -168,10 +181,6 @@
 	if (this.GetCapacity() < this.spaceOccupied + 1)
 		return false;
 
-	var cmpPosition = Engine.QueryInterface(entity, IID_Position);
-	if (!cmpPosition)
-		return false;
-
 	if (!this.timer && this.GetHealRate() > 0)
 	{
  		var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
@@ -181,7 +190,6 @@
 	// Actual garrisoning happens here
 	this.entities.push(entity);
 	this.spaceOccupied += 1;
-	cmpPosition.MoveOutOfWorld();
 	this.UpdateGarrisonFlag();
 	var cmpProductionQueue = Engine.QueryInterface(entity, IID_ProductionQueue);
 	if (cmpProductionQueue)
@@ -254,6 +262,9 @@
 		if (rallyPos)
 		{
 			var commands = GetRallyPointCommands(cmpRallyPoint, entities);
+			// ignore the rally point if it is autogarrison
+			if (commands[0].type == "garrison" && commands[0].target == this.entity)
+				return;
 			for each (var com in commands)
 			{
 				ProcessCommand(cmpOwnership.GetOwner(), com);
Index: binaries/data/mods/public/simulation/components/ProductionQueue.js
===================================================================
--- binaries/data/mods/public/simulation/components/ProductionQueue.js	(révision 13817)
+++ binaries/data/mods/public/simulation/components/ProductionQueue.js	(copie de travail)
@@ -442,8 +442,9 @@
 };
 
 /*
- * This function creates the entities and places them in world if possible.
- * returns the number of successfully spawned entities.
+ * This function creates the entities and places them in world if possible
+ * and returns the number of successfully created entities.
+ * (some of these entities may be garrisoned directly if autogarrison, the others are spawned).
  */
 ProductionQueue.prototype.SpawnUnits = function(templateName, count, metadata)
 {
@@ -452,6 +453,7 @@
 	var cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership);
 	var cmpRallyPoint = Engine.QueryInterface(this.entity, IID_RallyPoint);
 	
+	var createdEnts = [];
 	var spawnedEnts = [];
 	
 	if (this.entityCache.length == 0)
@@ -476,35 +478,52 @@
 		}
 	}
 
+	var cmpAutoGarrison = undefined;
+	if (cmpRallyPoint)
+	{
+		var data = cmpRallyPoint.GetData()[0];
+		if (data && data.target == this.entity && data.command == "garrison")
+			cmpAutoGarrison = Engine.QueryInterface(this.entity, IID_GarrisonHolder);
+	}
+
 	for (var i = 0; i < count; ++i)
 	{
 		var ent = this.entityCache[0];
-		var pos = cmpFootprint.PickSpawnPoint(ent);
-		if (pos.y < 0)
+		var cmpNewOwnership = Engine.QueryInterface(ent, IID_Ownership);
+		cmpNewOwnership.SetOwner(cmpOwnership.GetOwner());
+
+		if (cmpAutoGarrison && cmpAutoGarrison.PerformGarrison(ent))
 		{
-			// Fail: there wasn't any space to spawn the unit
-			break;
+			var cmpUnitAI = Engine.QueryInterface(ent, IID_UnitAI);
+			cmpUnitAI.Autogarrison();
 		}
 		else
 		{
-			// Successfully spawned
-			var cmpNewPosition = Engine.QueryInterface(ent, IID_Position);
-			cmpNewPosition.JumpTo(pos.x, pos.z);
-			// TODO: what direction should they face in?
+			var pos = cmpFootprint.PickSpawnPoint(ent);
+			if (pos.y < 0)
+			{
+				// Fail: there wasn't any space to spawn the unit
+				break;
+			}
+			else
+			{
+				// Successfully spawned
+				var cmpNewPosition = Engine.QueryInterface(ent, IID_Position);
+				cmpNewPosition.JumpTo(pos.x, pos.z);
+				// TODO: what direction should they face in?
+				spawnedEnts.push(ent);
+			}
+		}
 
-			var cmpNewOwnership = Engine.QueryInterface(ent, IID_Ownership);
-			cmpNewOwnership.SetOwner(cmpOwnership.GetOwner());
-			
-			var cmpPlayerStatisticsTracker = QueryOwnerInterface(this.entity, IID_StatisticsTracker);
-			cmpPlayerStatisticsTracker.IncreaseTrainedUnitsCounter();
+		var cmpPlayerStatisticsTracker = QueryOwnerInterface(this.entity, IID_StatisticsTracker);
+		cmpPlayerStatisticsTracker.IncreaseTrainedUnitsCounter();
 
-			// Play a sound, but only for the first in the batch (to avoid nasty phasing effects)
-			if (spawnedEnts.length == 0)
-				PlaySound("trained", ent);
+		// Play a sound, but only for the first in the batch (to avoid nasty phasing effects)
+		if (createdEnts.length == 0)
+			PlaySound("trained", ent);
 			
-			this.entityCache.shift();
-			spawnedEnts.push(ent);
-		}
+		this.entityCache.shift();
+		createdEnts.push(ent);
 	}
 
 	if (spawnedEnts.length > 0)
@@ -518,20 +537,21 @@
 			{
 				var commands = GetRallyPointCommands(cmpRallyPoint, spawnedEnts);
 				for each(var com in commands)
-				{
 					ProcessCommand(cmpOwnership.GetOwner(), com);
-				}
 			}
 		}
+	}
 
+	if (createdEnts.length > 0)
+	{
 		Engine.PostMessage(this.entity, MT_TrainingFinished, {
-			"entities": spawnedEnts,
+			"entities": createdEnts,
 			"owner": cmpOwnership.GetOwner(),
 			"metadata": metadata,
 		});
 	}
 	
-	return spawnedEnts.length;
+	return createdEnts.length;
 };
 
 /*
Index: binaries/data/mods/public/simulation/components/UnitAI.js
===================================================================
--- binaries/data/mods/public/simulation/components/UnitAI.js	(révision 13817)
+++ binaries/data/mods/public/simulation/components/UnitAI.js	(copie de travail)
@@ -620,6 +620,10 @@
 		}
 	},
 
+	"Order.Autogarrison": function(msg) {
+		this.SetNextState("INDIVIDUAL.AUTOGARRISON");
+	},
+
 	"Order.Cheering": function(msg) {
 		this.SetNextState("INDIVIDUAL.CHEERING");
 	},
@@ -2331,6 +2335,22 @@
 			},
 		},
 
+		"AUTOGARRISON": {
+			"enter": function() {
+				this.isGarrisoned = true;
+				return false;
+			},
+				
+			"Order.Ungarrison": function() {
+				if (this.FinishOrder())
+					return;
+			},
+
+			"leave": function() {
+				this.isGarrisoned = false;
+			}
+		},
+
 		"CHEERING": {
 			"enter": function() {
 				// Unit is invulnerable while cheering
@@ -3942,6 +3962,14 @@
 };
 
 /**
+ * Adds autogarrison order to the queue (only used by ProductionQueue for auto-garrisoning)
+ */
+UnitAI.prototype.Autogarrison = function()
+{
+	this.AddOrder("Autogarrison", null, false);
+};
+
+/**
  * Adds gather order to the queue, forced by the player 
  * until the target is reached 
  */
Index: binaries/data/config/default.cfg
===================================================================
--- binaries/data/config/default.cfg	(révision 13817)
+++ binaries/data/config/default.cfg	(copie de travail)
@@ -270,6 +270,7 @@
 hotkey.session.attack = "Ctrl+Alt"          ; Modifier to force attack instead of another action
 hotkey.session.garrison = Ctrl              ; Modifier to garrison when clicking on building
 hotkey.session.attackmove = Ctrl            ; Modifier to attackmove when clicking on a point
+hotkey.session.autorallypoint = Ctrl        ; Modifier to set the rally point on the building itself
 hotkey.session.queue = Shift                ; Modifier to queue unit orders instead of replacing
 hotkey.session.batchtrain = Shift           ; Modifier to train units in batches
 hotkey.session.massbarter = Shift           ; Modifier to barter bunch of resources
