LCOV - code coverage report
Current view: top level - simulation/components - EntityLimits.js (source / functions) Hit Total Coverage
Test: lcov.info Lines: 124 132 93.9 %
Date: 2023-04-02 12:52:40 Functions: 16 16 100.0 %

          Line data    Source code
       1             : function EntityLimits() {}
       2             : 
       3           2 : EntityLimits.prototype.Schema =
       4             :         "<a:help>Specifies per category limits on number of entities (buildings or units) that can be created for each player</a:help>" +
       5             :         "<a:example>" +
       6             :                 "<Limits>" +
       7             :                         "<Apadana>1</Apadana>" +
       8             :                         "<Fortress>10</Fortress>" +
       9             :                         "<Hero>1</Hero>" +
      10             :                         "<Monument>5</Monument>" +
      11             :                         "<Tower>25</Tower>" +
      12             :                         "<Wonder>1</Wonder>" +
      13             :                 "</Limits>" +
      14             :                 "<LimitChangers>" +
      15             :                         "<Monument>" +
      16             :                                 "<CivilCentre>2</CivilCentre>" +
      17             :                         "</Monument>" +
      18             :                 "</LimitChangers>" +
      19             :                 "<LimitRemovers>" +
      20             :                         "<CivilCentre>" +
      21             :                                 "<RequiredTechs datatype=\"tokens\">town_phase</RequiredTechs>" +
      22             :                         "</CivilCentre>" +
      23             :                 "</LimitRemovers>" +
      24             :         "</a:example>" +
      25             :         "<element name='Limits'>" +
      26             :                 "<zeroOrMore>" +
      27             :                         "<element a:help='Specifies a category of building/unit on which to apply this limit. See BuildRestrictions/TrainingRestrictions for possible categories'>" +
      28             :                                 "<anyName />" +
      29             :                                 "<data type='integer'/>" +
      30             :                         "</element>" +
      31             :                 "</zeroOrMore>" +
      32             :         "</element>" +
      33             :         "<element name='LimitChangers'>" +
      34             :                 "<zeroOrMore>" +
      35             :                         "<element a:help='Specifies a category of building/unit on which to apply this limit. See BuildRestrictions/TrainingRestrictions for possible categories'>" +
      36             :                                 "<anyName />" +
      37             :                                 "<zeroOrMore>" +
      38             :                                         "<element a:help='Specifies the class that changes the entity limit'>" +
      39             :                                                 "<anyName />" +
      40             :                                                 "<data type='integer'/>" +
      41             :                                         "</element>" +
      42             :                                 "</zeroOrMore>" +
      43             :                         "</element>" +
      44             :                 "</zeroOrMore>" +
      45             :         "</element>" +
      46             :         "<element name='LimitRemovers'>" +
      47             :                 "<zeroOrMore>" +
      48             :                         "<element a:help='Specifies a category of building/unit on which to remove this limit. The limit will be removed if all the followings requirements are satisfied'>" +
      49             :                                 "<anyName />" +
      50             :                                 "<oneOrMore>" +
      51             :                                         "<element a:help='Possible requirements are: RequiredTechs and RequiredClasses'>" +
      52             :                                                 "<anyName />" +
      53             :                                                 "<attribute name='datatype'>" +
      54             :                                                         "<value>tokens</value>" +
      55             :                                                 "</attribute>" +
      56             :                                                 "<text/>" +
      57             :                                         "</element>" +
      58             :                                 "</oneOrMore>" +
      59             :                         "</element>" +
      60             :                 "</zeroOrMore>" +
      61             :         "</element>";
      62             : 
      63             : 
      64           2 : const TRAINING = "training";
      65           2 : const BUILD = "build";
      66             : 
      67           2 : EntityLimits.prototype.Init = function()
      68             : {
      69           3 :         this.limit = {};
      70             :         // Counts entities which change the limit of the given category.
      71           3 :         this.count = {};
      72           3 :         this.changers = {};
      73           3 :         this.removers = {};
      74             :         // Counts entities with the given class, used in the limit removal.
      75           3 :         this.classCount = {};
      76           3 :         this.removedLimit = {};
      77           3 :         this.matchTemplateCount = {};
      78           3 :         for (var category in this.template.Limits)
      79             :         {
      80           6 :                 this.limit[category] = +this.template.Limits[category];
      81           6 :                 this.count[category] = 0;
      82           6 :                 if (category in this.template.LimitChangers)
      83             :                 {
      84           1 :                         this.changers[category] = {};
      85           1 :                         for (var c in this.template.LimitChangers[category])
      86           1 :                                 this.changers[category][c] = +this.template.LimitChangers[category][c];
      87             :                 }
      88           6 :                 if (category in this.template.LimitRemovers)
      89             :                 {
      90             :                         // Keep a copy of removable limits for possible restoration.
      91           2 :                         this.removedLimit[category] = this.limit[category];
      92           2 :                         this.removers[category] = {};
      93           2 :                         for (var c in this.template.LimitRemovers[category])
      94             :                         {
      95           2 :                                 this.removers[category][c] = this.template.LimitRemovers[category][c]._string.split(/\s+/);
      96           2 :                                 if (c === "RequiredClasses")
      97           1 :                                         for (var cls of this.removers[category][c])
      98           1 :                                                 this.classCount[cls] = 0;
      99             :                         }
     100             :                 }
     101             :         }
     102             : };
     103             : 
     104           2 : EntityLimits.prototype.ChangeCount = function(category, value)
     105             : {
     106          26 :         if (this.count[category] !== undefined)
     107          26 :                 this.count[category] += value;
     108             : };
     109             : 
     110           2 : EntityLimits.prototype.ChangeMatchCount = function(template, value)
     111             : {
     112           8 :         if (!this.matchTemplateCount[template])
     113           3 :                 this.matchTemplateCount[template] = 0;
     114             : 
     115           8 :         this.matchTemplateCount[template] += value;
     116             : };
     117             : 
     118           2 : EntityLimits.prototype.GetLimits = function()
     119             : {
     120          13 :         return this.limit;
     121             : };
     122             : 
     123           2 : EntityLimits.prototype.GetCounts = function()
     124             : {
     125          17 :         return this.count;
     126             : };
     127             : 
     128           2 : EntityLimits.prototype.GetMatchCounts = function()
     129             : {
     130           4 :         return this.matchTemplateCount;
     131             : };
     132             : 
     133           2 : EntityLimits.prototype.GetLimitChangers = function()
     134             : {
     135           1 :         return this.changers;
     136             : };
     137             : 
     138           2 : EntityLimits.prototype.UpdateLimitsFromTech = function(tech)
     139             : {
     140           3 :         for (var category in this.removers)
     141           6 :                 if ("RequiredTechs" in this.removers[category] && this.removers[category].RequiredTechs.indexOf(tech) !== -1)
     142           1 :                         this.removers[category].RequiredTechs.splice(this.removers[category].RequiredTechs.indexOf(tech), 1);
     143             : 
     144           3 :         this.UpdateLimitRemoval();
     145             : };
     146             : 
     147           2 : EntityLimits.prototype.UpdateLimitRemoval = function()
     148             : {
     149          10 :         for (var category in this.removers)
     150             :         {
     151          18 :                 var nolimit = true;
     152          18 :                 if ("RequiredTechs" in this.removers[category])
     153           9 :                         nolimit = !this.removers[category].RequiredTechs.length;
     154          18 :                 if (nolimit && "RequiredClasses" in this.removers[category])
     155           9 :                         for (var cls of this.removers[category].RequiredClasses)
     156           9 :                                 nolimit = nolimit && this.classCount[cls] > 0;
     157             : 
     158          18 :                 if (nolimit && this.limit[category] !== undefined)
     159           3 :                         this.limit[category] = undefined;
     160          15 :                 else if (!nolimit && this.limit[category] === undefined)
     161           2 :                         this.limit[category] = this.removedLimit[category];
     162             :         }
     163             : };
     164             : 
     165           2 : EntityLimits.prototype.AllowedToCreate = function(limitType, category, count, templateName, matchLimit)
     166             : {
     167          21 :         if (this.count[category] !== undefined && this.limit[category] !== undefined &&
     168             :                 this.count[category] + count > this.limit[category])
     169             :         {
     170           5 :                 this.NotifyLimit(limitType, category, this.limit[category]);
     171           5 :                 return false;
     172             :         }
     173             : 
     174          16 :         if (this.matchTemplateCount[templateName] !== undefined && matchLimit !== undefined &&
     175             :                 this.matchTemplateCount[templateName] + count > matchLimit)
     176             :         {
     177           0 :                 this.NotifyLimit(limitType, category, matchLimit);
     178           0 :                 return false;
     179             :         }
     180             : 
     181          16 :         return true;
     182             : };
     183             : 
     184           2 : EntityLimits.prototype.NotifyLimit = function(limitType, category, limit)
     185             : {
     186           5 :         let cmpPlayer = Engine.QueryInterface(this.entity, IID_Player);
     187           5 :         let notification = {
     188             :                 "players": [cmpPlayer.GetPlayerID()],
     189             :                 "translateMessage": true,
     190             :                 "translateParameters": ["category"],
     191             :                 "parameters": { "category": category, "limit": limit },
     192             :         };
     193             : 
     194           5 :         if (limitType == BUILD)
     195           1 :                 notification.message = markForTranslation("%(category)s build limit of %(limit)s reached");
     196           4 :         else if (limitType == TRAINING)
     197           4 :                 notification.message = markForTranslation("%(category)s training limit of %(limit)s reached");
     198             :         else
     199             :         {
     200           0 :                 warn("EntityLimits.js: Unknown LimitType " + limitType);
     201           0 :                 notification.message = markForTranslation("%(category)s limit of %(limit)s reached");
     202             :         }
     203           5 :         let cmpGUIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
     204           5 :         cmpGUIInterface.PushNotification(notification);
     205             : };
     206             : 
     207           2 : EntityLimits.prototype.AllowedToBuild = function(category)
     208             : {
     209             :         // We pass count 0 as the creation of the building has already taken place and
     210             :         // the ownership has been set (triggering OnGlobalOwnershipChanged)
     211           1 :         return this.AllowedToCreate(BUILD, category, 0);
     212             : };
     213             : 
     214           2 : EntityLimits.prototype.AllowedToTrain = function(category, count, templateName, matchLimit)
     215             : {
     216          16 :         return this.AllowedToCreate(TRAINING, category, count, templateName, matchLimit);
     217             : };
     218             : 
     219             : /**
     220             :  * @param {number} ent - id of the entity which would be replaced.
     221             :  * @param {string} template - name of the new template.
     222             :  * @return {boolean} - whether we can replace ent.
     223             :  */
     224           2 : EntityLimits.prototype.AllowedToReplace = function(ent, template)
     225             : {
     226           4 :         let cmpTemplateManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager);
     227           4 :         let templateFrom = cmpTemplateManager.GetTemplate(cmpTemplateManager.GetCurrentTemplateName(ent));
     228           4 :         let templateTo = cmpTemplateManager.GetTemplate(template);
     229             : 
     230           4 :         if (templateTo.TrainingRestrictions)
     231             :         {
     232           2 :                 let category = templateTo.TrainingRestrictions.Category;
     233           2 :                 return this.AllowedToCreate(TRAINING, category, templateFrom.TrainingRestrictions && templateFrom.TrainingRestrictions.Category == category ? 0 : 1);
     234             :         }
     235             : 
     236           2 :         if (templateTo.BuildRestrictions)
     237             :         {
     238           2 :                 let category = templateTo.BuildRestrictions.Category;
     239           2 :                 return this.AllowedToCreate(BUILD, category, templateFrom.BuildRestrictions && templateFrom.BuildRestrictions.Category == category ? 0 : 1);
     240             :         }
     241             : 
     242           0 :         return true;
     243             : };
     244             : 
     245           2 : EntityLimits.prototype.OnGlobalOwnershipChanged = function(msg)
     246             : {
     247             :         // check if we are adding or removing an entity from this player
     248          19 :         var cmpPlayer = Engine.QueryInterface(this.entity, IID_Player);
     249          19 :         if (!cmpPlayer)
     250             :         {
     251           0 :                 error("EntityLimits component is defined on a non-player entity");
     252           0 :                 return;
     253             :         }
     254          19 :         if (msg.from == cmpPlayer.GetPlayerID())
     255           9 :                 var modifier = -1;
     256          10 :         else if (msg.to == cmpPlayer.GetPlayerID())
     257          10 :                 var modifier = 1;
     258             :         else
     259           0 :                 return;
     260             : 
     261             :         // Update entity counts
     262          19 :         var category = null;
     263          19 :         var cmpBuildRestrictions = Engine.QueryInterface(msg.entity, IID_BuildRestrictions);
     264          19 :         if (cmpBuildRestrictions)
     265           2 :                 category = cmpBuildRestrictions.GetCategory();
     266          19 :         var cmpTrainingRestrictions = Engine.QueryInterface(msg.entity, IID_TrainingRestrictions);
     267          19 :         if (cmpTrainingRestrictions)
     268          11 :                 category = cmpTrainingRestrictions.GetCategory();
     269          19 :         if (category)
     270          13 :                 this.ChangeCount(category, modifier);
     271             : 
     272             :         // Update entity limits
     273          19 :         var cmpIdentity = Engine.QueryInterface(msg.entity, IID_Identity);
     274          19 :         if (!cmpIdentity)
     275          10 :                 return;
     276             : 
     277             :         // foundations shouldn't change the entity limits until they're completed
     278           9 :         var cmpFoundation = Engine.QueryInterface(msg.entity, IID_Foundation);
     279           9 :         if (cmpFoundation)
     280           2 :                 return;
     281           7 :         var classes = cmpIdentity.GetClassesList();
     282           7 :         for (var category in this.changers)
     283           6 :                 for (var c in this.changers[category])
     284           6 :                         if (classes.indexOf(c) >= 0)
     285             :                         {
     286           2 :                                 if (this.limit[category] != undefined)
     287           2 :                                         this.limit[category] += modifier * this.changers[category][c];
     288           2 :                                 if (this.removedLimit[category] != undefined)   // update removed limits in case we want to restore it
     289           2 :                                         this.removedLimit[category] += modifier * this.changers[category][c];
     290             :                         }
     291             : 
     292           7 :         for (var category in this.removers)
     293          12 :                 if ("RequiredClasses" in this.removers[category])
     294           6 :                         for (var cls of this.removers[category].RequiredClasses)
     295           6 :                                 if (classes.indexOf(cls) !== -1)
     296           4 :                                         this.classCount[cls] += modifier;
     297             : 
     298           7 :         this.UpdateLimitRemoval();
     299             : };
     300             : 
     301           2 : Engine.RegisterComponentType(IID_EntityLimits, "EntityLimits", EntityLimits);

Generated by: LCOV version 1.14