LCOV - code coverage report
Current view: top level - simulation/components - AIInterface.js (source / functions) Hit Total Coverage
Test: lcov.info Lines: 0 161 0.0 %
Date: 2023-04-02 12:52:40 Functions: 0 19 0.0 %

          Line data    Source code
       1             : function AIInterface() {}
       2             : 
       3           0 : AIInterface.prototype.Schema =
       4             :         "<a:component type='system'/><empty/>";
       5             : 
       6           0 : AIInterface.prototype.EventNames = [
       7             :         "Create",
       8             :         "Destroy",
       9             :         "Attacked",
      10             :         "ConstructionFinished",
      11             :         "DiplomacyChanged",
      12             :         "TrainingStarted",
      13             :         "TrainingFinished",
      14             :         "AIMetadata",
      15             :         "PlayerDefeated",
      16             :         "EntityRenamed",
      17             :         "ValueModification",
      18             :         "OwnershipChanged",
      19             :         "Garrison",
      20             :         "UnGarrison",
      21             :         "TerritoriesChanged",
      22             :         "TerritoryDecayChanged",
      23             :         "TributeExchanged",
      24             :         "AttackRequest",
      25             :         "CeasefireEnded",
      26             :         "DiplomacyRequest",
      27             :         "TributeRequest"
      28             : ];
      29             : 
      30           0 : AIInterface.prototype.Init = function()
      31             : {
      32           0 :         this.events = {};
      33           0 :         for (let name of this.EventNames)
      34           0 :                 this.events[name] = [];
      35             : 
      36           0 :         this.changedEntities = {};
      37             : 
      38             :         // cache for technology changes;
      39             :         // this one is PlayerID->TemplateName->{StringForTheValue, ActualValue}
      40           0 :         this.changedTemplateInfo = {};
      41             :         // this is for auras and is EntityID->{StringForTheValue, ActualValue}
      42           0 :         this.changedEntityTemplateInfo = {};
      43           0 :         this.enabled = true;
      44             : };
      45             : 
      46           0 : AIInterface.prototype.Serialize = function()
      47             : {
      48           0 :         let state = {};
      49           0 :         for (var key in this)
      50             :         {
      51           0 :                 if (!this.hasOwnProperty(key))
      52           0 :                         continue;
      53           0 :                 if (typeof this[key] == "function")
      54           0 :                         continue;
      55           0 :                 if (key == "templates")
      56           0 :                         continue;
      57           0 :                 state[key] = this[key];
      58             :         }
      59           0 :         return state;
      60             : };
      61             : 
      62           0 : AIInterface.prototype.Deserialize = function(data)
      63             : {
      64           0 :         for (let key in data)
      65             :         {
      66           0 :                 if (!data.hasOwnProperty(key))
      67           0 :                         continue;
      68           0 :                 this[key] = data[key];
      69             :         }
      70           0 :         if (!this.enabled)
      71           0 :                 this.Disable();
      72             : };
      73             : 
      74             : /**
      75             :  * Disable all registering functions for this component
      76             :  * Gets called in case no AI players are present to save resources
      77             :  */
      78           0 : AIInterface.prototype.Disable = function()
      79             : {
      80           0 :         this.enabled = false;
      81           0 :         let nop = function(){};
      82           0 :         this.ChangedEntity = nop;
      83           0 :         this.PushEvent = nop;
      84           0 :         this.OnGlobalPlayerDefeated = nop;
      85           0 :         this.OnGlobalEntityRenamed = nop;
      86           0 :         this.OnGlobalTributeExchanged = nop;
      87           0 :         this.OnTemplateModification = nop;
      88           0 :         this.OnGlobalValueModification = nop;
      89             : };
      90             : 
      91           0 : AIInterface.prototype.GetNonEntityRepresentation = function()
      92             : {
      93           0 :         let cmpGuiInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
      94             : 
      95             :         // Return the same game state as the GUI uses
      96           0 :         let state = cmpGuiInterface.GetSimulationState();
      97             : 
      98             :         // Add some extra AI-specific data
      99             :         // add custom events and reset them for the next turn
     100           0 :         state.events = {};
     101           0 :         for (let name of this.EventNames)
     102             :         {
     103           0 :                 state.events[name] = this.events[name];
     104           0 :                 this.events[name] = [];
     105             :         }
     106             : 
     107           0 :         return state;
     108             : };
     109             : 
     110           0 : AIInterface.prototype.GetRepresentation = function()
     111             : {
     112           0 :         let state = this.GetNonEntityRepresentation();
     113             : 
     114             :         // Add entity representations
     115           0 :         Engine.ProfileStart("proxy representations");
     116           0 :         state.entities = {};
     117           0 :         for (let id in this.changedEntities)
     118             :         {
     119           0 :                 let cmpAIProxy = Engine.QueryInterface(+id, IID_AIProxy);
     120           0 :                 if (cmpAIProxy)
     121           0 :                         state.entities[id] = cmpAIProxy.GetRepresentation();
     122             :         }
     123           0 :         this.changedEntities = {};
     124           0 :         Engine.ProfileStop();
     125             : 
     126           0 :         state.changedTemplateInfo = this.changedTemplateInfo;
     127           0 :         this.changedTemplateInfo = {};
     128           0 :         state.changedEntityTemplateInfo = this.changedEntityTemplateInfo;
     129           0 :         this.changedEntityTemplateInfo = {};
     130             : 
     131           0 :         return state;
     132             : };
     133             : 
     134             : /**
     135             :  * Intended to be called first, during the map initialization: no caching
     136             :  */
     137           0 : AIInterface.prototype.GetFullRepresentation = function(flushEvents)
     138             : {
     139           0 :         let state = this.GetNonEntityRepresentation();
     140             : 
     141           0 :         if (flushEvents)
     142           0 :                 for (let name of this.EventNames)
     143           0 :                         state.events[name] = [];
     144             : 
     145             :         // Add entity representations
     146           0 :         Engine.ProfileStart("proxy representations");
     147           0 :         state.entities = {};
     148             :         // all entities are changed in the initial state.
     149           0 :         for (let id of Engine.GetEntitiesWithInterface(IID_AIProxy))
     150           0 :                 state.entities[id] = Engine.QueryInterface(id, IID_AIProxy).GetFullRepresentation();
     151           0 :         Engine.ProfileStop();
     152             : 
     153           0 :         state.changedTemplateInfo = this.changedTemplateInfo;
     154           0 :         this.changedTemplateInfo = {};
     155           0 :         state.changedEntityTemplateInfo = this.changedEntityTemplateInfo;
     156           0 :         this.changedEntityTemplateInfo = {};
     157             : 
     158           0 :         return state;
     159             : };
     160             : 
     161           0 : AIInterface.prototype.ChangedEntity = function(ent)
     162             : {
     163           0 :         this.changedEntities[ent] = 1;
     164             : };
     165             : 
     166             : /**
     167             :  * AIProxy sets up a load of event handlers to capture interesting things going on
     168             :  * in the world, which we will report to AI. Handle those, and add a few more handlers
     169             :  * for events that AIProxy won't capture.
     170             :  */
     171           0 : AIInterface.prototype.PushEvent = function(type, msg)
     172             : {
     173           0 :         if (this.events[type] === undefined)
     174           0 :                 warn("Tried to push unknown event type " + type +", please add it to AIInterface.js");
     175           0 :         this.events[type].push(msg);
     176             : };
     177             : 
     178           0 : AIInterface.prototype.OnDiplomacyChanged = function(msg)
     179             : {
     180           0 :         this.events.DiplomacyChanged.push(msg);
     181             : };
     182             : 
     183           0 : AIInterface.prototype.OnGlobalPlayerDefeated = function(msg)
     184             : {
     185           0 :         this.events.PlayerDefeated.push(msg);
     186             : };
     187             : 
     188           0 : AIInterface.prototype.OnGlobalEntityRenamed = function(msg)
     189             : {
     190           0 :         if (!Engine.QueryInterface(msg.entity, IID_Mirage))
     191           0 :                 this.events.EntityRenamed.push(msg);
     192             : };
     193             : 
     194           0 : AIInterface.prototype.OnGlobalTributeExchanged = function(msg)
     195             : {
     196           0 :         this.events.TributeExchanged.push(msg);
     197             : };
     198             : 
     199           0 : AIInterface.prototype.OnTerritoriesChanged = function(msg)
     200             : {
     201           0 :         this.events.TerritoriesChanged.push(msg);
     202             : };
     203             : 
     204           0 : AIInterface.prototype.OnCeasefireEnded = function(msg)
     205             : {
     206           0 :         this.events.CeasefireEnded.push(msg);
     207             : };
     208             : 
     209             : /**
     210             :  * When a new technology is researched, check which templates it affects,
     211             :  * and send the updated values to the AI.
     212             :  * this relies on the fact that any "value" in a technology can only ever change
     213             :  * one template value, and that the naming is the same (with / in place of .)
     214             :  */
     215           0 : AIInterface.prototype.OnTemplateModification = function(msg)
     216             : {
     217           0 :         let cmpTemplateManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager);
     218           0 :         if (!this.templates)
     219             :         {
     220           0 :                 this.templates = [];
     221           0 :                 for (let templateName of cmpTemplateManager.FindAllTemplates(false))
     222             :                 {
     223             :                         // Remove templates that we obviously don't care about.
     224           0 :                         if (templateName.startsWith("campaigns/") || templateName.startsWith("rubble/") ||
     225             :                             templateName.startsWith("skirmish/"))
     226           0 :                                 continue;
     227           0 :                         let template = cmpTemplateManager.GetTemplateWithoutValidation(templateName);
     228           0 :                         if (!template || !template.Identity || !template.Identity.Civ)
     229           0 :                                 continue;
     230           0 :                         this.templates.push(templateName);
     231             :                 }
     232             :         }
     233             : 
     234           0 :         for (let name of this.templates)
     235             :         {
     236           0 :                 let template = cmpTemplateManager.GetTemplateWithoutValidation(name);
     237           0 :                 if (!template || !template[msg.component])
     238           0 :                         continue;
     239           0 :                 for (let valName of msg.valueNames)
     240             :                 {
     241             :                         // let's get the base template value.
     242           0 :                         let strings = valName.split("/");
     243           0 :                         let item = template;
     244           0 :                         let ended = true;
     245           0 :                         for (let str of strings)
     246             :                         {
     247           0 :                                 if (item !== undefined && item[str] !== undefined)
     248           0 :                                         item = item[str];
     249             :                                 else
     250           0 :                                         ended = false;
     251             :                         }
     252           0 :                         if (!ended)
     253           0 :                                 continue;
     254             :                         // item now contains the template value for this.
     255           0 :                         let oldValue = +item == item ? +item : item;
     256           0 :                         let newValue = ApplyValueModificationsToTemplate(valName, oldValue, msg.player, template);
     257             :                         // Apply the same roundings as in the components
     258           0 :                         if (valName === "Player/MaxPopulation" || valName === "Cost/Population" ||
     259             :                             valName === "Population/Bonus")
     260           0 :                                 newValue = Math.round(newValue);
     261             :                         // TODO in some cases, we can have two opposite changes which bring us to the old value,
     262             :                         // and we should keep it. But how to distinguish it ?
     263           0 :                         if(newValue == oldValue)
     264           0 :                                 continue;
     265           0 :                         if (!this.changedTemplateInfo[msg.player])
     266           0 :                                 this.changedTemplateInfo[msg.player] = {};
     267           0 :                         if (!this.changedTemplateInfo[msg.player][name])
     268           0 :                                 this.changedTemplateInfo[msg.player][name] = [{ "variable": valName, "value": newValue }];
     269             :                         else
     270           0 :                                 this.changedTemplateInfo[msg.player][name].push({ "variable": valName, "value": newValue });
     271             :                 }
     272             :         }
     273             : };
     274             : 
     275           0 : AIInterface.prototype.OnGlobalValueModification = function(msg)
     276             : {
     277           0 :         this.events.ValueModification.push(msg);
     278           0 :         let cmpTemplateManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager);
     279           0 :         for (let ent of msg.entities)
     280             :         {
     281           0 :                 let templateName = cmpTemplateManager.GetCurrentTemplateName(ent);
     282             :                 // if there's no template name, the unit is probably killed, ignore it.
     283           0 :                 if (!templateName || !templateName.length)
     284           0 :                         continue;
     285           0 :                 let template = cmpTemplateManager.GetTemplateWithoutValidation(templateName);
     286           0 :                 if (!template || !template[msg.component])
     287           0 :                         continue;
     288           0 :                 for (let valName of msg.valueNames)
     289             :                 {
     290             :                         // let's get the base template value.
     291           0 :                         let strings = valName.split("/");
     292           0 :                         let item = template;
     293           0 :                         let ended = true;
     294           0 :                         for (let str of strings)
     295             :                         {
     296           0 :                                 if (item !== undefined && item[str] !== undefined)
     297           0 :                                         item = item[str];
     298             :                                 else
     299           0 :                                         ended = false;
     300             :                         }
     301           0 :                         if (!ended)
     302           0 :                                 continue;
     303             :                         // "item" now contains the unmodified template value for this.
     304           0 :                         let oldValue = +item == item ? +item : item;
     305           0 :                         let newValue = ApplyValueModificationsToEntity(valName, oldValue, ent);
     306             :                         // Apply the same roundings as in the components
     307           0 :                         if (valName === "Player/MaxPopulation" || valName === "Cost/Population" ||
     308             :                             valName === "Population/Bonus")
     309           0 :                                 newValue = Math.round(newValue);
     310             :                         // TODO in some cases, we can have two opposite changes which bring us to the old value,
     311             :                         // and we should keep it. But how to distinguish it ?
     312           0 :                         if (newValue == oldValue)
     313           0 :                                 continue;
     314           0 :                         if (!this.changedEntityTemplateInfo[ent])
     315           0 :                                 this.changedEntityTemplateInfo[ent] = [{ "variable": valName, "value": newValue }];
     316             :                         else
     317           0 :                                 this.changedEntityTemplateInfo[ent].push({ "variable": valName, "value": newValue });
     318             :                 }
     319             :         }
     320             : };
     321             : 
     322           0 : Engine.RegisterSystemComponentType(IID_AIInterface, "AIInterface", AIInterface);

Generated by: LCOV version 1.14