Index: binaries/data/mods/public/simulation/templates/gaia/fauna_wolf.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/gaia/fauna_wolf.xml	(révision 13268)
+++ binaries/data/mods/public/simulation/templates/gaia/fauna_wolf.xml	(copie de travail)
@@ -15,6 +15,7 @@
   </Footprint>
   <Identity>
     <Civ>gaia</Civ>
+    <Species>Canis lupus</Species>
     <SpecificName>Wolf</SpecificName>
     <Icon>gaia/fauna_wolf.png</Icon>
   </Identity>
Index: binaries/data/mods/public/simulation/templates/gaia/fauna_elephant_asian.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/gaia/fauna_elephant_asian.xml	(révision 13268)
+++ binaries/data/mods/public/simulation/templates/gaia/fauna_elephant_asian.xml	(copie de travail)
@@ -44,6 +44,7 @@
   <Identity>
     <Civ>gaia</Civ>
     <Classes datatype="tokens">Elephant</Classes>
+    <Species>Elephant</Species>
     <SpecificName>Asian Elephant</SpecificName>
     <Icon>gaia/fauna_elephant_african_bush.png</Icon>
   </Identity>
Index: binaries/data/mods/public/simulation/templates/gaia/fauna_elephant_african_infant.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/gaia/fauna_elephant_african_infant.xml	(révision 13268)
+++ binaries/data/mods/public/simulation/templates/gaia/fauna_elephant_african_infant.xml	(copie de travail)
@@ -12,6 +12,7 @@
   <Identity>
     <Civ>gaia</Civ>
     <Classes datatype="tokens">Elephant</Classes>
+    <Species>Elephant</Species>
     <SpecificName>African Elephant (Infant)</SpecificName>
     <Icon>gaia/fauna_elephant_african_infant.png</Icon>
   </Identity>
Index: binaries/data/mods/public/simulation/templates/gaia/fauna_elephant.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/gaia/fauna_elephant.xml	(révision 13268)
+++ binaries/data/mods/public/simulation/templates/gaia/fauna_elephant.xml	(copie de travail)
@@ -11,6 +11,7 @@
   <Identity>
     <Civ>gaia</Civ>
     <Classes datatype="tokens">Elephant</Classes>
+    <Species>Elephant</Species>
     <SpecificName>Elephant</SpecificName>
     <Icon>gaia/fauna_elephant.png</Icon>
   </Identity>
Index: binaries/data/mods/public/simulation/templates/gaia/fauna_wolf_snow.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/gaia/fauna_wolf_snow.xml	(révision 13268)
+++ binaries/data/mods/public/simulation/templates/gaia/fauna_wolf_snow.xml	(copie de travail)
@@ -15,6 +15,7 @@
   </Footprint>
   <Identity>
     <Civ>gaia</Civ>
+    <Species>Canis lupus</Species>
     <SpecificName>Snow Wolf</SpecificName>
     <Icon>gaia/fauna_wolf_snow.png</Icon>
   </Identity>
Index: binaries/data/mods/public/simulation/templates/gaia/fauna_lion.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/gaia/fauna_lion.xml	(révision 13268)
+++ binaries/data/mods/public/simulation/templates/gaia/fauna_lion.xml	(copie de travail)
@@ -15,6 +15,7 @@
   </Footprint>
   <Identity>
     <Civ>gaia</Civ>
+    <Species>Panthera leo</Species>
     <SpecificName>Lion</SpecificName>
     <Icon>gaia/fauna_lion.png</Icon>
   </Identity>
Index: binaries/data/mods/public/simulation/templates/gaia/fauna_lioness.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/gaia/fauna_lioness.xml	(révision 13268)
+++ binaries/data/mods/public/simulation/templates/gaia/fauna_lioness.xml	(copie de travail)
@@ -15,6 +15,7 @@
   </Footprint>
   <Identity>
     <Civ>gaia</Civ>
+    <Species>Panthera leo</Species>
     <SpecificName>Lion</SpecificName>
     <Icon>gaia/fauna_lion.png</Icon>
   </Identity>
