Index: binaries/data/config/default.cfg
===================================================================
--- binaries/data/config/default.cfg	(révision 13544)
+++ binaries/data/config/default.cfg	(copie de travail)
@@ -278,6 +278,8 @@
 hotkey.session.deselectgroup = Ctrl         ; Modifier to deselect units when clicking group icon, instead of selecting
 hotkey.session.rotate.cw = RightBracket     ; Rotate building placement preview clockwise
 hotkey.session.rotate.ccw = LeftBracket     ; Rotate building placement preview anticlockwise
+hotkey.selection.includeorder = F1          ; Include in selection the units with special orders (i.e. escort)
+hotkey.session.escort = AltGr               ; Modifier to escort/guard when clicking on unit/building
 hotkey.timewarp.fastforward = Space         ; If timewarp mode enabled, speed up the game
 hotkey.timewarp.rewind = Backspace          ; If timewarp mode enabled, go back to earlier point in the game
 
Index: binaries/data/mods/public/gui/session/input.js
===================================================================
--- binaries/data/mods/public/gui/session/input.js	(révision 13544)
+++ binaries/data/mods/public/gui/session/input.js	(copie de travail)
@@ -416,6 +416,19 @@
 			if (entState.attack && targetState.hitpoints && (enemyOwned || neutralOwned))
 				return {"possible": Engine.GuiInterfaceCall("CanAttack", {"entity": entState.id, "target": target})};
 			break;
+		case "escort":
+			if (targetState.escort && playerOwned)
+			{
+				var allowedClasses = targetState.escort.allowedClasses;
+				for each (var unitClass in entState.identity.classes)
+				{
+					if (allowedClasses.indexOf(unitClass) != -1)
+					{
+						return {"possible": true};
+					}
+				}
+			}
+			break;
 		}
 	}
 	if (action == "move" || action == "attack-move")
@@ -503,6 +516,10 @@
 	{
 			return {"type": "attack-move", "cursor": "action-attack-move"};
 	}
+	else if (Engine.HotkeyIsPressed("session.escort") && getActionInfo("escort", target).possible)
+	{
+		return {"type": "escort", "cursor": "action-guard", "target": target};
+	}
 	else
 	{
 		if ((actionInfo = getActionInfo("setup-trade-route", target)).possible)
@@ -653,6 +670,31 @@
 	return preferredEnts;
 }
 
+// Removes any units with special orders (i.e. escort)
+function getNoOrderEntities(ents)
+{
+	var noOrderEnts = [];
+	for each (var ent in ents)
+	{
+		var keep = true;
+		var entState = GetEntityState(ent);
+		if (entState.unitAI)
+		{
+			for each (var order in entState.unitAI.orders)
+			{
+				if (order.type == "Escort")
+				{
+					keep = false;
+					break;
+				}
+			}
+		}
+		if (keep)
+			noOrderEnts.push(ent);
+	}
+	return noOrderEnts;
+}
+
 // Removes any support units from the passed list of entities
 function getMilitaryEntities(ents)
 {
@@ -750,6 +792,10 @@
 					}
 				}
 
+				// By default, we do not want to select units with special orders
+				if (!Engine.HotkeyIsPressed("selection.includeorder"))
+					ents = getNoOrderEntities(ents);
+
 				// Remove the bandbox hover highlighting
 				g_Selection.setHighlightList([]);
 
@@ -1206,6 +1252,10 @@
 
 					// TODO: Should we handle "control all units" here as well?
 					ents = Engine.PickSimilarFriendlyEntities(templateToMatch, showOffscreen, matchRank, false);
