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) {};
|