Index: binaries/data/mods/public/simulation/templates/gaia/fauna_elephant_north_african.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/gaia/fauna_elephant_north_african.xml	(révision 13268)
+++ binaries/data/mods/public/simulation/templates/gaia/fauna_elephant_north_african.xml	(copie de travail)
@@ -31,6 +31,7 @@
   <Identity>
     <Civ>gaia</Civ>
     <Classes datatype="tokens">Elephant</Classes>
+    <Species>Elephant</Species>
     <SpecificName>North African Elephant</SpecificName>
     <Icon>gaia/fauna_elephant_north_african.png</Icon>
   </Identity>
Index: binaries/data/mods/public/simulation/templates/gaia/fauna_elephant_african_bush.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/gaia/fauna_elephant_african_bush.xml	(révision 13268)
+++ binaries/data/mods/public/simulation/templates/gaia/fauna_elephant_african_bush.xml	(copie de travail)
@@ -44,6 +44,7 @@
   <Identity>
     <Civ>gaia</Civ>
     <Classes datatype="tokens">Elephant</Classes>
+    <Species>Elephant</Species>
     <SpecificName>African Bush Elephant</SpecificName>
     <Icon>gaia/fauna_elephant_african_bush.png</Icon>
   </Identity>
Index: binaries/data/mods/public/simulation/components/Identity.js
===================================================================
--- binaries/data/mods/public/simulation/components/Identity.js	(révision 13268)
+++ binaries/data/mods/public/simulation/components/Identity.js	(copie de travail)
@@ -15,6 +15,11 @@
 		"<text/>" +
 	"</element>" +
 	"<optional>" +
+		"<element name='Species' a:help='Species this unit belongs to'>" +
+			"<text/>" +
+		"</element>" +
+	"</optional>" +
+	"<optional>" +
 		"<element name='SpecificName' a:help='Specific native-language name for this unit type'>" +
 			"<text/>" +
 		"</element>" +
Index: binaries/data/mods/public/simulation/components/UnitAI.js
===================================================================
--- binaries/data/mods/public/simulation/components/UnitAI.js	(révision 13268)
+++ binaries/data/mods/public/simulation/components/UnitAI.js	(copie de travail)
@@ -333,9 +333,7 @@
 	},
 
 	"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);
-		var ok = this.MoveToTargetRangeExplicit(this.order.data.target, distance, -1);
+		var ok = this.PerformFlee(this.order.data);
 		if (ok)
 		{
 			// We've started fleeing from the given target
@@ -345,11 +343,7 @@
 				this.SetNextState("INDIVIDUAL.FLEEING");
 		}
 		else
-		{
-			// We are already at the target, or can't move at all
-			this.StopMoving();
 			this.FinishOrder();
-		}
 	},
 
 	"Order.Attack": function(msg) {
@@ -1342,6 +1336,8 @@
 						// Return to our original position
 						if (this.GetStance().respondHoldGround)
 							this.WalkToHeldPosition();
+						else if (this.IsAnimal())
+							this.SetNextState("ROAMING");
 					}
 				},
 
@@ -1526,6 +1522,8 @@
 						// Return to our original position
 						if (this.GetStance().respondHoldGround)
 							this.WalkToHeldPosition();
+						else if (this.IsAnimal())
+							this.SetNextState("ROAMING");
 					}
 				},
 