+
+					// By default, we do not want to select units with special orders
+					if (!Engine.HotkeyIsPressed("selection.includeorder"))
+						ents = getNoOrderEntities(ents);
 				}
 				else
 				{
@@ -1381,6 +1431,11 @@
 		Engine.GuiInterfaceCall("PlaySound", { "name": "order_garrison", "entity": selection[0] });
 		return true;
 
+	case "escort":
+		Engine.PostNetworkCommand({"type": "escort", "entities": selection, "target": action.target, "queued": queued});
+		//Engine.GuiInterfaceCall("PlaySound", { "name": "order_attack", "entity": selection[0] });
+		return true;
+
 	case "set-rallypoint":
 		var pos = undefined;
 		// if there is a position set in the action then use this so that when setting a 
Index: binaries/data/mods/public/simulation/templates/template_unit_support.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_support.xml	(révision 13544)
+++ binaries/data/mods/public/simulation/templates/template_unit_support.xml	(copie de travail)
@@ -15,6 +15,10 @@
     <GenericName>Support</GenericName>
     <Classes datatype="tokens">Support Organic</Classes>
   </Identity>
+  <Escort>
+    <List datatype="tokens">Support Infantry Cavalry</List>
+    <LandEscortRange>8</LandEscortRange>
+  </Escort>
   <Loot>
     <xp>10</xp>
     <food>1</food>
Index: binaries/data/mods/public/simulation/templates/template_unit_infantry.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_infantry.xml	(révision 13544)
+++ binaries/data/mods/public/simulation/templates/template_unit_infantry.xml	(copie de travail)
@@ -51,6 +51,10 @@
     <Classes datatype="tokens">Infantry CitizenSoldier Worker Organic</Classes>
     <Rank>Basic</Rank>
   </Identity>
+  <Escort>
+    <List datatype="tokens">Support Infantry Cavalry</List>
+    <LandEscortRange>8</LandEscortRange>
+  </Escort>
   <Loot>
     <xp>100</xp>
     <food>5</food>
Index: binaries/data/mods/public/simulation/templates/template_unit_mechanical_ship.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_mechanical_ship.xml	(révision 13544)
+++ binaries/data/mods/public/simulation/templates/template_unit_mechanical_ship.xml	(copie de travail)
@@ -33,6 +33,10 @@
     <GenericName>Ship</GenericName>
     <Classes datatype="tokens">Ship</Classes>
   </Identity>
+  <Escort>
+    <List datatype="tokens">Ship</List>
+    <NavalEscortRange>20</NavalEscortRange>
+  </Escort>
   <Obstruction>
     <Unit radius="8.0"/>
   </Obstruction>
Index: binaries/data/mods/public/simulation/templates/template_unit_cavalry.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_cavalry.xml	(révision 13544)
+++ binaries/data/mods/public/simulation/templates/template_unit_cavalry.xml	(copie de travail)
@@ -33,6 +33,10 @@
     <GenericName>Cavalry</GenericName>
     <Rank>Basic</Rank>
   </Identity>
+  <Escort>
+    <List datatype="tokens">Cavalry</List>
+    <LandEscortRange>8</LandEscortRange>
+  </Escort>
   <Loot>
     <xp>130</xp>
     <food>10</food>
Index: binaries/data/mods/public/simulation/templates/template_structure.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_structure.xml	(révision 13544)
+++ binaries/data/mods/public/simulation/templates/template_structure.xml	(copie de travail)
@@ -14,6 +14,10 @@
     <DefaultArrowCount>0</DefaultArrowCount>
     <GarrisonArrowMultiplier>0</GarrisonArrowMultiplier>
   </BuildingAI>
+  <Escort>
+    <List datatype="tokens">Infantry</List>
+    <LandEscortRange>12</LandEscortRange>
+  </Escort>
   <BuildRestrictions>
     <PlacementType>land</PlacementType>
     <Territory>own</Territory>
Index: binaries/data/mods/public/simulation/templates/template_unit_mechanical.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_mechanical.xml	(révision 13544)
+++ binaries/data/mods/public/simulation/templates/template_unit_mechanical.xml	(copie de travail)
@@ -14,6 +14,10 @@
     <GenericName>Mechanical</GenericName>
     <Classes datatype="tokens">Mechanical</Classes>
   </Identity>
+  <Escort>
+    <List datatype="tokens">Infantry Cavalry</List>
+    <LandEscortRange>8</LandEscortRange>
+  </Escort>
   <Loot>
     <xp>60</xp>
     <food>0</food>
Index: binaries/data/mods/public/simulation/templates/template_unit_champion.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_champion.xml	(révision 13544)
+++ binaries/data/mods/public/simulation/templates/template_unit_champion.xml	(copie de travail)
@@ -8,6 +8,10 @@
     <Classes datatype="tokens">Champion Organic</Classes>
     <RequiredTechnology>phase_city</RequiredTechnology>
   </Identity>
+  <Escort>
+    <List datatype="tokens">Support Infantry Cavalry</List>
+    <LandEscortRange>8</LandEscortRange>
+  </Escort>
   <Loot>
     <xp>150</xp>
     <food>10</food>
Index: binaries/data/mods/public/simulation/templates/template_unit_hero.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_hero.xml	(révision 13544)
+++ binaries/data/mods/public/simulation/templates/template_unit_hero.xml	(copie de travail)
@@ -38,6 +38,10 @@
     <Classes datatype="tokens">Hero Organic</Classes>
     <RequiredTechnology>phase_city</RequiredTechnology>
   </Identity>
+  <Escort>
+    <List datatype="tokens">Support Infantry Cavalry</List>
+    <LandEscortRange>8</LandEscortRange>
+  </Escort>
   <Loot>
     <xp>400</xp>
     <food>10</food>
Index: binaries/data/mods/public/simulation/templates/template_structure_military_dock.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_structure_military_dock.xml	(révision 13544)
+++ binaries/data/mods/public/simulation/templates/template_structure_military_dock.xml	(copie de travail)
@@ -26,6 +26,11 @@
     <Classes datatype="tokens">Village Naval Market NavalMarket Dock</Classes>
     <Icon>structures/dock.png</Icon>
   </Identity>
+  <Escort>
+    <List datatype="tokens">Infantry Ship</List>
+    <LandEscortRange>12</LandEscortRange>
+    <NavalEscortRange>20</NavalEscortRange>
+  </Escort>
   <Loot>
     <xp>100</xp>
     <food>0</food>
Index: binaries/data/mods/public/simulation/helpers/Commands.js
===================================================================
--- binaries/data/mods/public/simulation/helpers/Commands.js	(révision 13544)
+++ binaries/data/mods/public/simulation/helpers/Commands.js	(copie de travail)
@@ -319,6 +319,20 @@
 		}
 		break;
 
