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

          Line data    Source code
       1             : function AIProxy() {}
       2             : 
       3           0 : AIProxy.prototype.Schema =
       4             :         "<empty/>";
       5             : 
       6             : /**
       7             :  * AIProxy passes its entity's state data to AI scripts.
       8             :  *
       9             :  * Efficiency is critical: there can be many thousands of entities,
      10             :  * and the data returned by this component is serialized and copied to
      11             :  * the AI thread every turn, so it can be quite expensive.
      12             :  *
      13             :  * We omit all data that can be derived statically from the template XML
      14             :  * files - the AI scripts can parse the templates themselves.
      15             :  * This violates the component interface abstraction and is potentially
      16             :  * fragile if the template formats change (since both the component code
      17             :  * and the AI will have to be updated in sync), but it's not *that* bad
      18             :  * really and it helps performance significantly.
      19             :  *
      20             :  * We also add an optimisation to avoid copying non-changing values.
      21             :  * The first call to GetRepresentation calls GetFullRepresentation,
      22             :  * which constructs the complete entity state representation.
      23             :  * After that, we simply listen to events from the rest of the gameplay code,
      24             :  * and store the changed data in this.changes.
      25             :  * Properties in this.changes will override those previously returned
      26             :  * from GetRepresentation; if a property isn't overridden then the AI scripts
      27             :  * will keep its old value.
      28             :  *
      29             :  * The event handlers should set this.changes.whatever to exactly the
      30             :  * same as GetFullRepresentation would set.
      31             :  */
      32             : 
      33           0 : AIProxy.prototype.Init = function()
      34             : {
      35           0 :         this.changes = null;
      36           0 :         this.needsFullGet = true;
      37           0 :         this.cmpAIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_AIInterface);
      38             : };
      39             : 
      40           0 : AIProxy.prototype.Serialize = null; // we have no dynamic state to save
      41             : 
      42           0 : AIProxy.prototype.Deserialize = function()
      43             : {
      44           0 :         this.Init();
      45             : };
      46             : 
      47           0 : AIProxy.prototype.GetRepresentation = function()
      48             : {
      49             :         // Return the full representation the first time we're called
      50             :         let ret;
      51           0 :         if (this.needsFullGet)
      52           0 :                 ret = this.GetFullRepresentation();
      53             :         else
      54           0 :                 ret = this.changes;
      55             : 
      56             :         // Initialise changes to null instead of {}, to avoid memory allocations in the
      57             :         // common case where there will be no changes; event handlers should each reset
      58             :         // it to {} if needed
      59           0 :         this.changes = null;
      60             : 
      61           0 :         return ret;
      62             : };
      63             : 
      64           0 : AIProxy.prototype.NotifyChange = function()
      65             : {
      66           0 :         if (this.needsFullGet)
      67             :         {
      68             :                 // not yet notified, be sure that the owner is set before doing so
      69             :                 // as the Create event is sent only on first ownership changed
      70           0 :                 let cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership);
      71           0 :                 if (!cmpOwnership || cmpOwnership.GetOwner() < 0)
      72           0 :                         return false;
      73             :         }
      74             : 
      75           0 :         if (!this.changes)
      76             :         {
      77           0 :                 this.changes = {};
      78           0 :                 this.cmpAIInterface.ChangedEntity(this.entity);
      79             :         }
      80           0 :         return true;
      81             : };
      82             : 
      83             : // AI representation-updating event handlers:
      84             : 
      85           0 : AIProxy.prototype.OnPositionChanged = function(msg)
      86             : {
      87           0 :         if (!this.NotifyChange())
      88           0 :                 return;
      89             : 
      90           0 :         if (msg.inWorld)
      91             :         {
      92           0 :                 this.changes.position = [msg.x, msg.z];
      93           0 :                 this.changes.angle = msg.a;
      94             :         }
      95             :         else
      96             :         {
      97           0 :                 this.changes.position = undefined;
      98           0 :                 this.changes.angle = undefined;
      99             :         }
     100             : };
     101             : 
     102           0 : AIProxy.prototype.OnHealthChanged = function(msg)
     103             : {
     104           0 :         if (!this.NotifyChange())
     105           0 :                 return;
     106           0 :         this.changes.hitpoints = msg.to;
     107             : };
     108             : 
     109           0 : AIProxy.prototype.OnGarrisonedStateChanged = function(msg)
     110             : {
     111           0 :         if (!this.NotifyChange())
     112           0 :                 return;
     113           0 :         this.changes.garrisonHolderID = msg.holderID;
     114             : };
     115             : 
     116           0 : AIProxy.prototype.OnCapturePointsChanged = function(msg)
     117             : {
     118           0 :         if (!this.NotifyChange())
     119           0 :                 return;
     120           0 :         this.changes.capturePoints = msg.capturePoints;
     121             : };
     122             : 
     123           0 : AIProxy.prototype.OnInvulnerabilityChanged = function(msg)
     124             : {
     125           0 :         if (!this.NotifyChange())
     126           0 :                 return;
     127           0 :         this.changes.invulnerability = msg.invulnerability;
     128             : };
     129             : 
     130           0 : AIProxy.prototype.OnUnitIdleChanged = function(msg)
     131             : {
     132           0 :         if (!this.NotifyChange())
     133           0 :                 return;
     134           0 :         this.changes.idle = msg.idle;
     135             : };
     136             : 
     137           0 : AIProxy.prototype.OnUnitStanceChanged = function(msg)
     138             : {
     139           0 :         if (!this.NotifyChange())
     140           0 :                 return;
     141           0 :         this.changes.stance = msg.to;
     142             : };
     143             : 
     144           0 : AIProxy.prototype.OnUnitAIStateChanged = function(msg)
     145             : {
     146           0 :         if (!this.NotifyChange())
     147           0 :                 return;
     148           0 :         this.changes.unitAIState = msg.to;
     149             : };
     150             : 
     151           0 : AIProxy.prototype.OnUnitAIOrderDataChanged = function(msg)
     152             : {
     153           0 :         if (!this.NotifyChange())
     154           0 :                 return;
     155           0 :         this.changes.unitAIOrderData = msg.to;
     156             : };
     157             : 
     158           0 : AIProxy.prototype.OnProductionQueueChanged = function(msg)
     159             : {
     160           0 :         if (!this.NotifyChange())
     161           0 :                 return;
     162           0 :         let cmpProductionQueue = Engine.QueryInterface(this.entity, IID_ProductionQueue);
     163           0 :         this.changes.trainingQueue = cmpProductionQueue.GetQueue();
     164             : };
     165             : 
     166           0 : AIProxy.prototype.OnGarrisonedUnitsChanged = function(msg)
     167             : {
     168           0 :         if (!this.NotifyChange())
     169           0 :                 return;
     170             : 
     171           0 :         let cmpGarrisonHolder = Engine.QueryInterface(this.entity, IID_GarrisonHolder);
     172           0 :         this.changes.garrisoned = cmpGarrisonHolder.GetEntities();
     173             : 
     174             :         // Send a message telling a unit garrisoned or ungarrisoned.
     175             :         // I won't check if the unit is still alive so it'll be up to the AI.
     176           0 :         for (let ent of msg.added)
     177           0 :                 this.cmpAIInterface.PushEvent("Garrison", { "entity": ent, "holder": this.entity });
     178           0 :         for (let ent of msg.removed)
     179           0 :                 this.cmpAIInterface.PushEvent("UnGarrison", { "entity": ent, "holder": this.entity });
     180             : };
     181             : 
     182           0 : AIProxy.prototype.OnFoundationProgressChanged = function(msg)
     183             : {
     184           0 :         if (!this.NotifyChange())
     185           0 :                 return;
     186           0 :         this.changes.foundationProgress = msg.to;
     187             : };
     188             : 
     189           0 : AIProxy.prototype.OnFoundationBuildersChanged = function(msg)
     190             : {
     191           0 :         if (!this.NotifyChange())
     192           0 :                 return;
     193           0 :         this.changes.foundationBuilders = msg.to;
     194             : };
     195             : 
     196           0 : AIProxy.prototype.OnDropsiteSharingChanged = function(msg)
     197             : {
     198           0 :         if (!this.NotifyChange())
     199           0 :                 return;
     200           0 :         this.changes.sharedDropsite = msg.shared;
     201             : };
     202             : 
     203           0 : AIProxy.prototype.OnTerritoryDecayChanged = function(msg)
     204             : {
     205           0 :         if (!this.NotifyChange())
     206           0 :                 return;
     207           0 :         this.changes.decaying = msg.to;
     208           0 :         this.cmpAIInterface.PushEvent("TerritoryDecayChanged", msg);
     209             : };
     210             : 
     211             : // TODO: event handlers for all the other things
     212             : 
     213           0 : AIProxy.prototype.GetFullRepresentation = function()
     214             : {
     215           0 :         this.needsFullGet = false;
     216           0 :         let cmpTemplateManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager);
     217             : 
     218           0 :         let ret = {
     219             :                 // These properties are constant and won't need to be updated
     220             :                 "id": this.entity,
     221             :                 "template": cmpTemplateManager.GetCurrentTemplateName(this.entity)
     222             :         };
     223             : 
     224           0 :         let cmpPosition = Engine.QueryInterface(this.entity, IID_Position);
     225           0 :         if (cmpPosition)
     226             :         {
     227             :                 // Updated by OnPositionChanged
     228             : 
     229           0 :                 if (cmpPosition.IsInWorld())
     230             :                 {
     231           0 :                         let pos = cmpPosition.GetPosition2D();
     232           0 :                         ret.position = [pos.x, pos.y];
     233           0 :                         ret.angle = cmpPosition.GetRotation().y;
     234             :                 }
     235             :                 else
     236             :                 {
     237           0 :                         ret.position = undefined;
     238           0 :                         ret.angle = undefined;
     239             :                 }
     240             :         }
     241             : 
     242           0 :         let cmpHealth = Engine.QueryInterface(this.entity, IID_Health);
     243           0 :         if (cmpHealth)
     244             :         {
     245             :                 // Updated by OnHealthChanged
     246           0 :                 ret.hitpoints = cmpHealth.GetHitpoints();
     247             :         }
     248             : 
     249           0 :         let cmpResistance = Engine.QueryInterface(this.entity, IID_Resistance);
     250           0 :         if (cmpResistance)
     251           0 :                 ret.invulnerability = cmpResistance.IsInvulnerable();
     252             : 
     253           0 :         let cmpOwnership = Engine.QueryInterface(this.entity, IID_Ownership);
     254           0 :         if (cmpOwnership)
     255             :         {
     256             :                 // Updated by OnOwnershipChanged
     257           0 :                 ret.owner = cmpOwnership.GetOwner();
     258             :         }
     259             : 
     260           0 :         let cmpUnitAI = Engine.QueryInterface(this.entity, IID_UnitAI);
     261           0 :         if (cmpUnitAI)
     262             :         {
     263             :                 // Updated by OnUnitIdleChanged
     264           0 :                 ret.idle = cmpUnitAI.IsIdle();
     265             :                 // Updated by OnUnitStanceChanged
     266           0 :                 ret.stance = cmpUnitAI.GetStanceName();
     267             :                 // Updated by OnUnitAIStateChanged
     268           0 :                 ret.unitAIState = cmpUnitAI.GetCurrentState();
     269             :                 // Updated by OnUnitAIOrderDataChanged
     270           0 :                 ret.unitAIOrderData = cmpUnitAI.GetOrderData();
     271             :         }
     272             : 
     273           0 :         let cmpProductionQueue = Engine.QueryInterface(this.entity, IID_ProductionQueue);
     274           0 :         if (cmpProductionQueue)
     275             :         {
     276             :                 // Updated by OnProductionQueueChanged
     277           0 :                 ret.trainingQueue = cmpProductionQueue.GetQueue();
     278             :         }
     279             : 
     280           0 :         let cmpFoundation = Engine.QueryInterface(this.entity, IID_Foundation);
     281           0 :         if (cmpFoundation)
     282             :         {
     283             :                 // Updated by OnFoundationProgressChanged
     284           0 :                 ret.foundationProgress = cmpFoundation.GetBuildPercentage();
     285             :         }
     286             : 
     287           0 :         let cmpResourceDropsite = Engine.QueryInterface(this.entity, IID_ResourceDropsite);
     288           0 :         if (cmpResourceDropsite)
     289             :         {
     290             :                 // Updated by OnDropsiteSharingChanged
     291           0 :                 ret.sharedDropsite = cmpResourceDropsite.IsShared();
     292             :         }
     293             : 
     294           0 :         let cmpGarrisonHolder = Engine.QueryInterface(this.entity, IID_GarrisonHolder);
     295           0 :         if (cmpGarrisonHolder)
     296             :         {
     297             :                 // Updated by OnGarrisonedUnitsChanged
     298           0 :                 ret.garrisoned = cmpGarrisonHolder.GetEntities();
     299             :         }
     300             : 
     301           0 :         let cmpGarrisonable = Engine.QueryInterface(this.entity, IID_Garrisonable);
     302           0 :         if (cmpGarrisonable)
     303             :         {
     304             :                 // Updated by OnGarrisonedStateChanged
     305           0 :                 ret.garrisonHolderID = cmpGarrisonable.HolderID();
     306             :         }
     307             : 
     308           0 :         let cmpTerritoryDecay = Engine.QueryInterface(this.entity, IID_TerritoryDecay);
     309           0 :         if (cmpTerritoryDecay)
     310           0 :                 ret.decaying = cmpTerritoryDecay.IsDecaying();
     311             : 
     312           0 :         let cmpCapturable = Engine.QueryInterface(this.entity, IID_Capturable);
     313           0 :         if (cmpCapturable)
     314           0 :                 ret.capturePoints = cmpCapturable.GetCapturePoints();
     315             : 
     316           0 :         return ret;
     317             : };
     318             : 
     319             : // AI event handlers:
     320             : // (These are passed directly as events to the AI scripts, rather than updating
     321             : // our proxy representation.)
     322             : // (This shouldn't include extremely high-frequency events, like PositionChanged,
     323             : // because that would be very expensive and AI will rarely care about all those
     324             : // events.)
     325             : 
     326             : // special case: this changes the state and sends an event.
     327           0 : AIProxy.prototype.OnOwnershipChanged = function(msg)
     328             : {
     329           0 :         this.NotifyChange();
     330             : 
     331           0 :         if (msg.from == INVALID_PLAYER)
     332             :         {
     333           0 :                 this.cmpAIInterface.PushEvent("Create", { "entity": msg.entity });
     334           0 :                 return;
     335             :         }
     336           0 :         if (msg.to == INVALID_PLAYER)
     337             :         {
     338           0 :                 this.cmpAIInterface.PushEvent("Destroy", { "entity": msg.entity });
     339           0 :                 return;
     340             :         }
     341             : 
     342           0 :         this.changes.owner = msg.to;
     343           0 :         this.cmpAIInterface.PushEvent("OwnershipChanged", msg);
     344             : };
     345             : 
     346           0 : AIProxy.prototype.OnAttacked = function(msg)
     347             : {
     348           0 :         this.cmpAIInterface.PushEvent("Attacked", msg);
     349             : };
     350             : 
     351           0 : AIProxy.prototype.OnConstructionFinished = function(msg)
     352             : {
     353           0 :         this.cmpAIInterface.PushEvent("ConstructionFinished", msg);
     354             : };
     355             : 
     356           0 : AIProxy.prototype.OnTrainingStarted = function(msg)
     357             : {
     358           0 :         this.cmpAIInterface.PushEvent("TrainingStarted", msg);
     359             : };
     360             : 
     361           0 : AIProxy.prototype.OnTrainingFinished = function(msg)
     362             : {
     363           0 :         this.cmpAIInterface.PushEvent("TrainingFinished", msg);
     364             : };
     365             : 
     366           0 : AIProxy.prototype.OnAIMetadata = function(msg)
     367             : {
     368           0 :         this.cmpAIInterface.PushEvent("AIMetadata", msg);
     369             : };
     370             : 
     371           0 : Engine.RegisterComponentType(IID_AIProxy, "AIProxy", AIProxy);

Generated by: LCOV version 1.14