@blizzhackers/d2data 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (185) hide show
  1. package/.eslintrc.js +59 -0
  2. package/LICENSE +21 -0
  3. package/README.md +12 -0
  4. package/_config.yml +1 -0
  5. package/compile.js +789 -0
  6. package/docs/Avqest.ttf +0 -0
  7. package/docs/_config.yml +1 -0
  8. package/docs/armor.js +134 -0
  9. package/docs/css/d2font.css +10 -0
  10. package/docs/drops/.vscode/extensions.json +3 -0
  11. package/docs/drops/README.md +7 -0
  12. package/docs/drops/dist/assets/index.12ae3c90.js +6675 -0
  13. package/docs/drops/dist/index.html +24 -0
  14. package/docs/drops/index.html +23 -0
  15. package/docs/drops/package-lock.json +1143 -0
  16. package/docs/drops/package.json +17 -0
  17. package/docs/drops/src/App.vue +870 -0
  18. package/docs/drops/src/main.js +4 -0
  19. package/docs/drops/vite.config.js +8 -0
  20. package/docs/drops.html +10 -0
  21. package/docs/effort.css +118 -0
  22. package/docs/effort.js +367 -0
  23. package/docs/elem-effort.html +130 -0
  24. package/docs/images/0-0.svg +18 -0
  25. package/docs/images/0-1.svg +17 -0
  26. package/docs/images/0-2.svg +17 -0
  27. package/docs/images/1-0.svg +18 -0
  28. package/docs/images/1-1.svg +18 -0
  29. package/docs/images/1-2.svg +17 -0
  30. package/docs/images/2-0.svg +17 -0
  31. package/docs/images/2-1.svg +11 -0
  32. package/docs/images/2-2.svg +18 -0
  33. package/docs/images/3-0.svg +17 -0
  34. package/docs/images/3-1.svg +18 -0
  35. package/docs/images/3-2.svg +18 -0
  36. package/docs/images/4-0.svg +18 -0
  37. package/docs/images/4-1.svg +18 -0
  38. package/docs/images/4-2.svg +18 -0
  39. package/docs/images/5-0.svg +18 -0
  40. package/docs/images/5-1.svg +18 -0
  41. package/docs/images/5-2.svg +14 -0
  42. package/docs/images/6-0.svg +17 -0
  43. package/docs/images/6-1.svg +17 -0
  44. package/docs/images/6-2.svg +17 -0
  45. package/docs/images/AmSkillicon.png +0 -0
  46. package/docs/images/AsSkillicon.png +0 -0
  47. package/docs/images/BaSkillicon.png +0 -0
  48. package/docs/images/DrSkillicon.png +0 -0
  49. package/docs/images/NeSkillicon.png +0 -0
  50. package/docs/images/PaSkillicon.png +0 -0
  51. package/docs/images/Skillicon.png +0 -0
  52. package/docs/images/SoSkillicon.png +0 -0
  53. package/docs/index.md +5 -0
  54. package/docs/items.html +170 -0
  55. package/docs/leveling/.vscode/extensions.json +3 -0
  56. package/docs/leveling/README.md +7 -0
  57. package/docs/leveling/dist/assets/index.f9765357.js +1 -0
  58. package/docs/leveling/dist/index.html +24 -0
  59. package/docs/leveling/index.html +23 -0
  60. package/docs/leveling/package-lock.json +1143 -0
  61. package/docs/leveling/package.json +17 -0
  62. package/docs/leveling/src/App.vue +526 -0
  63. package/docs/leveling/src/main.js +4 -0
  64. package/docs/leveling/vite.config.js +8 -0
  65. package/docs/runewords/.vscode/extensions.json +3 -0
  66. package/docs/runewords/README.md +7 -0
  67. package/docs/runewords/dist/assets/index.0838f27c.js +6673 -0
  68. package/docs/runewords/dist/assets/index.73bf3e23.css +1 -0
  69. package/docs/runewords/dist/index.html +25 -0
  70. package/docs/runewords/index.html +23 -0
  71. package/docs/runewords/package-lock.json +1143 -0
  72. package/docs/runewords/package.json +17 -0
  73. package/docs/runewords/src/App.vue +781 -0
  74. package/docs/runewords/src/main.js +4 -0
  75. package/docs/runewords/src/modorder.json +234 -0
  76. package/docs/runewords/vite.config.js +8 -0
  77. package/docs/skills.css +1250 -0
  78. package/docs/skills.html +63 -0
  79. package/docs/skills.js +2192 -0
  80. package/docs/vue-setup.js +12 -0
  81. package/docs/weapons.js +149 -0
  82. package/font-example.html +34 -0
  83. package/json/.gitkeep +0 -0
  84. package/json/actinfo.json +116 -0
  85. package/json/armor.json +17232 -0
  86. package/json/armtype.json +17 -0
  87. package/json/atomic.json +1026 -0
  88. package/json/automagic.json +696 -0
  89. package/json/automap.json +40732 -0
  90. package/json/belts.json +743 -0
  91. package/json/bodylocs.json +52 -0
  92. package/json/books.json +34 -0
  93. package/json/charstats.json +525 -0
  94. package/json/colors.json +107 -0
  95. package/json/compcode.json +577 -0
  96. package/json/composit.json +82 -0
  97. package/json/cubemain.json +2112 -0
  98. package/json/cubemod.json +57 -0
  99. package/json/difficultylevels.json +110 -0
  100. package/json/elemtypes.json +62 -0
  101. package/json/events.json +72 -0
  102. package/json/experience.json +1214 -0
  103. package/json/gamble.json +520 -0
  104. package/json/gems.json +1868 -0
  105. package/json/hireling.json +7640 -0
  106. package/json/hitclass.json +67 -0
  107. package/json/inventory.json +2148 -0
  108. package/json/itemratio.json +134 -0
  109. package/json/items.json +54705 -0
  110. package/json/itemstatcost.json +5503 -0
  111. package/json/itemtypes.json +2567 -0
  112. package/json/levels.json +15515 -0
  113. package/json/localestrings-chi.json +8717 -0
  114. package/json/localestrings-deu.json +8717 -0
  115. package/json/localestrings-eng.json +8722 -0
  116. package/json/localestrings-esp.json +8717 -0
  117. package/json/localestrings-fra.json +8718 -0
  118. package/json/localestrings-ita.json +8717 -0
  119. package/json/localestrings-kor.json +8715 -0
  120. package/json/localestrings-pol.json +8716 -0
  121. package/json/lowqualityitems.json +6 -0
  122. package/json/lvlmaze.json +910 -0
  123. package/json/lvlprest.json +28595 -0
  124. package/json/lvlsub.json +824 -0
  125. package/json/lvltypes.json +1377 -0
  126. package/json/lvlwarp.json +1515 -0
  127. package/json/magicprefix.json +12349 -0
  128. package/json/magicsuffix.json +13833 -0
  129. package/json/misc.json +11602 -0
  130. package/json/misscalc.json +217 -0
  131. package/json/missiles.json +23871 -0
  132. package/json/monai.json +1111 -0
  133. package/json/moncountest.json +642 -0
  134. package/json/monequip.json +361 -0
  135. package/json/monlvl.json +3665 -0
  136. package/json/monmode.json +98 -0
  137. package/json/monplace.json +39 -0
  138. package/json/monpopulationest.json +17383 -0
  139. package/json/monpreset.json +922 -0
  140. package/json/monprop.json +144 -0
  141. package/json/monseq.json +5211 -0
  142. package/json/monsounds.json +2530 -0
  143. package/json/monstats.json +66101 -0
  144. package/json/monstats2.json +34882 -0
  145. package/json/montype.json +274 -0
  146. package/json/monumod.json +504 -0
  147. package/json/npc.json +232 -0
  148. package/json/objects.json +87041 -0
  149. package/json/objgroup.json +3733 -0
  150. package/json/objmode.json +42 -0
  151. package/json/objpreset.json +2906 -0
  152. package/json/objtype.json +2457 -0
  153. package/json/overlay.json +8008 -0
  154. package/json/pettype.json +226 -0
  155. package/json/playerclass.json +39 -0
  156. package/json/plrmode.json +122 -0
  157. package/json/plrtype.json +39 -0
  158. package/json/properties.json +2823 -0
  159. package/json/qualityitems.json +149 -0
  160. package/json/rareprefix.json +296 -0
  161. package/json/raresuffix.json +921 -0
  162. package/json/runes.json +3280 -0
  163. package/json/setitems.json +3871 -0
  164. package/json/sets.json +1001 -0
  165. package/json/shrines.json +319 -0
  166. package/json/skillcalc.json +457 -0
  167. package/json/skilldesc.json +7532 -0
  168. package/json/skills.json +16717 -0
  169. package/json/soundenviron.json +2862 -0
  170. package/json/sounds.json +446543 -0
  171. package/json/states.json +1578 -0
  172. package/json/storepage.json +22 -0
  173. package/json/superuniques.json +1405 -0
  174. package/json/tcprecalc.json +29302 -0
  175. package/json/treasureclassex.json +14932 -0
  176. package/json/treasureclassgroupsex.json +711 -0
  177. package/json/uniqueappellation.json +27 -0
  178. package/json/uniqueitems.json +14171 -0
  179. package/json/uniqueprefix.json +55 -0
  180. package/json/uniquesuffix.json +71 -0
  181. package/json/wanderingmon.json +3 -0
  182. package/json/weapons.json +25875 -0
  183. package/objext.js +53 -0
  184. package/package.json +16 -0
  185. package/string.js +42 -0