+	case "escort":
+		// Verify that the building can be controlled by the player
+		if (CanControlUnit(cmd.target, player, controlAllUnits))
+		{
+			GetFormationUnitAIs(entities, player).forEach(function(cmpUnitAI) {
+				cmpUnitAI.Escort(cmd.target, cmd.queued);
+			});
+		}
+		else if (g_DebugCommands)
+		{
+			warn("Invalid command: escort target cannot be controlled by player "+player+": "+uneval(cmd));
+		}
+		break;
+
 	case "stop":
 		GetFormationUnitAIs(entities, player).forEach(function(cmpUnitAI) {
 			cmpUnitAI.Stop(cmd.queued);
Index: binaries/data/mods/public/simulation/components/UnitAI.js
===================================================================
--- binaries/data/mods/public/simulation/components/UnitAI.js	(révision 13544)
+++ binaries/data/mods/public/simulation/components/UnitAI.js	(copie de travail)
@@ -160,6 +160,10 @@
 		// ignore
 	},
 
+	"EscortedAttacked": function(msg) {
+		// ignore
+	},
+
 	// Formation handlers:
 
 	"FormationLeave": function(msg) {
@@ -328,6 +332,22 @@
 		}
 	},
 
+	"Order.Escort": function(msg) {
+		var cmpEscort = Engine.QueryInterface(this.order.data.target, IID_Escort);
+		if (!cmpEscort || !cmpEscort.AllowedToEscort(this.entity))
+		{
+			this.FinishOrder();
+			return;
+		}
+		this.escortRange = cmpEscort.GetRange(this.entity);
+
+		var ok = this.MoveToTargetRangeExplicit(this.order.data.target, 0, this.escortRange);
+		if (ok)
+			this.SetNextState("INDIVIDUAL.ESCORT.ESCORTING");
+		else
+			this.SetNextState("INDIVIDUAL.ESCORT.GUARDING");
+	},
+
 	"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);
@@ -685,6 +705,12 @@
 				this.FinishOrder();
 		},
 
+		"Order.Escort": function(msg) {
+			var cmpFormation = Engine.QueryInterface(this.entity, IID_Formation);
+			cmpFormation.CallMemberFunction("Escort", [msg.data.target, false]);
+			cmpFormation.Disband();
+		},
+
 		"Order.Stop": function(msg) {
 			var cmpFormation = Engine.QueryInterface(this.entity, IID_Formation);
 			cmpFormation.CallMemberFunction("Stop", [false]);
@@ -1148,6 +1174,10 @@
 			}
 		},
 
+		"EscortedAttacked": function(msg) {
+			this.RespondToTargetedEntities([msg.data.attacker]);
+		},
+
 		"IDLE": {
 			"enter": function() {
 				// Switch back to idle animation to guarantee we won't
@@ -1252,6 +1282,144 @@
 			},
 		},
 
