LCOV - code coverage report
Current view: top level - globalscripts - MultiKeyMap.js (source / functions) Hit Total Coverage
Test: lcov.info Lines: 85 87 97.7 %
Date: 2023-04-02 12:52:40 Functions: 19 19 100.0 %

          Line data    Source code
       1             : // Convenient container abstraction for storing items referenced by a 3-tuple.
       2             : // Used by the ModifiersManager to store items by (property Name, entity, item ID).
       3             : // Methods starting with an underscore are private to the storage.
       4             : // This supports stackable items as it stores count for each 3-tuple.
       5             : // It is designed to be as fast as can be for a JS container.
       6             : function MultiKeyMap()
       7             : {
       8          27 :         this.items = new Map();
       9             :         // Keys are referred to as 'primaryKey', 'secondaryKey', 'itemID'.
      10             : }
      11             : 
      12          72 : MultiKeyMap.prototype.Serialize = function()
      13             : {
      14           6 :         let ret = [];
      15           6 :         for (let primary of this.items.keys())
      16             :         {
      17             :                 // Keys of a Map can be arbitrary types whereas objects only support string, so use a list.
      18          16 :                 let vals = [primary, []];
      19          16 :                 ret.push(vals);
      20          16 :                 for (let secondary of this.items.get(primary).keys())
      21          24 :                         vals[1].push([secondary, this.items.get(primary).get(secondary)]);
      22             :         }
      23           6 :         return ret;
      24             : };
      25             : 
      26          72 : MultiKeyMap.prototype.Deserialize = function(data)
      27             : {
      28           6 :         for (let primary in data)
      29             :         {
      30          16 :                 this.items.set(data[primary][0], new Map());
      31          16 :                 for (let secondary in data[primary][1])
      32          24 :                         this.items.get(data[primary][0]).set(data[primary][1][secondary][0], data[primary][1][secondary][1]);
      33             :         }
      34             : };
      35             : 
      36             : /**
      37             :  * Add a single item.
      38             :  * NB: if you add an item with a different value but the same itemID, the original value remains.
      39             :  * @param item - an object.
      40             :  * @param itemID - internal ID of this item, for later removal and/or updating
      41             :  * @param stackable - if stackable, changing the count of items invalides, otherwise not.
      42             :  * @returns true if the items list changed in such a way that cached values are possibly invalidated.
      43             :  */
      44          72 : MultiKeyMap.prototype.AddItem = function(primaryKey, itemID, item, secondaryKey, stackable = false)
      45             : {
      46          60 :         if (!this._AddItem(primaryKey, itemID, item, secondaryKey, stackable))
      47           6 :                 return false;
      48             : 
      49          54 :         this._OnItemModified(primaryKey, secondaryKey, itemID);
      50          54 :         return true;
      51             : };
      52             : 
      53             : /**
      54             :  * Add items to multiple properties at once (only one item per property)
      55             :  * @param items - Dictionnary of { primaryKey: item }
      56             :  * @returns true if the items list changed in such a way that cached values are possibly invalidated.
      57             :  */
      58          72 : MultiKeyMap.prototype.AddItems = function(itemID, items, secondaryKey, stackable = false)
      59             : {
      60          13 :         let modified = false;
      61          13 :         for (let primaryKey in items)
      62          14 :                 modified = this.AddItem(primaryKey, itemID, items[primaryKey], secondaryKey, stackable) || modified;
      63          13 :         return modified;
      64             : };
      65             : 
      66             : /**
      67             :  * Removes a item on a property.
      68             :  * @param primaryKey - property to change (e.g. "Health/Max")
      69             :  * @param itemID - internal ID of the item to remove
      70             :  * @param secondaryKey - secondaryKey ID
      71             :  * @returns true if the items list changed in such a way that cached values are possibly invalidated.
      72             :  */
      73          72 : MultiKeyMap.prototype.RemoveItem = function(primaryKey, itemID, secondaryKey, stackable = false)
      74             : {
      75          25 :         if (!this._RemoveItem(primaryKey, itemID, secondaryKey, stackable))
      76           4 :                 return false;
      77             : 
      78          21 :         this._OnItemModified(primaryKey, secondaryKey, itemID);
      79          21 :         return true;
      80             : };
      81             : 
      82             : /**
      83             :  * Removes items with this ID for any property name.
      84             :  * Naively iterates all property names.
      85             :  * @returns true if the items list changed in such a way that cached values are possibly invalidated.
      86             :  */
      87          72 : MultiKeyMap.prototype.RemoveAllItems = function(itemID, secondaryKey, stackable = false)
      88             : {
      89           4 :         let modified = false;
      90             :         // Map doesn't implement some so use a for-loop here.
      91           4 :         for (let primaryKey of this.items.keys())
      92           4 :                 modified = this.RemoveItem(primaryKey, itemID, secondaryKey, stackable) || modified;
      93           4 :         return modified;
      94             : };
      95             : 
      96             : /**
      97             :  * @param itemID - internal ID of the item to try and find.
      98             :  * @returns true if there is at least one item with that itemID
      99             :  */
     100          72 : MultiKeyMap.prototype.HasItem = function(primaryKey, itemID, secondaryKey)
     101             : {
     102             :         // some() returns false for an empty list which is wanted here.
     103          64 :         return this._getItems(primaryKey, secondaryKey).some(item => item._ID === itemID);
     104             : };
     105             : 
     106             : /**
     107             :  * Check if we have a item for any property name.
     108             :  * Naively iterates all property names.
     109             :  * @returns true if there is at least one item with that itemID
     110             :  */
     111          72 : MultiKeyMap.prototype.HasAnyItem = function(itemID, secondaryKey)
     112             : {
     113             :         // Map doesn't implement some so use for loops instead.
     114          10 :         for (let primaryKey of this.items.keys())
     115          22 :                 if (this.HasItem(primaryKey, itemID, secondaryKey))
     116           5 :                         return true;
     117           5 :         return false;
     118             : };
     119             : 
     120             : /**
     121             :  * @returns A list of items (references to stored items to avoid copying)
     122             :  * (these need to be treated as constants to not break the map)
     123             :  */
     124          72 : MultiKeyMap.prototype.GetItems = function(primaryKey, secondaryKey)
     125             : {
     126          89 :         return this._getItems(primaryKey, secondaryKey);
     127             : };
     128             : 
     129             : /**
     130             :  * @returns A dictionary of { Property Name: items } for the secondary Key.
     131             :  * Naively iterates all property names.
     132             :  */
     133          72 : MultiKeyMap.prototype.GetAllItems = function(secondaryKey)
     134             : {
     135           3 :         let items = {};
     136             : 
     137             :         // Map doesn't implement filter so use a for loop.
     138           3 :         for (let primaryKey of this.items.keys())
     139             :         {
     140           7 :                 if (!this.items.get(primaryKey).has(secondaryKey))
     141           0 :                         continue;
     142           7 :                 items[primaryKey] = this.GetItems(primaryKey, secondaryKey);
     143             :         }
     144           3 :         return items;
     145             : };
     146             : 
     147             : /**
     148             :  * @returns a list of items.
     149             :  * This does not necessarily return a reference to items' list, use _getItemsOrInit for that.
     150             :  */
     151          72 : MultiKeyMap.prototype._getItems = function(primaryKey, secondaryKey)
     152             : {
     153         158 :         let cache = this.items.get(primaryKey);
     154         158 :         if (cache)
     155         140 :                 cache = cache.get(secondaryKey);
     156         158 :         return cache ? cache : [];
     157             : };
     158             : 
     159             : /**
     160             :  * @returns a reference to the list of items for that property name and secondaryKey.
     161             :  */
     162          72 : MultiKeyMap.prototype._getItemsOrInit = function(primaryKey, secondaryKey)
     163             : {
     164          60 :         let cache = this.items.get(primaryKey);
     165          60 :         if (!cache)
     166          26 :                 cache = this.items.set(primaryKey, new Map()).get(primaryKey);
     167             : 
     168          60 :         let cache2 = cache.get(secondaryKey);
     169          60 :         if (!cache2)
     170          38 :                 cache2 = cache.set(secondaryKey, []).get(secondaryKey);
     171          60 :         return cache2;
     172             : };
     173             : 
     174             : /**
     175             :  * @returns true if the items list changed in such a way that cached values are possibly invalidated.
     176             :  */
     177          72 : MultiKeyMap.prototype._AddItem = function(primaryKey, itemID, item, secondaryKey, stackable)
     178             : {
     179          60 :         let items = this._getItemsOrInit(primaryKey, secondaryKey);
     180          60 :         for (let it of items)
     181          42 :                 if (it._ID == itemID)
     182             :                 {
     183           8 :                         it._count++;
     184           8 :                         return stackable;
     185             :                 }
     186          52 :         items.push({ "_ID": itemID, "_count": 1, "value": item });
     187          52 :         return true;
     188             : };
     189             : 
     190             : /**
     191             :  * @returns true if the items list changed in such a way that cached values are possibly invalidated.
     192             :  */
     193          72 : MultiKeyMap.prototype._RemoveItem = function(primaryKey, itemID, secondaryKey, stackable)
     194             : {
     195          25 :         let items = this._getItems(primaryKey, secondaryKey);
     196             : 
     197          48 :         let existingItem = items.filter(item => { return item._ID == itemID; });
     198          25 :         if (!existingItem.length)
     199           0 :                 return false;
     200             : 
     201          25 :         if (--existingItem[0]._count > 0)
     202           6 :                 return stackable;
     203             : 
     204          30 :         let stilValidItems = items.filter(item => item._count > 0);
     205             : 
     206             :         // Delete entries from the map if necessary to clean up.
     207          19 :         if (!stilValidItems.length)
     208             :         {
     209          13 :                 this.items.get(primaryKey).delete(secondaryKey);
     210          13 :                 if (!this.items.get(primaryKey).size)
     211          10 :                         this.items.delete(primaryKey);
     212          13 :                 return true;
     213             :         }
     214             : 
     215           6 :         this.items.get(primaryKey).set(secondaryKey, stilValidItems);
     216             : 
     217           6 :         return true;
     218             : };
     219             : 
     220             : /**
     221             :  * Stub method, to overload.
     222             :  */
     223          72 : MultiKeyMap.prototype._OnItemModified = function(primaryKey, secondaryKey, itemID) {};

Generated by: LCOV version 1.14