Index: binaries/data/mods/public/simulation/components/UnitAI.js
===================================================================
--- binaries/data/mods/public/simulation/components/UnitAI.js	(révision 13400)
+++ binaries/data/mods/public/simulation/components/UnitAI.js	(copie de travail)
@@ -328,6 +328,19 @@
 		}
 	},
 
+	"Order.PickUpUnit": function(msg) {
+		if (this.MoveToTarget(this.order.data.target))
+		{
+			this.SetNextState("INDIVIDUAL.PICKUP.APPROACHING");
+		}
+		else
+		{
+			// We are already at the target, or can't move at all
+			this.StopMoving();
+			this.SetNextState("INDIVIDUAL.PICKUP.LOADING");
+		}
+	},
+
 	"Order.Flee": function(msg) {
 		// We use the distance between the enities to account for ranged attacks
 		var distance = DistanceBetweenEntities(this.entity, this.order.data.target) + (+this.template.FleeDistance);
@@ -597,6 +610,11 @@
 			return;
 		}
 
+		// If to be garrisoned inside a ship, warn it so it can take needed action
+		var cmpIdentity = Engine.QueryInterface(this.order.data.target, IID_Identity);
+		if (cmpIdentity && cmpIdentity.HasClass("Ship"))
+			Engine.PostMessage(this.order.data.target, MT_TransportRequired, { "entity": this.entity });
+
 		if (this.MoveToTarget(this.order.data.target))
 		{
 			this.SetNextState("INDIVIDUAL.GARRISON.APPROACHING");
@@ -724,8 +742,17 @@
 					// The target was destroyed
 					this.FinishOrder();
 				else
+				{
 					// Out of range; move there in formation
 					this.PushOrderFront("WalkToTargetRange", { "target": msg.data.target, "min": 0, "max": 10 });
+					// If to be garrisoned inside a ship, warn it so it can take needed action
+					var cmpIdentity = Engine.QueryInterface(msg.data.target, IID_Identity);
+					{
+						var cmpFormation = Engine.QueryInterface(this.entity, IID_Formation);
+						var entity = cmpFormation.GetPrimaryMember();
+						Engine.PostMessage(msg.data.target, MT_TransportRequired, { "entity": entity });
+					}
+				}
 				return;
 			}
 
@@ -2228,6 +2255,12 @@
 						{
 							// Unable to reach the target, try again
 							// (or follow if it's a moving target)
+							// But first if garrisoning inside a ship, warn it again
+							// in case the ship's orders have been changed
+							var cmpIdentity = Engine.QueryInterface(target, IID_Identity);
+							if (cmpIdentity && cmpIdentity.HasClass("Ship"))
+								Engine.PostMessage(target, MT_TransportRequired, { "entity": this.entity });
+
 							if (this.MoveToTarget(target))
 							{
 								this.SetNextState("APPROACHING");
@@ -2307,6 +2340,44 @@
 				// Ignore attacks while unpacking
 			},
 		},
+
+		"PICKUP": {
+			"APPROACHING": {
+				"enter": function() {
+					this.SelectAnimation("move");
+				},
+
+				"MoveCompleted": function() {
+					this.SetNextState("LOADING");
+				},
+				
+				"leave": function() {
+				}
+			},
+
+			"LOADING": {
+				"enter": function() {
+					this.StartTimer(0, 1000);
+					this.SelectAnimation("idle");
+				},
+
+				"Timer": function() {
+					var cmpUnitAI = Engine.QueryInterface(this.order.data.target, IID_UnitAI);
+					// Stop waiting if the target does not exist any more, or is already garrisoned or its orders have changed
+					if (!cmpUnitAI || cmpUnitAI.isGarrisoned || !cmpUnitAI.order || cmpUnitAI.order.type != "Garrison"
+						|| cmpUnitAI.order.data.target != this.entity)
+						this.FinishOrder();
+					// Or if the capacity is already full
+					var cmpGarrisonHolder = Engine.QueryInterface(this.entity, IID_GarrisonHolder);
+					if (cmpGarrisonHolder.GetEntities().length >= cmpGarrisonHolder.GetCapacity())
+						this.FinishOrder();
+				},
+
+				"leave": function() {
+					this.StopTimer();
+				},
+			},
+		},
 	},
 
 	"ANIMAL": {
@@ -2585,6 +2656,22 @@
 		this.SetupRangeQueries();
 };
 
+UnitAI.prototype.OnTransportRequired = function(msg)
+{
+	var cmpGarrisonHolder = Engine.QueryInterface(this.entity, IID_GarrisonHolder);
+	if (cmpGarrisonHolder && cmpGarrisonHolder.GetEntities().length < cmpGarrisonHolder.GetCapacity())
+	{
+		// First check if we already have such a requirement
+		for each (var order in this.orderQueue)
+		{
+			if (order.type == "PickUpUnit" && order.data.target == msg.entity)
+				return;
+		}
+		// Insert the PickUp order after the last forced order
+		this.PushOrderAfterForced("PickUpUnit", { "target": msg.entity });
+	}
+};
+
 // Wrapper function that sets up the normal, healer, and Gaia range queries.
 UnitAI.prototype.SetupRangeQueries = function()
 {
@@ -2850,6 +2937,31 @@
 	}
 };
 
+/**
+ * Insert an order after the last forced order onto the queue
+ * and after the other orders of the same type
+ */
+UnitAI.prototype.PushOrderAfterForced = function(type, data)
+{
+	if (!this.order || ((!this.order.data || !this.order.data.force) && this.order.type != type))
+	{
+		this.PushOrderFront(type, data);
+	}
+	else
+	{
+		for (var i = 1; i < this.orderQueue.length; ++i)
+		{
+			if (this.orderQueue[i].data && this.orderQueue[i].data.force)
+				continue;
+			if (this.orderQueue[i].type == type)
+				continue;
+			this.orderQueue.splice(i, 0, {"type": type, "data": data});
+			return;
+		}
+		this.PushOrder(type, data);
+	}
+};
+
 UnitAI.prototype.ReplaceOrder = function(type, data)
 {
 	// Special cases of orders that shouldn't be replaced:
Index: binaries/data/mods/public/simulation/components/interfaces/UnitAI.js
===================================================================
--- binaries/data/mods/public/simulation/components/interfaces/UnitAI.js	(révision 13400)
+++ binaries/data/mods/public/simulation/components/interfaces/UnitAI.js	(copie de travail)
@@ -11,3 +11,7 @@
 // Message of the form { "to": orderData }.
 // sent whenever the unit changes state
 Engine.RegisterMessageType("UnitAIOrderDataChanged");
+
+// Message of the form { "entity": entity },
+// sent whenever a transport (ship) is required
+Engine.RegisterMessageType("TransportRequired");