+		"ESCORT": {
+			"enter": function () {
+				if (this.escort && this.escort != this.order.data.target)
+				{
+					Engine.PostMessage(this.escort, MT_EscortChanged, { "escort": this.entity });
+					this.escort = undefined;
+				}
+
+				if (!this.escort)
+				{
+					this.escort = this.order.data.target;
+					Engine.PostMessage(this.escort, MT_EscortChanged, { "escort": this.entity });
+				}
+
+				this.order.data.status = "running";
+			},
+
+			"leave": function () {
+				// if not dead, check if it's just a temporary leave (i.e. to attack or repair)
+				if (this.TargetIsAlive(this.entity))
+				{
+					for each (var order in this.orderQueue)
+					{
+						if (order.type == "Escort"  && order.data.target == this.escort && order.data.status == "running")
+							return;
+					}
+				}
+				Engine.PostMessage(this.escort, MT_EscortChanged, { "escort": this.entity });
+				delete this.escortRange;
+				delete this.escort;
+			},
+
+			"ESCORTING": {
+				"enter": function () {
+					this.StartTimer(0, 1000);
+					this.SelectAnimation("move");
+					var cmpPosition = Engine.QueryInterface(this.escort, IID_Position);
+					if (cmpPosition && cmpPosition.IsInWorld())
+					{
+						var pos = cmpPosition.GetPosition();
+						this.SetHeldPosition(pos.x, pos.z);
+					}
+					// adapt the speed to the one of the target
+					var cmpUnitAI = Engine.QueryInterface(this.escort, IID_UnitAI);
+					if (cmpUnitAI)
+					{
+						var speed = cmpUnitAI.GetWalkSpeed();
+						if (speed < this.GetWalkSpeed())
+							this.SetMoveSpeed(speed);
+					}
+				},
+
+				"Timer": function(msg) {
+					// Check the target is alive
+					if (!this.TargetIsAlive(this.escort))
+					{
+						this.FinishOrder();
+						return;
+					}
+					var cmpPosition = Engine.QueryInterface(this.escort, IID_Position);
+					if (cmpPosition && cmpPosition.IsInWorld())
+					{
+						var pos = cmpPosition.GetPosition();
+						this.SetHeldPosition(pos.x, pos.z);
+					}
+					this.FindNewTargets();
+				},
+
+				"leave": function(msg) {
+					this.SetMoveSpeed(this.GetWalkSpeed());
+					this.StopTimer();
+				},
+
+				"MoveCompleted": function() {
+					var ok = this.MoveToTargetRangeExplicit(this.escort, 0, this.escortRange);
+					if (!ok)
+						this.SetNextState("GUARDING");
+				},
+			},
+
+			"GUARDING": {
+				"enter": function () {
+					this.StartTimer(1000, 1000);
+					var cmpPosition = Engine.QueryInterface(this.entity, IID_Position);
+					if (cmpPosition && cmpPosition.IsInWorld())
+					{
+						var pos = cmpPosition.GetPosition();
+						this.SetHeldPosition(pos.x, pos.z);
+					}
+					this.SelectAnimation("idle");
+					if (this.FindNewTargets())
+						return true;
+					return false;
+				},
+
+				"LosRangeUpdate": function(msg) {
+					// Start attacking one of the newly-seen enemy (if any)
+					if (this.GetStance().targetVisibleEnemies)
+						this.AttackEntitiesByPreference(msg.data.added);
+				},
+
+				"LosGaiaRangeUpdate": function(msg) {
+					// Start attacking one of the newly-seen enemy (if any)
+					if (this.GetStance().targetVisibleEnemies)
+						this.AttackGaiaEntitiesByPreference(msg.data.added);
+				},
+
+				"Timer": function(msg) {
+					// Check the target is alive
+					if (!this.TargetIsAlive(this.escort))
+					{
+						this.FinishOrder();
+						return;
+					}
+					// then check is the target has moved
+					var ok = this.MoveToTargetRangeExplicit(this.escort, 0, this.escortRange);
+					if (ok) 
+						this.SetNextState("ESCORTING");
+					else
+					{
+						// if nothing else to do, check if the escorted needs to be healed or repaired
+						var cmpHealth = Engine.QueryInterface(this.escort, IID_Health);
+						if (cmpHealth && (cmpHealth.GetHitpoints() < cmpHealth.GetMaxHitpoints()))
+						{
+							if (this.CanHeal(this.escort))
+								this.PushOrderFront("Heal", { "target": this.escort, "force": false });
+							else if (this.CanRepair(this.escort) && cmpHealth.IsRepairable())
+								this.PushOrderFront("Repair", { "target": this.escort, "autocontinue": false, "force": false });
+						}
+					}
+				},
+
+				"leave": function(msg) {
+					this.StopTimer();
+				},
+			},
+		},
+
 		"FLEEING": {
 			"enter": function() {
 				this.PlaySound("panic");
@@ -2099,6 +2267,18 @@
 					return false;
 				},
 
+				"LosRangeUpdate": function(msg) {
+					// if we are escorting, stop repairing to be able to fight this ennemy
+					if (this.escort && this.GetStance().targetVisibleEnemies)
+						this.FinishOrder();
+				},
+
+				"LosGaiaRangeUpdate": function(msg) {
+					// if we are escorting, stop repairing to be able to fight this ennemy
+					if (this.escort && this.GetStance().targetVisibleEnemies)
+						this.FinishOrder();
+				},
+
 				"leave": function() {
 					var cmpFoundation = Engine.QueryInterface(this.repairTarget, IID_Foundation);
 					if (cmpFoundation)
@@ -2978,6 +3158,11 @@
 	UnitFsm.ProcessMessage(this, {"type": "Attacked", "data": msg});
 };
 
+UnitAI.prototype.OnEscortedAttacked = function(msg)
+{
+	UnitFsm.ProcessMessage(this, {"type": "EscortedAttacked", "data": msg.data});
+};
+
 UnitAI.prototype.OnHealthChanged = function(msg)
 {
 	UnitFsm.ProcessMessage(this, {"type": "HealthChanged", "from": msg.from, "to": msg.to});
@@ -3519,6 +3704,16 @@
 	if (force)
 		return false;
 
+	// If we are escorting, don't stop as long as the attacker is within attack range of the escorted
+	if (this.escort)
+	{
+		var cmpUnitAI =  Engine.QueryInterface(target, IID_UnitAI);
+		var cmpAttack = Engine.QueryInterface(target, IID_Attack);
+		for each (var type in cmpAttack.GetAttackTypes())
+			if (cmpUnitAI.CheckTargetRange(this.escort, IID_Attack, type))
+				return false;
+	}
+
 	// Stop if we're in hold-ground mode and it's too far from the holding point
 	if (this.GetStance().respondHoldGround)
 	{
@@ -3553,6 +3748,15 @@
 	if (this.GetStance().respondChase)
 		return true;
 
+	if (this.escort)
+	{
+		var cmpUnitAI =  Engine.QueryInterface(target, IID_UnitAI);
+		var cmpAttack = Engine.QueryInterface(target, IID_Attack);
+		for each (var type in cmpAttack.GetAttackTypes())
+			if (cmpUnitAI.CheckTargetRange(this.escort, IID_Attack, type))
+				return true;
+	}
+
 	if (force)
 		return true;
 
@@ -3653,6 +3857,7 @@
 
 		case "WalkToTarget":
 		case "WalkToTargetRange": // This doesn't move to the target (just into range), but a later order will.
+		case "Escort":
 		case "Flee":
 		case "LeaveFoundation":
 		case "Attack":
@@ -3698,6 +3903,14 @@
 };
 
 /**
+ * Adds escort order to the queue, forced by the player.
+ */
+UnitAI.prototype.Escort = function(target, queued)
+{
+	this.AddOrder("Escort", { "target": target, "status": "waiting", "force": false }, queued);
+};
+
+/**
  * Adds walk order to queue, forced by the player.
  */
 UnitAI.prototype.Walk = function(x, z, queued)
@@ -4161,7 +4374,7 @@
 {
 	if (this.heldPosition)
 	{
-		this.AddOrder("Walk", { "x": this.heldPosition.x, "z": this.heldPosition.z, "force": false }, false);
+		this.PushOrderFront("Walk", { "x": this.heldPosition.x, "z": this.heldPosition.z, "force": false });
 		return true;
 	}
 	return false;
Index: binaries/data/mods/public/simulation/components/Escort.js
===================================================================
--- binaries/data/mods/public/simulation/components/Escort.js	(révision 0)
+++ binaries/data/mods/public/simulation/components/Escort.js	(copie de travail)
@@ -0,0 +1,80 @@
+function Escort() {}
+
+Escort.prototype.Schema =
+	"<element name='List' a:help='Classes of entities which are allowed to escort this escorted (from Identity)'>" +
+		"<attribute name='datatype'>" +
+			"<value>tokens</value>" +
+		"</attribute>" +
+		"<text/>" +
+	"</element>" +
+	"<optional><element name='LandEscortRange' a:help='Distance between the escort and this escorted'>" +
+		"<ref name='nonNegativeDecimal'/>" +
+	"</element></optional>" +
+	"<optional><element name='NavalEscortRange' a:help='Distance between the escort ship and this escorted'>" +
+			"<ref name='nonNegativeDecimal'/>" +
+	"</element></optional>";
+
+Escort.prototype.Init = function()
+{
+	this.escortList = [];
+};
+
+Escort.prototype.GetRange = function(entity)
+{
+	var entityClasses = (Engine.QueryInterface(entity, IID_Identity)).GetClassesList();
+	if (entityClasses.indexOf("Ship") == -1)
+		return (+this.template.LandEscortRange || 8);
+	else
+		return (+this.template.NavalEscortRange || 20);
+};
+
+/**
+ * Returns an array of unit classes which can escort this entity.
+ * Obtained from the entity's template 
+ */
+Escort.prototype.GetAllowedClassesList = function()
+{
+	var string = this.template.List._string || "";
+	return string.split(/\s+/);
+};
+
+/**
+ * Checks if an entity can be allowed to escort this escorted
+ * based on its class
+ */
+Escort.prototype.AllowedToEscort = function(entity)
+{
+	var allowedClasses = this.GetAllowedClassesList();
+	var entityClasses = (Engine.QueryInterface(entity, IID_Identity)).GetClassesList();
+	for each (var allowedClass in allowedClasses)
+	{
+		if (entityClasses.indexOf(allowedClass) != -1)
+			return true;
+	}
+	return false;
+};
+
+Escort.prototype.OnAttacked = function(msg)
+{
+	for each (var escort in this.escortList)
+		Engine.PostMessage(escort, MT_EscortedAttacked, { "escorted": this.entity, "data": msg });
+};
+
+Escort.prototype.OnEscortChanged = function(msg)
+{
+	if (this.escortList.indexOf(msg.escort) == -1)
+		this.escortList.push(msg.escort);
+	else
+		this.escortList.splice(this.escortList.indexOf(msg.escort), 1);
+};
+
+Escort.prototype.OnGlobalEntityRenamed = function(msg)
+{
+	if (this.escortList.length == 0)
+		return;
+	var entityIndex = this.escortList.indexOf(msg.entity);
+	if (entityIndex != -1)
+		this.escortList[entityIndex] = msg.newentity;
+};
+
+Engine.RegisterComponentType(IID_Escort, "Escort", Escort);
Index: binaries/data/mods/public/simulation/components/GuiInterface.js
===================================================================
--- binaries/data/mods/public/simulation/components/GuiInterface.js	(révision 13544)
+++ binaries/data/mods/public/simulation/components/GuiInterface.js	(copie de travail)
@@ -323,6 +323,14 @@
 		if (cmpUnitAI.isGarrisoned && ret.player)
 			ret.template = "p" + ret.player + "&" + ret.template;
 	}
+
+	var cmpEscort = Engine.QueryInterface(ent, IID_Escort);
+	if (cmpEscort)
+	{
+		ret.escort = {
+			"allowedClasses": cmpEscort.GetAllowedClassesList()
+		}
+	}
 	
 	var cmpGate = Engine.QueryInterface(ent, IID_Gate);
 	if (cmpGate)
Index: binaries/data/mods/public/simulation/components/interfaces/UnitAI.js
===================================================================
--- binaries/data/mods/public/simulation/components/interfaces/UnitAI.js	(révision 13544)
+++ 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 { "escort": escort }.
+// sent whenever an escort is added or removed
+Engine.RegisterMessageType("EscortChanged");
Index: binaries/data/mods/public/simulation/components/interfaces/Escort.js
===================================================================
--- binaries/data/mods/public/simulation/components/interfaces/Escort.js	(révision 0)
+++ binaries/data/mods/public/simulation/components/interfaces/Escort.js	(copie de travail)
@@ -0,0 +1,5 @@
+Engine.RegisterInterface("Escort");
+
+// Message of the form { "escorted": entity, "data": msg },
+// sent whenever an escorted unit is attacked
+Engine.RegisterMessageType("EscortedAttacked");
