Ticket #2010: buildingAI.diff

File buildingAI.diff, 18.6 KB (added by sanderd17, 11 years ago)
  • binaries/data/mods/public/simulation/components/BuildingAI.js

     
    11//Number of rounds of firing per 2 seconds
    22const roundCount = 10;
    33const timerInterval = 2000 / roundCount;
     4const attackType = "Ranged";
    45
    56function BuildingAI() {}
    67
     
    1011    "</element>" +
    1112    "<element name='GarrisonArrowMultiplier'>" +
    1213        "<ref name='nonNegativeDecimal'/>" +
     14    "</element>" +
     15    "<element name='GarrisonArrowClasses'>" +
     16        "<attribute name='datatype'>" +
     17            "<value>tokens</value>" +
     18        "</attribute>" +
     19        "<text/>" +
    1320    "</element>";
    1421
    1522/**
     
    1926{
    2027    if (this.GetDefaultArrowCount() > 0 || this.GetGarrisonArrowMultiplier() > 0)
    2128    {
    22         var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
    2329        this.currentRound = 0;
    2430        //Arrows left to fire
    2531        this.arrowsLeft = 0;
    26         this.timer = cmpTimer.SetTimeout(this.entity, IID_BuildingAI, "FireArrows", timerInterval, {});
    2732        this.targetUnits = [];
    2833    }
    2934};
     
    96101            players.push(i);
    97102    }
    98103    var cmpAttack = Engine.QueryInterface(this.entity, IID_Attack);
    99     if (cmpAttack)
    100     {
    101         var range = cmpAttack.GetRange("Ranged");
    102         this.enemyUnitsQuery = cmpRangeManager.CreateActiveQuery(this.entity, range.min, range.max, players, IID_DamageReceiver, cmpRangeManager.GetEntityFlagMask("normal"));
    103         cmpRangeManager.EnableActiveQuery(this.enemyUnitsQuery);
    104     }
     104    if (!cmpAttack)
     105        return;
     106   
     107    var range = cmpAttack.GetRange(attackType);
     108    this.enemyUnitsQuery = cmpRangeManager.CreateActiveQuery(this.entity, range.min, range.max, players, IID_DamageReceiver, cmpRangeManager.GetEntityFlagMask("normal"));
     109    cmpRangeManager.EnableActiveQuery(this.enemyUnitsQuery);
    105110};
    106111
    107112// Set up a range query for Gaia units within LOS range which can be attacked.
     
    111116    var cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership);
    112117    var owner = cmpOwnership.GetOwner();
    113118
    114     var rangeMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
     119    var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
    115120    var playerMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager);
    116121
    117122    if (this.gaiaUnitsQuery)
    118123    {
    119         rangeMan.DestroyActiveQuery(this.gaiaUnitsQuery);
     124        cmpRangeManager.DestroyActiveQuery(this.gaiaUnitsQuery);
    120125        this.gaiaUnitsQuery = undefined;
    121126    }
    122127
     
    128133        return;
    129134
    130135    var cmpAttack = Engine.QueryInterface(this.entity, IID_Attack);
    131     if (cmpAttack)
    132     {
    133         var range = cmpAttack.GetRange("Ranged");
     136    if (!cmpAttack)
     137        return;
     138   
     139    var range = cmpAttack.GetRange(attackType);
    134140
    135         // This query is only interested in Gaia entities that can attack.
    136         this.gaiaUnitsQuery = rangeMan.CreateActiveQuery(this.entity, range.min, range.max, [0], IID_Attack, rangeMan.GetEntityFlagMask("normal"));
    137         rangeMan.EnableActiveQuery(this.gaiaUnitsQuery);
    138     }
     141    // This query is only interested in Gaia entities that can attack.
     142    this.gaiaUnitsQuery = cmpRangeManager.CreateActiveQuery(this.entity, range.min, range.max, [0], IID_Attack, cmpRangeManager.GetEntityFlagMask("normal"));
     143    cmpRangeManager.EnableActiveQuery(this.gaiaUnitsQuery);
    139144};
    140145
    141146/**
     
    143148 */
    144149BuildingAI.prototype.OnRangeUpdate = function(msg)
    145150{
     151
     152    var cmpAttack = Engine.QueryInterface(this.entity, IID_Attack);
     153    if (!cmpAttack)
     154        return;
     155
    146156    if (msg.tag == this.gaiaUnitsQuery)
    147157    {
    148158        const filter = function(e) {
     
    161171    else if (msg.tag != this.enemyUnitsQuery)
    162172        return;
    163173
     174    const restrictedClasses = cmpAttack.GetRestrictedClasses(attackType);
     175
    164176    if (msg.added.length > 0)
    165177    {
    166178        for each (var entity in msg.added)
    167179        {
    168             this.targetUnits.push(entity);
     180            var cmpIdentity = Engine.QueryInterface(entity, IID_Identity);
     181            var targetClasses = cmpIdentity.GetClassesList();
     182
     183            if (!targetClasses.some(function(c){return restrictedClasses.indexOf(c) > -1;}))
     184                this.targetUnits.push(entity);
    169185        }
    170186    }
    171187    if (msg.removed.length > 0)
    172188    {
    173189        for each (var entity in msg.removed)
    174         {
    175             this.targetUnits.splice(this.targetUnits.indexOf(entity), 1);
     190        {   
     191            var index = this.targetUnits.indexOf(entity);
     192            if (index > -1)
     193                this.targetUnits.splice(index, 1);
    176194        }
    177195    }
     196   
     197    if (!this.targetUnits.length || this.timer)
     198        return;
     199    // units entered the range, prepare to shoot   
     200    var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
     201    var attackTimers = cmpAttack.GetTimers(attackType);
     202    this.timer = cmpTimer.SetInterval(this.entity, IID_BuildingAI, "FireArrows", attackTimers.prepare, attackTimers.repeat / roundCount, {});
     203   
    178204};
    179205
    180206BuildingAI.prototype.GetDefaultArrowCount = function()
     
    189215    return ApplyTechModificationsToEntity("BuildingAI/GarrisonArrowMultiplier", arrowMult, this.entity);
    190216};
    191217
     218BuildingAI.prototype.GetGarrisonArrowClasses = function()
     219{
     220    var string = this.template.GarrisonArrowClasses._string || "";
     221    return string.split(/\s+/);
     222};
     223
    192224/**
    193225 * Returns the number of arrows which needs to be fired.
    194226 * DefaultArrowCount + Garrisoned Archers(ie., any unit capable
     
    200232    var cmpGarrisonHolder = Engine.QueryInterface(this.entity, IID_GarrisonHolder);
    201233    if (cmpGarrisonHolder)
    202234    {
    203         count += Math.round(cmpGarrisonHolder.GetGarrisonedArcherCount() * this.GetGarrisonArrowMultiplier());
     235        count += Math.round(cmpGarrisonHolder.GetGarrisonedArcherCount(this.GetGarrisonArrowClasses()) * this.GetGarrisonArrowMultiplier());
    204236    }
    205237    return count;
    206238};
    207239
    208240/**
    209  * Fires arrows. Called every N times every 2 seconds
    210  * where N is the number of Arrows
     241 * Fires arrows. Called 'roundCount' times every 'RepeatTime' seconds when there are units in the range
    211242 */
    212243BuildingAI.prototype.FireArrows = function()
    213244{
     245
     246    if (!this.targetUnits.length && this.timer)
     247    {
     248        // stop the timer
     249        var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
     250        cmpTimer.CancelTimer(this.timer);
     251        this.timer = undefined;
     252        return;
     253    }
     254
    214255    var cmpAttack = Engine.QueryInterface(this.entity, IID_Attack);
    215     if (cmpAttack)
     256    if (!cmpAttack)
     257        return;
     258
     259    var cmpUnitAI = Engine.QueryInterface(this.entity, IID_UnitAI);
     260    if (cmpUnitAI)
    216261    {
    217         var cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
    218         this.timer = cmpTimer.SetTimeout(this.entity, IID_BuildingAI, "FireArrows", timerInterval, {});
    219         var arrowsToFire = 0;
    220         if (this.currentRound > (roundCount - 1))
    221         {
    222             //Reached end of rounds. Reset count
    223             this.currentRound = 0;
    224         }
    225        
    226         if (this.currentRound == 0)
    227         {
    228             //First round. Calculate arrows to fire
    229             this.arrowsLeft = this.GetArrowCount();
    230         }
    231        
    232         if (this.currentRound == (roundCount - 1))
    233         {
    234             //Last round. Need to fire all left-over arrows
    235             arrowsToFire = this.arrowsLeft;
    236         }
     262        var orders = cmpUnitAI.GetOrders();
     263        if (orders.length && orders[0].type == 'Attack' && orders[0].data.force)
     264            // user is forcing this entity to attack a certian target
     265            var forcedTarget = +orders[0].data.target;
     266    }
     267
     268    var arrowsToFire = 0;
     269   
     270    if (this.currentRound === 0)
     271        this.arrowsLeft = this.GetArrowCount();
     272   
     273    if (this.currentRound === (roundCount - 1))
     274    {
     275        //Last round. Need to fire all left-over arrows
     276        arrowsToFire = this.arrowsLeft;
     277    }
     278    else
     279    {
     280        //Fire N arrows, 0 <= N <= Number of arrows left
     281        arrowsToFire = Math.min(
     282            Math.round(2*Math.random() * this.GetArrowCount()/roundCount), 
     283            this.arrowsLeft
     284        );
     285    }
     286   
     287    for (var i = 0; i < arrowsToFire; ++i)
     288    {
     289        var target;
     290        if (forcedTarget && this.targetUnits.indexOf(forcedTarget) > -1)
     291            // user forced to attack a certain one, and we're able to attack the target
     292            target = forcedTarget;
    237293        else
    238         {
    239             //Fire N arrows, 0 <= N <= Number of arrows left
    240             arrowsToFire = Math.floor(Math.random() * this.arrowsLeft);
    241         }
    242         if (this.targetUnits.length > 0)
    243         {
    244             for (var i = 0;i < arrowsToFire;i++)
    245             {
    246                 cmpAttack.PerformAttack("Ranged", this.targetUnits[Math.floor(Math.random() * this.targetUnits.length)]);
    247                 PlaySound("arrowfly", this.entity);
    248             }
    249             this.arrowsLeft -= arrowsToFire;
    250         }
    251         this.currentRound++;
     294            target = this.targetUnits[Math.floor(Math.random() * this.targetUnits.length)];
     295
     296        cmpAttack.PerformAttack(attackType, target);
     297        // TODO use Sound.js sounds
     298        PlaySound("arrowfly", this.entity);
    252299    }
     300    this.arrowsLeft -= arrowsToFire;
     301   
     302    this.currentRound = ++this.currentRound % roundCount;
     303   
    253304};
    254305
    255306Engine.RegisterComponentType(IID_BuildingAI, "BuildingAI", BuildingAI);
  • binaries/data/mods/public/simulation/components/GarrisonHolder.js

     
    7878 * Get number of garrisoned units capable of shooting arrows
    7979 * Not necessarily archers
    8080 */
    81 GarrisonHolder.prototype.GetGarrisonedArcherCount = function()
     81GarrisonHolder.prototype.GetGarrisonedArcherCount = function(garrisonArrowClasses)
    8282{
    8383    var count = 0;
    8484    for each (var entity in this.entities)
     
    8585    {
    8686        var cmpIdentity = Engine.QueryInterface(entity, IID_Identity);
    8787        var classes = cmpIdentity.GetClassesList();
    88         if (classes.indexOf("Infantry") != -1 || classes.indexOf("Ranged") != -1)
     88        if (classes.some(function(c){return garrisonArrowClasses.indexOf(c) > -1;}))
    8989            count++;
    9090    }
    9191    return count;
  • binaries/data/mods/public/simulation/templates/structures/brit_crannog.xml

     
    33  <BuildingAI>
    44    <DefaultArrowCount>3</DefaultArrowCount>
    55    <GarrisonArrowMultiplier>1</GarrisonArrowMultiplier>
     6    <GarrisonArrowClasses datatype="tokens">Infanty Ranged</GarrisonArrowClasses>
    67  </BuildingAI>
    78  <BuildRestrictions>
    89    <Territory>own ally neutral</Territory>
  • binaries/data/mods/public/simulation/templates/structures/rome_army_camp.xml

     
    2323      <Spread>1.5</Spread>
    2424    </Ranged>
    2525  </Attack>
    26   <BuildingAI>
    27     <DefaultArrowCount>3</DefaultArrowCount>
    28     <GarrisonArrowMultiplier>0.5</GarrisonArrowMultiplier>
    29   </BuildingAI>
    30   <BuildRestrictions>
    31     <Territory>own neutral enemy</Territory>
     26  <BuildingAI>
     27    <DefaultArrowCount>3</DefaultArrowCount>
     28    <GarrisonArrowMultiplier>0.5</GarrisonArrowMultiplier>
     29    <GarrisonArrowClasses datatype="tokens">Infanty Ranged</GarrisonArrowClasses>
     30  </BuildingAI>
     31  <BuildRestrictions>
     32    <Territory>own neutral enemy</Territory>
    3233    <Category>Fortress</Category>
    3334  </BuildRestrictions>
    3435  <Cost>
  • binaries/data/mods/public/simulation/templates/template_structure.xml

     
    1313  <BuildingAI>
    1414    <DefaultArrowCount>0</DefaultArrowCount>
    1515    <GarrisonArrowMultiplier>0</GarrisonArrowMultiplier>
     16    <GarrisonArrowClasses datatype="tokens">Infanty Ranged</GarrisonArrowClasses>
    1617  </BuildingAI>
    1718  <BuildRestrictions>
    1819    <PlacementType>land</PlacementType>
  • binaries/data/mods/public/simulation/templates/template_structure_defense_defense_tower.xml

     
    1010      <ProjectileSpeed>75.0</ProjectileSpeed>
    1111      <PrepareTime>1200</PrepareTime>
    1212      <RepeatTime>2000</RepeatTime>
     13      <RestrictedClasses datatype="tokens">Structure</RestrictedClasses>
    1314      <Spread>1.5</Spread>
    1415    </Ranged>
    1516  </Attack>
    16   <BuildingAI>
    17     <DefaultArrowCount>1</DefaultArrowCount>
    18     <GarrisonArrowMultiplier>1</GarrisonArrowMultiplier>
    19   </BuildingAI>
    20   <BuildRestrictions>
    21     <Category>DefenseTower</Category>
     17  <BuildingAI>
     18    <DefaultArrowCount>1</DefaultArrowCount>
     19    <GarrisonArrowMultiplier>1</GarrisonArrowMultiplier>
     20    <GarrisonArrowClasses datatype="tokens">Infanty Ranged</GarrisonArrowClasses>
     21  </BuildingAI>
     22  <BuildRestrictions>
     23    <Category>DefenseTower</Category>
    2224  </BuildRestrictions>
    2325  <Cost>
    2426    <BuildTime>120</BuildTime>
  • binaries/data/mods/public/simulation/templates/template_structure_defense_outpost.xml

     
    2020      <ProjectileSpeed>75.0</ProjectileSpeed>
    2121      <PrepareTime>1200</PrepareTime>
    2222      <RepeatTime>2000</RepeatTime>
     23      <RestrictedClasses datatype="tokens">Structure</RestrictedClasses>
    2324      <Spread>2.0</Spread>
    2425    </Ranged>
    2526  </Attack>
    2627  <BuildingAI>
    2728    <GarrisonArrowMultiplier>1</GarrisonArrowMultiplier>
     29    <GarrisonArrowClasses datatype="tokens">Infanty Ranged</GarrisonArrowClasses>
    2830  </BuildingAI>
    2931  <BuildRestrictions>
    3032    <Territory>own neutral</Territory>
  • binaries/data/mods/public/simulation/templates/template_structure_military_fortress.xml

     
    1010      <ProjectileSpeed>75.0</ProjectileSpeed>
    1111      <PrepareTime>1200</PrepareTime>
    1212      <RepeatTime>2000</RepeatTime>
     13      <RestrictedClasses datatype="tokens">Structure</RestrictedClasses>
    1314      <Spread>1.5</Spread>
    1415    </Ranged>
    1516  </Attack>
     
    1617  <BuildingAI>
    1718    <DefaultArrowCount>3</DefaultArrowCount>
    1819    <GarrisonArrowMultiplier>1</GarrisonArrowMultiplier>
     20    <GarrisonArrowClasses datatype="tokens">Infanty Ranged</GarrisonArrowClasses>
    1921  </BuildingAI>
    2022  <BuildRestrictions>
    2123    <Category>Fortress</Category>
  • binaries/data/mods/public/simulation/templates/template_unit_mechanical_ship_bireme.xml

     
    1616  <BuildingAI>
    1717    <DefaultArrowCount>2</DefaultArrowCount>
    1818    <GarrisonArrowMultiplier>1</GarrisonArrowMultiplier>
     19    <GarrisonArrowClasses datatype="tokens">Infanty Ranged</GarrisonArrowClasses>
    1920  </BuildingAI>
    2021  <Cost>
    2122    <Population>2</Population>
  • binaries/data/mods/public/simulation/templates/template_unit_mechanical_ship_quinquereme.xml

     
    22<Entity parent="template_unit_mechanical_ship">
    33  <Attack>
    44    <Ranged>
    5       <Hack>0.0</Hack>
    6       <Pierce>40.0</Pierce>
    7       <Crush>100.0</Crush>
     5      <Hack>30.0</Hack>
     6      <Pierce>10.0</Pierce>
     7      <Crush>30.0</Crush>
    88      <MaxRange>65.0</MaxRange>
    99      <MinRange>20.0</MinRange>
    1010      <ProjectileSpeed>40.0</ProjectileSpeed>
    11       <PrepareTime>2000</PrepareTime>
     11      <PrepareTime>5000</PrepareTime>
    1212      <RepeatTime>4000</RepeatTime>
    1313      <Spread>1.5</Spread>
    1414    </Ranged>
     
    2121      <metal>200</metal>
    2222    </Resources>
    2323  </Cost>
     24  <BuildingAI>
     25    <DefaultArrowCount>0</DefaultArrowCount>
     26    <GarrisonArrowMultiplier>0.5</GarrisonArrowMultiplier>
     27    <GarrisonArrowClasses datatype="tokens">Ballista</GarrisonArrowClasses>
     28  </BuildingAI>
    2429  <Footprint>
    2530    <Square width="12.0" depth="48.0"/>
    2631    <Height>8.0</Height>
  • binaries/data/mods/public/simulation/templates/template_unit_mechanical_ship_trireme.xml

     
    1616  <BuildingAI>
    1717    <DefaultArrowCount>3</DefaultArrowCount>
    1818    <GarrisonArrowMultiplier>1</GarrisonArrowMultiplier>
     19    <GarrisonArrowClasses datatype="tokens">Infanty Ranged</GarrisonArrowClasses>
    1920  </BuildingAI>
    2021  <Cost>
    2122    <Population>3</Population>
  • binaries/data/mods/public/simulation/templates/template_unit_mechanical_siege_onager.xml

     
    5353  <Identity>
    5454    <GenericName>Siege Catapult</GenericName>
    5555    <Tooltip>Bonused vs. Structures and Massed Infantry.</Tooltip>
     56    <Classes datatype="tokens">Ballista</Classes>
    5657  </Identity>
    5758  <Loot>
    5859    <xp>300</xp>
  • binaries/data/mods/public/simulation/templates/template_unit_mechanical_siege_tower.xml

     
    1515      <ProjectileSpeed>75.0</ProjectileSpeed>
    1616      <PrepareTime>1200</PrepareTime>
    1717      <RepeatTime>2000</RepeatTime>
     18      <RestrictedClasses datatype="tokens">Structure</RestrictedClasses>
    1819      <Spread>1.5</Spread>
    1920    </Ranged>
    2021  </Attack>
     
    2122  <BuildingAI>
    2223    <DefaultArrowCount>1</DefaultArrowCount>
    2324    <GarrisonArrowMultiplier>1</GarrisonArrowMultiplier>
     25    <GarrisonArrowClasses datatype="tokens">Infanty Ranged</GarrisonArrowClasses>
    2426  </BuildingAI>
    2527  <Cost>
    2628    <Population>5</Population>