@@ -2256,11 +2254,13 @@
 			    this.template.NaturalBehaviour == "passive")
 			{
 				this.Flee(msg.data.attacker, false);
+				this.NearbyAnimalsRespondToAttack(msg.data.attacker);
 			}
 			else if (this.IsDangerousAnimal() || this.template.NaturalBehaviour == "defensive")
 			{
 				if (this.CanAttack(msg.data.attacker))
 					this.Attack(msg.data.attacker, false);
+				this.NearbyAnimalsRespondToAttack(msg.data.attacker);
 			}
 			else if (this.template.NaturalBehaviour == "domestic")
 			{
@@ -2339,6 +2339,14 @@
 
 			"leave": function() {
 				this.StopTimer();
+				// If not yet done, setup heldPosition for wild animals
+				// (initialize here as animals start in feeding state)
+				if (!this.IsDomestic() && !this.heldPosition)
+				{
+					var cmpPosition = Engine.QueryInterface(this.entity, IID_Position);
+					if (cmpPosition && cmpPosition.IsInWorld())
+						this.heldPosition = cmpPosition.GetPosition();
+				}
 			},
 
 			"LosRangeUpdate": function(msg) {
@@ -3408,7 +3416,7 @@
 
 	if (this.GetStance().respondFlee)
 	{
-		this.PushOrderFront("Flee", { "target": ents[0], "force": false });
+		this.PushOrderFront("Flee", { "attacker": ents[0], "target": undefined, "force": false });
 		return true;
 	}
 
@@ -3895,15 +3903,81 @@
 };
 
 /**
- * Adds flee order to the queue, not forced, so it can be
- * interrupted by attacks.
+ * Adds flee order to the queue, not forced, so it can be interrupted by attacks.
+ * (target is undefined when the unit is itself the target of the attack)
  */
-UnitAI.prototype.Flee = function(target, queued)
+UnitAI.prototype.Flee = function(attacker, queued, target)
 {
-	this.AddOrder("Flee", { "target": target, "force": false }, queued);
+	this.AddOrder("Flee", { "attacker": attacker, "target": target, "force": false }, queued);
 };
 
 /**
+ * Internal function to abstract the details of the runaway
+ * data.attacker is the attacker
+ * data.target is the real target of the attack when it is not the unit itself, otherwise undefined
+ */
+UnitAI.prototype.PerformFlee = function(data)
+{
+	var cmpPosition = Engine.QueryInterface(this.entity, IID_Position);
+	if (!cmpPosition || !cmpPosition.IsInWorld())
+		return false;
+	var cmpAttackerPosition = Engine.QueryInterface(data.attacker, IID_Position);
+	if (!cmpAttackerPosition || !cmpAttackerPosition.IsInWorld())
+		return false;
+
+	var pos = cmpPosition.GetPosition();
+	var posAttacker = cmpAttackerPosition.GetPosition();
+	var dx = posAttacker.x - pos.x;
+	var dz = posAttacker.z - pos.z;
+	var fleeDistance = (+this.template.FleeDistance);
+	// allow for a small random flee direction :
+	// take a distribution flat in sin between about -pi/4 and pi/4
+	// and approximate cos by 1-sin*sin/2 for these small angles
+	var sinmin = -0.7;
+	var sinmax =  0.7;
+	var sina = sinmin + (sinmax-sinmin)*Math.random();
+	var cosa = 1 - sina*sina/2;
+	if (data.target)
+	{
+		// if the unit is not the target of the attack, it will escape preferentially on the left
+		// or on the right depending on its position with respect to the target,
+		// and its fleeDistance will be reduced.
+		var cmpTargetUnitAI = Engine.QueryInterface(data.target, IID_UnitAI);
+		var cmpTargetPosition = Engine.QueryInterface(data.target, IID_Position);
+		if (cmpTargetUnitAI && cmpTargetPosition && cmpTargetPosition.IsInWorld())
+		{
+			var posTarget = cmpTargetPosition.GetPosition();
+			var ex = posAttacker.x - posTarget.x;
+			var ez = posAttacker.z - posTarget.z;
+			var crossprod = ex*dz - ez*dx;
+			var cosn = cosa;
+			if (crossprod > 0)
+			{
+				cosa = 0.866*cosn - 0.500*sina;
+				sina = 0.866*sina + 0.500*cosn;
+			}
+			else
+			{
+				cosa = 0.866*cosn + 0.500*sina;
+				sina = 0.866*sina - 0.500*cosn;
+			}
+			// Decrease the fleeDistance according to the distance between the unit and the target
+			var fleeInfluence = (+cmpTargetUnitAI.template.FleeDistance);
+			ex = posTarget.x - pos.x;
+			ez = posTarget.z - pos.z;
+			fleeDistance *= (1 - Math.min(1, (ex*ex+ez*ez)/(fleeInfluence*fleeInfluence)));
+		}
+	}
+	var tx = cosa*dx - sina*dz + pos.x;
+	var tz = sina*dx + cosa*dz + pos.z;
+	// We add the distance between the unit and the attacker to account for ranged attacks
+       	var distance = fleeDistance + Math.sqrt(dx*dx + dz*dz);
+       	var ok = this.MoveToPointRange(tx, tz, distance, -1);
+       	if (!ok) this.StopMoving();
+       	return ok;
+};
+
+/**
  * Adds cheer order to the queue. Forced so it won't be interrupted by attacks.
  */
 UnitAI.prototype.Cheer = function()
@@ -4327,24 +4401,35 @@
 
 UnitAI.prototype.MoveRandomly = function(distance)
 {
-	// We want to walk in a random direction, but avoid getting stuck
+	var cmpPosition = Engine.QueryInterface(this.entity, IID_Position);
+	if (!cmpPosition || !cmpPosition.IsInWorld())
+		return;
+	var pos = cmpPosition.GetPosition();
+
+	// If we are far away from our starting position, we try to move closer
+	// with a probability increasing with the distance
+	if (this.heldPosition)
+	{
+		var dx = this.heldPosition.x - pos.x;
+		var dz = this.heldPosition.z - pos.z;
+		var dist = Math.sqrt(dx*dx + dz*dz);
+		var proba = RandomInt(0, 100);
+		if (20*dist/distance > Math.max(proba,30))
+		{
+			dist -= distance;
+			var cmpMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
+			if (!cmpMotion.MoveToPointRange(this.heldPosition.x, this.heldPosition.z, dist, dist))
+				this.heldPosition = pos;
+			return;
+		}
+	}
+
+	// Otherwise we want to walk in a random direction, but avoid getting stuck
 	// in obstacles or narrow spaces.
 	// So pick a circular range from approximately our current position,
 	// and move outwards to the nearest point on that circle, which will
 	// lead to us avoiding obstacles and moving towards free space.
 
-	// TODO: we probably ought to have a 'home' point, and drift towards
-	// that, so we don't spread out all across the whole map
-
-	var cmpPosition = Engine.QueryInterface(this.entity, IID_Position);
-	if (!cmpPosition)
-		return;
-
-	if (!cmpPosition.IsInWorld())
-		return;
-
-	var pos = cmpPosition.GetPosition();
-
 	var jitter = 0.5;
 
 	// Randomly adjust the range's center a bit, so we tend to prefer
@@ -4385,4 +4470,38 @@
 	);
 };
 