package/compile.js ADDED
@@ -0,0 +1,789 @@
1
+ /**
2
+ * Compile script for the json data. Basically, put all your txt files into
3
+ * the txt/ directory, and this will compile it all into json. It assumes that
4
+ * you've provided at least 'armor.txt', 'weapons.txt', 'TreasureClassEx.txt',
5
+ * 'ItemTypes.txt', and 'monstats.txt'.
6
+ *
7
+ * @todo Refactor it, since I hacked it together fairly quickly.
8
+ */
9
+
10
+ require('./objext.js');
11
+ const fs = require('fs');
12
+ const lineEnd = /[\n\r]+/g, fieldEnd = /\t/g, full = {};
13
+ const inDir = 'txt/';
14
+ const outDir = 'json/';
15
+ const files = fs.readdirSync(inDir).filter(fn => fn.slice(-4) === '.txt').map(fn => fn.slice(0, -4));
16
+
17
+ function keySort(obj) {
18
+ let keys = Object.keys(obj).sort(), ret = {};
19
+
20
+ keys.forEach(key => {
21
+ ret[key] = obj[key];
22
+ });
23
+
24
+ return ret;
25
+ }
26
+
27
+ const indexes = {
28
+ actinfo: 'act',
29
+ armor: 'code',
30
+ armtype: 'Token',
31
+ bodylocs: 'Code',
32
+ books: 'Name',
33
+ charstats: 'class',
34
+ colors: 'Code',
35
+ compcode: 'code',
36
+ composit: 'Name',
37
+ cubemod: 'Code',
38
+ difficultylevels: 'Name',
39
+ elemtypes: 'Code',
40
+ experience: 'Level',
41
+ events: 'event',
42
+ gems: 'code',
43
+ hitclass: 'Code',
44
+ inventory: 'class',
45
+ itemstatcost: 'Stat',
46
+ itemtypes: 'Code',
47
+ levels: 'Id',
48
+ lvlmaze: 'Level',
49
+ lvlprest: 'Def',
50
+ lvltypes: 'Name',
51
+ misc: 'code',
52
+ misscalc: 'code',
53
+ missiles: 'Missile',
54
+ monai: 'AI',
55
+ monmode: 'code',
56
+ monprop: 'Id',
57
+ monstats: 'Id',
58
+ monstats2: 'Id',
59
+ monsounds: 'Id',
60
+ montype: 'type',
61
+ monumod: 'id',
62
+ npc: 'npc',
63
+ objmode: 'Token',
64
+ overlay: 'overlay',
65
+ pettype: 'pet type',
66
+ plrmode: 'Code',
67
+ plrtype: 'Token',
68
+ playerclass: 'Code',
69
+ properties: 'code',
70
+ runes: '*Rune Name',
71
+ setitems: 'index',
72
+ sets: 'index',
73
+ shrines: 'Code',
74
+ skillcalc: 'code',
75
+ skilldesc: 'code',
76
+ skills: '*Id',
77
+ soundenviron: 'Index',
78
+ sounds: '*Index',
79
+ states: 'state',
80
+ storepage: 'Code',
81
+ superuniques: 'Superunique',
82
+ treasureclassex: 'Treasure Class',
83
+ uniqueitems: '*ID',
84
+ weapons: 'code',
85
+ weaponclass: 'Code',
86
+ };
87
+
88
+ const filterValues = {
89
+ '': true,
90
+ 'unused': true,
91
+ 'none': true,
92
+ 'null': true,
93
+ };
94
+
95
+ function noDrop(e, nd, ...d) {
96
+ if (e <= 1) {
97
+ return nd | 0;
98
+ }
99
+
100
+ e = e | 0;
101
+ nd = nd | 0;
102
+ d = d.reduce((t, v) => t + v | 0, 0);
103
+
104
+ if (d < 1) {
105
+ return Infinity;
106
+ }
107
+
108
+ return (d / (((nd + d) / nd)**e - 1)) | 0;
109
+ }
110
+
111
+ files.forEach(fn => {
112
+ let data = fs.readFileSync(inDir + fn + '.txt').toString().split(lineEnd);
113
+ let header = data.shift().split(fieldEnd);
114
+ let indexColumn = header.indexOf(indexes[fn]);
115
+ let usesVersion = header.includes('version');
116
+ let expansion = false;
117
+ let maxKeyCount = 0;
118
+
119
+ if (indexColumn === -1) {
120
+ console.log('Using default Index for:', fn);
121
+ }
122
+
123
+ full[fn] = data.reduce((obj, line, index) => {
124
+ if (line.trim()) {
125
+ line = line.split(fieldEnd).map(v => v.trim());
126
+
127
+ if (line[0].toLowerCase() === 'expansion') {
128
+ expansion = true;
129
+ } else if (header.length === 1 || line.filter(Boolean).length > 1) {
130
+ let key = indexColumn >= 0 ? (line[indexColumn]) : index;
131
+
132
+ if (key !== undefined) {
133
+ if (key !== '') {
134
+ if (obj[key]) {
135
+ throw new Error('Duplicate key ' + JSON.stringify(key) + ' in ' + fn);
136
+ } else {
137
+ let tmp = {};
138
+
139
+ for (let c = 0; c < header.length; c++) {
140
+ if (indexColumn >= 0 && c === indexColumn || !filterValues[line[c].toString().toLowerCase()]) {
141
+ tmp[header[c] || 'unknown'] = line[c] !== 'Infinity' && Number(line[c]).toString() === line[c].trim() ? Number(line[c]) : line[c];
142
+ }
143
+ }
144
+
145
+ let keyCount = Object.keys(tmp).length;
146
+
147
+ if (keyCount > 0) {
148
+ if (usesVersion) {
149
+ if (tmp.version >= 100) {
150
+ tmp.expansion = 1;
151
+ }
152
+ } else if (expansion) {
153
+ tmp.expansion = 1;
154
+ }
155
+
156
+ obj[key] = tmp;
157
+
158
+ delete obj[key]['*eol'];
159
+ delete obj[key]['*EOL'];
160
+ delete obj[key]['eol'];
161
+ delete obj[key]['EOL'];
162
+
163
+ if (indexColumn >= 0) {
164
+ obj[key].lineNumber = index;
165
+ }
166
+ }
167
+
168
+ maxKeyCount = Math.max(maxKeyCount, keyCount);
169
+ }
170
+ }
171
+ } else {
172
+ throw new Error('No viable key in: ' + fn + ' : ' + key);
173
+ }
174
+ }
175
+ }
176
+
177
+ return obj;
178
+ }, {});
179
+
180
+ if (maxKeyCount === 1) {
181
+ full[fn] = Object.values(full[fn]).map(line => Object.values(line)[0]);
182
+ console.log(fn, 'was reduced!');
183
+ }
184
+
185
+ if (fn === 'treasureclassex') {
186
+ full[fn].forEach(tc => {
187
+ let precalc = {}, nodropcalc = {
188
+ 1: 0,
189
+ 2: 0,
190
+ 3: 0,
191
+ 4: 0,
192
+ 5: 0,
193
+ 6: 0,
194
+ 7: 0,
195
+ 8: 0,
196
+ };
197
+
198
+ if (tc.Picks > 0) {
199
+ let total = 0;
200
+
201
+ for (let c = 1; c <= 9; c++) {
202
+ if (tc['Item' + c]) {
203
+ total += tc['Prob' + c] | 0;
204
+ }
205
+ }
206
+
207
+ [1, 2, 3, 4, 5, 6, 7, 8].forEach(exp => {
208
+ nodropcalc[exp] = noDrop(exp, tc.NoDrop, total);
209
+ });
210
+
211
+ for (let c = 1; c <= 9; c++) {
212
+ if (tc['Item' + c]) {
213
+ let prob = (tc['Prob' + c] | 0) / total;
214
+ precalc[tc['Item' + c]] = precalc[tc['Item' + c]] || 0;
215
+ precalc[tc['Item' + c]] += prob;
216
+ }
217
+ }
218
+
219
+ precalc = precalc.map(v => v * tc.Picks);
220
+ tc['*ItemProbTotal'] = total;
221
+ }
222
+ else if (tc.Picks < 0) {
223
+ let picksleft = -tc.Picks;
224
+
225
+ for (let c = 1; c <= 9; c++) {
226
+ if (tc['Item' + c] && picksleft > 0) {
227
+ let pickcount = Math.min(picksleft, tc['Prob' + c] | 0);
228
+
229
+ if (pickcount > 0) {
230
+ precalc[tc['Item' + c]] = precalc[tc['Item' + c]] || 0;
231
+ precalc[tc['Item' + c]] += pickcount;
232
+ picksleft -= pickcount;
233
+ }
234
+ }
235
+ }
236
+ }
237
+
238
+ tc.nodropcalc = nodropcalc;
239
+ tc.precalc = precalc;
240
+ });
241
+ }
242
+
243
+ if (fn === 'superuniques') {
244
+ full[fn]['Bishibosh'].areaId = 3;
245
+ full[fn]['Bonebreak'].areaId = 18;
246
+ full[fn]['Coldcrow'].areaId = 9;
247
+ full[fn]['Rakanishu'].areaId = 4;
248
+ full[fn]['Treehead WoodFist'].areaId = 5;
249
+ full[fn]['Griswold'].areaId = 38;
250
+ full[fn]['The Countess'].areaId = 25;
251
+ full[fn]['Pitspawn Fouldog'].areaId = 30;
252
+ full[fn]['Boneash'].areaId = 33;
253
+ full[fn]['Radament'].areaId = 49;
254
+ full[fn]['Bloodwitch the Wild'].areaId = 60;
255
+ full[fn]['Fangskin'].areaId = 61;
256
+ full[fn]['Beetleburst'].areaId = 43;
257
+ full[fn]['Leatherarm'].areaId = 59;
258
+ full[fn]['Coldworm the Burrower'].areaId = 64;
259
+ full[fn]['Fire Eye'].areaId = 54;
260
+ full[fn]['Dark Elder'].areaId = 44;
261
+ full[fn]['The Summoner'].areaId = 74;
262
+ full[fn]['The Smith'].areaId = 28;
263
+ full[fn]['Web Mage the Burning'].areaId = 85;
264
+ full[fn]['Witch Doctor Endugu'].areaId = 91;
265
+ full[fn]['Stormtree'].areaId = 78;
266
+ full[fn]['Sarina the Battlemaid'].areaId = 94;
267
+ full[fn]['Icehawk Riftwing'].areaId = 92;
268
+ full[fn]['Ismail Vilehand'].areaId = 83;
269
+ full[fn]['Geleb Flamefinger'].areaId = 83;
270
+ full[fn]['Bremm Sparkfist'].areaId = 102;
271
+ full[fn]['Toorc Icefist'].areaId = 83;
272
+ full[fn]['Wyand Voidfinger'].areaId = 102;
273
+ full[fn]['Maffer Dragonhand'].areaId = 102;
274
+ full[fn]['Infector of Souls'].areaId = 108;
275
+ full[fn]['Lord De Seis'].areaId = 108;
276
+ full[fn]['Grand Vizier of Chaos'].areaId = 108;
277
+ full[fn]['The Cow King'].areaId = 39;
278
+ full[fn]['Corpsefire'].areaId = 8;
279
+ full[fn]['The Feature Creep'].areaId = 107;
280
+ full[fn]['Siege Boss'].areaId = 110;
281
+ full[fn]['Ancient Barbarian 1'].areaId = 120;
282
+ full[fn]['Ancient Barbarian 2'].areaId = 120;
283
+ full[fn]['Ancient Barbarian 3'].areaId = 120;
284
+ full[fn]['Bonesaw Breaker'].areaId = 115;
285
+ full[fn]['Dac Farren'].areaId = 110;
286
+ full[fn]['Megaflow Rectifier'].areaId = 111;
287
+ full[fn]['Eyeback Unleashed'].areaId = 111;
288
+ full[fn]['Threash Socket'].areaId = 112;
289
+ full[fn]['Pindleskin'].areaId = 121;
290
+ full[fn]['Snapchip Shatter'].areaId = 119;
291
+ full[fn]['Sharp Tooth Sayer'].areaId = 111;
292
+ full[fn]['Frozenstein'].areaId = 114;
293
+ full[fn]['Nihlathak Boss'].areaId = 124;
294
+ full[fn]['Baal Subject 1'].areaId = 131;
295
+ full[fn]['Baal Subject 2'].areaId = 131;
296
+ full[fn]['Baal Subject 3'].areaId = 131;
297
+ full[fn]['Baal Subject 4'].areaId = 131;
298
+ full[fn]['Baal Subject 5'].areaId = 131;
299
+ }
300
+
301
+ if (fn === 'monstats') {
302
+ full[fn]['andariel'].areaId = 37;
303
+ full[fn]['duriel'].areaId = 73;
304
+ full[fn]['mephisto'].areaId = 102;
305
+ full[fn]['diablo'].areaId = 108;
306
+ full[fn]['baalcrab'].areaId = 132;
307
+ full[fn]['ubermephisto'].areaId = 136;
308
+ full[fn]['uberdiablo'].areaId = 136;
309
+ full[fn]['uberizual'].areaId = 135;
310
+ full[fn]['uberandariel'].areaId = 133;
311
+ full[fn]['uberduriel'].areaId = 134;
312
+ full[fn]['uberbaal'].areaId = 136;
313
+ }
314
+ });
315
+
316
+ const items = {};
317
+
318
+ [
319
+ ...Object.values(full.weapons).sort((a, b) => a.lineNumber - b.lineNumber),
320
+ ...Object.values(full.armor).sort((a, b) => a.lineNumber - b.lineNumber),
321
+ ...Object.values(full.misc).sort((a, b) => a.lineNumber - b.lineNumber),
322
+ ].forEach((item, classid) => {
323
+ item.classid = classid;
324
+ items[item.code] = item;
325
+ });
326
+
327
+ let atomic = {};
328
+ let atomicTypes = {};
329
+ let atomicMax = 87;
330
+
331
+ let calcTC = x => {
332
+ let ret = Math.max(1, Math.ceil((x || 0) / 3)) * 3;
333
+ atomicMax = Math.max(ret, atomicMax);
334
+ return ret;
335
+ }
336
+
337
+ let weaponsarmor = [...Object.values(full.weapons), ...Object.values(full.armor)];
338
+
339
+ weaponsarmor.forEach(item => {
340
+ if (!item.spawnable) {
341
+ return;
342
+ }
343
+
344
+ let tc = calcTC(item.level);
345
+
346
+ function handleAtomic(itemType) {
347
+ if (full.itemtypes[itemType] && itemType !== 'tpot') {
348
+ if (full.itemtypes[itemType].TreasureClass) {
349
+ atomicTypes[itemType] = true;
350
+ atomic[itemType + tc] = atomic[itemType + tc] || [];
351
+ atomic[itemType + tc][item.lineNumber] = item.code;
352
+ }
353
+
354
+ if (full.itemtypes[itemType].Equiv1) {
355
+ handleAtomic(full.itemtypes[itemType].Equiv1);
356
+ }
357
+
358
+ if (full.itemtypes[itemType].Equiv2) {
359
+ handleAtomic(full.itemtypes[itemType].Equiv2);
360
+ }
361
+ }
362
+ }
363
+
364
+ handleAtomic(item.type);
365
+ });
366
+
367
+ atomicTypes = Object.keys(atomicTypes);
368
+
369
+ for (let c = 3; c <= atomicMax; c += 3) {
370
+ atomicTypes.forEach(type => {
371
+ atomic[type + c] = atomic[type + c] || [];
372
+ });
373
+ }
374
+
375
+ atomic.forEach((atom, atomName) => {
376
+ let precalc = {}, total = 0;
377
+
378
+ atom.forEach((itc, i) => {
379
+ let rarity = full.itemtypes[items[itc].type].Rarity | 0;
380
+ total += rarity;
381
+ atom[i] = [itc, rarity];
382
+ });
383
+
384
+ atom.forEach(([itc, chance]) => {
385
+ precalc[itc] = chance / total;
386
+ });
387
+
388
+ atomic[atomName] = precalc;
389
+ });
390
+
391
+ full.atomic = atomic;
392
+
393
+ let groupsEx = {};
394
+
395
+ full.treasureclassex.forEach((tc, key) => {
396
+ if (tc.group) {
397
+ groupsEx[tc.group] = groupsEx[tc.group] || [];
398
+ groupsEx[tc.group][tc.level|0] = key;
399
+ }
400
+ });
401
+
402
+ groupsEx = groupsEx.map(group => {
403
+ let length = group.length;
404
+ group = Object.assign({}, group);
405
+ group.length = length;
406
+ return group;
407
+ });
408
+
409
+ function avg(...nums) {
410
+ return nums.reduce((t, v) => t + v, 0) / nums.length || 0;
411
+ }
412
+
413
+ function _s(diff) {
414
+ return (str) => str + ["", "(N)", "(H)"][diff];
415
+ }
416
+
417
+ function monlevel(mon, level, diff) {
418
+ if (!diff) {
419
+ return mon["Level"] || 0;
420
+ }
421
+
422
+ let s = _s(diff),
423
+ lvl = level[s("MonLvlEx")] || 0,
424
+ mlvl = mon[s("Level")] || 0;
425
+
426
+ return lvl > mlvl ? lvl : mlvl;
427
+ }
428
+
429
+ function forEachMonster(level, diff, func) {
430
+ let s = _s(diff);
431
+
432
+ [0, 1, 2].forEach((type) => {
433
+ if (type && !level[s("MonUMin")] && !level[s("MonUMax")]) {
434
+ return;
435
+ }
436
+
437
+ let m = (num) => level[(diff ? "nmon" : type ? "umon" : "mon") + num];
438
+
439
+ for (let c = 1; c <= 9; c++) {
440
+ if (m(c)) {
441
+ let mon = full.monstats[m(c)];
442
+
443
+ if (mon && mon.enabled && mon.killable) {
444
+ let mlvl = monlevel(mon, level, diff) + [0, 2, 3][type];
445
+
446
+ func(mon, mlvl, type);
447
+ }
448
+ }
449
+ }
450
+ });
451
+ }
452
+
453
+ const moncountest = require('./json/moncountest.json');
454
+
455
+ let monpopulation = {};
456
+
457
+ [0, 1, 2].forEach((diff) => {
458
+ let s = _s(diff);
459
+ full.levels.forEach((level) => {
460
+ let l = (key) => level[key] || 0;
461
+
462
+ monpopulation[level.Id] = monpopulation[level.Id] || {
463
+ 'normal': {},
464
+ 'champion': {},
465
+ 'unique': {},
466
+ 'superunique': {},
467
+ 'boss': {},
468
+ };
469
+
470
+ if (level.Id) {
471
+ let supers = full.superuniques.filter(s => s.areaId == level.Id || (s.hcIdx === 19 && [66, 67, 68, 69, 70, 71, 72].includes(level.Id | 0))),
472
+ bosses = full.monstats.filter((mon) => mon.areaId == level.Id),
473
+ acount = (moncountest[level.Id] && moncountest[level.Id][diff]) || 0,
474
+ scount = supers.reduce((total, sup) => {
475
+ return (
476
+ total +
477
+ 1 +
478
+ diff +
479
+ ((sup["MinGrp"] || 0) + (sup["MaxGrp"] || 0)) / 2
480
+ );
481
+ }, 0),
482
+ bcount = bosses.reduce((total, mon) => {
483
+ return (
484
+ total + 1 + +((mon["MinGrp"] || 0) + (mon["MaxGrp"] || 0)) / 2
485
+ );
486
+ }, 0),
487
+ ucount = avg(l(s("MonUMin")), l(s("MonUMax"))) * 0.8 * 5.5,
488
+ ccount = avg(l(s("MonUMin")), l(s("MonUMax"))) * 0.2 * 3,
489
+ count = acount - ucount - ccount - scount - bcount;
490
+
491
+ if (count > 0) {
492
+ let totalpackssize = 0,
493
+ udiv = 0;
494
+
495
+ forEachMonster(level, diff, (mon, mlvl, type) => {
496
+ if (!type) {
497
+ let m = (key) => mon[key] || 0;
498
+ let packsize = avg(
499
+ m("PartyMin") + m("PartyMax"),
500
+ m("MinGrp") + m("MaxGrp")
501
+ );
502
+
503
+ totalpackssize += packsize;
504
+ }
505
+
506
+ if (type === 2) {
507
+ udiv++;
508
+ }
509
+ });
510
+
511
+ forEachMonster(level, diff, (mon, mlvl, type) => {
512
+ let mult = [
513
+ count / totalpackssize,
514
+ ccount / 3 / udiv,
515
+ ucount / 5.5 / udiv,
516
+ ][type];
517
+ monpopulation[level.Id][['normal', 'champion', 'unique'][type]][mon.Id] = monpopulation[level.Id][['normal', 'champion', 'unique'][type]][mon.Id] || {
518
+ "mlvl": 0,
519
+ "packCount": 0,
520
+ "mlvl(N)": 0,
521
+ "packCount(N)": 0,
522
+ "mlvl(H)": 0,
523
+ "packCount(H)": 0,
524
+ };
525
+ monpopulation[level.Id][['normal', 'champion', 'unique'][type]][mon.Id][s('mlvl')] = mlvl;
526
+ monpopulation[level.Id][['normal', 'champion', 'unique'][type]][mon.Id][s('packCount')] = mult;
527
+ });
528
+ }
529
+
530
+ supers.forEach((sup) => {
531
+ let mon = full.monstats[sup.Class],
532
+ mlvl = monlevel(mon, level, diff) + 3;
533
+
534
+ monpopulation[level.Id]['superunique'][sup.Superunique] = monpopulation[level.Id]['superunique'][sup.Superunique] || {
535
+ "mlvl": 0,
536
+ "packCount": 0,
537
+ "mlvl(N)": 0,
538
+ "packCount(N)": 0,
539
+ "mlvl(H)": 0,
540
+ "packCount(H)": 0,
541
+ };
542
+ monpopulation[level.Id]['superunique'][sup.Superunique][s('mlvl')] = mlvl;
543
+ monpopulation[level.Id]['superunique'][sup.Superunique][s('packCount')] = sup.hcIdx === 19 ? 1 / 7 : 1;
544
+ });
545
+
546
+ bosses.forEach((mon) => {
547
+ let mlvl = monlevel(mon, level, diff);
548
+
549
+ monpopulation[level.Id]['boss'][mon.Id] = monpopulation[level.Id]['boss'][mon.Id] || {
550
+ "mlvl": 0,
551
+ "packCount": 0,
552
+ "mlvl(N)": 0,
553
+ "packCount(N)": 0,
554
+ "mlvl(H)": 0,
555
+ "packCount(H)": 0,
556
+ };
557
+ monpopulation[level.Id]['boss'][mon.Id][s('mlvl')] = mlvl;
558
+ monpopulation[level.Id]['boss'][mon.Id][s('packCount')] = 1;
559
+ });
560
+ }
561
+ });
562
+ });
563
+
564
+ let tcprecalc = {};
565
+
566
+ full.treasureclassex.forEach((tc, key) => {
567
+ tcprecalc[key] = tcprecalc[key] || {};
568
+ tcprecalc[key].droprate = tc.nodropcalc.map(noDrop => noDrop ? tc['*ItemProbTotal'] / (noDrop + tc['*ItemProbTotal']) : 1);
569
+ tcprecalc[key].droprateRoot = {};
570
+ tcprecalc[key].counts = tc.precalc;
571
+ delete tc['nodropcalc'];
572
+ delete tc['precalc'];
573
+ });
574
+
575
+ atomic.forEach((precalc, key) => {
576
+ tcprecalc[key] = {
577
+ droprate: {
578
+ 1: 1,
579
+ 2: 1,
580
+ 3: 1,
581
+ 4: 1,
582
+ 5: 1,
583
+ 6: 1,
584
+ 7: 1,
585
+ 8: 1,
586
+ },
587
+ droprateRoot: {},
588
+ counts: precalc,
589
+ };
590
+ });
591
+
592
+ // Formulas and coefficients for the 6 item cap are from https://github.com/realmonster/diablo2_drop_calc
593
+ // He did a lot of work figuring out the formulas that I don't care to replicate,
594
+ // so I'm taking the path of least resistance.
595
+
596
+ // Adjust droprates for TCs with 7 picks for the 6 item cap.
597
+ tcprecalc.forEach((basetc, basekey) => {
598
+ if (full.treasureclassex[basekey] && full.treasureclassex[basekey].Picks === 7) {
599
+ [1, 2, 3, 4, 5, 6, 7, 8].forEach(playerCount => {
600
+ let newvalue = basetc.droprate[playerCount] * (7 - basetc.droprate[playerCount]**6) / 7;
601
+ basetc.droprateRoot[playerCount] = newvalue;
602
+ });
603
+ } else {
604
+ basetc.droprateRoot = Object.assign({}, basetc.droprate);
605
+ }
606
+ });
607
+
608
+ // This adjusts the drop rates to fit the 6 item cap for Duriel.
609
+ tcprecalc['Duriel']['droprateRoot'][1] *= 0.40188714943604026;
610
+ tcprecalc['Duriel']['droprateRoot'][2] *= 0.3016508805227563;
611
+ tcprecalc['Duriel']['droprateRoot'][3] *= 0.28809929433644293;
612
+ tcprecalc['Duriel']['droprateRoot'][4] *= 0.28604061515129975;
613
+ tcprecalc['Duriel']['droprateRoot'][5] *= 0.28604061515129975;
614
+ tcprecalc['Duriel']['droprateRoot'][6] *= 0.28604061515129975;
615
+ tcprecalc['Duriel']['droprateRoot'][7] *= 0.28604061515129975;
616
+ tcprecalc['Duriel']['droprateRoot'][8] *= 0.28604061515129975;
617
+ tcprecalc['Duriel - Base']['droprate'][1] *= 0.31707696865583446 / 0.40188714943604026;
618
+ tcprecalc['Duriel - Base']['droprate'][2] *= 0.25418252986014334 / 0.3016508805227563;
619
+ tcprecalc['Duriel - Base']['droprate'][3] *= 0.24045372397688228 / 0.28809929433644293;
620
+ tcprecalc['Duriel - Base']['droprate'][4] *= 0.23562610229276895 / 0.28604061515129975;
621
+ tcprecalc['Duriel - Base']['droprate'][5] *= 0.23562610229276895 / 0.28604061515129975;
622
+ tcprecalc['Duriel - Base']['droprate'][6] *= 0.23562610229276895 / 0.28604061515129975;
623
+ tcprecalc['Duriel - Base']['droprate'][7] *= 0.23562610229276895 / 0.28604061515129975;
624
+ tcprecalc['Duriel - Base']['droprate'][8] *= 0.23562610229276895 / 0.28604061515129975;
625
+
626
+ tcprecalc['Duriel (N)']['droprateRoot'][1] *= 0.40188714943604026;
627
+ tcprecalc['Duriel (N)']['droprateRoot'][2] *= 0.3016508805227563;
628
+ tcprecalc['Duriel (N)']['droprateRoot'][3] *= 0.28809929433644293;
629
+ tcprecalc['Duriel (N)']['droprateRoot'][4] *= 0.28604061515129975;
630
+ tcprecalc['Duriel (N)']['droprateRoot'][5] *= 0.28604061515129975;
631
+ tcprecalc['Duriel (N)']['droprateRoot'][6] *= 0.28604061515129975;
632
+ tcprecalc['Duriel (N)']['droprateRoot'][7] *= 0.28604061515129975;
633
+ tcprecalc['Duriel (N)']['droprateRoot'][8] *= 0.28604061515129975;
634
+ tcprecalc['Duriel (N) - Base']['droprate'][1] *= 0.31707696865583446 / 0.40188714943604026;
635
+ tcprecalc['Duriel (N) - Base']['droprate'][2] *= 0.25418252986014334 / 0.3016508805227563;
636
+ tcprecalc['Duriel (N) - Base']['droprate'][3] *= 0.24045372397688228 / 0.28809929433644293;
637
+ tcprecalc['Duriel (N) - Base']['droprate'][4] *= 0.23562610229276895 / 0.28604061515129975;
638
+ tcprecalc['Duriel (N) - Base']['droprate'][5] *= 0.23562610229276895 / 0.28604061515129975;
639
+ tcprecalc['Duriel (N) - Base']['droprate'][6] *= 0.23562610229276895 / 0.28604061515129975;
640
+ tcprecalc['Duriel (N) - Base']['droprate'][7] *= 0.23562610229276895 / 0.28604061515129975;
641
+ tcprecalc['Duriel (N) - Base']['droprate'][8] *= 0.23562610229276895 / 0.28604061515129975;
642
+
643
+ tcprecalc['Duriel (H)']['droprateRoot'][1] *= 0.3977971198210286;
644
+ tcprecalc['Duriel (H)']['droprateRoot'][2] *= 0.30109536777656887;
645
+ tcprecalc['Duriel (H)']['droprateRoot'][3] *= 0.28836833573076587;
646
+ tcprecalc['Duriel (H)']['droprateRoot'][4] *= 0.28645033061957725;
647
+ tcprecalc['Duriel (H)']['droprateRoot'][5] *= 0.28645033061957725;
648
+ tcprecalc['Duriel (H)']['droprateRoot'][6] *= 0.28645033061957725;
649
+ tcprecalc['Duriel (H)']['droprateRoot'][7] *= 0.28645033061957725;
650
+ tcprecalc['Duriel (H)']['droprateRoot'][8] *= 0.28645033061957725;
651
+ tcprecalc['Duriel (H) - Base']['droprate'][1] *= 0.31394497266967347 / 0.3977971198210286;
652
+ tcprecalc['Duriel (H) - Base']['droprate'][2] *= 0.25347220341955656 / 0.30109536777656887;
653
+ tcprecalc['Duriel (H) - Base']['droprate'][3] *= 0.24026354414195675 / 0.28836833573076587;
654
+ tcprecalc['Duriel (H) - Base']['droprate'][4] *= 0.23562610229276895 / 0.28645033061957725;
655
+ tcprecalc['Duriel (H) - Base']['droprate'][5] *= 0.23562610229276895 / 0.28645033061957725;
656
+ tcprecalc['Duriel (H) - Base']['droprate'][6] *= 0.23562610229276895 / 0.28645033061957725;
657
+ tcprecalc['Duriel (H) - Base']['droprate'][7] *= 0.23562610229276895 / 0.28645033061957725;
658
+ tcprecalc['Duriel (H) - Base']['droprate'][8] *= 0.23562610229276895 / 0.28645033061957725;
659
+
660
+ tcprecalc['Durielq']['droprateRoot'][1] *= 0.5236458617828885;
661
+ tcprecalc['Durielq']['droprateRoot'][2] *= 0.3778815510942014;
662
+ tcprecalc['Durielq']['droprateRoot'][3] *= 0.3148960979106187;
663
+ tcprecalc['Durielq']['droprateRoot'][4] *= 0.3035687614166196;
664
+ tcprecalc['Durielq']['droprateRoot'][5] *= 0.29876543209876544;
665
+ tcprecalc['Durielq']['droprateRoot'][6] *= 0.29876543209876544;
666
+ tcprecalc['Durielq']['droprateRoot'][7] *= 0.29876543209876544;
667
+ tcprecalc['Durielq']['droprateRoot'][8] *= 0.29876543209876544;
668
+ tcprecalc['Durielq - Base']['droprate'][1] *= 0.4089824816019127 / 0.5236458617828885;
669
+ tcprecalc['Durielq - Base']['droprate'][2] *= 0.2926940985197233 / 0.3778815510942014;
670
+ tcprecalc['Durielq - Base']['droprate'][3] *= 0.2557897178063293 / 0.3148960979106187;
671
+ tcprecalc['Durielq - Base']['droprate'][4] *= 0.2459776894089001 / 0.3035687614166196;
672
+ tcprecalc['Durielq - Base']['droprate'][5] *= 0.23562610229276895 / 0.29876543209876544;
673
+ tcprecalc['Durielq - Base']['droprate'][6] *= 0.23562610229276895 / 0.29876543209876544;
674
+ tcprecalc['Durielq - Base']['droprate'][7] *= 0.23562610229276895 / 0.29876543209876544;
675
+ tcprecalc['Durielq - Base']['droprate'][8] *= 0.23562610229276895 / 0.29876543209876544;
676
+
677
+ tcprecalc['Durielq (N)']['droprateRoot'][1] *= 0.5236458617828885;
678
+ tcprecalc['Durielq (N)']['droprateRoot'][2] *= 0.3778815510942014;
679
+ tcprecalc['Durielq (N)']['droprateRoot'][3] *= 0.3148960979106187;
680
+ tcprecalc['Durielq (N)']['droprateRoot'][4] *= 0.3035687614166196;
681
+ tcprecalc['Durielq (N)']['droprateRoot'][5] *= 0.29876543209876544;
682
+ tcprecalc['Durielq (N)']['droprateRoot'][6] *= 0.29876543209876544;
683
+ tcprecalc['Durielq (N)']['droprateRoot'][7] *= 0.29876543209876544;
684
+ tcprecalc['Durielq (N)']['droprateRoot'][8] *= 0.29876543209876544;
685
+ tcprecalc['Durielq (N) - Base']['droprate'][1] *= 0.4089824816019127 / 0.5236458617828885;
686
+ tcprecalc['Durielq (N) - Base']['droprate'][2] *= 0.2926940985197233 / 0.3778815510942014;
687
+ tcprecalc['Durielq (N) - Base']['droprate'][3] *= 0.2557897178063293 / 0.3148960979106187;
688
+ tcprecalc['Durielq (N) - Base']['droprate'][4] *= 0.24597768940890014 / 0.3035687614166196;
689
+ tcprecalc['Durielq (N) - Base']['droprate'][5] *= 0.23562610229276895 / 0.29876543209876544;
690
+ tcprecalc['Durielq (N) - Base']['droprate'][6] *= 0.23562610229276895 / 0.29876543209876544;
691
+ tcprecalc['Durielq (N) - Base']['droprate'][7] *= 0.23562610229276895 / 0.29876543209876544;
692
+ tcprecalc['Durielq (N) - Base']['droprate'][8] *= 0.23562610229276895 / 0.29876543209876544;
693
+
694
+ tcprecalc['Durielq (H)']['droprateRoot'][1] *= 0.509798203037134;
695
+ tcprecalc['Durielq (H)']['droprateRoot'][2] *= 0.3551358075004491;
696
+ tcprecalc['Durielq (H)']['droprateRoot'][3] *= 0.31271044447151597;
697
+ tcprecalc['Durielq (H)']['droprateRoot'][4] *= 0.29876543209876544;
698
+ tcprecalc['Durielq (H)']['droprateRoot'][5] *= 0.29876543209876544;
699
+ tcprecalc['Durielq (H)']['droprateRoot'][6] *= 0.29876543209876544;
700
+ tcprecalc['Durielq (H)']['droprateRoot'][7] *= 0.29876543209876544;
701
+ tcprecalc['Durielq (H)']['droprateRoot'][8] *= 0.29876543209876544;
702
+ tcprecalc['Durielq (H) - Base']['droprate'][1] *= 0.39501521709445914 / 0.509798203037134;
703
+ tcprecalc['Durielq (H) - Base']['droprate'][2] *= 0.27984922935137513 / 0.3551358075004491;
704
+ tcprecalc['Durielq (H) - Base']['droprate'][3] *= 0.25418252986014334 / 0.31271044447151597;
705
+ tcprecalc['Durielq (H) - Base']['droprate'][4] *= 0.23562610229276895 / 0.29876543209876544;
706
+ tcprecalc['Durielq (H) - Base']['droprate'][5] *= 0.23562610229276895 / 0.29876543209876544;
707
+ tcprecalc['Durielq (H) - Base']['droprate'][6] *= 0.23562610229276895 / 0.29876543209876544;
708
+ tcprecalc['Durielq (H) - Base']['droprate'][7] *= 0.23562610229276895 / 0.29876543209876544;
709
+ tcprecalc['Durielq (H) - Base']['droprate'][8] *= 0.23562610229276895 / 0.29876543209876544;
710
+
711
+ // Flatten and abstract countess rune TCs so we can adjust the drop rate of each via coefficient.
712
+ tcprecalc.forEach((tc, key) => {
713
+ ({
714
+ 'Countess Rune': tcprecalc['Countess Rune'],
715
+ 'Countess Rune (N)': tcprecalc['Countess Rune (N)'],
716
+ 'Countess Rune (H)': tcprecalc['Countess Rune (H)'],
717
+ }).forEach((tc, baseName) => {
718
+ function flattenAndAbstract (tcName, mult = 1, ret = {}) {
719
+ if (tcprecalc[tcName]) {
720
+ tcprecalc[tcName].counts.forEach((submult, subtcName) => {
721
+ flattenAndAbstract(subtcName, mult * submult, ret);
722
+ });
723
+ } else {
724
+ let newtcName = `${baseName} - ${tcName}`;
725
+
726
+ if (!tcprecalc[newtcName]) {
727
+ tcprecalc[newtcName] = {
728
+ droprate: {
729
+ 1: 1,
730
+ 2: 1,
731
+ 3: 1,
732
+ 4: 1,
733
+ 5: 1,
734
+ 6: 1,
735
+ 7: 1,
736
+ 8: 1,
737
+ },
738
+ droprateRoot: {
739
+ 1: 1,
740
+ 2: 1,
741
+ 3: 1,
742
+ 4: 1,
743
+ 5: 1,
744
+ 6: 1,
745
+ 7: 1,
746
+ 8: 1,
747
+ },
748
+ counts: {
749
+ [tcName]: 1,
750
+ }
751
+ };
752
+ }
753
+
754
+ ret[newtcName] = ret[newtcName] || 0;
755
+ ret[newtcName] += mult;
756
+ }
757
+
758
+ return ret;
759
+ }
760
+
761
+ tc.counts = flattenAndAbstract(baseName);
762
+ });
763
+ });
764
+
765
+ // Multiply the coefficients to the abstracted TCs
766
+ ({
767
+ 'Countess Rune': {"1":{"0.8412177979895897":["r01"],"0.8412177979895898":["r02"],"0.8279977514350507":["r03"],"0.8279977514350506":["r04"],"0.8238064081864378":["r05","r06","r07","r08"]},"2":{"0.5384142786164905":["r01","r02"],"0.4988295919434789":["r03"],"0.498829591943479":["r04"],"0.4862413242371927":["r05","r08"],"0.48624132423719274":["r06","r07"]},"3":{"0.43127418667153955":["r01","r02"],"0.38274414977638443":["r03","r04"],"0.3673191805912878":["r05","r08"],"0.3673191805912879":["r06","r07"]},"4":{"0.4019832189168574":["r01"],"0.40198321891685734":["r02"],"0.3499170812603648":["r03"],"0.34991708126036486":["r04"],"0.3333333333333333":["r05","r07","r08"],"0.33333333333333337":["r06"]},"5":{"0.4019832189168574":["r01"],"0.40198321891685734":["r02"],"0.3499170812603648":["r03"],"0.34991708126036486":["r04"],"0.3333333333333333":["r05","r07","r08"],"0.33333333333333337":["r06"]},"6":{"0.4019832189168574":["r01"],"0.40198321891685734":["r02"],"0.3499170812603648":["r03"],"0.34991708126036486":["r04"],"0.3333333333333333":["r05","r07","r08"],"0.33333333333333337":["r06"]},"7":{"0.4019832189168574":["r01"],"0.40198321891685734":["r02"],"0.3499170812603648":["r03"],"0.34991708126036486":["r04"],"0.3333333333333333":["r05","r07","r08"],"0.33333333333333337":["r06"]},"8":{"0.4019832189168574":["r01"],"0.40198321891685734":["r02"],"0.3499170812603648":["r03"],"0.34991708126036486":["r04"],"0.3333333333333333":["r05","r07","r08"],"0.33333333333333337":["r06"]}},
768
+ 'Countess Rune (N)': {"1":{"0.8256244546455895":["r01","r02","r05","r06","r11","r14","r16"],"0.8256244546455894":["r03","r07","r09","r12","r13","r15"],"0.8256244546455896":["r04","r08","r10"]},"2":{"0.491703901847278":["r01","r06"],"0.4917039018472779":["r02","r03","r07","r09","r12","r15","r16"],"0.49170390184727797":["r04","r05","r08","r10","r11","r13","r14"]},"3":{"0.37401224832526186":["r01","r02","r05","r06","r11","r12","r14","r15","r16"],"0.3740122483252619":["r03","r04","r07","r08","r09","r10","r13"]},"4":{"0.34053127677806344":["r01","r02","r03","r04","r05","r06","r07","r08","r09","r10","r11"],"0.3405312767780634":["r12","r13","r14","r15","r16"]},"5":{"0.34053127677806344":["r01","r02","r03","r04","r05","r06","r07","r08","r09","r10","r11"],"0.3405312767780634":["r12","r13","r14","r15","r16"]},"6":{"0.34053127677806344":["r01","r02","r03","r04","r05","r06","r07","r08","r09","r10","r11"],"0.3405312767780634":["r12","r13","r14","r15","r16"]},"7":{"0.34053127677806344":["r01","r02","r03","r04","r05","r06","r07","r08","r09","r10","r11"],"0.3405312767780634":["r12","r13","r14","r15","r16"]},"8":{"0.34053127677806344":["r01","r02","r03","r04","r05","r06","r07","r08","r09","r10","r11"],"0.3405312767780634":["r12","r13","r14","r15","r16"]}},
769
+ 'Countess Rune (H)': {"1":{"0.8219588445252253":["r01","r06"],"0.8219588445252254":["r02","r03","r04","r05","r07","r08","r09","r10","r11","r12","r13","r15","r17","r18","r19","r20","r22","r23","r24"],"0.8219588445252255":["r14","r16","r21"]},"2":{"0.4893751399649079":["r01","r02","r11","r17","r19"],"0.48937513996490795":["r03","r05","r06","r07","r09","r12","r13","r15","r16","r18","r20","r21","r22","r23","r24"],"0.489375139964908":["r04","r08","r10","r14"]},"3":{"0.3733514492176993":["r01","r17"],"0.37335144921769925":["r02","r03","r04","r05","r06","r07","r08","r09","r10","r11","r12","r14","r15","r16","r19","r20","r21","r22","r23","r24"],"0.3733514492176992":["r13","r18"]},"4":{"0.34053976872957364":["r01","r02","r03","r04","r05","r06","r07","r08","r09","r10","r12","r13","r14","r15","r16","r17","r19","r20","r23","r24"],"0.3405397687295737":["r11","r18","r21"],"0.3405397687295736":["r22"]},"5":{"0.34053976872957364":["r01","r02","r03","r04","r05","r06","r07","r08","r09","r10","r12","r13","r14","r15","r16","r17","r19","r20","r23","r24"],"0.3405397687295737":["r11","r18","r21"],"0.3405397687295736":["r22"]},"6":{"0.34053976872957364":["r01","r02","r03","r04","r05","r06","r07","r08","r09","r10","r12","r13","r14","r15","r16","r17","r19","r20","r23","r24"],"0.3405397687295737":["r11","r18","r21"],"0.3405397687295736":["r22"]},"7":{"0.34053976872957364":["r01","r02","r03","r04","r05","r06","r07","r08","r09","r10","r12","r13","r14","r15","r16","r17","r19","r20","r23","r24"],"0.3405397687295737":["r11","r18","r21"],"0.3405397687295736":["r22"]},"8":{"0.34053976872957364":["r01","r02","r03","r04","r05","r06","r07","r08","r09","r10","r12","r13","r14","r15","r16","r17","r19","r20","r23","r24"],"0.3405397687295737":["r11","r18","r21"],"0.3405397687295736":["r22"]}},
770
+ }).forEach((data, baseName) => {
771
+ [1, 2, 3, 4, 5, 6, 7, 8].forEach(players => {
772
+ data[players].forEach((runes, value) => {
773
+ value = Number(value);
774
+ runes.forEach(runeName => {
775
+ tcprecalc[baseName + ' - ' + runeName]['droprate'][players] *= value;
776
+ });
777
+ });
778
+ });
779
+ });
780
+
781
+ files.forEach(fn => {
782
+ fs.writeFileSync(outDir + fn + '.json', JSON.stringify(keySort(full[fn]), null, ' '));
783
+ });
784
+
785
+ fs.writeFileSync(outDir + 'items.json', JSON.stringify(items, null, ' '));
786
+ fs.writeFileSync(outDir + 'atomic.json', JSON.stringify(keySort(atomic), null, ' '));
787
+ fs.writeFileSync(outDir + 'treasureclassgroupsex.json', JSON.stringify(groupsEx, null, ' '));
788
+ fs.writeFileSync(outDir + 'monpopulationest.json', JSON.stringify(monpopulation, null, ' '));
789
+ fs.writeFileSync(outDir + 'tcprecalc.json', JSON.stringify(tcprecalc, null, ' '));