Ticket #1724: collectiveBehavior-v3.patch

File collectiveBehavior-v3.patch, 14.6 KB (added by mimo, 11 years ago)
  • binaries/data/mods/public/simulation/templates/gaia/fauna_elephant.xml

     
    1111  <Identity>
    1212    <Civ>gaia</Civ>
    1313    <Classes datatype="tokens">Elephant</Classes>
     14    <Species>Elephant</Species>
    1415    <SpecificName>Elephant</SpecificName>
    1516    <Icon>gaia/fauna_elephant.png</Icon>
    1617  </Identity>
  • binaries/data/mods/public/simulation/templates/gaia/fauna_lion.xml

     
    1515  </Footprint>
    1616  <Identity>
    1717    <Civ>gaia</Civ>
     18    <Species>Panthera leo</Species>
    1819    <SpecificName>Lion</SpecificName>
    1920    <Icon>gaia/fauna_lion.png</Icon>
    2021  </Identity>
  • binaries/data/mods/public/simulation/templates/gaia/fauna_wolf_snow.xml

     
    1515  </Footprint>
    1616  <Identity>
    1717    <Civ>gaia</Civ>
     18    <Species>Canis lupus</Species>
    1819    <SpecificName>Snow Wolf</SpecificName>
    1920    <Icon>gaia/fauna_wolf_snow.png</Icon>
    2021  </Identity>
  • binaries/data/mods/public/simulation/templates/gaia/fauna_elephant_african_bush.xml

     
    4444  <Identity>
    4545    <Civ>gaia</Civ>
    4646    <Classes datatype="tokens">Elephant</Classes>
     47    <Species>Elephant</Species>
    4748    <SpecificName>African Bush Elephant</SpecificName>
    4849    <Icon>gaia/fauna_elephant_african_bush.png</Icon>
    4950  </Identity>
  • binaries/data/mods/public/simulation/templates/gaia/fauna_wolf.xml

     
    1515  </Footprint>
    1616  <Identity>
    1717    <Civ>gaia</Civ>
     18    <Species>Canis lupus</Species>
    1819    <SpecificName>Wolf</SpecificName>
    1920    <Icon>gaia/fauna_wolf.png</Icon>
    2021  </Identity>
  • binaries/data/mods/public/simulation/templates/gaia/fauna_elephant_north_african.xml

     
    3131  <Identity>
    3232    <Civ>gaia</Civ>
    3333    <Classes datatype="tokens">Elephant</Classes>
     34    <Species>Elephant</Species>
    3435    <SpecificName>North African Elephant</SpecificName>
    3536    <Icon>gaia/fauna_elephant_north_african.png</Icon>
    3637  </Identity>
  • binaries/data/mods/public/simulation/templates/gaia/fauna_lioness.xml

     
    1515  </Footprint>
    1616  <Identity>
    1717    <Civ>gaia</Civ>
     18    <Species>Panthera leo</Species>
    1819    <SpecificName>Lion</SpecificName>
    1920    <Icon>gaia/fauna_lion.png</Icon>
    2021  </Identity>
  • binaries/data/mods/public/simulation/templates/gaia/fauna_elephant_asian.xml

     
    4444  <Identity>
    4545    <Civ>gaia</Civ>
    4646    <Classes datatype="tokens">Elephant</Classes>
     47    <Species>Elephant</Species>
    4748    <SpecificName>Asian Elephant</SpecificName>
    4849    <Icon>gaia/fauna_elephant_african_bush.png</Icon>
    4950  </Identity>
  • binaries/data/mods/public/simulation/templates/gaia/fauna_elephant_african_infant.xml

     
    1212  <Identity>
    1313    <Civ>gaia</Civ>
    1414    <Classes datatype="tokens">Elephant</Classes>
     15    <Species>Elephant</Species>
    1516    <SpecificName>African Elephant (Infant)</SpecificName>
    1617    <Icon>gaia/fauna_elephant_african_infant.png</Icon>
    1718  </Identity>
  • binaries/data/mods/public/simulation/components/UnitAI.js

     
     1//range of influence for flee
     2const fleeInfluence = 28;  // TODO find best value
     3
    14function UnitAI() {}
    25
    36UnitAI.prototype.Schema =
     
    306309    },
    307310
    308311    "Order.Flee": function(msg) {
    309         // We use the distance between the enities to account for ranged attacks
    310         var distance = DistanceBetweenEntities(this.entity, this.order.data.target) + (+this.template.FleeDistance);
    311         var ok = this.MoveToTargetRangeExplicit(this.order.data.target, distance, -1);
     312        var ok = this.PerformFlee(this.order.data);
    312313        if (ok)
    313314        {
    314315            // We've started fleeing from the given target
     
    318319                this.SetNextState("INDIVIDUAL.FLEEING");
    319320        }
    320321        else
    321         {
    322             // We are already at the target, or can't move at all
    323             this.StopMoving();
    324322            this.FinishOrder();
    325         }
    326323    },
    327324
    328325    "Order.Attack": function(msg) {
     
    12181215                        // Return to our original position
    12191216                        if (this.GetStance().respondHoldGround)
    12201217                            this.WalkToHeldPosition();
     1218                        // If wild animal, roam in case we are far from our territory
     1219                        else if (this.territory)
     1220                            this.SetNextState("ROAMING");
    12211221                    }
    12221222                },
    12231223
     
    13971397                        // Return to our original position
    13981398                        if (this.GetStance().respondHoldGround)
    13991399                            this.WalkToHeldPosition();
     1400                        // If wild animal, roam in case we are far from our territory
     1401                        else if (this.territory)
     1402                            this.SetNextState("ROAMING");
    14001403                    }
    14011404                },
    14021405
     
    21272130                this.template.NaturalBehaviour == "passive")
    21282131            {
    21292132                this.Flee(msg.data.attacker, false);
     2133                this.NearbyAnimalsRespondToAttack(msg.data.attacker);
    21302134            }
    21312135            else if (this.IsDangerousAnimal() || this.template.NaturalBehaviour == "defensive")
    21322136            {
    21332137                if (this.CanAttack(msg.data.attacker))
    21342138                    this.Attack(msg.data.attacker, false);
     2139                this.NearbyAnimalsRespondToAttack(msg.data.attacker);
    21352140            }
    21362141            else if (this.template.NaturalBehaviour == "domestic")
    21372142            {
     
    22102215
    22112216            "leave": function() {
    22122217                this.StopTimer();
     2218                // If not yet done, setup territory (initialize here as animals start in feeding state)
     2219                this.SetUpTerritory();
    22132220            },
    22142221
    22152222            "LosRangeUpdate": function(msg) {
     
    32713278
    32723279    if (this.GetStance().respondFlee)
    32733280    {
    3274         this.PushOrderFront("Flee", { "target": ents[0], "force": false });
     3281        this.PushOrderFront("Flee", { "attacker": ents[0], "target": undefined, "force": false });
    32753282        return true;
    32763283    }
    32773284
     
    37503757/**
    37513758 * Adds flee order to the queue, not forced, so it can be
    37523759 * interrupted by attacks.
     3760 * (target is undefined when the unit is the target of the attack)
    37533761 */
    3754 UnitAI.prototype.Flee = function(target, queued)
     3762UnitAI.prototype.Flee = function(attacker, queued, target)
    37553763{
    3756     this.AddOrder("Flee", { "target": target, "force": false }, queued);
     3764    this.AddOrder("Flee", { "attacker": attacker, "target": target, "force": false }, queued);
    37573765};
    37583766
    37593767/**
     3768 * Internal function to abstract the details of the runaway
     3769 * data.attacker is the attacker
     3770 * data.target is the real target of the attack when it is not the unit itself, otherwise undefined
     3771 */
     3772UnitAI.prototype.PerformFlee = function(data)
     3773{
     3774    var cmpPosition = Engine.QueryInterface(this.entity, IID_Position);
     3775    if (!cmpPosition || !cmpPosition.IsInWorld())
     3776        return false;
     3777    var cmpAttackerPosition = Engine.QueryInterface(data.attacker, IID_Position);
     3778    if (!cmpAttackerPosition || !cmpAttackerPosition.IsInWorld())
     3779        return false;
     3780
     3781    var pos = cmpPosition.GetPosition();
     3782    var posAttacker = cmpAttackerPosition.GetPosition();
     3783    var dx = posAttacker.x - pos.x;
     3784    var dz = posAttacker.z - pos.z;
     3785    var fleeDistance = (+this.template.FleeDistance);
     3786    // allow for a small random flee direction :
     3787    // take a distribution flat in sin between about -pi/4 and pi/4
     3788    // and approximate cos by 1-sin*sin/2 for these small angles
     3789    var sinmin = -0.7;
     3790    var sinmax =  0.7;
     3791    var sina = sinmin + (sinmax-sinmin)*Math.random();
     3792    var cosa = 1 - sina*sina/2;
     3793    if (data.target)
     3794    {
     3795        // if the unit is not the target of the attack, it will escape preferentially on the left
     3796        // or on the right depending on its position with respect to the target,
     3797        // and its fleeDistance will be reduced.
     3798        var cmpTargetPosition = Engine.QueryInterface(data.target, IID_Position);
     3799        if (cmpTargetPosition && cmpTargetPosition.IsInWorld())
     3800        {
     3801            var posTarget = cmpTargetPosition.GetPosition();
     3802            var ex = posAttacker.x - posTarget.x;
     3803            var ez = posAttacker.z - posTarget.z;
     3804            var crossprod = ex*dz - ez*dx;
     3805            var cosn = cosa;
     3806            if (crossprod > 0)
     3807            {
     3808                cosa = 0.866*cosn - 0.500*sina;
     3809                sina = 0.866*sina + 0.500*cosn;
     3810            }
     3811            else
     3812            {
     3813                cosa = 0.866*cosn + 0.500*sina;
     3814                sina = 0.866*sina - 0.500*cosn;
     3815            }
     3816            // Decrease the fleeDistance according to the distance between the unit and the target
     3817            ex = posTarget.x - pos.x;
     3818            ez = posTarget.z - pos.z;
     3819            fleeDistance *= (1 - Math.min(1, (ex*ex+ez*ez)/(fleeInfluence*fleeInfluence)));
     3820        }
     3821    }
     3822    var tx = cosa*dx - sina*dz + pos.x;
     3823    var tz = sina*dx + cosa*dz + pos.z;
     3824    // We add the distance between the unit and the attacker to account for ranged attacks
     3825       var distance = fleeDistance + Math.sqrt(dx*dx + dz*dz);
     3826       var ok = this.MoveToPointRange(tx, tz, distance, -1);
     3827       if (!ok) this.StopMoving();
     3828       return ok;
     3829};
     3830
     3831/**
    37603832 * Adds cheer order to the queue. Forced so it won't be interrupted by attacks.
    37613833 */
    37623834UnitAI.prototype.Cheer = function()
     
    41804252
    41814253UnitAI.prototype.MoveRandomly = function(distance)
    41824254{
    4183     // We want to walk in a random direction, but avoid getting stuck
     4255    var cmpPosition = Engine.QueryInterface(this.entity, IID_Position);
     4256    if (!cmpPosition || !cmpPosition.IsInWorld())
     4257        return;
     4258    var pos = cmpPosition.GetPosition();
     4259
     4260    // If we are far away from our starting position, we try to move closer
     4261    // with a probability increasing with the distance
     4262    if (this.territory)
     4263    {
     4264        var dx = this.territory.x - pos.x;
     4265        var dz = this.territory.z - pos.z;
     4266        var dist = Math.sqrt(dx*dx + dz*dz);
     4267        var proba = RandomInt(0, 100);
     4268        if (20*dist/distance > Math.max(proba,30))
     4269        {
     4270            dist -= distance;
     4271            var cmpMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
     4272            if (!cmpMotion.MoveToPointRange(this.territory.x, this.territory.z, dist, dist))
     4273                this.territory = pos;
     4274            return;
     4275        }
     4276    }
     4277
     4278    // Otherwise we want to walk in a random direction, but avoid getting stuck
    41844279    // in obstacles or narrow spaces.
    41854280    // So pick a circular range from approximately our current position,
    41864281    // and move outwards to the nearest point on that circle, which will
    41874282    // lead to us avoiding obstacles and moving towards free space.
    41884283
    4189     // TODO: we probably ought to have a 'home' point, and drift towards
    4190     // that, so we don't spread out all across the whole map
    4191 
    4192     var cmpPosition = Engine.QueryInterface(this.entity, IID_Position);
    4193     if (!cmpPosition)
    4194         return;
    4195 
    4196     if (!cmpPosition.IsInWorld())
    4197         return;
    4198 
    4199     var pos = cmpPosition.GetPosition();
    4200 
    42014284    var jitter = 0.5;
    42024285
    42034286    // Randomly adjust the range's center a bit, so we tend to prefer
     
    42384321    );
    42394322};
    42404323
     4324UnitAI.prototype.SetUpTerritory = function()
     4325{
     4326    if (!this.IsAnimal() || this.IsDomestic())
     4327        return;
     4328    if (this.territory)
     4329        return;
     4330    var cmpPosition = Engine.QueryInterface(this.entity, IID_Position);
     4331    if (cmpPosition && cmpPosition.IsInWorld())
     4332        this.territory = cmpPosition.GetPosition();
     4333};
     4334
     4335/**
     4336 * All wild animals in a given distance respond to the attack :
     4337 * If skittish behaviour, they flee
     4338 * If defensive behaviour, they attack if it is a member of their species which is involved
     4339 */
     4340UnitAI.prototype.NearbyAnimalsRespondToAttack = function(attacker)
     4341{
     4342    var cmpOwnIdentity = Engine.QueryInterface(this.entity, IID_Identity);
     4343    var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
     4344    // Get other animals ; range = fleeInfluence, players = [0] (gaia only)
     4345    var nearby = cmpRangeManager.ExecuteQuery(this.entity, 0, fleeInfluence, [0], IID_UnitAI);
     4346    for each (var ent in nearby)
     4347    {
     4348        var cmpUnitAI = Engine.QueryInterface(ent, IID_UnitAI);
     4349        if (cmpUnitAI.template.NaturalBehaviour == "skittish" ||
     4350            cmpUnitAI.template.NaturalBehaviour == "passive")
     4351        {
     4352            cmpUnitAI.Flee(attacker, false, this.entity);
     4353        }
     4354        else if (cmpOwnIdentity && cmpOwnIdentity.template.Species &&
     4355                 (cmpUnitAI.IsDangerousAnimal() || cmpUnitAI.template.NaturalBehaviour == "defensive"))
     4356        {
     4357            var cmpIdentity = Engine.QueryInterface(ent, IID_Identity);
     4358            if (cmpIdentity && cmpIdentity.template.Species &&
     4359                cmpIdentity.template.Species == cmpOwnIdentity.template.Species)
     4360            {
     4361                if(cmpUnitAI.CanAttack(attacker))
     4362                    cmpUnitAI.Attack(attacker, false);
     4363            }
     4364        }
     4365    }
     4366};
     4367
    42414368Engine.RegisterComponentType(IID_UnitAI, "UnitAI", UnitAI);
  • binaries/data/mods/public/simulation/components/Identity.js

     
    1515        "<text/>" +
    1616    "</element>" +
    1717    "<optional>" +
     18        "<element name='Species' a:help='Species this unit belongs to'>" +
     19            "<text/>" +
     20        "</element>" +
     21    "</optional>" +
     22    "<optional>" +
    1823        "<element name='SpecificName' a:help='Specific native-language name for this unit type'>" +
    1924            "<text/>" +
    2025        "</element>" +