+/**
+ * All wild animals in a given distance respond to the attack :
+ * If skittish behaviour, they flee
+ * If defensive behaviour, they attack if it is a member of their species which is involved
+ */
+UnitAI.prototype.NearbyAnimalsRespondToAttack = function(attacker)
+{
+	var cmpOwnIdentity = Engine.QueryInterface(this.entity, IID_Identity);
+	var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
+	// Get other animals ; range = fleeDistance, players = [0] (gaia only)
+	var range = (+this.template.FleeDistance);
+	var nearby = cmpRangeManager.ExecuteQuery(this.entity, 0, range, [0], IID_UnitAI);
+	for each (var ent in nearby)
+	{
+		var cmpUnitAI = Engine.QueryInterface(ent, IID_UnitAI);
+		if (cmpUnitAI.template.NaturalBehaviour == "skittish" ||
+		    cmpUnitAI.template.NaturalBehaviour == "passive")
+		{
+			cmpUnitAI.Flee(attacker, false, this.entity);
+		}
+		else if (cmpOwnIdentity && cmpOwnIdentity.template.Species &&
+		         (cmpUnitAI.IsDangerousAnimal() || cmpUnitAI.template.NaturalBehaviour == "defensive"))
+		{
+			var cmpIdentity = Engine.QueryInterface(ent, IID_Identity);
+			if (cmpIdentity && cmpIdentity.template.Species &&
+			    cmpIdentity.template.Species == cmpOwnIdentity.template.Species)
+			{
+				if(cmpUnitAI.CanAttack(attacker))
+					cmpUnitAI.Attack(attacker, false);
+			}
+		}
+	}
+};
+
 Engine.RegisterComponentType(IID_UnitAI, "UnitAI", UnitAI);
