Ticket #2377: AIAuraTech.3.patch

File AIAuraTech.3.patch, 52.8 KB (added by sanderd17, 10 years ago)
  • binaries/data/mods/public/simulation/ai/aegis/attack_plan.js

     
    544544                    count++;
    545545                    if (count > 1000){
    546546                        m.debug("No target with a valid position found");
     547                        Engine.ProfileStop();
     548                        Engine.ProfileStop();
    547549                        return false;
    548550                    }
    549551                }
  • binaries/data/mods/public/simulation/ai/aegis/base-manager.js

     
    908908        }
    909909    }
    910910
    911     // auras/techs are buggy and the AI tries to repair healthy buildings.
    912     // TODO: reimplement once that's fixed.
    913     return;
    914 
    915911    // don't repair if we're still under attack, unless it's like a vital (civcentre or wall) building that's getting destroyed.
    916912    for (var i in damagedBuildings) {
    917913        var target = damagedBuildings[i];
     
    955951   
    956952    Engine.ProfileStart("Assign builders");
    957953    this.assignToFoundations(gameState);
    958     Engine.ProfileStop()
     954    Engine.ProfileStop();
    959955   
    960956    if (this.constructing && this.anchor)
    961957    {
  • binaries/data/mods/public/simulation/ai/aegis/headquarters.js

     
    11211121    this.checkEvents(gameState,events,queues);
    11221122    //this.buildMoreHouses(gameState);
    11231123
    1124     //Engine.ProfileStart("Train workers and build farms, houses. Research techs.");
    11251124    this.trainMoreWorkers(gameState, queues);
    11261125   
    11271126    // sandbox doesn't expand.
  • binaries/data/mods/public/simulation/ai/common-api/entity.js

     
    11var API3 = function(m)
    22{
    33
    4 m.EntityTemplate = m.Class({
     4// defines a template.
     5// It's completely raw data, except it's slightly cleverer now and then.
     6m.Template = m.Class({
    57
    6     // techModifications should be the tech modifications of only one player.
    7     // gamestates handle "GetTemplate" and should push the player's
    8     // while entities should push the owner's
    9     _init: function(template, techModifications)
     8    _init: function(template)
    109    {
    11         this._techModifications = techModifications;
    1210        this._template = template;
     11        this._tpCache = {};
    1312    },
    1413   
     14    // helper function to return a template value, optionally adjusting for tech.
     15    // TODO: there's no support for "_string" values here.
     16    get: function(string)
     17    {
     18        var value = this._template;
     19        if (this._auraTemplateModif && this._auraTemplateModif[string])     {
     20            return this._auraTemplateModif[string];
     21        } else if (this._techModif && this._techModif[string]) {
     22            return this._techModif[string];
     23        } else {
     24            if (this._tpCache[string] === undefined)
     25            {
     26                var args = string.split("/");
     27                for (var i = 0; i < args.length; ++i)
     28                    if (value[args[i]])
     29                        value = value[args[i]];
     30                    else
     31                    {
     32                        value = undefined;
     33                        break;
     34                    }
     35                this._tpCache[string] = value;
     36            }
     37            return this._tpCache[string];
     38        }
     39    },
     40
    1541    genericName: function() {
    16         if (!this._template.Identity || !this._template.Identity.GenericName)
     42        if (!this.get("Identity") || !this.get("Identity/GenericName"))
    1743            return undefined;
    18         return this._template.Identity.GenericName;
     44        return this.get("Identity/GenericName");
    1945    },
    20                            
     46                         
    2147    rank: function() {
    22         if (!this._template.Identity)
     48        if (!this.get("Identity"))
    2349            return undefined;
    24         return this._template.Identity.Rank;
     50        return this.get("Identity/Rank");
    2551    },
    2652
    2753    classes: function() {
    28         if (!this._template.Identity || !this._template.Identity.Classes || !this._template.Identity.Classes._string)
     54        if (!this.get("Identity") || !this.get("Identity/Classes") || !this.get("Identity/Classes/_string"))
    2955            return undefined;
    30         return this._template.Identity.Classes._string.split(/\s+/);
     56        return this.get("Identity/Classes/_string").split(/\s+/);
    3157    },
    3258   
    3359    requiredTech: function() {
    34         if (!this._template.Identity || !this._template.Identity.RequiredTechnology)
    35             return undefined;
    36         return this._template.Identity.RequiredTechnology;
     60        return this.get("Identity/RequiredTechnology");
    3761    },
    38                            
     62                         
    3963    available: function(gameState) {
    40         if (!this._template.Identity || !this._template.Identity.RequiredTechnology)
    41             return true;
    42         return gameState.isResearched(this._template.Identity.RequiredTechnology);
     64        return gameState.isResearched(this.get("Identity/RequiredTechnology"));
    4365    },
    44                            
     66                         
    4567    // specifically
    4668    phase: function() {
    47         if (!this._template.Identity || !this._template.Identity.RequiredTechnology)
     69        if (!this.get("Identity/RequiredTechnology"))
    4870            return 0;
    49         if (this.template.Identity.RequiredTechnology == "phase_village")
     71        if (this.get("Identity/RequiredTechnology") == "phase_village")
    5072            return 1;
    51         if (this.template.Identity.RequiredTechnology == "phase_town")
     73        if (this.get("Identity/RequiredTechnology") == "phase_town")
    5274            return 2;
    53         if (this.template.Identity.RequiredTechnology == "phase_city")
     75        if (this.get("Identity/RequiredTechnology") == "phase_city")
    5476            return 3;
    5577        return 0;
    5678    },
     
    7294    },
    7395
    7496    civ: function() {
    75         if (!this._template.Identity)
    76             return undefined;
    77         return this._template.Identity.Civ;
     97        return this.get("Identity/Civ");
    7898    },
    7999
    80100    cost: function() {
    81         if (!this._template.Cost)
     101        if (!this.get("Cost"))
    82102            return undefined;
    83103
    84104        var ret = {};
    85         for (var type in this._template.Cost.Resources)
    86             ret[type] = GetTechModifiedProperty(this._techModifications, this._template, "Cost/Resources/"+type, +this._template.Cost.Resources[type]);
     105        for (var type in this.get("Cost/Resources"))
     106            ret[type] = +this.get("Cost/Resources/" + type);
    87107        return ret;
    88108    },
    89109   
    90110    costSum: function() {
    91         if (!this._template.Cost)
     111        if (!this.get("Cost"))
    92112            return undefined;
    93113       
    94114        var ret = 0;
    95         for (var type in this._template.Cost.Resources)
    96             ret += +this._template.Cost.Resources[type];
     115        for (var type in this.get("Cost/Resources"))
     116            ret += +this.get("Cost/Resources/" + type);
    97117        return ret;
    98118    },
    99119
     
    102122     * obstruction shape, or undefined if no obstruction.
    103123     */
    104124    obstructionRadius: function() {
    105         if (!this._template.Obstruction)
     125        if (!this.get("Obstruction"))
    106126            return undefined;
    107127
    108         if (this._template.Obstruction.Static)
     128        if (this.get("Obstruction/Static"))
    109129        {
    110             var w = +this._template.Obstruction.Static["@width"];
    111             var h = +this._template.Obstruction.Static["@depth"];
     130            var w = +this.get("Obstruction/Static/@width");
     131            var h = +this.get("Obstruction/Static/@depth");
    112132            return Math.sqrt(w*w + h*h) / 2;
    113133        }
    114134
    115         if (this._template.Obstruction.Unit)
    116             return +this._template.Obstruction.Unit["@radius"];
     135        if (this.get("Obstruction/Unit"))
     136            return +this.get("Obstruction/Unit/@radius");
    117137
    118138        return 0; // this should never happen
    119139    },
    120                            
     140                         
    121141    /**
    122142     * Returns the radius of a circle surrounding this entity's
    123143     * footprint.
    124144     */
    125145    footprintRadius: function() {
    126         if (!this._template.Footprint)
     146        if (!this.get("Footprint"))
    127147            return undefined;
    128148       
    129         if (this._template.Footprint.Square)
     149        if (this.get("Footprint/Square"))
    130150        {
    131             var w = +this._template.Footprint.Square["@width"];
    132             var h = +this._template.Footprint.Square["@depth"];
     151            var w = +this.get("Footprint/Square/@width");
     152            var h = +this.get("Footprint/Square/@depth");
    133153            return Math.sqrt(w*w + h*h) / 2;
    134154        }
    135155       
    136         if (this._template.Footprint.Circle)
    137             return +this._template.Footprint.Circle["@radius"];
     156        if (this.get("Footprint/Circle"))
     157            return +this.get("Footprint/Circle/@radius");
    138158       
    139159        return 0; // this should never happen
    140160    },
     
    141161
    142162    maxHitpoints: function()
    143163    {
    144         if (this._template.Health !== undefined)
    145             return GetTechModifiedProperty(this._techModifications, this._template, "Health/Max",+this._template.Health.Max);
     164        if (this.get("Health") !== undefined)
     165            return +this.get("Health/Max");
    146166        return 0;
    147167    },
     168
    148169    isHealable: function()
    149170    {
    150         if (this._template.Health !== undefined)
    151             return this._template.Health.Unhealable !== "true";
     171        if (this.get("Health") !== undefined)
     172            return this.get("Health/Unhealable") !== "true";
    152173        return false;
    153174    },
     175
    154176    isRepairable: function()
    155177    {
    156         if (this._template.Health !== undefined)
    157             return this._template.Health.Repairable === "true";
     178        if (this.get("Health") !== undefined)
     179            return this.get("Health/Repairable") === "true";
    158180        return false;
    159181    },
    160182
    161183    getPopulationBonus: function() {
    162         if (!this._template.Cost || !this._template.Cost.PopulationBonus)
    163             return undefined;
    164         return this._template.Cost.PopulationBonus;
     184        return this.get("Cost/PopulationBonus");
    165185    },
    166186
    167187    armourStrengths: function() {
    168         if (!this._template.Armour)
     188        if (!this.get("Armour"))
    169189            return undefined;
    170190
    171191        return {
    172             hack: GetTechModifiedProperty(this._techModifications, this._template, "Armour/Hack", +this._template.Armour.Hack),
    173             pierce: GetTechModifiedProperty(this._techModifications, this._template, "Armour/Pierce", +this._template.Armour.Pierce),
    174             crush: GetTechModifiedProperty(this._techModifications, this._template, "Armour/Crush", +this._template.Armour.Crush)
     192            hack: +this.get("Armour/Hack"),
     193            pierce: +this.get("Armour/Pierce"),
     194            crush: +this.get("Armour/Crush")
    175195        };
    176196    },
    177197
    178198    attackTypes: function() {
    179         if (!this._template.Attack)
     199        if (!this.get("Attack"))
    180200            return undefined;
    181201
    182202        var ret = [];
    183         for (var type in this._template.Attack)
     203        for (var type in this.get("Attack"))
    184204            ret.push(type);
    185205
    186206        return ret;
     
    187207    },
    188208
    189209    attackRange: function(type) {
    190         if (!this._template.Attack || !this._template.Attack[type])
     210        if (!this.get("Attack/" + type +""))
    191211            return undefined;
    192212
    193213        return {
    194                 max: GetTechModifiedProperty(this._techModifications, this._template, "Attack/MaxRange", +this._template.Attack[type].MaxRange),
    195                 min: GetTechModifiedProperty(this._techModifications, this._template, "Attack/MinRange", +(this._template.Attack[type].MinRange || 0))
     214                max: +this.get("Attack/" + type +"/MaxRange"),
     215                min: +(this.get("Attack/" + type +"/MinRange") || 0)
    196216        };
    197217    },
    198218
    199219    attackStrengths: function(type) {
    200         if (!this._template.Attack || !this._template.Attack[type])
     220        if (!this.get("Attack/" + type +""))
    201221            return undefined;
    202222
    203223        return {
    204             hack: GetTechModifiedProperty(this._techModifications, this._template, "Attack/"+type+"/Hack", +(this._template.Attack[type].Hack || 0)),
    205             pierce: GetTechModifiedProperty(this._techModifications, this._template, "Attack/"+type+"/Pierce", +(this._template.Attack[type].Pierce || 0)),
    206             crush: GetTechModifiedProperty(this._techModifications, this._template, "Attack/"+type+"/Crush", +(this._template.Attack[type].Crush || 0))
     224            hack: +(this.get("Attack/" + type + "/Hack") || 0),
     225            pierce: +(this.get("Attack/" + type + "/Pierce") || 0),
     226            crush: +(this.get("Attack/" + type + "/Crush") || 0)
    207227        };
    208228    },
    209229   
    210230    attackTimes: function(type) {
    211         if (!this._template.Attack || !this._template.Attack[type])
     231        if (!this.get("Attack/" + type +""))
    212232            return undefined;
    213233
    214234        return {
    215             prepare: GetTechModifiedProperty(this._techModifications, this._template, "Attack/"+type+"/PrepareTime", +(this._template.Attack[type].PrepareTime || 0)),
    216             repeat: GetTechModifiedProperty(this._techModifications, this._template, "Attack/"+type+"/RepeatTime", +(this._template.Attack[type].RepeatTime || 1000))
     235            prepare: +(this.get("Attack/" + type + "/PrepareTime") || 0),
     236            repeat: +(this.get("Attack/" + type + "/RepeatTime") || 1000)
    217237        };
    218238    },
    219239
    220240    // returns the classes this templates counters:
    221     // Return type is [ [-neededClasses-] , multiplier ].
     241    // Return type is [ [-neededClasses- , multiplier], … ].
    222242    getCounteredClasses: function() {
    223         if (!this._template.Attack)
     243        if (!this.get("Attack"))
    224244            return undefined;
    225245       
    226246        var Classes = [];
    227         for (var i in this._template.Attack) {
    228             if (!this._template.Attack[i].Bonuses)
     247        for (var i in this.get("Attack")) {
     248            if (!this.get("Attack/" + i + "/Bonuses"))
    229249                continue;
    230             for (var o in this._template.Attack[i].Bonuses)
    231                 if (this._template.Attack[i].Bonuses[o].Classes)
    232                     Classes.push([this._template.Attack[i].Bonuses[o].Classes.split(" "), +this._template.Attack[i].Bonuses[o].Multiplier]);
     250            for (var o in this.get("Attack/" + i + "/Bonuses"))
     251                if (this.get("Attack/" + i + "/Bonuses/" + o + "/Classes"))
     252                    Classes.push([this.get("Attack/" + i +"/Bonuses/" + o +"/Classes").split(" "), +this.get("Attack/" + i +"/Bonuses" +o +"/Multiplier")]);
    233253        }
    234254        return Classes;
    235255    },
     
    237257    // returns true if the entity counters those classes.
    238258    // TODO: refine using the multiplier
    239259    countersClasses: function(classes) {
    240         if (!this._template.Attack)
     260        if (!this.get("Attack"))
    241261            return false;
    242262        var mcounter = [];
    243         for (var i in this._template.Attack) {
    244             if (!this._template.Attack[i].Bonuses)
     263        for (var i in this.get("Attack")) {
     264            if (!this.get("Attack/" + i + "/Bonuses"))
    245265                continue;
    246             for (var o in this._template.Attack[i].Bonuses)
    247                 if (this._template.Attack[i].Bonuses[o].Classes)
    248                     mcounter.concat(this._template.Attack[i].Bonuses[o].Classes.split(" "));
     266            for (var o in this.get("Attack/" + i + "/Bonuses"))
     267                if (this.get("Attack/" + i + "/Bonuses/" + o + "/Classes"))
     268                    mcounter.concat(this.get("Attack/" + i + "/Bonuses/" + o + "/Classes").split(" "));
    249269        }
    250270        for (var i in classes)
    251271        {
     
    257277
    258278    // returns, if it exists, the multiplier from each attack against a given class
    259279    getMultiplierAgainst: function(type, againstClass) {
    260         if (!this._template.Attack || !this._template.Attack[type])
     280        if (!this.get("Attack/" + type +""))
    261281            return undefined;
    262282
    263         if (this._template.Attack[type].Bonuses)
    264             for (var o in this._template.Attack[type].Bonuses) {
    265                 if (!this._template.Attack[type].Bonuses[o].Classes)
     283        if (this.get("Attack/" + type + "/Bonuses"))
     284            for (var o in this.get("Attack/" + type + "/Bonuses")) {
     285                if (!this.get("Attack/" + type + "/Bonuses/" + o + "/Classes"))
    266286                    continue;
    267                 var total = this._template.Attack[type].Bonuses[o].Classes.split(" ");
     287                var total = this.get("Attack/" + type + "/Bonuses/" + o + "/Classes").split(" ");
    268288                for (var j in total)
    269289                    if (total[j] === againstClass)
    270                         return this._template.Attack[type].Bonuses[o].Multiplier;
     290                        return this.get("Attack/" + type + "/Bonuses/" + o + "/Multiplier");
    271291            }
    272292        return 1;
    273293    },
     
    274294
    275295    // returns true if the entity can attack the given class
    276296    canAttackClass: function(saidClass) {
    277         if (!this._template.Attack)
     297        if (!this.get("Attack"))
    278298            return false;
    279299       
    280         for (var i in this._template.Attack) {
    281             if (!this._template.Attack[i].RestrictedClasses || !this._template.Attack[i].RestrictedClasses._string)
     300        for (var i in this.get("Attack")) {
     301            if (!this.get("Attack/" + i + "/RestrictedClasses") || !this.get("Attack/" + i + "/RestrictedClasses/_string"))
    282302                continue;
    283             var cannotAttack = this._template.Attack[i].RestrictedClasses._string.split(" ");
     303            var cannotAttack = this.get("Attack/" + i + "/RestrictedClasses/_string").split(" ");
    284304            if (cannotAttack.indexOf(saidClass) !== -1)
    285305                return false;
    286306        }
     
    288308    },
    289309
    290310    buildableEntities: function() {
    291         if (!this._template.Builder)
    292             return undefined;
    293         if (!this._template.Builder.Entities._string)
     311        if (!this.get("Builder/Entities/_string"))
    294312            return [];
    295313        var civ = this.civ();
    296         var templates = this._template.Builder.Entities._string.replace(/\{civ\}/g, civ).split(/\s+/);
     314        var templates = this.get("Builder/Entities/_string").replace(/\{civ\}/g, civ).split(/\s+/);
    297315        return templates; // TODO: map to Entity?
    298316    },
    299317
    300318    trainableEntities: function() {
    301         if (!this._template.ProductionQueue || !this._template.ProductionQueue.Entities || !this._template.ProductionQueue.Entities._string)
     319        if (!this.get("ProductionQueue/Entities/_string"))
    302320            return undefined;
    303321        var civ = this.civ();
    304         var templates = this._template.ProductionQueue.Entities._string.replace(/\{civ\}/g, civ).split(/\s+/);
     322        var templates = this.get("ProductionQueue/Entities/_string").replace(/\{civ\}/g, civ).split(/\s+/);
    305323        return templates;
    306324    },
    307325
    308326    researchableTechs: function() {
    309         if (!this._template.ProductionQueue || !this._template.ProductionQueue.Technologies || !this._template.ProductionQueue.Technologies._string)
     327        if (!this.get("ProductionQueue/Technologies/_string"))
    310328            return undefined;
    311         var templates = this._template.ProductionQueue.Technologies._string.split(/\s+/);
     329        var templates = this.get("ProductionQueue/Technologies/_string").split(/\s+/);
    312330        return templates;
    313331    },
    314332
    315333    resourceSupplyType: function() {
    316         if (!this._template.ResourceSupply)
     334        if (!this.get("ResourceSupply"))
    317335            return undefined;
    318         var [type, subtype] = this._template.ResourceSupply.Type.split('.');
     336        var [type, subtype] = this.get("ResourceSupply/Type").split('.');
    319337        return { "generic": type, "specific": subtype };
    320338    },
    321339    // will return either "food", "wood", "stone", "metal" and not treasure.
    322340    getResourceType: function() {
    323         if (!this._template.ResourceSupply)
     341        if (!this.get("ResourceSupply"))
    324342            return undefined;
    325         var [type, subtype] = this._template.ResourceSupply.Type.split('.');
     343        var [type, subtype] = this.get("ResourceSupply/Type").split('.');
    326344        if (type == "treasure")
    327345            return subtype;
    328346        return type;
     
    329347    },
    330348
    331349    resourceSupplyMax: function() {
    332         if (!this._template.ResourceSupply)
     350        if (!this.get("ResourceSupply"))
    333351            return undefined;
    334         return +this._template.ResourceSupply.Amount;
     352        return +this.get("ResourceSupply/Amount");
    335353    },
    336354
    337355    maxGatherers: function()
    338356    {
    339         if (this._template.ResourceSupply !== undefined)
    340             return +this._template.ResourceSupply.MaxGatherers;
     357        if (this.get("ResourceSupply") !== undefined)
     358            return +this.get("ResourceSupply/MaxGatherers");
    341359        return 0;
    342360    },
    343361   
    344362    resourceGatherRates: function() {
    345         if (!this._template.ResourceGatherer)
     363        if (!this.get("ResourceGatherer"))
    346364            return undefined;
    347365        var ret = {};
    348         var baseSpeed = GetTechModifiedProperty(this._techModifications, this._template, "ResourceGatherer/BaseSpeed", +this._template.ResourceGatherer.BaseSpeed);
    349         for (var r in this._template.ResourceGatherer.Rates)
    350             ret[r] = GetTechModifiedProperty(this._techModifications, this._template, "ResourceGatherer/Rates/"+r, +this._template.ResourceGatherer.Rates[r]) * baseSpeed;
     366        var baseSpeed = +this.get("ResourceGatherer/BaseSpeed");
     367        for (var r in this.get("ResourceGatherer/Rates"))
     368            ret[r] = +this.get("ResourceGatherer/Rates/" + r) * baseSpeed;
    351369        return ret;
    352370    },
    353371
    354372    resourceDropsiteTypes: function() {
    355         if (!this._template.ResourceDropsite)
     373        if (!this.get("ResourceDropsite"))
    356374            return undefined;
    357         return this._template.ResourceDropsite.Types.split(/\s+/);
     375        return this.get("ResourceDropsite/Types").split(/\s+/);
    358376    },
    359377
    360378
    361379    garrisonableClasses: function() {
    362         if (!this._template.GarrisonHolder || !this._template.GarrisonHolder.List._string)
     380        if (!this.get("GarrisonHolder") || !this.get("GarrisonHolder/List/_string"))
    363381            return undefined;
    364         return this._template.GarrisonHolder.List._string.split(/\s+/);
     382        return this.get("GarrisonHolder/List/_string").split(/\s+/);
    365383    },
    366384
    367385    garrisonMax: function() {
    368         if (!this._template.GarrisonHolder)
     386        if (!this.get("GarrisonHolder"))
    369387            return undefined;
    370         return this._template.GarrisonHolder.Max;
     388        return this.get("GarrisonHolder/Max");
    371389    },
    372390   
    373391    /**
     
    375393     * (Any non domestic currently.)
    376394     */
    377395    isUnhuntable: function() {
    378         if (!this._template.UnitAI || !this._template.UnitAI.NaturalBehaviour)
     396        if (!this.get("UnitAI") || !this.get("UnitAI/NaturalBehaviour"))
    379397            return false;
    380398
    381399        // only attack domestic animals since they won't flee nor retaliate.
    382         return this._template.UnitAI.NaturalBehaviour !== "domestic";
     400        return this.get("UnitAI/NaturalBehaviour") !== "domestic";
    383401    },
    384                            
     402                         
    385403    walkSpeed: function() {
    386         if (!this._template.UnitMotion || !this._template.UnitMotion.WalkSpeed)
     404        if (!this.get("UnitMotion") || !this.get("UnitMotion/WalkSpeed"))
    387405             return undefined;
    388         return this._template.UnitMotion.WalkSpeed;
     406        return this.get("UnitMotion/WalkSpeed");
    389407    },
    390408
    391409    buildCategory: function() {
    392         if (!this._template.BuildRestrictions || !this._template.BuildRestrictions.Category)
     410        if (!this.get("BuildRestrictions") || !this.get("BuildRestrictions/Category"))
    393411            return undefined;
    394         return this._template.BuildRestrictions.Category;
     412        return this.get("BuildRestrictions/Category");
    395413    },
    396414   
    397415    buildTime: function() {
    398         if (!this._template.Cost || !this._template.Cost.BuildTime)
     416        if (!this.get("Cost") || !this.get("Cost/BuildTime"))
    399417            return undefined;
    400         return this._template.Cost.BuildTime;
     418        return this.get("Cost/BuildTime");
    401419    },
    402420
    403421    buildDistance: function() {
    404         if (!this._template.BuildRestrictions || !this._template.BuildRestrictions.Distance)
     422        if (!this.get("BuildRestrictions") || !this.get("BuildRestrictions/Distance"))
    405423            return undefined;
    406         return this._template.BuildRestrictions.Distance;
     424        return this.get("BuildRestrictions/Distance");
    407425    },
    408426
    409427    buildPlacementType: function() {
    410         if (!this._template.BuildRestrictions || !this._template.BuildRestrictions.PlacementType)
     428        if (!this.get("BuildRestrictions") || !this.get("BuildRestrictions/PlacementType"))
    411429            return undefined;
    412         return this._template.BuildRestrictions.PlacementType;
     430        return this.get("BuildRestrictions/PlacementType");
    413431    },
    414432
    415433    buildTerritories: function() {
    416         if (!this._template.BuildRestrictions || !this._template.BuildRestrictions.Territory)
     434        if (!this.get("BuildRestrictions") || !this.get("BuildRestrictions/Territory"))
    417435            return undefined;
    418         return this._template.BuildRestrictions.Territory.split(/\s+/);
     436        return this.get("BuildRestrictions/Territory").split(/\s+/);
    419437    },
    420438
    421439    hasBuildTerritory: function(territory) {
     
    424442    },
    425443
    426444    hasTerritoryInfluence: function() {
    427         return (this._template.TerritoryInfluence !== undefined);
     445        return (this.get("TerritoryInfluence") !== undefined);
    428446    },
    429447
    430448    territoryInfluenceRadius: function() {
    431         if (this._template.TerritoryInfluence !== undefined)
    432             return (this._template.TerritoryInfluence.Radius);
     449        if (this.get("TerritoryInfluence") !== undefined)
     450            return (this.get("TerritoryInfluence/Radius"));
    433451        else
    434452            return -1;
    435453    },
    436454
    437455    territoryInfluenceWeight: function() {
    438         if (this._template.TerritoryInfluence !== undefined)
    439             return (this._template.TerritoryInfluence.Weight);
     456        if (this.get("TerritoryInfluence") !== undefined)
     457            return (this.get("TerritoryInfluence/Weight"));
    440458        else
    441459            return -1;
    442460    },
    443461
    444462    visionRange: function() {
    445         if (!this._template.Vision)
    446             return undefined;
    447         return this._template.Vision.Range;
     463        return this.get("Vision/Range");
    448464    }
    449465});
    450466
    451467
    452 
     468// defines an entity, with a super Template.
     469// also redefines several of the template functions where the only change is applying aura and tech modifications.
    453470m.Entity = m.Class({
    454     _super: m.EntityTemplate,
     471    _super: m.Template,
    455472
    456473    _init: function(sharedAI, entity)
    457474    {
    458         this._super.call(this, sharedAI.GetTemplate(entity.template), sharedAI._techModifications[entity.owner]);
     475        this._super.call(this, sharedAI.GetTemplate(entity.template));
    459476
    460         this._ai = sharedAI;
    461477        this._templateName = entity.template;
    462478        this._entity = entity;
     479        this._auraTemplateModif = {};   // template modification from auras. this is only for this entity.
     480        this._ai = sharedAI;
     481        if (!sharedAI._techModifications[entity.owner][this._templateName])
     482            sharedAI._techModifications[entity.owner][this._templateName] = {};
     483        this._techModif = sharedAI._techModifications[entity.owner][this._templateName]; // save a reference to the template tech modifications
    463484    },
    464485
    465486    toString: function() {
     
    477498    /**
    478499     * Returns extra data that the AI scripts have associated with this entity,
    479500     * for arbitrary local annotations.
    480      * (This data is not shared with any other AI scripts.)
     501     * (This data should not be shared with any other AI scripts.)
    481502     */
    482503    getMetadata: function(player, key) {
    483504        return this._ai.getMetadata(player, this, key);
     
    493514    deleteAllMetadata: function(player) {
    494515        delete this._ai._entityMetadata[player][this.id()];
    495516    },
    496                    
     517                 
    497518    deleteMetadata: function(player, key) {
    498519        this._ai.deleteMetadata(player, this, key);
    499520    },
     
    508529   
    509530    unitAIState: function() { return this._entity.unitAIState; },
    510531    unitAIOrderData: function() { return this._entity.unitAIOrderData; },
    511     hitpoints: function() { if (this._entity.hitpoints !== undefined) return this._entity.hitpoints; return undefined; },
     532   
     533    hitpoints: function() {if (this._entity.hitpoints !== undefined) return this._entity.hitpoints; return undefined; },
    512534    isHurt: function() { return this.hitpoints() < this.maxHitpoints(); },
    513535    healthLevel: function() { return (this.hitpoints() / this.maxHitpoints()); },
    514536    needsHeal: function() { return this.isHurt() && this.isHealable(); },
     
    540562    owner: function() {
    541563        return this._entity.owner;
    542564    },
     565
    543566    isOwn: function(player) {
    544567        if (typeof(this._entity.owner) === "undefined")
    545568            return false;
    546569        return this._entity.owner === player;
    547570    },
     571   
    548572    isFriendly: function(player) {
    549573        return this.isOwn(player); // TODO: diplomacy
    550574    },
     575   
    551576    isEnemy: function(player) {
    552577        return !this.isOwn(player); // TODO: diplomacy
    553578    },
     
    557582            return undefined;
    558583        return this._entity.resourceSupplyAmount;
    559584    },
    560                    
     585                 
    561586    resourceSupplyGatherers: function(player)
    562587    {
    563588        if (this._entity.resourceSupplyGatherers !== undefined)
    564             return this._entity.resourceSupplyGatherers[player-1];
     589            return this._entity.resourceSupplyGatherers[player];
    565590        return [];
    566591    },
    567                    
     592                 
    568593    isFull: function(player)
    569594    {
    570595        if (this._entity.resourceSupplyGatherers !== undefined)
    571             return (this.maxGatherers() === this._entity.resourceSupplyGatherers[player-1].length);
     596            return (this.maxGatherers() === this._entity.resourceSupplyGatherers[player].length);
    572597
    573598        return undefined;
    574599    },
     
    578603            return undefined;
    579604        return this._entity.resourceCarrying;
    580605    },
    581                    
     606                 
    582607    currentGatherRate: function() {
    583608        // returns the gather rate for the current target if applicable.
    584         if (!this._template.ResourceGatherer)
     609        if (!this.get("ResourceGatherer"));
    585610            return undefined;
    586611       
    587612        if (this.unitAIOrderData().length &&
     
    599624           
    600625            var type = ress.resourceSupplyType();
    601626            var tstring = type.generic + "." + type.specific;
    602                    
     627                 
    603628            if (type.generic == "treasure")
    604629                return 1000;
    605630               
    606             var speed = GetTechModifiedProperty(this._techModifications, this._template, "ResourceGatherer/BaseSpeed", +this._template.ResourceGatherer.BaseSpeed);
    607             speed *= GetTechModifiedProperty(this._techModifications, this._template, "ResourceGatherer/Rates/"+tstring, +this._template.ResourceGatherer.Rates[tstring]);
    608                    
     631            var speed = +this.get("ResourceGatherer/BaseSpeed");
     632            speed *= +this.get("ResourceGatherer/Rates/" +tstring);
     633                 
    609634            if (speed)
    610635                return speed;
    611636            return 0;
     
    644669    },
    645670
    646671    unload: function(id) {
    647         if (!this._template.GarrisonHolder)
     672        if (!this.get("GarrisonHolder"))
    648673            return undefined;
    649674        Engine.PostCommand(PlayerID,{"type": "unload", "garrisonHolder": this.id(), "entities": [id]});
    650675        return this;
     
    652677
    653678    // Unloads all owned units, don't unload allies
    654679    unloadAll: function() {
    655         if (!this._template.GarrisonHolder)
     680        if (!this.get("GarrisonHolder"))
    656681            return undefined;
    657682        Engine.PostCommand(PlayerID,{"type": "unload-all-own", "garrisonHolders": [this.id()]});
    658683        return this;
     
    751776        });
    752777        return this;
    753778    },
    754                    
     779                 
    755780     research: function(template) {
    756781        Engine.PostCommand(PlayerID,{ "type": "research", "entity": this.id(), "template": template });
    757782        return this;
  • binaries/data/mods/public/simulation/ai/common-api/gamestate.js

     
    126126    if (!this.templates[type])
    127127        return null;
    128128   
    129     return new m.EntityTemplate(this.templates[type], this.techModifications);
     129    return new m.Template(this.templates[type], this.techModifications);
    130130};
    131131
    132132m.GameState.prototype.applyCiv = function(str) {
  • binaries/data/mods/public/simulation/ai/common-api/shared.js

     
    5858// Components that will be disabled in foundation entity templates.
    5959// (This is a bit yucky and fragile since it's the inverse of
    6060// CCmpTemplateManager::CopyFoundationSubset and only includes components
    61 // that our EntityTemplate class currently uses.)
     61// that our Template class currently uses.)
    6262m.g_FoundationForbiddenComponents = {
    6363    "ProductionQueue": 1,
    6464    "ResourceSupply": 1,
     
    119119// We need to now the initial state of the game for this, as we will use it.
    120120// This is called right at the end of the map generation.
    121121m.SharedScript.prototype.init = function(state) {
     122    this.ApplyTemplatesDelta(state);
     123
    122124    this.passabilityClasses = state.passabilityClasses;
    123125    this.passabilityMap = state.passabilityMap;
    124126    this.players = this._players;
     
    127129    this.timeElapsed = state.timeElapsed;
    128130    this.barterPrices = state.barterPrices;
    129131
    130     for (var o in state.players)
    131         this._techModifications[o] = state.players[o].techModifications;
    132    
    133132    this._entities = {};
    134133    for (var id in state.entities)
    135134        this._entities[id] = new m.Entity(this, state.entities[id]);
     
    166165        return;
    167166    // deals with updating based on create and destroy messages.
    168167    this.ApplyEntitiesDelta(state);
     168    this.ApplyTemplatesDelta(state);
    169169
    170170    Engine.ProfileStart("onUpdate");
    171171
     
    178178    this.timeElapsed = state.timeElapsed;
    179179    this.barterPrices = state.barterPrices;
    180180   
    181     for (var o in state.players)
    182         this._techModifications[o] = state.players[o].techModifications;
    183 
    184181    for (var i in this.gameState)
    185182        this.gameState[i].update(this,state);
    186183
     
    313310            this.updateEntityCollections(prop, this._entities[id]);
    314311        }
    315312    }
     313
     314    // apply per-entity aura-related changes.
     315    // this supersedes tech-related changes.
     316    for (var id in state.changedEntityTemplateInfo)
     317    {
     318        var changes = state.changedEntityTemplateInfo[id];
     319        for each (var change in changes)
     320            this._entities[id]._auraTemplateModif[change.variable] = change.value;
     321    }
    316322    Engine.ProfileStop();
    317323};
    318324
     325m.SharedScript.prototype.ApplyTemplatesDelta = function(state)
     326{
     327    Engine.ProfileStart("Shared ApplyTemplatesDelta");
     328
     329    for (var player in state.changedTemplateInfo)
     330    {
     331        var playerDiff = state.changedTemplateInfo[player];
     332        for (var template in playerDiff)
     333        {
     334            var changes = playerDiff[template];
     335            if (!this._techModifications[player][template])
     336                this._techModifications[player][template] = {};
     337            for each (var change in changes)
     338                this._techModifications[player][template][change.variable] = change.value;
     339        }
     340    }
     341    Engine.ProfileStop();
     342};
     343
    319344m.SharedScript.prototype.registerUpdatingEntityCollection = function(entCollection, noPush)
    320345{
    321346    if (!noPush) {
  • binaries/data/mods/public/simulation/components/AIInterface.js

     
    1818    this.events["OwnershipChanged"] = [];
    1919
    2020    this.changedEntities = {};
     21
     22    // cache for technology changes;
     23    // this one is PlayerID->TemplateName->{StringForTheValue, ActualValue}
     24    this.changedTemplateInfo = {};
     25    // this is for auras and is EntityID->{StringForTheValue, ActualValue}
     26    this.changedEntityTemplateInfo = {};
    2127};
    2228
    23 AIInterface.prototype.GetRepresentation = function()
     29AIInterface.prototype.GetNonEntityRepresentation = function()
    2430{
    2531    var cmpGuiInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
    26 
     32   
    2733    // Return the same game state as the GUI uses
    2834    var state = cmpGuiInterface.GetExtendedSimulationState(-1);
    29 
     35   
    3036    // Add some extra AI-specific data
    3137    state.events = {};
    3238    state.events["Create"] = this.events["Create"];
     
    3945    state.events["PlayerDefeated"] = this.events["PlayerDefeated"];
    4046    state.events["EntityRenamed"] = this.events["EntityRenamed"];
    4147    state.events["OwnershipChanged"] = this.events["OwnershipChanged"];
    42 
    4348    // Reset the event list for the next turn
    4449    this.events["Create"] = [];
    4550    this.events["Destroy"] = [];
     
    5156    this.events["PlayerDefeated"] = [];
    5257    this.events["EntityRenamed"] = [];
    5358    this.events["OwnershipChanged"] = [];
    54    
     59
     60    return state;
     61}
     62
     63AIInterface.prototype.GetRepresentation = function()
     64{
     65    var state = this.GetNonEntityRepresentation();
     66
    5567    // Add entity representations
    5668    Engine.ProfileStart("proxy representations");
    5769    state.entities = {};
     
    6476    this.changedEntities = {};
    6577    Engine.ProfileStop();
    6678
     79    state.changedTemplateInfo = this.changedTemplateInfo;
     80    this.changedTemplateInfo = {};
     81    state.changedEntityTemplateInfo = this.changedEntityTemplateInfo;
     82    this.changedEntityTemplateInfo = {};
     83
    6784    return state;
    6885};
     86
    6987// Intended to be called first, during the map initialization: no caching
    7088AIInterface.prototype.GetFullRepresentation = function(flushEvents)
    71 {
    72     var cmpGuiInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
    73    
    74     // Return the same game state as the GUI uses
    75     var state = cmpGuiInterface.GetExtendedSimulationState(-1);
    76    
    77     // Add some extra AI-specific data
    78     state.events = {};
    79     state.events["Create"] = this.events["Create"];
    80     state.events["Destroy"] = this.events["Destroy"];
    81     state.events["Attacked"] = this.events["Attacked"];
    82     state.events["RangeUpdate"] = this.events["RangeUpdate"];
    83     state.events["ConstructionFinished"] = this.events["ConstructionFinished"];
    84     state.events["TrainingFinished"] = this.events["TrainingFinished"];
    85     state.events["AIMetadata"] = this.events["AIMetadata"];
    86     state.events["PlayerDefeated"] = this.events["PlayerDefeated"];
    87     state.events["EntityRenamed"] = this.events["EntityRenamed"];
    88     state.events["OwnershipChanged"] = this.events["OwnershipChanged"];
    89    
     89{   
     90    var state = this.GetNonEntityRepresentation();
    9091
    9192    if (flushEvents)
    9293    {
     
    102103        state.events["OwnershipChanged"] = [];
    103104    }
    104105
    105     // Reset the event list for the next turn
    106     this.events["Create"] = [];
    107     this.events["Destroy"] = [];
    108     this.events["Attacked"] = [];
    109     this.events["RangeUpdate"] = [];
    110     this.events["ConstructionFinished"] = [];
    111     this.events["TrainingFinished"] = [];
    112     this.events["AIMetadata"] = [];
    113     this.events["PlayerDefeated"] = [];
    114     this.events["EntityRenamed"] = [];
    115     this.events["OwnershipChanged"] = [];
    116 
    117 
    118106    // Add entity representations
    119107    Engine.ProfileStart("proxy representations");
    120108    state.entities = {};
     
    127115    }
    128116    Engine.ProfileStop();
    129117   
     118    state.changedTemplateInfo = this.changedTemplateInfo;
     119    this.changedTemplateInfo = {};
     120    state.changedEntityTemplateInfo = this.changedEntityTemplateInfo;
     121    this.changedEntityTemplateInfo = {};
     122
    130123    return state;
    131124};
    132125
     
    156149    this.events["EntityRenamed"].push(msg);
    157150};
    158151
     152// When a new technology is researched, check which templates it affects,
     153// and send the updated values to the AI.
     154// this relies on the fact that any "value" in a technology can only ever change
     155// one template value, and that the naming is the same (with / in place of .)
     156// it's not incredibly fast but it's not incredibly slow.
     157AIInterface.prototype.OnTemplateModification = function(msg)
     158{
     159    var cmpTemplateManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager);
     160    if (!this.templates)
     161        this.templates = cmpTemplateManager.FindAllTemplates(false);
     162
     163    for each (var value in msg.valueNames)
     164    {
     165        for (var o = 0; o < this.templates.length; ++o)
     166        {
     167            var tmp = this.templates[o];
     168            var template = cmpTemplateManager.GetTemplateWithoutValidation(this.templates[o]);
     169            // remove templates that we obviously don't care about.
     170            if (!template || !template.Identity || ! template.Identity.Civ)
     171            {
     172                this.templates.splice(o--,1);
     173                continue;
     174            }
     175
     176            // let's get the base template value.
     177            var strings = value.split("/");
     178            var item = template;
     179            var ended = true;
     180            for (var i = 0; i < strings.length; ++i)
     181            {
     182                if (item !== undefined && item[strings[i]] !== undefined)
     183                    item = item[strings[i]];
     184                else
     185                    ended = false;
     186            }
     187            if (!ended)
     188                continue;
     189            // item now contains the template value for this.
     190
     191            var newValue = ApplyValueModificationsToTemplate(value, +item, msg.player, template);
     192            newValue = typeof(newValue) === "Number" ? Math.round(newValue) : newValue;
     193            if(item != newValue)
     194            {
     195                if (!this.changedTemplateInfo[msg.player])
     196                    this.changedTemplateInfo[msg.player] = {};
     197                if (!this.changedTemplateInfo[msg.player][this.templates[o]])
     198                    this.changedTemplateInfo[msg.player][this.templates[o]] = [ { "variable" : value, "value" : newValue} ];
     199                else
     200                    this.changedTemplateInfo[msg.player][this.templates[o]].push({ "variable" : value, "value" : newValue });
     201            }
     202        }
     203    }
     204};
     205
     206AIInterface.prototype.OnGlobalValueModification = function(msg)
     207{
     208    var cmpTemplateManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager);
     209    for each (var ent in msg.entities)
     210    {
     211        var template = cmpTemplateManager.GetTemplateWithoutValidation(cmpTemplateManager.GetCurrentTemplateName(ent));
     212        for each (var value in msg.valueNames)
     213        {
     214            // let's get the base template value.
     215            var strings = value.split("/");
     216            var item = template;
     217            var ended = true;
     218            for (var i = 0; i < strings.length; ++i)
     219            {
     220                if (item !== undefined && item[strings[i]] !== undefined)
     221                    item = item[strings[i]];
     222                else
     223                    ended = false;
     224            }
     225            if (!ended)
     226                continue;
     227            // "item" now contains the unmodified template value for this.
     228            var newValue = ApplyValueModificationsToEntity(value, +item, ent);
     229            newValue = typeof(newValue) === "Number" ? Math.round(newValue) : newValue;
     230            if(item != newValue)
     231            {
     232                if (!this.changedEntityTemplateInfo[ent])
     233                    this.changedEntityTemplateInfo[ent] = [{ "variable" : value, "value" : newValue }];
     234                else
     235                    this.changedEntityTemplateInfo[ent].push({ "variable" : value, "value" : newValue });
     236            }
     237        }
     238    }
     239};
     240
    159241Engine.RegisterComponentType(IID_AIInterface, "AIInterface", AIInterface);
  • binaries/data/mods/public/simulation/components/AIProxy.js

     
    3636    this.needsFullGet = true;
    3737    this.owner = -1;    // for convenience now and then.
    3838   
     39    this.cmpAIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_AIInterface);
     40
    3941    // Let the AIInterface know that we exist and that it should query us
    4042    this.NotifyChange();
    4143};
    4244
     45AIProxy.prototype.Serialize = null; // we have no dynamic state to save
     46
    4347AIProxy.prototype.GetRepresentation = function()
    4448{
    4549    // Return the full representation the first time we're called
     
    6771    if (!this.changes)
    6872    {
    6973        this.changes = {};
    70 
    71         var cmpAIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_AIInterface);
    72         cmpAIInterface.ChangedEntity(this.entity);
     74    this.cmpAIInterface.ChangedEntity(this.entity);
    7375    }
    7476};
    7577
     
    261263   
    262264    if (msg.from === -1)
    263265    {
    264         var cmpAIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_AIInterface);
    265         cmpAIInterface.PushEvent("Create", {"entity" : msg.entity});
     266        this.cmpAIInterface.PushEvent("Create", {"entity" : msg.entity});
    266267        return;
    267268    } else if (msg.to === -1)
    268269    {
    269         var cmpAIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_AIInterface);
    270         cmpAIInterface.PushEvent("Destroy", {"entity" : msg.entity});
     270        this.cmpAIInterface.PushEvent("Destroy", {"entity" : msg.entity});
    271271        return;
    272272    }
    273273   
     
    274274    this.owner = msg.to;
    275275    this.changes.owner = msg.to;
    276276   
    277     var cmpAIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_AIInterface);
    278     cmpAIInterface.PushEvent("OwnershipChanged", msg);
     277this.cmpAIInterface.PushEvent("OwnershipChanged", msg);
    279278};
    280279
    281280AIProxy.prototype.OnAttacked = function(msg)
    282281{
    283     var cmpAIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_AIInterface);
    284     cmpAIInterface.PushEvent("Attacked", msg);
     282this.cmpAIInterface.PushEvent("Attacked", msg);
    285283};
    286284
    287285/*
     
    288286 Deactivated for actually not really being practical for most uses.
    289287 AIProxy.prototype.OnRangeUpdate = function(msg)
    290288{
    291     var cmpAIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_AIInterface);
    292289    msg.owner = this.owner;
    293     cmpAIInterface.PushEvent("RangeUpdate", msg);
     290this.cmpAIInterface.PushEvent("RangeUpdate", msg);
    294291    warn(uneval(msg));
    295292};*/
    296293
    297294AIProxy.prototype.OnConstructionFinished = function(msg)
    298295{
    299     var cmpAIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_AIInterface);
    300     cmpAIInterface.PushEvent("ConstructionFinished", msg);
     296    this.cmpAIInterface.PushEvent("ConstructionFinished", msg);
    301297};
    302298
    303299AIProxy.prototype.OnTrainingFinished = function(msg)
    304300{
    305     var cmpAIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_AIInterface);
    306     cmpAIInterface.PushEvent("TrainingFinished", msg);
     301    this.cmpAIInterface.PushEvent("TrainingFinished", msg);
    307302};
    308303
    309304AIProxy.prototype.OnAIMetadata = function(msg)
    310305{
    311     var cmpAIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_AIInterface);
    312     cmpAIInterface.PushEvent("AIMetadata", msg);
     306    this.cmpAIInterface.PushEvent("AIMetadata", msg);
    313307};
    314308
    315309Engine.RegisterComponentType(IID_AIProxy, "AIProxy", AIProxy);
  • binaries/data/mods/public/simulation/components/AuraManager.js

     
    4646        this.modificationsCache[value][ent].add += data.add;
    4747
    4848    // post message to the entity to notify it about the change
    49     Engine.PostMessage(ent, MT_ValueModification, { "component": value.split("/")[0] });
     49    Engine.PostMessage(ent, MT_ValueModification, { "entities": [ent], "component": value.split("/")[0], "valueNames": [value] });
    5050};
    5151
    5252AuraManager.prototype.ApplyTemplateBonus = function(value, player, classes, data, key)
     
    7373        if (data.add)
    7474            this.templateModificationsCache[value][player][c][key].add += data.add;
    7575
     76        Engine.PostMessage(SYSTEM_ENTITY, MT_TemplateModification, { "player": player, "component": value.split("/")[0], "valueNames": [value] });
    7677    }
    7778};
    7879
     
    9899        this.modificationsCache[value][ent].multiply /= data.multiply;
    99100
    100101    // post message to the entity to notify it about the change
    101     Engine.PostMessage(ent, MT_ValueModification, { "component": value.split("/")[0] });
     102    var effects = {};
     103    effects[value] = this.modificationsCache[value][ent];
     104    Engine.PostMessage(ent, MT_ValueModification, { "entities": [ent], "component": value.split("/")[0], "valueNames": [value] });
    102105};
    103106
    104107AuraManager.prototype.RemoveTemplateBonus = function(value, player, classes, key)
     
    119122        this.templateModificationsCache[value][player][c][key].multiply = 1;
    120123        this.templateModificationsCache[value][player][c][key].add = 0;
    121124    }
     125    Engine.PostMessage(SYSTEM_ENTITY, MT_TemplateModification, { "player": player, "component": value.split("/")[0], "valueNames": [value] });
    122126};
    123127
    124128AuraManager.prototype.ApplyModifications = function(valueName, value, ent)
  • binaries/data/mods/public/simulation/components/GuiInterface.js

     
    9797            "entityLimits": cmpPlayerEntityLimits.GetLimits(),
    9898            "entityCounts": cmpPlayerEntityLimits.GetCounts(),
    9999            "entityLimitChangers": cmpPlayerEntityLimits.GetLimitChangers(),
    100             "techModifications": cmpTechnologyManager.GetTechModifications(),
    101100            "researchQueued": cmpTechnologyManager.GetQueuedResearch(),
    102101            "researchStarted": cmpTechnologyManager.GetStartedResearch(),
    103102            "researchedTechs": cmpTechnologyManager.GetResearchedTechs(),
     
    475474    {
    476475        ret.armour = {
    477476            "hack": ApplyValueModificationsToTemplate("Armour/Hack", +template.Armour.Hack, player, template),
    478             "pierce": ApplyValueModificationsToTemplate("Armour/Pierce", +template.Armour.Hack, player, template),
    479             "crush": ApplyValueModificationsToTemplate("Armour/Crush", +template.Armour.Hack, player, template),
     477            "pierce": ApplyValueModificationsToTemplate("Armour/Pierce", +template.Armour.Pierce, player, template),
     478            "crush": ApplyValueModificationsToTemplate("Armour/Crush", +template.Armour.Crush, player, template),
    480479        };
    481480    }
    482481   
  • binaries/data/mods/public/simulation/components/TechnologyManager.js

     
    226226                var modifications = this.modifications[name];
    227227                var component = name.split("/")[0];
    228228                for (var i in modifications)
    229                     if (!modifiedComponents[component] && DoesModificationApply(modifications[i], classes))
    230                         modifiedComponents[component] = true;
     229                    if (DoesModificationApply(modifications[i], classes))
     230                    {
     231                        if (!modifiedComponents[component])
     232                            modifiedComponents[component] = [];
     233                        modifiedComponents[component].push(name);
     234                    }
    231235            }
    232236
    233237            // Send mesage(s) to the entity so it knows about researched techs
    234238            for (var component in modifiedComponents)
    235                 Engine.PostMessage(msg.entity, MT_ValueModification, { "component": component });
     239                Engine.PostMessage(msg.entity, MT_ValueModification, { "entities": [msg.entity], "component": component, "valueNames": modifiedComponents[component] });
    236240        }
    237241    }
    238242    if (msg.from == playerID)
     
    322326                    mod[j] = modification[j];
    323327           
    324328            this.modifications[modification.value].push(mod);
    325             modifiedComponents[modification.value.split("/")[0]] = true;
     329            var component = modification.value.split("/")[0];
     330            if (!modifiedComponents[component])
     331                modifiedComponents[component] = [];
     332            modifiedComponents[component].push(modification.value);
    326333            this.modificationCache[modification.value] = {};
    327334        }
    328335    }
     
    329336   
    330337    this.UpdateAutoResearch();
    331338   
     339
     340    var cmpPlayer = Engine.QueryInterface(this.entity, IID_Player);
     341    if (!cmpPlayer || ! cmpPlayer.GetPlayerID())
     342        return;
     343    var playerID = cmpPlayer.GetPlayerID();
     344    var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
     345    var ents = cmpRangeManager.GetEntitiesByPlayer(playerID);
    332346    // TODO: Handle technology broadcasting for autoresearch properly (some components might not be initialized currently)
    333347    for (var component in modifiedComponents)
    334         Engine.BroadcastMessage(MT_ValueModification, { "component": component });
     348    {
     349        Engine.PostMessage(SYSTEM_ENTITY, MT_TemplateModification, { "player": playerID, "component": component, "valueNames": modifiedComponents[component]});
     350        Engine.BroadcastMessage(MT_ValueModification, { "entities": ents, "component": component, "valueNames": modifiedComponents[component]});
     351    }
    335352};
    336353
    337354// Clears the cached data for an entity from the modifications cache
  • binaries/data/mods/public/simulation/helpers/Commands.js

     
    11// Setting this to true will display some warnings when commands
    22//  are likely to fail, which may be useful for debugging AIs
    3 var g_DebugCommands = false;
     3var g_DebugCommands = true;
    44
    55function ProcessCommand(player, cmd)
    66{
  • source/simulation2/MessageTypes.h

     
    366366};
    367367
    368368/**
    369  * Sent by value modification manager when a value of a certain component is changed
     369 * Sent by aura manager when a value of a certain entity's component is changed
    370370 */
    371371class CMessageValueModification : public CMessage
    372372{
     
    373373public:
    374374    DEFAULT_MESSAGE_IMPL(ValueModification)
    375375
    376     CMessageValueModification(std::wstring component) :
    377         component(component)
     376    CMessageValueModification(const std::vector<entity_id_t>& entities, std::wstring component, const std::vector<std::wstring>& valueNames) :
     377        entities(entities),
     378        component(component),
     379        valueNames(valueNames)
    378380    {
    379381    }
    380382
     383    std::vector<entity_id_t> entities;
    381384    std::wstring component;
     385    std::vector<std::wstring> valueNames;
    382386};
    383387
    384388/**
     389 * Sent by aura and tech managers when a value of a certain template's component is changed
     390 */
     391class CMessageTemplateModification : public CMessage
     392{
     393public:
     394    DEFAULT_MESSAGE_IMPL(TemplateModification)
     395   
     396    CMessageTemplateModification(player_id_t player, std::wstring component, const std::vector<std::wstring>& valueNames) :
     397    player(player),
     398    component(component),
     399    valueNames(valueNames)
     400    {
     401    }
     402   
     403    player_id_t player;
     404    std::wstring component;
     405    std::vector<std::wstring> valueNames;
     406};
     407
     408/**
    385409 * Sent by CCmpVision when an entity's vision range changes.
    386410 */
    387411class CMessageVisionRangeChanged : public CMessage
  • source/simulation2/TypeList.h

     
    4848MESSAGE(TerritoriesChanged)
    4949MESSAGE(PathResult)
    5050MESSAGE(ValueModification)
     51MESSAGE(TemplateModification)
    5152MESSAGE(VisionRangeChanged)
    5253MESSAGE(MinimapPing)
    5354
  • source/simulation2/components/ICmpTemplateManager.cpp

     
    2323
    2424BEGIN_INTERFACE_WRAPPER(TemplateManager)
    2525DEFINE_INTERFACE_METHOD_1("GetTemplate", const CParamNode*, ICmpTemplateManager, GetTemplate, std::string)
     26DEFINE_INTERFACE_METHOD_1("GetTemplateWithoutValidation", const CParamNode*, ICmpTemplateManager, GetTemplateWithoutValidation, std::string)
    2627DEFINE_INTERFACE_METHOD_1("GetCurrentTemplateName", std::string, ICmpTemplateManager, GetCurrentTemplateName, entity_id_t)
    2728DEFINE_INTERFACE_METHOD_1("FindAllTemplates", std::vector<std::string>, ICmpTemplateManager, FindAllTemplates, bool)
    2829DEFINE_INTERFACE_METHOD_1("GetEntitiesUsingTemplate", std::vector<entity_id_t>, ICmpTemplateManager, GetEntitiesUsingTemplate, std::string)
  • source/simulation2/scripting/MessageTypeConversions.cpp

     
    303303jsval CMessageValueModification::ToJSVal(ScriptInterface& scriptInterface) const
    304304{
    305305    TOJSVAL_SETUP();
     306    SET_MSG_PROPERTY(entities);
    306307    SET_MSG_PROPERTY(component);
     308    SET_MSG_PROPERTY(valueNames);
    307309    return OBJECT_TO_JSVAL(obj);
    308310}
    309311
     
    310312CMessage* CMessageValueModification::FromJSVal(ScriptInterface& scriptInterface, jsval val)
    311313{
    312314    FROMJSVAL_SETUP();
     315    GET_MSG_PROPERTY(std::vector<entity_id_t>, entities);
    313316    GET_MSG_PROPERTY(std::wstring, component);
    314     return new CMessageValueModification(component);
     317    GET_MSG_PROPERTY(std::vector<std::wstring>, valueNames);
     318    return new CMessageValueModification(entities, component, valueNames);
    315319}
    316320
    317321////////////////////////////////
    318322
     323jsval CMessageTemplateModification::ToJSVal(ScriptInterface& scriptInterface) const
     324{
     325    TOJSVAL_SETUP();
     326    SET_MSG_PROPERTY(player);
     327    SET_MSG_PROPERTY(component);
     328    SET_MSG_PROPERTY(valueNames);
     329    return OBJECT_TO_JSVAL(obj);
     330}
     331
     332CMessage* CMessageTemplateModification::FromJSVal(ScriptInterface& scriptInterface, jsval val)
     333{
     334    FROMJSVAL_SETUP();
     335    GET_MSG_PROPERTY(player_id_t, player);
     336    GET_MSG_PROPERTY(std::wstring, component);
     337    GET_MSG_PROPERTY(std::vector<std::wstring>, valueNames);
     338    return new CMessageTemplateModification(player, component, valueNames);
     339}
     340
     341////////////////////////////////
     342
    319343jsval CMessageVisionRangeChanged::ToJSVal(ScriptInterface& scriptInterface) const
    320344{
    321345    TOJSVAL_SETUP();