Ticket #1724: collectiveBehavior-v4.diff
File collectiveBehavior-v4.diff, 14.6 KB (added by , 11 years ago) |
---|
-
binaries/data/mods/public/simulation/templates/gaia/fauna_wolf.xml
15 15 </Footprint> 16 16 <Identity> 17 17 <Civ>gaia</Civ> 18 <Species>Canis lupus</Species> 18 19 <SpecificName>Wolf</SpecificName> 19 20 <Icon>gaia/fauna_wolf.png</Icon> 20 21 </Identity> -
binaries/data/mods/public/simulation/templates/gaia/fauna_elephant_asian.xml
44 44 <Identity> 45 45 <Civ>gaia</Civ> 46 46 <Classes datatype="tokens">Elephant</Classes> 47 <Species>Elephant</Species> 47 48 <SpecificName>Asian Elephant</SpecificName> 48 49 <Icon>gaia/fauna_elephant_african_bush.png</Icon> 49 50 </Identity> -
binaries/data/mods/public/simulation/templates/gaia/fauna_elephant_african_infant.xml
12 12 <Identity> 13 13 <Civ>gaia</Civ> 14 14 <Classes datatype="tokens">Elephant</Classes> 15 <Species>Elephant</Species> 15 16 <SpecificName>African Elephant (Infant)</SpecificName> 16 17 <Icon>gaia/fauna_elephant_african_infant.png</Icon> 17 18 </Identity> -
binaries/data/mods/public/simulation/templates/gaia/fauna_elephant.xml
11 11 <Identity> 12 12 <Civ>gaia</Civ> 13 13 <Classes datatype="tokens">Elephant</Classes> 14 <Species>Elephant</Species> 14 15 <SpecificName>Elephant</SpecificName> 15 16 <Icon>gaia/fauna_elephant.png</Icon> 16 17 </Identity> -
binaries/data/mods/public/simulation/templates/gaia/fauna_wolf_snow.xml
15 15 </Footprint> 16 16 <Identity> 17 17 <Civ>gaia</Civ> 18 <Species>Canis lupus</Species> 18 19 <SpecificName>Snow Wolf</SpecificName> 19 20 <Icon>gaia/fauna_wolf_snow.png</Icon> 20 21 </Identity> -
binaries/data/mods/public/simulation/templates/gaia/fauna_lion.xml
15 15 </Footprint> 16 16 <Identity> 17 17 <Civ>gaia</Civ> 18 <Species>Panthera leo</Species> 18 19 <SpecificName>Lion</SpecificName> 19 20 <Icon>gaia/fauna_lion.png</Icon> 20 21 </Identity> -
binaries/data/mods/public/simulation/templates/gaia/fauna_lioness.xml
15 15 </Footprint> 16 16 <Identity> 17 17 <Civ>gaia</Civ> 18 <Species>Panthera leo</Species> 18 19 <SpecificName>Lion</SpecificName> 19 20 <Icon>gaia/fauna_lion.png</Icon> 20 21 </Identity> -
binaries/data/mods/public/simulation/templates/gaia/fauna_elephant_north_african.xml
31 31 <Identity> 32 32 <Civ>gaia</Civ> 33 33 <Classes datatype="tokens">Elephant</Classes> 34 <Species>Elephant</Species> 34 35 <SpecificName>North African Elephant</SpecificName> 35 36 <Icon>gaia/fauna_elephant_north_african.png</Icon> 36 37 </Identity> -
binaries/data/mods/public/simulation/templates/gaia/fauna_elephant_african_bush.xml
44 44 <Identity> 45 45 <Civ>gaia</Civ> 46 46 <Classes datatype="tokens">Elephant</Classes> 47 <Species>Elephant</Species> 47 48 <SpecificName>African Bush Elephant</SpecificName> 48 49 <Icon>gaia/fauna_elephant_african_bush.png</Icon> 49 50 </Identity> -
binaries/data/mods/public/simulation/components/Identity.js
15 15 "<text/>" + 16 16 "</element>" + 17 17 "<optional>" + 18 "<element name='Species' a:help='Species this unit belongs to'>" + 19 "<text/>" + 20 "</element>" + 21 "</optional>" + 22 "<optional>" + 18 23 "<element name='SpecificName' a:help='Specific native-language name for this unit type'>" + 19 24 "<text/>" + 20 25 "</element>" + -
binaries/data/mods/public/simulation/components/UnitAI.js
333 333 }, 334 334 335 335 "Order.Flee": function(msg) { 336 // We use the distance between the enities to account for ranged attacks 337 var distance = DistanceBetweenEntities(this.entity, this.order.data.target) + (+this.template.FleeDistance); 338 var ok = this.MoveToTargetRangeExplicit(this.order.data.target, distance, -1); 336 var ok = this.PerformFlee(this.order.data); 339 337 if (ok) 340 338 { 341 339 // We've started fleeing from the given target … … 345 343 this.SetNextState("INDIVIDUAL.FLEEING"); 346 344 } 347 345 else 348 {349 // We are already at the target, or can't move at all350 this.StopMoving();351 346 this.FinishOrder(); 352 }353 347 }, 354 348 355 349 "Order.Attack": function(msg) { … … 1342 1336 // Return to our original position 1343 1337 if (this.GetStance().respondHoldGround) 1344 1338 this.WalkToHeldPosition(); 1339 else if (this.IsAnimal()) 1340 this.SetNextState("ROAMING"); 1345 1341 } 1346 1342 }, 1347 1343 … … 1526 1522 // Return to our original position 1527 1523 if (this.GetStance().respondHoldGround) 1528 1524 this.WalkToHeldPosition(); 1525 else if (this.IsAnimal()) 1526 this.SetNextState("ROAMING"); 1529 1527 } 1530 1528 }, 1531 1529 … … 2256 2254 this.template.NaturalBehaviour == "passive") 2257 2255 { 2258 2256 this.Flee(msg.data.attacker, false); 2257 this.NearbyAnimalsRespondToAttack(msg.data.attacker); 2259 2258 } 2260 2259 else if (this.IsDangerousAnimal() || this.template.NaturalBehaviour == "defensive") 2261 2260 { 2262 2261 if (this.CanAttack(msg.data.attacker)) 2263 2262 this.Attack(msg.data.attacker, false); 2263 this.NearbyAnimalsRespondToAttack(msg.data.attacker); 2264 2264 } 2265 2265 else if (this.template.NaturalBehaviour == "domestic") 2266 2266 { … … 2339 2339 2340 2340 "leave": function() { 2341 2341 this.StopTimer(); 2342 // If not yet done, setup heldPosition for wild animals 2343 // (initialize here as animals start in feeding state) 2344 if (!this.IsDomestic() && !this.heldPosition) 2345 { 2346 var cmpPosition = Engine.QueryInterface(this.entity, IID_Position); 2347 if (cmpPosition && cmpPosition.IsInWorld()) 2348 this.heldPosition = cmpPosition.GetPosition(); 2349 } 2342 2350 }, 2343 2351 2344 2352 "LosRangeUpdate": function(msg) { … … 3408 3416 3409 3417 if (this.GetStance().respondFlee) 3410 3418 { 3411 this.PushOrderFront("Flee", { " target": ents[0], "force": false });3419 this.PushOrderFront("Flee", { "attacker": ents[0], "target": undefined, "force": false }); 3412 3420 return true; 3413 3421 } 3414 3422 … … 3895 3903 }; 3896 3904 3897 3905 /** 3898 * Adds flee order to the queue, not forced, so it can be 3899 * interrupted by attacks.3906 * Adds flee order to the queue, not forced, so it can be interrupted by attacks. 3907 * (target is undefined when the unit is itself the target of the attack) 3900 3908 */ 3901 UnitAI.prototype.Flee = function( target, queued)3909 UnitAI.prototype.Flee = function(attacker, queued, target) 3902 3910 { 3903 this.AddOrder("Flee", { " target": target, "force": false }, queued);3911 this.AddOrder("Flee", { "attacker": attacker, "target": target, "force": false }, queued); 3904 3912 }; 3905 3913 3906 3914 /** 3915 * Internal function to abstract the details of the runaway 3916 * data.attacker is the attacker 3917 * data.target is the real target of the attack when it is not the unit itself, otherwise undefined 3918 */ 3919 UnitAI.prototype.PerformFlee = function(data) 3920 { 3921 var cmpPosition = Engine.QueryInterface(this.entity, IID_Position); 3922 if (!cmpPosition || !cmpPosition.IsInWorld()) 3923 return false; 3924 var cmpAttackerPosition = Engine.QueryInterface(data.attacker, IID_Position); 3925 if (!cmpAttackerPosition || !cmpAttackerPosition.IsInWorld()) 3926 return false; 3927 3928 var pos = cmpPosition.GetPosition(); 3929 var posAttacker = cmpAttackerPosition.GetPosition(); 3930 var dx = posAttacker.x - pos.x; 3931 var dz = posAttacker.z - pos.z; 3932 var fleeDistance = (+this.template.FleeDistance); 3933 // allow for a small random flee direction : 3934 // take a distribution flat in sin between about -pi/4 and pi/4 3935 // and approximate cos by 1-sin*sin/2 for these small angles 3936 var sinmin = -0.7; 3937 var sinmax = 0.7; 3938 var sina = sinmin + (sinmax-sinmin)*Math.random(); 3939 var cosa = 1 - sina*sina/2; 3940 if (data.target) 3941 { 3942 // if the unit is not the target of the attack, it will escape preferentially on the left 3943 // or on the right depending on its position with respect to the target, 3944 // and its fleeDistance will be reduced. 3945 var cmpTargetUnitAI = Engine.QueryInterface(data.target, IID_UnitAI); 3946 var cmpTargetPosition = Engine.QueryInterface(data.target, IID_Position); 3947 if (cmpTargetUnitAI && cmpTargetPosition && cmpTargetPosition.IsInWorld()) 3948 { 3949 var posTarget = cmpTargetPosition.GetPosition(); 3950 var ex = posAttacker.x - posTarget.x; 3951 var ez = posAttacker.z - posTarget.z; 3952 var crossprod = ex*dz - ez*dx; 3953 var cosn = cosa; 3954 if (crossprod > 0) 3955 { 3956 cosa = 0.866*cosn - 0.500*sina; 3957 sina = 0.866*sina + 0.500*cosn; 3958 } 3959 else 3960 { 3961 cosa = 0.866*cosn + 0.500*sina; 3962 sina = 0.866*sina - 0.500*cosn; 3963 } 3964 // Decrease the fleeDistance according to the distance between the unit and the target 3965 var fleeInfluence = (+cmpTargetUnitAI.template.FleeDistance); 3966 ex = posTarget.x - pos.x; 3967 ez = posTarget.z - pos.z; 3968 fleeDistance *= (1 - Math.min(1, (ex*ex+ez*ez)/(fleeInfluence*fleeInfluence))); 3969 } 3970 } 3971 var tx = cosa*dx - sina*dz + pos.x; 3972 var tz = sina*dx + cosa*dz + pos.z; 3973 // We add the distance between the unit and the attacker to account for ranged attacks 3974 var distance = fleeDistance + Math.sqrt(dx*dx + dz*dz); 3975 var ok = this.MoveToPointRange(tx, tz, distance, -1); 3976 if (!ok) this.StopMoving(); 3977 return ok; 3978 }; 3979 3980 /** 3907 3981 * Adds cheer order to the queue. Forced so it won't be interrupted by attacks. 3908 3982 */ 3909 3983 UnitAI.prototype.Cheer = function() … … 4327 4401 4328 4402 UnitAI.prototype.MoveRandomly = function(distance) 4329 4403 { 4330 // We want to walk in a random direction, but avoid getting stuck 4404 var cmpPosition = Engine.QueryInterface(this.entity, IID_Position); 4405 if (!cmpPosition || !cmpPosition.IsInWorld()) 4406 return; 4407 var pos = cmpPosition.GetPosition(); 4408 4409 // If we are far away from our starting position, we try to move closer 4410 // with a probability increasing with the distance 4411 if (this.heldPosition) 4412 { 4413 var dx = this.heldPosition.x - pos.x; 4414 var dz = this.heldPosition.z - pos.z; 4415 var dist = Math.sqrt(dx*dx + dz*dz); 4416 var proba = RandomInt(0, 100); 4417 if (20*dist/distance > Math.max(proba,30)) 4418 { 4419 dist -= distance; 4420 var cmpMotion = Engine.QueryInterface(this.entity, IID_UnitMotion); 4421 if (!cmpMotion.MoveToPointRange(this.heldPosition.x, this.heldPosition.z, dist, dist)) 4422 this.heldPosition = pos; 4423 return; 4424 } 4425 } 4426 4427 // Otherwise we want to walk in a random direction, but avoid getting stuck 4331 4428 // in obstacles or narrow spaces. 4332 4429 // So pick a circular range from approximately our current position, 4333 4430 // and move outwards to the nearest point on that circle, which will 4334 4431 // lead to us avoiding obstacles and moving towards free space. 4335 4432 4336 // TODO: we probably ought to have a 'home' point, and drift towards4337 // that, so we don't spread out all across the whole map4338 4339 var cmpPosition = Engine.QueryInterface(this.entity, IID_Position);4340 if (!cmpPosition)4341 return;4342 4343 if (!cmpPosition.IsInWorld())4344 return;4345 4346 var pos = cmpPosition.GetPosition();4347 4348 4433 var jitter = 0.5; 4349 4434 4350 4435 // Randomly adjust the range's center a bit, so we tend to prefer … … 4385 4470 ); 4386 4471 }; 4387 4472 4473 /** 4474 * All wild animals in a given distance respond to the attack : 4475 * If skittish behaviour, they flee 4476 * If defensive behaviour, they attack if it is a member of their species which is involved 4477 */ 4478 UnitAI.prototype.NearbyAnimalsRespondToAttack = function(attacker) 4479 { 4480 var cmpOwnIdentity = Engine.QueryInterface(this.entity, IID_Identity); 4481 var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager); 4482 // Get other animals ; range = fleeDistance, players = [0] (gaia only) 4483 var range = (+this.template.FleeDistance); 4484 var nearby = cmpRangeManager.ExecuteQuery(this.entity, 0, range, [0], IID_UnitAI); 4485 for each (var ent in nearby) 4486 { 4487 var cmpUnitAI = Engine.QueryInterface(ent, IID_UnitAI); 4488 if (cmpUnitAI.template.NaturalBehaviour == "skittish" || 4489 cmpUnitAI.template.NaturalBehaviour == "passive") 4490 { 4491 cmpUnitAI.Flee(attacker, false, this.entity); 4492 } 4493 else if (cmpOwnIdentity && cmpOwnIdentity.template.Species && 4494 (cmpUnitAI.IsDangerousAnimal() || cmpUnitAI.template.NaturalBehaviour == "defensive")) 4495 { 4496 var cmpIdentity = Engine.QueryInterface(ent, IID_Identity); 4497 if (cmpIdentity && cmpIdentity.template.Species && 4498 cmpIdentity.template.Species == cmpOwnIdentity.template.Species) 4499 { 4500 if(cmpUnitAI.CanAttack(attacker)) 4501 cmpUnitAI.Attack(attacker, false); 4502 } 4503 } 4504 } 4505 }; 4506 4388 4507 Engine.RegisterComponentType(IID_UnitAI, "UnitAI", UnitAI);