@blizzhackers/d2data 3.1.91638 → 3.1.91639

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 (102) hide show
  1. package/compile.js +812 -806
  2. package/json/base/.gitkeep +0 -0
  3. package/json/base/actinfo.json +116 -0
  4. package/json/base/actprofile.json +1890 -0
  5. package/json/base/armor.json +17382 -0
  6. package/json/base/armtype.json +17 -0
  7. package/json/base/atomic.json +1026 -0
  8. package/json/base/automagic.json +744 -0
  9. package/json/base/automap.json +40732 -0
  10. package/json/base/belts.json +743 -0
  11. package/json/base/bodylocs.json +52 -0
  12. package/json/base/books.json +34 -0
  13. package/json/base/charstats.json +600 -0
  14. package/json/base/coldmasterybreakpoints.json +8 -0
  15. package/json/base/colors.json +107 -0
  16. package/json/base/compcode.json +577 -0
  17. package/json/base/composit.json +82 -0
  18. package/json/base/cubemain.json +2116 -0
  19. package/json/base/cubemod.json +57 -0
  20. package/json/base/difficultylevels.json +110 -0
  21. package/json/base/elemtypes.json +62 -0
  22. package/json/base/events.json +82 -0
  23. package/json/base/experience.json +1214 -0
  24. package/json/base/gamble.json +520 -0
  25. package/json/base/gems.json +1868 -0
  26. package/json/base/hireling.json +7640 -0
  27. package/json/base/hitclass.json +67 -0
  28. package/json/base/inventory.json +2302 -0
  29. package/json/base/itemratio.json +134 -0
  30. package/json/base/items.json +55036 -0
  31. package/json/base/itemstatcost.json +5621 -0
  32. package/json/base/itemtypes.json +2766 -0
  33. package/json/base/itemuicategories.json +318 -0
  34. package/json/base/levelgroups.json +786 -0
  35. package/json/base/levels.json +15788 -0
  36. package/json/base/lowqualityitems.json +6 -0
  37. package/json/base/lvlmaze.json +910 -0
  38. package/json/base/lvlprest.json +28622 -0
  39. package/json/base/lvlsub.json +824 -0
  40. package/json/base/lvltypes.json +1377 -0
  41. package/json/base/lvlwarp.json +1515 -0
  42. package/json/base/magicprefix.json +12624 -0
  43. package/json/base/magicsuffix.json +14053 -0
  44. package/json/base/misc.json +11783 -0
  45. package/json/base/misscalc.json +237 -0
  46. package/json/base/missiles.json +24098 -0
  47. package/json/base/monai.json +1181 -0
  48. package/json/base/moncountest.json +642 -0
  49. package/json/base/monequip.json +361 -0
  50. package/json/base/monlvl.json +3665 -0
  51. package/json/base/monmode.json +98 -0
  52. package/json/base/monpet.json +28 -0
  53. package/json/base/monplace.json +39 -0
  54. package/json/base/monpopulationest.json +17551 -0
  55. package/json/base/monpreset.json +922 -0
  56. package/json/base/monprop.json +144 -0
  57. package/json/base/monseq.json +5312 -0
  58. package/json/base/monsounds.json +2707 -0
  59. package/json/base/monstats.json +67543 -0
  60. package/json/base/monstats2.json +34936 -0
  61. package/json/base/montype.json +274 -0
  62. package/json/base/monumod.json +504 -0
  63. package/json/base/npc.json +232 -0
  64. package/json/base/objects.json +87061 -0
  65. package/json/base/objgroup.json +3733 -0
  66. package/json/base/objmode.json +42 -0
  67. package/json/base/objpreset.json +2926 -0
  68. package/json/base/objtype.json +2457 -0
  69. package/json/base/overlay.json +8208 -0
  70. package/json/base/pettype.json +226 -0
  71. package/json/base/playerclass.json +45 -0
  72. package/json/base/plrmode.json +122 -0
  73. package/json/base/plrtype.json +45 -0
  74. package/json/base/properties.json +3224 -0
  75. package/json/base/propertygroups.json +307 -0
  76. package/json/base/qualityitems.json +149 -0
  77. package/json/base/rareprefix.json +296 -0
  78. package/json/base/raresuffix.json +921 -0
  79. package/json/base/runes.json +3567 -0
  80. package/json/base/runeworduicategories.json +27 -0
  81. package/json/base/setitems.json +4181 -0
  82. package/json/base/sets.json +1046 -0
  83. package/json/base/shrines.json +319 -0
  84. package/json/base/skillcalc.json +607 -0
  85. package/json/base/skilldesc.json +7591 -0
  86. package/json/base/skills.json +16959 -0
  87. package/json/base/soundenviron.json +2879 -0
  88. package/json/base/sounds.json +466498 -0
  89. package/json/base/states.json +1797 -0
  90. package/json/base/storepage.json +22 -0
  91. package/json/base/superuniques.json +1495 -0
  92. package/json/base/tcprecalc.json +35785 -0
  93. package/json/base/treasureclassex.json +19465 -0
  94. package/json/base/treasureclassgroupsex.json +1025 -0
  95. package/json/base/uniqueappellation.json +27 -0
  96. package/json/base/uniqueitems.json +14459 -0
  97. package/json/base/uniqueprefix.json +55 -0
  98. package/json/base/uniquesuffix.json +71 -0
  99. package/json/base/wanderingmon.json +3 -0
  100. package/json/base/weapons.json +25875 -0
  101. package/package.json +1 -1
  102. package/json/hirelingdesc.json +0 -28
package/compile.js CHANGED
@@ -9,884 +9,890 @@
9
9
 
10
10
  require('./objext.js');
11
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
- const decimalPrecision = 15;
17
12
 
18
- function keySort(obj) {
19
- let keys = Object.keys(obj).sort(), ret = {};
20
-
21
- keys.forEach(key => {
22
- ret[key] = obj[key];
23
- });
24
-
25
- return ret;
26
- }
27
-
28
- const indexes = {
29
- actinfo: 'act',
30
- armor: 'code',
31
- armtype: 'Token',
32
- bodylocs: 'Code',
33
- books: 'Name',
34
- charstats: 'class',
35
- colors: 'Code',
36
- compcode: 'code',
37
- composit: 'Name',
38
- cubemod: 'Code',
39
- difficultylevels: 'Name',
40
- elemtypes: 'Code',
41
- experience: 'Level',
42
- events: 'event',
43
- gems: 'code',
44
- hirelingdesc: 'id',
45
- hitclass: 'Code',
46
- inventory: 'class',
47
- itemstatcost: 'Stat',
48
- itemtypes: 'Code',
49
- levelgroups: 'LevelGroupId',
50
- levels: 'Id',
51
- lvlmaze: 'Level',
52
- lvlprest: 'Def',
53
- lvltypes: 'Name',
54
- misc: 'code',
55
- misscalc: 'code',
56
- missiles: 'Missile',
57
- monai: 'AI',
58
- monmode: 'code',
59
- monpet: 'monster',
60
- monprop: 'Id',
61
- monstats: 'Id',
62
- monstats2: 'Id',
63
- monsounds: 'Id',
64
- montype: 'type',
65
- monumod: 'id',
66
- npc: 'npc',
67
- objmode: 'Token',
68
- overlay: 'overlay',
69
- pettype: 'pet type',
70
- plrmode: 'Code',
71
- plrtype: 'Token',
72
- playerclass: 'Code',
73
- properties: 'code',
74
- propertygroups: 'code',
75
- runes: '*Rune Name',
76
- runeworduicategories: 'Name',
77
- setitems: 'index',
78
- sets: 'index',
79
- shrines: 'Code',
80
- skillcalc: 'code',
81
- skilldesc: 'code',
82
- skills: '*Id',
83
- soundenviron: 'Handle',
84
- sounds: '*Index',
85
- states: 'state',
86
- storepage: 'Code',
87
- superuniques: 'Superunique',
88
- treasureclassex: 'Treasure Class',
89
- uniqueitems: '*ID',
90
- weapons: 'code',
91
- weaponclass: 'Code',
92
- };
93
-
94
- const filterValues = {
95
- '': true,
96
- 'unused': true,
97
- 'none': true,
98
- 'null': true,
99
- };
100
-
101
- function noDrop(e, nd, ...d) {
102
- if (e <= 1) {
103
- return nd | 0;
104
- }
105
-
106
- e = e | 0;
107
- nd = nd | 0;
108
- d = d.reduce((t, v) => t + v | 0, 0);
109
-
110
- if (d < 1) {
111
- return Infinity;
13
+ [
14
+ ['txt/', 'json/'],
15
+ ['txt/base/', 'json/base/'],
16
+ ].forEach(dirs => {
17
+ const inDir = dirs[0], outDir = dirs[1];
18
+ const lineEnd = /[\n\r]+/g, fieldEnd = /\t/g, full = {};
19
+ const files = fs.readdirSync(inDir).filter(fn => fn.slice(-4) === '.txt').map(fn => fn.slice(0, -4));
20
+ const decimalPrecision = 15;
21
+
22
+ function keySort(obj) {
23
+ let keys = Object.keys(obj).sort(), ret = {};
24
+
25
+ keys.forEach(key => {
26
+ ret[key] = obj[key];
27
+ });
28
+
29
+ return ret;
112
30
  }
113
-
114
- return (d / (((nd + d) / nd)**e - 1)) | 0;
115
- }
116
-
117
- files.forEach(fn => {
118
- let data = fs.readFileSync(inDir + fn + '.txt').toString().split(lineEnd);
119
- let header = data.shift().split(fieldEnd);
120
- let indexColumn = header.indexOf(indexes[fn]);
121
- let usesVersion = header.includes('version');
122
- let expansion = false;
123
- let maxKeyCount = 0;
124
-
125
- if (indexColumn === -1) {
126
- console.log('Using default Index for:', fn);
31
+
32
+ const indexes = {
33
+ actinfo: 'act',
34
+ armor: 'code',
35
+ armtype: 'Token',
36
+ bodylocs: 'Code',
37
+ books: 'Name',
38
+ charstats: 'class',
39
+ colors: 'Code',
40
+ compcode: 'code',
41
+ composit: 'Name',
42
+ cubemod: 'Code',
43
+ difficultylevels: 'Name',
44
+ elemtypes: 'Code',
45
+ experience: 'Level',
46
+ events: 'event',
47
+ gems: 'code',
48
+ hirelingdesc: 'id',
49
+ hitclass: 'Code',
50
+ inventory: 'class',
51
+ itemstatcost: 'Stat',
52
+ itemtypes: 'Code',
53
+ levelgroups: 'LevelGroupId',
54
+ levels: 'Id',
55
+ lvlmaze: 'Level',
56
+ lvlprest: 'Def',
57
+ lvltypes: 'Name',
58
+ misc: 'code',
59
+ misscalc: 'code',
60
+ missiles: 'Missile',
61
+ monai: 'AI',
62
+ monmode: 'code',
63
+ monpet: 'monster',
64
+ monprop: 'Id',
65
+ monstats: 'Id',
66
+ monstats2: 'Id',
67
+ monsounds: 'Id',
68
+ montype: 'type',
69
+ monumod: 'id',
70
+ npc: 'npc',
71
+ objmode: 'Token',
72
+ overlay: 'overlay',
73
+ pettype: 'pet type',
74
+ plrmode: 'Code',
75
+ plrtype: 'Token',
76
+ playerclass: 'Code',
77
+ properties: 'code',
78
+ propertygroups: 'code',
79
+ runes: '*Rune Name',
80
+ runeworduicategories: 'Name',
81
+ setitems: 'index',
82
+ sets: 'index',
83
+ shrines: 'Code',
84
+ skillcalc: 'code',
85
+ skilldesc: 'code',
86
+ skills: '*Id',
87
+ soundenviron: 'Handle',
88
+ sounds: '*Index',
89
+ states: 'state',
90
+ storepage: 'Code',
91
+ superuniques: 'Superunique',
92
+ treasureclassex: 'Treasure Class',
93
+ uniqueitems: '*ID',
94
+ weapons: 'code',
95
+ weaponclass: 'Code',
96
+ };
97
+
98
+ const filterValues = {
99
+ '': true,
100
+ 'unused': true,
101
+ 'none': true,
102
+ 'null': true,
103
+ };
104
+
105
+ function noDrop(e, nd, ...d) {
106
+ if (e <= 1) {
107
+ return nd | 0;
108
+ }
109
+
110
+ e = e | 0;
111
+ nd = nd | 0;
112
+ d = d.reduce((t, v) => t + v | 0, 0);
113
+
114
+ if (d < 1) {
115
+ return Infinity;
116
+ }
117
+
118
+ return (d / (((nd + d) / nd)**e - 1)) | 0;
127
119
  }
128
-
129
- full[fn] = data.reduce((obj, line, index) => {
130
- if (line.trim()) {
131
- line = line.split(fieldEnd).map(v => v.trim());
132
-
133
- if (line[0].toLowerCase() === 'expansion') {
134
- expansion = true;
135
- } else if (header.length === 1 || line.filter(Boolean).length > 1) {
136
- let key = indexColumn >= 0 ? (line[indexColumn]) : index;
137
-
138
- if (key !== undefined) {
139
- if (key !== '') {
140
- while (obj[key]) {
141
- console.warn('Duplicate key ' + JSON.stringify(key) + ' in ' + fn);
142
- key += ' [dup]';
143
- }
144
-
145
- {
146
- let tmp = {};
147
-
148
- for (let c = 0; c < header.length; c++) {
149
- if (indexColumn >= 0 && c === indexColumn || !filterValues[line[c].toString().toLowerCase()]) {
150
- tmp[header[c] || 'unknown'] = line[c] !== 'Infinity' && Number(line[c]).toString() === line[c].trim() ? Number(line[c]) : line[c];
151
- }
120
+
121
+ files.forEach(fn => {
122
+ let data = fs.readFileSync(inDir + fn + '.txt').toString().split(lineEnd);
123
+ let header = data.shift().split(fieldEnd);
124
+ let indexColumn = header.indexOf(indexes[fn]);
125
+ let usesVersion = header.includes('version');
126
+ let expansion = false;
127
+ let maxKeyCount = 0;
128
+
129
+ if (indexColumn === -1) {
130
+ console.log('Using default Index for:', fn);
131
+ }
132
+
133
+ full[fn] = data.reduce((obj, line, index) => {
134
+ if (line.trim()) {
135
+ line = line.split(fieldEnd).map(v => v.trim());
136
+
137
+ if (line[0].toLowerCase() === 'expansion') {
138
+ expansion = true;
139
+ } else if (header.length === 1 || line.filter(Boolean).length > 1) {
140
+ let key = indexColumn >= 0 ? (line[indexColumn]) : index;
141
+
142
+ if (key !== undefined) {
143
+ if (key !== '') {
144
+ while (obj[key]) {
145
+ console.warn('Duplicate key ' + JSON.stringify(key) + ' in ' + fn);
146
+ key += ' [dup]';
152
147
  }
153
-
154
- let keyCount = Object.keys(tmp).length;
155
-
156
- if (keyCount > 0) {
157
- if (usesVersion) {
158
- if (tmp.version >= 100) {
159
- tmp.expansion = 1;
148
+
149
+ {
150
+ let tmp = {};
151
+
152
+ for (let c = 0; c < header.length; c++) {
153
+ if (indexColumn >= 0 && c === indexColumn || !filterValues[line[c].toString().toLowerCase()]) {
154
+ tmp[header[c] || 'unknown'] = line[c] !== 'Infinity' && Number(line[c]).toString() === line[c].trim() ? Number(line[c]) : line[c];
160
155
  }
161
- } else if (expansion) {
162
- tmp.expansion = 1;
163
156
  }
164
-
165
- obj[key] = tmp;
166
-
167
- delete obj[key]['*eol'];
168
- delete obj[key]['*EOL'];
169
- delete obj[key]['eol'];
170
- delete obj[key]['EOL'];
171
-
172
- if (indexColumn >= 0) {
173
- obj[key].lineNumber = index;
157
+
158
+ let keyCount = Object.keys(tmp).length;
159
+
160
+ if (keyCount > 0) {
161
+ if (usesVersion) {
162
+ if (tmp.version >= 100) {
163
+ tmp.expansion = 1;
164
+ }
165
+ } else if (expansion) {
166
+ tmp.expansion = 1;
167
+ }
168
+
169
+ obj[key] = tmp;
170
+
171
+ delete obj[key]['*eol'];
172
+ delete obj[key]['*EOL'];
173
+ delete obj[key]['eol'];
174
+ delete obj[key]['EOL'];
175
+
176
+ if (indexColumn >= 0) {
177
+ obj[key].lineNumber = index;
178
+ }
174
179
  }
180
+
181
+ maxKeyCount = Math.max(maxKeyCount, keyCount);
175
182
  }
176
-
177
- maxKeyCount = Math.max(maxKeyCount, keyCount);
178
183
  }
184
+ } else {
185
+ throw new Error('No viable key in: ' + fn + ' : ' + key);
179
186
  }
180
- } else {
181
- throw new Error('No viable key in: ' + fn + ' : ' + key);
182
187
  }
183
188
  }
189
+
190
+ return obj;
191
+ }, {});
192
+
193
+ if (maxKeyCount === 1) {
194
+ full[fn] = Object.values(full[fn]).map(line => Object.values(line)[0]);
195
+ console.log(fn, 'was reduced!');
184
196
  }
185
-
186
- return obj;
187
- }, {});
188
-
189
- if (maxKeyCount === 1) {
190
- full[fn] = Object.values(full[fn]).map(line => Object.values(line)[0]);
191
- console.log(fn, 'was reduced!');
192
- }
193
-
194
- if (fn === 'treasureclassex') {
195
- full[fn].forEach(tc => {
196
- let precalc = {}, nodropcalc = {
197
- 1: 0,
198
- 2: 0,
199
- 3: 0,
200
- 4: 0,
201
- 5: 0,
202
- 6: 0,
203
- 7: 0,
204
- 8: 0,
205
- };
206
-
207
- if (tc.Picks > 0) {
208
- let total = 0;
209
-
210
- for (let c = 1; c <= 9; c++) {
211
- if (tc['Item' + c]) {
212
- total += tc['Prob' + c] | 0;
197
+
198
+ if (fn === 'treasureclassex') {
199
+ full[fn].forEach(tc => {
200
+ let precalc = {}, nodropcalc = {
201
+ 1: 0,
202
+ 2: 0,
203
+ 3: 0,
204
+ 4: 0,
205
+ 5: 0,
206
+ 6: 0,
207
+ 7: 0,
208
+ 8: 0,
209
+ };
210
+
211
+ if (tc.Picks > 0) {
212
+ let total = 0;
213
+
214
+ for (let c = 1; c <= 9; c++) {
215
+ if (tc['Item' + c]) {
216
+ total += tc['Prob' + c] | 0;
217
+ }
213
218
  }
214
- }
215
-
216
- [1, 2, 3, 4, 5, 6, 7, 8].forEach(exp => {
217
- nodropcalc[exp] = noDrop(exp, tc.NoDrop, total);
218
- });
219
-
220
- for (let c = 1; c <= 9; c++) {
221
- if (tc['Item' + c]) {
222
- let prob = (tc['Prob' + c] | 0) / total;
223
- precalc[tc['Item' + c]] = precalc[tc['Item' + c]] || 0;
224
- precalc[tc['Item' + c]] += prob;
219
+
220
+ [1, 2, 3, 4, 5, 6, 7, 8].forEach(exp => {
221
+ nodropcalc[exp] = noDrop(exp, tc.NoDrop, total);
222
+ });
223
+
224
+ for (let c = 1; c <= 9; c++) {
225
+ if (tc['Item' + c]) {
226
+ let prob = (tc['Prob' + c] | 0) / total;
227
+ precalc[tc['Item' + c]] = precalc[tc['Item' + c]] || 0;
228
+ precalc[tc['Item' + c]] += prob;
229
+ }
225
230
  }
231
+
232
+ precalc = precalc.map(v => v * tc.Picks);
233
+ tc['*ItemProbTotal'] = total;
226
234
  }
227
-
228
- precalc = precalc.map(v => v * tc.Picks);
229
- tc['*ItemProbTotal'] = total;
230
- }
231
- else if (tc.Picks < 0) {
232
- let picksleft = -tc.Picks;
233
-
234
- for (let c = 1; c <= 9; c++) {
235
- if (tc['Item' + c] && picksleft > 0) {
236
- let pickcount = Math.min(picksleft, tc['Prob' + c] | 0);
237
-
238
- if (pickcount > 0) {
239
- precalc[tc['Item' + c]] = precalc[tc['Item' + c]] || 0;
240
- precalc[tc['Item' + c]] += pickcount;
241
- picksleft -= pickcount;
235
+ else if (tc.Picks < 0) {
236
+ let picksleft = -tc.Picks;
237
+
238
+ for (let c = 1; c <= 9; c++) {
239
+ if (tc['Item' + c] && picksleft > 0) {
240
+ let pickcount = Math.min(picksleft, tc['Prob' + c] | 0);
241
+
242
+ if (pickcount > 0) {
243
+ precalc[tc['Item' + c]] = precalc[tc['Item' + c]] || 0;
244
+ precalc[tc['Item' + c]] += pickcount;
245
+ picksleft -= pickcount;
246
+ }
242
247
  }
243
248
  }
244
249
  }
245
- }
246
-
247
- tc.nodropcalc = nodropcalc;
248
- tc.precalc = precalc;
249
- });
250
- }
251
-
252
- if (fn === 'superuniques') {
253
- full[fn]['Bishibosh'].areaId = 3;
254
- full[fn]['Bonebreak'].areaId = 18;
255
- full[fn]['Coldcrow'].areaId = 9;
256
- full[fn]['Rakanishu'].areaId = 4;
257
- full[fn]['Treehead WoodFist'].areaId = 5;
258
- full[fn]['Griswold'].areaId = 38;
259
- full[fn]['The Countess'].areaId = 25;
260
- full[fn]['Pitspawn Fouldog'].areaId = 30;
261
- full[fn]['Boneash'].areaId = 33;
262
- full[fn]['Radament'].areaId = 49;
263
- full[fn]['Bloodwitch the Wild'].areaId = 60;
264
- full[fn]['Fangskin'].areaId = 61;
265
- full[fn]['Beetleburst'].areaId = 43;
266
- full[fn]['Leatherarm'].areaId = 59;
267
- full[fn]['Coldworm the Burrower'].areaId = 64;
268
- full[fn]['Fire Eye'].areaId = 54;
269
- full[fn]['Dark Elder'].areaId = 44;
270
- // full[fn]['The Summoner'].areaId = 74; // I don't think he spawns a superunique.
271
- full[fn]['The Smith'].areaId = 28;
272
- full[fn]['Web Mage the Burning'].areaId = 85;
273
- full[fn]['Witch Doctor Endugu'].areaId = 91;
274
- full[fn]['Stormtree'].areaId = 78;
275
- full[fn]['Sarina the Battlemaid'].areaId = 94;
276
- full[fn]['Icehawk Riftwing'].areaId = 92;
277
- full[fn]['Ismail Vilehand'].areaId = 83;
278
- full[fn]['Geleb Flamefinger'].areaId = 83;
279
- full[fn]['Bremm Sparkfist'].areaId = 102;
280
- full[fn]['Toorc Icefist'].areaId = 83;
281
- full[fn]['Wyand Voidfinger'].areaId = 102;
282
- full[fn]['Maffer Dragonhand'].areaId = 102;
283
- full[fn]['Infector of Souls'].areaId = 108;
284
- full[fn]['Lord De Seis'].areaId = 108;
285
- full[fn]['Grand Vizier of Chaos'].areaId = 108;
286
- full[fn]['The Cow King'].areaId = 39;
287
- full[fn]['Corpsefire'].areaId = 8;
288
- full[fn]['The Feature Creep'].areaId = 107;
289
- full[fn]['Siege Boss'].areaId = 110;
290
- full[fn]['Ancient Barbarian 1'].areaId = 120;
291
- full[fn]['Ancient Barbarian 2'].areaId = 120;
292
- full[fn]['Ancient Barbarian 3'].areaId = 120;
293
- full[fn]['Bonesaw Breaker'].areaId = 115;
294
- full[fn]['Dac Farren'].areaId = 110;
295
- full[fn]['Megaflow Rectifier'].areaId = 111;
296
- full[fn]['Eyeback Unleashed'].areaId = 111;
297
- full[fn]['Threash Socket'].areaId = 112;
298
- full[fn]['Pindleskin'].areaId = 121;
299
- full[fn]['Snapchip Shatter'].areaId = 119;
300
- full[fn]['Sharp Tooth Sayer'].areaId = 111;
301
- full[fn]['Frozenstein'].areaId = 114;
302
- full[fn]['Nihlathak Boss'].areaId = 124;
303
- full[fn]['Baal Subject 1'].areaId = 131;
304
- full[fn]['Baal Subject 2'].areaId = 131;
305
- full[fn]['Baal Subject 3'].areaId = 131;
306
- full[fn]['Baal Subject 4'].areaId = 131;
307
- full[fn]['Baal Subject 5'].areaId = 131;
308
- }
309
-
310
- if (fn === 'monstats') {
311
- full[fn]['bloodraven'].areaId = 17;
312
- full[fn]['summoner'].areaId = 74;
313
- full[fn]['andariel'].areaId = 37;
314
- full[fn]['duriel'].areaId = 73;
315
- full[fn]['mephisto'].areaId = 102;
316
- full[fn]['izual'].areaId = 105;
317
- full[fn]['diablo'].areaId = 108;
318
- full[fn]['baalcrab'].areaId = 132;
319
- full[fn]['ubermephisto'].areaId = 136;
320
- full[fn]['uberdiablo'].areaId = 136;
321
- full[fn]['uberizual'].areaId = 135;
322
- full[fn]['uberandariel'].areaId = 133;
323
- full[fn]['uberduriel'].areaId = 134;
324
- full[fn]['uberbaal'].areaId = 136;
325
- }
326
- });
327
-
328
- const items = {};
329
-
330
- [
331
- ...Object.values(full.weapons).sort((a, b) => a.lineNumber - b.lineNumber),
332
- ...Object.values(full.armor).sort((a, b) => a.lineNumber - b.lineNumber),
333
- ...Object.values(full.misc).sort((a, b) => a.lineNumber - b.lineNumber),
334
- ].forEach((item, classid) => {
335
- item.classid = classid;
336
- items[item.code] = item;
337
- });
338
-
339
- let atomic = {};
340
- let atomicTypes = {};
341
- let atomicMax = 87;
342
-
343
- let calcTC = x => {
344
- let ret = Math.max(1, Math.ceil((x || 0) / 3)) * 3;
345
- atomicMax = Math.max(ret, atomicMax);
346
- return ret;
347
- }
348
-
349
- let weaponsarmor = [...Object.values(full.weapons), ...Object.values(full.armor)];
350
-
351
- weaponsarmor.forEach(item => {
352
- if (!item.spawnable) {
353
- return;
250
+
251
+ tc.nodropcalc = nodropcalc;
252
+ tc.precalc = precalc;
253
+ });
254
+ }
255
+
256
+ if (fn === 'superuniques') {
257
+ full[fn]['Bishibosh'].areaId = 3;
258
+ full[fn]['Bonebreak'].areaId = 18;
259
+ full[fn]['Coldcrow'].areaId = 9;
260
+ full[fn]['Rakanishu'].areaId = 4;
261
+ full[fn]['Treehead WoodFist'].areaId = 5;
262
+ full[fn]['Griswold'].areaId = 38;
263
+ full[fn]['The Countess'].areaId = 25;
264
+ full[fn]['Pitspawn Fouldog'].areaId = 30;
265
+ full[fn]['Boneash'].areaId = 33;
266
+ full[fn]['Radament'].areaId = 49;
267
+ full[fn]['Bloodwitch the Wild'].areaId = 60;
268
+ full[fn]['Fangskin'].areaId = 61;
269
+ full[fn]['Beetleburst'].areaId = 43;
270
+ full[fn]['Leatherarm'].areaId = 59;
271
+ full[fn]['Coldworm the Burrower'].areaId = 64;
272
+ full[fn]['Fire Eye'].areaId = 54;
273
+ full[fn]['Dark Elder'].areaId = 44;
274
+ // full[fn]['The Summoner'].areaId = 74; // I don't think he spawns a superunique.
275
+ full[fn]['The Smith'].areaId = 28;
276
+ full[fn]['Web Mage the Burning'].areaId = 85;
277
+ full[fn]['Witch Doctor Endugu'].areaId = 91;
278
+ full[fn]['Stormtree'].areaId = 78;
279
+ full[fn]['Sarina the Battlemaid'].areaId = 94;
280
+ full[fn]['Icehawk Riftwing'].areaId = 92;
281
+ full[fn]['Ismail Vilehand'].areaId = 83;
282
+ full[fn]['Geleb Flamefinger'].areaId = 83;
283
+ full[fn]['Bremm Sparkfist'].areaId = 102;
284
+ full[fn]['Toorc Icefist'].areaId = 83;
285
+ full[fn]['Wyand Voidfinger'].areaId = 102;
286
+ full[fn]['Maffer Dragonhand'].areaId = 102;
287
+ full[fn]['Infector of Souls'].areaId = 108;
288
+ full[fn]['Lord De Seis'].areaId = 108;
289
+ full[fn]['Grand Vizier of Chaos'].areaId = 108;
290
+ full[fn]['The Cow King'].areaId = 39;
291
+ full[fn]['Corpsefire'].areaId = 8;
292
+ full[fn]['The Feature Creep'].areaId = 107;
293
+ full[fn]['Siege Boss'].areaId = 110;
294
+ full[fn]['Ancient Barbarian 1'].areaId = 120;
295
+ full[fn]['Ancient Barbarian 2'].areaId = 120;
296
+ full[fn]['Ancient Barbarian 3'].areaId = 120;
297
+ full[fn]['Bonesaw Breaker'].areaId = 115;
298
+ full[fn]['Dac Farren'].areaId = 110;
299
+ full[fn]['Megaflow Rectifier'].areaId = 111;
300
+ full[fn]['Eyeback Unleashed'].areaId = 111;
301
+ full[fn]['Threash Socket'].areaId = 112;
302
+ full[fn]['Pindleskin'].areaId = 121;
303
+ full[fn]['Snapchip Shatter'].areaId = 119;
304
+ full[fn]['Sharp Tooth Sayer'].areaId = 111;
305
+ full[fn]['Frozenstein'].areaId = 114;
306
+ full[fn]['Nihlathak Boss'].areaId = 124;
307
+ full[fn]['Baal Subject 1'].areaId = 131;
308
+ full[fn]['Baal Subject 2'].areaId = 131;
309
+ full[fn]['Baal Subject 3'].areaId = 131;
310
+ full[fn]['Baal Subject 4'].areaId = 131;
311
+ full[fn]['Baal Subject 5'].areaId = 131;
312
+ }
313
+
314
+ if (fn === 'monstats') {
315
+ full[fn]['bloodraven'].areaId = 17;
316
+ full[fn]['summoner'].areaId = 74;
317
+ full[fn]['andariel'].areaId = 37;
318
+ full[fn]['duriel'].areaId = 73;
319
+ full[fn]['mephisto'].areaId = 102;
320
+ full[fn]['izual'].areaId = 105;
321
+ full[fn]['diablo'].areaId = 108;
322
+ full[fn]['baalcrab'].areaId = 132;
323
+ full[fn]['ubermephisto'].areaId = 136;
324
+ full[fn]['uberdiablo'].areaId = 136;
325
+ full[fn]['uberizual'].areaId = 135;
326
+ full[fn]['uberandariel'].areaId = 133;
327
+ full[fn]['uberduriel'].areaId = 134;
328
+ full[fn]['uberbaal'].areaId = 136;
329
+ }
330
+ });
331
+
332
+ const items = {};
333
+
334
+ [
335
+ ...Object.values(full.weapons).sort((a, b) => a.lineNumber - b.lineNumber),
336
+ ...Object.values(full.armor).sort((a, b) => a.lineNumber - b.lineNumber),
337
+ ...Object.values(full.misc).sort((a, b) => a.lineNumber - b.lineNumber),
338
+ ].forEach((item, classid) => {
339
+ item.classid = classid;
340
+ items[item.code] = item;
341
+ });
342
+
343
+ let atomic = {};
344
+ let atomicTypes = {};
345
+ let atomicMax = 87;
346
+
347
+ let calcTC = x => {
348
+ let ret = Math.max(1, Math.ceil((x || 0) / 3)) * 3;
349
+ atomicMax = Math.max(ret, atomicMax);
350
+ return ret;
354
351
  }
355
-
356
- let tc = calcTC(item.level);
357
-
358
- function handleAtomic(itemType) {
359
- if (full.itemtypes[itemType] && itemType !== 'tpot') {
360
- if (full.itemtypes[itemType].TreasureClass) {
361
- atomicTypes[itemType] = true;
362
- atomic[itemType + tc] = atomic[itemType + tc] || [];
363
- atomic[itemType + tc][item.lineNumber] = item.code;
364
- }
365
-
366
- if (full.itemtypes[itemType].Equiv1) {
367
- handleAtomic(full.itemtypes[itemType].Equiv1);
368
- }
369
-
370
- if (full.itemtypes[itemType].Equiv2) {
371
- handleAtomic(full.itemtypes[itemType].Equiv2);
352
+
353
+ let weaponsarmor = [...Object.values(full.weapons), ...Object.values(full.armor)];
354
+
355
+ weaponsarmor.forEach(item => {
356
+ if (!item.spawnable) {
357
+ return;
358
+ }
359
+
360
+ let tc = calcTC(item.level);
361
+
362
+ function handleAtomic(itemType) {
363
+ if (full.itemtypes[itemType] && itemType !== 'tpot') {
364
+ if (full.itemtypes[itemType].TreasureClass) {
365
+ atomicTypes[itemType] = true;
366
+ atomic[itemType + tc] = atomic[itemType + tc] || [];
367
+ atomic[itemType + tc][item.lineNumber] = item.code;
368
+ }
369
+
370
+ if (full.itemtypes[itemType].Equiv1) {
371
+ handleAtomic(full.itemtypes[itemType].Equiv1);
372
+ }
373
+
374
+ if (full.itemtypes[itemType].Equiv2) {
375
+ handleAtomic(full.itemtypes[itemType].Equiv2);
376
+ }
372
377
  }
373
378
  }
379
+
380
+ handleAtomic(item.type);
381
+ });
382
+
383
+ atomicTypes = Object.keys(atomicTypes);
384
+
385
+ for (let c = 3; c <= atomicMax; c += 3) {
386
+ atomicTypes.forEach(type => {
387
+ atomic[type + c] = atomic[type + c] || [];
388
+ });
374
389
  }
375
-
376
- handleAtomic(item.type);
377
- });
378
-
379
- atomicTypes = Object.keys(atomicTypes);
380
-
381
- for (let c = 3; c <= atomicMax; c += 3) {
382
- atomicTypes.forEach(type => {
383
- atomic[type + c] = atomic[type + c] || [];
390
+
391
+ atomic.forEach((atom, atomName) => {
392
+ let precalc = {}, total = 0;
393
+
394
+ atom.forEach((itc, i) => {
395
+ let rarity = full.itemtypes[items[itc].type].Rarity | 0;
396
+ total += rarity;
397
+ atom[i] = [itc, rarity];
398
+ });
399
+
400
+ atom.forEach(([itc, chance]) => {
401
+ precalc[itc] = chance / total;
402
+ });
403
+
404
+ atomic[atomName] = precalc;
384
405
  });
385
- }
386
-
387
- atomic.forEach((atom, atomName) => {
388
- let precalc = {}, total = 0;
389
-
390
- atom.forEach((itc, i) => {
391
- let rarity = full.itemtypes[items[itc].type].Rarity | 0;
392
- total += rarity;
393
- atom[i] = [itc, rarity];
406
+
407
+ full.atomic = atomic;
408
+
409
+ let groupsEx = {};
410
+
411
+ full.treasureclassex.forEach((tc, key) => {
412
+ if (tc.group) {
413
+ groupsEx[tc.group] = groupsEx[tc.group] || [];
414
+ groupsEx[tc.group][tc.level|0] = key;
415
+ }
394
416
  });
395
-
396
- atom.forEach(([itc, chance]) => {
397
- precalc[itc] = chance / total;
417
+
418
+ groupsEx = groupsEx.map(group => {
419
+ let length = group.length;
420
+ group = Object.assign({}, group);
421
+ group.length = length;
422
+ return group;
398
423
  });
399
-
400
- atomic[atomName] = precalc;
401
- });
402
-
403
- full.atomic = atomic;
404
-
405
- let groupsEx = {};
406
-
407
- full.treasureclassex.forEach((tc, key) => {
408
- if (tc.group) {
409
- groupsEx[tc.group] = groupsEx[tc.group] || [];
410
- groupsEx[tc.group][tc.level|0] = key;
424
+
425
+ function avg(...nums) {
426
+ return nums.reduce((t, v) => t + v, 0) / nums.length || 0;
411
427
  }
412
- });
413
-
414
- groupsEx = groupsEx.map(group => {
415
- let length = group.length;
416
- group = Object.assign({}, group);
417
- group.length = length;
418
- return group;
419
- });
420
-
421
- function avg(...nums) {
422
- return nums.reduce((t, v) => t + v, 0) / nums.length || 0;
423
- }
424
-
425
- function _s(diff) {
426
- return (str) => str + ["", "(N)", "(H)"][diff];
427
- }
428
-
429
- function monlevel(mon, level, diff) {
430
- if (!diff) {
431
- return mon["Level"] || 0;
428
+
429
+ function _s(diff) {
430
+ return (str) => str + ["", "(N)", "(H)"][diff];
432
431
  }
433
-
434
- let s = _s(diff),
435
- lvl = level[s("MonLvlEx")] || 0,
436
- mlvl = mon[s("Level")] || 0;
437
-
438
- return (diff && !mon.boss) ? lvl : mlvl;
439
- }
440
-
441
- function forEachMonster(level, diff, func) {
442
- let s = _s(diff);
443
-
444
- [0, 1, 2].forEach((type) => {
445
- if (type && !level[s("MonUMin")] && !level[s("MonUMax")]) {
446
- return;
432
+
433
+ function monlevel(mon, level, diff) {
434
+ if (!diff) {
435
+ return mon["Level"] || 0;
447
436
  }
448
-
449
- let m = (num) => level[(diff ? "nmon" : type ? "umon" : "mon") + num];
450
-
451
- let totalrarity = 0;
452
- let rarity = {};
453
-
454
- for (let c = 1; c <= 9; c++) {
455
- if (m(c)) {
456
- let mon = full.monstats[m(c)];
457
-
458
- if (mon && mon.enabled && mon.killable) {
459
- totalrarity += mon.Rarity || 0;
460
- rarity[mon.Id] = rarity[mon.Id] || 0;
461
- rarity[mon.Id] += (mon.Rarity || 0);
437
+
438
+ let s = _s(diff),
439
+ lvl = level[s("MonLvlEx")] || 0,
440
+ mlvl = mon[s("Level")] || 0;
441
+
442
+ return (diff && !mon.boss) ? lvl : mlvl;
443
+ }
444
+
445
+ function forEachMonster(level, diff, func) {
446
+ let s = _s(diff);
447
+
448
+ [0, 1, 2].forEach((type) => {
449
+ if (type && !level[s("MonUMin")] && !level[s("MonUMax")]) {
450
+ return;
451
+ }
452
+
453
+ let m = (num) => level[(diff ? "nmon" : type ? "umon" : "mon") + num];
454
+
455
+ let totalrarity = 0;
456
+ let rarity = {};
457
+
458
+ for (let c = 1; c <= 9; c++) {
459
+ if (m(c)) {
460
+ let mon = full.monstats[m(c)];
461
+
462
+ if (mon && mon.enabled && mon.killable) {
463
+ totalrarity += mon.Rarity || 0;
464
+ rarity[mon.Id] = rarity[mon.Id] || 0;
465
+ rarity[mon.Id] += (mon.Rarity || 0);
466
+ }
462
467
  }
463
468
  }
464
- }
465
-
466
- for (let key in rarity) {
467
- let mon = full.monstats[key];
468
-
469
- if (rarity[key] > 0 && mon && mon.enabled && mon.killable) {
470
- let mlvl = monlevel(mon, level, diff) + [0, 2, 3][type];
471
-
472
- func(mon, mlvl, type, rarity[key] / totalrarity);
469
+
470
+ for (let key in rarity) {
471
+ let mon = full.monstats[key];
472
+
473
+ if (rarity[key] > 0 && mon && mon.enabled && mon.killable) {
474
+ let mlvl = monlevel(mon, level, diff) + [0, 2, 3][type];
475
+
476
+ func(mon, mlvl, type, rarity[key] / totalrarity);
477
+ }
473
478
  }
474
- }
475
- });
476
- }
477
-
478
- const moncountest = require('./json/moncountest.json');
479
-
480
- let monpopulation = {};
481
-
482
- [0, 1, 2].forEach((diff) => {
483
- let uniqueCount = 3.5 + diff;
484
- let champCount = 3;
485
- let uniqueRatio = 0.8, champRatio = 0.2;
486
- let s = _s(diff);
487
- full.levels.forEach((level) => {
488
- let l = (key) => level[key] || 0;
489
-
490
- monpopulation[level.Id] = monpopulation[level.Id] || {
491
- 'normal': {},
492
- 'champion': {},
493
- 'unique': {},
494
- 'superunique': {},
495
- 'boss': {},
496
- };
497
-
498
- if (level.Id) {
499
- let supers = full.superuniques.filter(s => s.areaId == level.Id || (s.hcIdx === 19 && [66, 67, 68, 69, 70, 71, 72].includes(level.Id | 0))),
500
- bosses = full.monstats.filter((mon) => mon.areaId == level.Id),
501
- acount = (moncountest[level.Id] && moncountest[level.Id][diff]) || 0,
502
- scount = supers.reduce((total, sup) => {
503
- let mon = full.monstats[sup.Class];
504
-
505
- return (
506
- total +
507
- 1 +
508
- diff +
509
- avg(
510
- (sup["MinGrp"] || 0),
511
- (sup["MaxGrp"] || 0)
512
- ) +
513
- avg(
514
- (mon["PartyMin"] || 0),
515
- (mon["PartyMax"] || 0)
516
- )
517
- );
518
- }, 0),
519
- bcount = bosses.reduce((total, mon) => {
520
- return (
521
- total +
522
- 1 +
523
- avg(
524
- (mon["MinGrp"] || 0),
525
- (mon["MaxGrp"] || 0)
526
- ) +
479
+ });
480
+ }
481
+
482
+ const moncountest = require('./json/moncountest.json');
483
+
484
+ let monpopulation = {};
485
+
486
+ [0, 1, 2].forEach((diff) => {
487
+ let uniqueCount = 3.5 + diff;
488
+ let champCount = 3;
489
+ let uniqueRatio = 0.8, champRatio = 0.2;
490
+ let s = _s(diff);
491
+ full.levels.forEach((level) => {
492
+ let l = (key) => level[key] || 0;
493
+
494
+ monpopulation[level.Id] = monpopulation[level.Id] || {
495
+ 'normal': {},
496
+ 'champion': {},
497
+ 'unique': {},
498
+ 'superunique': {},
499
+ 'boss': {},
500
+ };
501
+
502
+ if (level.Id) {
503
+ let supers = full.superuniques.filter(s => s.areaId == level.Id || (s.hcIdx === 19 && [66, 67, 68, 69, 70, 71, 72].includes(level.Id | 0))),
504
+ bosses = full.monstats.filter((mon) => mon.areaId == level.Id),
505
+ acount = (moncountest[level.Id] && moncountest[level.Id][diff]) || 0,
506
+ scount = supers.reduce((total, sup) => {
507
+ let mon = full.monstats[sup.Class];
508
+
509
+ return (
510
+ total +
511
+ 1 +
512
+ diff +
513
+ avg(
514
+ (sup["MinGrp"] || 0),
515
+ (sup["MaxGrp"] || 0)
516
+ ) +
517
+ avg(
518
+ (mon["PartyMin"] || 0),
519
+ (mon["PartyMax"] || 0)
520
+ )
521
+ );
522
+ }, 0),
523
+ bcount = bosses.reduce((total, mon) => {
524
+ return (
525
+ total +
526
+ 1 +
527
+ avg(
528
+ (mon["MinGrp"] || 0),
529
+ (mon["MaxGrp"] || 0)
530
+ ) +
531
+ avg(
532
+ (mon["PartyMin"] || 0),
533
+ (mon["PartyMax"] || 0)
534
+ )
535
+ );
536
+ }, 0),
537
+ monucount = avg(l(s("MonUMin")), l(s("MonUMax"))),
538
+ ucount = monucount * uniqueRatio * uniqueCount,
539
+ ccount = 0;
540
+
541
+ forEachMonster(level, diff, (mon, mlvl, type, rarity) => {
542
+ if (type === 1) {
543
+ ccount += (monucount * rarity * champRatio) * (champCount + avg((mon["PartyMin"] || 0), (mon["PartyMax"] || 0)));
544
+ }
545
+ });
546
+
547
+ let count = acount - ucount - ccount - scount - bcount;
548
+
549
+ if (count > 0) {
550
+ let ratio = [{}, {}, {}, {}, {}];
551
+
552
+ forEachMonster(level, diff, (mon, mlvl, type, rarity) => {
553
+ let grp = [
554
+ avg(
555
+ (mon["MinGrp"] || 0),
556
+ (mon["MaxGrp"] || 0)
557
+ ),
558
+ 3,
559
+ 1,
560
+ ][type];
561
+ ratio[type][mon.Id] = rarity * grp +
527
562
  avg(
528
563
  (mon["PartyMin"] || 0),
529
564
  (mon["PartyMax"] || 0)
530
- )
531
- );
532
- }, 0),
533
- monucount = avg(l(s("MonUMin")), l(s("MonUMax"))),
534
- ucount = monucount * uniqueRatio * uniqueCount,
535
- ccount = 0;
536
-
537
- forEachMonster(level, diff, (mon, mlvl, type, rarity) => {
538
- if (type === 1) {
539
- ccount += (monucount * rarity * champRatio) * (champCount + avg((mon["PartyMin"] || 0), (mon["PartyMax"] || 0)));
540
- }
541
- });
542
-
543
- let count = acount - ucount - ccount - scount - bcount;
544
-
545
- if (count > 0) {
546
- let ratio = [{}, {}, {}, {}, {}];
547
-
548
- forEachMonster(level, diff, (mon, mlvl, type, rarity) => {
549
- let grp = [
550
- avg(
551
- (mon["MinGrp"] || 0),
552
- (mon["MaxGrp"] || 0)
553
- ),
554
- 3,
555
- 1,
556
- ][type];
557
- ratio[type][mon.Id] = rarity * grp +
558
- avg(
559
- (mon["PartyMin"] || 0),
560
- (mon["PartyMax"] || 0)
561
- );
562
- });
563
-
564
- for (let stype = 0; stype < 5; stype++) {
565
- let totalratio = 0;
566
-
567
- for (let skey in ratio[stype]) {
568
- totalratio += ratio[stype][skey];
569
- }
570
-
571
- for (let skey in ratio[stype]) {
572
- ratio[stype][skey] /= totalratio;
565
+ );
566
+ });
567
+
568
+ for (let stype = 0; stype < 5; stype++) {
569
+ let totalratio = 0;
570
+
571
+ for (let skey in ratio[stype]) {
572
+ totalratio += ratio[stype][skey];
573
+ }
574
+
575
+ for (let skey in ratio[stype]) {
576
+ ratio[stype][skey] /= totalratio;
577
+ }
573
578
  }
579
+
580
+ forEachMonster(level, diff, (mon, mlvl, type, rarity) => {
581
+ let mult = [
582
+ count * ratio[type][mon.Id] / (avg((mon["MinGrp"] || 0), (mon["MaxGrp"] || 0)) + avg((mon["PartyMin"] || 0), (mon["PartyMax"] || 0))),
583
+ monucount * rarity * champRatio,
584
+ monucount * rarity * uniqueRatio,
585
+ ][type];
586
+ monpopulation[level.Id][['normal', 'champion', 'unique'][type]][mon.Id] = monpopulation[level.Id][['normal', 'champion', 'unique'][type]][mon.Id] || {
587
+ "mlvl": 0,
588
+ "packCount": 0,
589
+ "mlvl(N)": 0,
590
+ "packCount(N)": 0,
591
+ "mlvl(H)": 0,
592
+ "packCount(H)": 0,
593
+ };
594
+ monpopulation[level.Id][['normal', 'champion', 'unique'][type]][mon.Id][s('mlvl')] = mlvl;
595
+ monpopulation[level.Id][['normal', 'champion', 'unique'][type]][mon.Id][s('packCount')] = Math.round(mult * (10 ** decimalPrecision)) / (10 ** decimalPrecision);
596
+ });
574
597
  }
575
-
576
- forEachMonster(level, diff, (mon, mlvl, type, rarity) => {
577
- let mult = [
578
- count * ratio[type][mon.Id] / (avg((mon["MinGrp"] || 0), (mon["MaxGrp"] || 0)) + avg((mon["PartyMin"] || 0), (mon["PartyMax"] || 0))),
579
- monucount * rarity * champRatio,
580
- monucount * rarity * uniqueRatio,
581
- ][type];
582
- monpopulation[level.Id][['normal', 'champion', 'unique'][type]][mon.Id] = monpopulation[level.Id][['normal', 'champion', 'unique'][type]][mon.Id] || {
598
+
599
+ supers.forEach((sup) => {
600
+ let mon = full.monstats[sup.Class],
601
+ mlvl = monlevel(mon, level, diff) + 3;
602
+
603
+ monpopulation[level.Id]['superunique'][sup.Superunique] = monpopulation[level.Id]['superunique'][sup.Superunique] || {
604
+ "mlvl": 0,
605
+ "packCount": 0,
606
+ "mlvl(N)": 0,
607
+ "packCount(N)": 0,
608
+ "mlvl(H)": 0,
609
+ "packCount(H)": 0,
610
+ "hasStaticLevel": false,
611
+ };
612
+
613
+ monpopulation[level.Id]['superunique'][sup.Superunique][s('mlvl')] = mlvl;
614
+ monpopulation[level.Id]['superunique'][sup.Superunique][s('packCount')] = sup.hcIdx === 19 ? 1 / 7 : 1;
615
+ monpopulation[level.Id]['superunique'][sup.Superunique].hasStaticLevel = Boolean(mon.boss);
616
+ });
617
+
618
+ bosses.forEach((mon) => {
619
+ let mlvl = monlevel(mon, level, diff);
620
+
621
+ monpopulation[level.Id]['boss'][mon.Id] = monpopulation[level.Id]['boss'][mon.Id] || {
583
622
  "mlvl": 0,
584
623
  "packCount": 0,
585
624
  "mlvl(N)": 0,
586
625
  "packCount(N)": 0,
587
626
  "mlvl(H)": 0,
588
627
  "packCount(H)": 0,
628
+ "hasStaticLevel": false,
589
629
  };
590
- monpopulation[level.Id][['normal', 'champion', 'unique'][type]][mon.Id][s('mlvl')] = mlvl;
591
- monpopulation[level.Id][['normal', 'champion', 'unique'][type]][mon.Id][s('packCount')] = Math.round(mult * (10 ** decimalPrecision)) / (10 ** decimalPrecision);
630
+ monpopulation[level.Id]['boss'][mon.Id][s('mlvl')] = mlvl;
631
+ monpopulation[level.Id]['boss'][mon.Id][s('packCount')] = 1;
632
+ monpopulation[level.Id]['boss'][mon.Id].hasStaticLevel = Boolean(mon.boss);
592
633
  });
593
634
  }
594
-
595
- supers.forEach((sup) => {
596
- let mon = full.monstats[sup.Class],
597
- mlvl = monlevel(mon, level, diff) + 3;
598
-
599
- monpopulation[level.Id]['superunique'][sup.Superunique] = monpopulation[level.Id]['superunique'][sup.Superunique] || {
600
- "mlvl": 0,
601
- "packCount": 0,
602
- "mlvl(N)": 0,
603
- "packCount(N)": 0,
604
- "mlvl(H)": 0,
605
- "packCount(H)": 0,
606
- "hasStaticLevel": false,
607
- };
608
-
609
- monpopulation[level.Id]['superunique'][sup.Superunique][s('mlvl')] = mlvl;
610
- monpopulation[level.Id]['superunique'][sup.Superunique][s('packCount')] = sup.hcIdx === 19 ? 1 / 7 : 1;
611
- monpopulation[level.Id]['superunique'][sup.Superunique].hasStaticLevel = Boolean(mon.boss);
612
- });
613
-
614
- bosses.forEach((mon) => {
615
- let mlvl = monlevel(mon, level, diff);
616
-
617
- monpopulation[level.Id]['boss'][mon.Id] = monpopulation[level.Id]['boss'][mon.Id] || {
618
- "mlvl": 0,
619
- "packCount": 0,
620
- "mlvl(N)": 0,
621
- "packCount(N)": 0,
622
- "mlvl(H)": 0,
623
- "packCount(H)": 0,
624
- "hasStaticLevel": false,
625
- };
626
- monpopulation[level.Id]['boss'][mon.Id][s('mlvl')] = mlvl;
627
- monpopulation[level.Id]['boss'][mon.Id][s('packCount')] = 1;
628
- monpopulation[level.Id]['boss'][mon.Id].hasStaticLevel = Boolean(mon.boss);
629
- });
630
- }
635
+ });
631
636
  });
632
- });
633
-
634
- let actprofile = {};
635
- let montypes = ['normal', 'champion', 'unique', 'superunique', 'boss'];
636
- let dmgtypes = [
637
- 'ResDm',
638
- 'ResMa',
639
- 'ResFi',
640
- 'ResLi',
641
- 'ResCo',
642
- 'ResPo',
643
- ];
644
-
645
- let progressionAreas = [ // Areas that we're forced to deal with while progressing through the main quest lines.
646
- // Act 1
647
- 2,3,4,5,6,7,10,26,27,28,29,30,31,32,33,34,35,36,
648
- // Act 2
649
- 41,42,43,44,45,46,50,51,52,53,54,56,57,58,60,61,62,63,64,66,67,68,69,70,71,72,73,
650
- // Act 3
651
- 76,77,78,79,80,81,82,83,85,88,89,91,92,93,100,101,
652
- // Act 4
653
- 104,105,106,107,
654
- // Act 5
655
- 110,111,112,113,115,117,118,120,128,129,130,
656
- ];
657
-
658
- let bossAreas = [ // Areas that we're forced to deal with for boss farming.
659
- 37,74,102,108,131,132,
660
- ];
661
-
662
- for (let diff of [0, 1, 2]) {
663
- let s = str => str + ['', '(N)', '(H)'][diff];
664
-
665
- for (let levelid in monpopulation) {
666
- levelid = Number(levelid);
667
-
668
- let level = full.levels[levelid];
669
- let act = (level.Act || 0);
670
- let pop = monpopulation[levelid];
671
-
672
- if (levelid < 1 || levelid > 132) {
673
- continue;
674
- }
675
-
676
- let category = bossAreas.indexOf(levelid) >= 0 ? 'boss' : (progressionAreas.indexOf(levelid) >= 0 ? 'progression' : 'optional');
677
-
678
- for (let montype of montypes) {
679
- for (let id in pop[montype]) {
680
- let sup = undefined,
681
- mon = undefined;
682
-
683
- if (montype === 'superunique') {
684
- sup = full.superuniques[id];
685
- mon = full.monstats[sup.Class];
686
- }
687
- else {
688
- mon = full.monstats[id];
689
- }
690
-
691
- let grp = Math.max(1, ((mon['MinGrp'] || 1) + (mon['MaxGrp'] || 1)) / 2),
692
- party = ((mon['PartyMin'] || 0) + mon['PartyMax'] || 0) / 4,
693
- minions = [
694
- mon['minion1'] || undefined,
695
- mon['minion2'] || undefined,
696
- ].filter(Boolean),
697
- packCount = pop[montype][id][s('packCount')],
698
- mlvl = pop[montype][id][s('mlvl')],
699
- hp = full.monlvl[mlvl][s('HP')] *
700
- (mon[['minHP', 'MinHP(N)', 'MinHP(H)'][diff]] + mon[['maxHP', 'MaxHP(N)', 'MaxHP(H)'][diff]]) / 200;
701
-
702
- if ((montype === 'superunique' || montype === 'unique') && !minions.length) {
703
- minions.push(mon.Id);
637
+
638
+ let actprofile = {};
639
+ let montypes = ['normal', 'champion', 'unique', 'superunique', 'boss'];
640
+ let dmgtypes = [
641
+ 'ResDm',
642
+ 'ResMa',
643
+ 'ResFi',
644
+ 'ResLi',
645
+ 'ResCo',
646
+ 'ResPo',
647
+ ];
648
+
649
+ let progressionAreas = [ // Areas that we're forced to deal with while progressing through the main quest lines.
650
+ // Act 1
651
+ 2,3,4,5,6,7,10,26,27,28,29,30,31,32,33,34,35,36,
652
+ // Act 2
653
+ 41,42,43,44,45,46,50,51,52,53,54,56,57,58,60,61,62,63,64,66,67,68,69,70,71,72,73,
654
+ // Act 3
655
+ 76,77,78,79,80,81,82,83,85,88,89,91,92,93,100,101,
656
+ // Act 4
657
+ 104,105,106,107,
658
+ // Act 5
659
+ 110,111,112,113,115,117,118,120,128,129,130,
660
+ ];
661
+
662
+ let bossAreas = [ // Areas that we're forced to deal with for boss farming.
663
+ 37,74,102,108,131,132,
664
+ ];
665
+
666
+ for (let diff of [0, 1, 2]) {
667
+ let s = str => str + ['', '(N)', '(H)'][diff];
668
+
669
+ for (let levelid in monpopulation) {
670
+ levelid = Number(levelid);
671
+
672
+ let level = full.levels[levelid];
673
+ let act = (level.Act || 0);
674
+ let pop = monpopulation[levelid];
675
+
676
+ if (levelid < 1 || levelid > 132) {
677
+ continue;
678
+ }
679
+
680
+ let category = bossAreas.indexOf(levelid) >= 0 ? 'boss' : (progressionAreas.indexOf(levelid) >= 0 ? 'progression' : 'optional');
681
+
682
+ for (let montype of montypes) {
683
+ for (let id in pop[montype]) {
684
+ let sup = undefined,
685
+ mon = undefined;
686
+
687
+ if (montype === 'superunique') {
688
+ sup = full.superuniques[id];
689
+ mon = full.monstats[sup.Class];
690
+ }
691
+ else {
692
+ mon = full.monstats[id];
704
693
  }
705
-
706
- actprofile[act] = actprofile[act] || {};
707
- actprofile[act][category] = actprofile[act][category] || {};
708
-
709
- for (let dmgtype of dmgtypes) {
710
- actprofile[act][category][s(dmgtype)] = actprofile[act][category][s(dmgtype)] || {};
711
- }
712
-
713
- for (let dmgtype of dmgtypes) {
714
- let resist = mon[s(dmgtype)] || 0;
715
694
 
716
- actprofile[act][category][s(dmgtype)][resist] = actprofile[act][category][s(dmgtype)][resist] || 0;
695
+ let grp = Math.max(1, ((mon['MinGrp'] || 1) + (mon['MaxGrp'] || 1)) / 2),
696
+ party = ((mon['PartyMin'] || 0) + mon['PartyMax'] || 0) / 4,
697
+ minions = [
698
+ mon['minion1'] || undefined,
699
+ mon['minion2'] || undefined,
700
+ ].filter(Boolean),
701
+ packCount = pop[montype][id][s('packCount')],
702
+ mlvl = pop[montype][id][s('mlvl')],
703
+ hp = full.monlvl[mlvl][s('HP')] *
704
+ (mon[['minHP', 'MinHP(N)', 'MinHP(H)'][diff]] + mon[['maxHP', 'MaxHP(N)', 'MaxHP(H)'][diff]]) / 200;
705
+
706
+ if ((montype === 'superunique' || montype === 'unique') && !minions.length) {
707
+ minions.push(mon.Id);
708
+ }
709
+
710
+ actprofile[act] = actprofile[act] || {};
711
+ actprofile[act][category] = actprofile[act][category] || {};
712
+
713
+ for (let dmgtype of dmgtypes) {
714
+ actprofile[act][category][s(dmgtype)] = actprofile[act][category][s(dmgtype)] || {};
715
+ }
717
716
 
718
- if (montype === 'superunique' || montype === 'unique') {
719
- if (montype === 'superunique') {
720
- grp = Math.max(1, ((sup['MinGrp'] || mon['MinGrp'] || 1) + (sup['MaxGrp'] || mon['MaxGrp'] || 1)) / 2);
717
+ for (let dmgtype of dmgtypes) {
718
+ let resist = mon[s(dmgtype)] || 0;
719
+
720
+ actprofile[act][category][s(dmgtype)][resist] = actprofile[act][category][s(dmgtype)][resist] || 0;
721
+
722
+ if (montype === 'superunique' || montype === 'unique') {
723
+ if (montype === 'superunique') {
724
+ grp = Math.max(1, ((sup['MinGrp'] || mon['MinGrp'] || 1) + (sup['MaxGrp'] || mon['MaxGrp'] || 1)) / 2);
725
+ }
726
+ else {
727
+ grp = Math.max(1, ((mon['MinGrp'] || 1) + (mon['MaxGrp'] || 1)) / 2);
728
+ }
729
+ actprofile[act][category][s(dmgtype)][resist] += packCount * hp * [4, 3, 2][diff];
730
+ party = 2.5 + diff;
731
+ }
732
+ else if (montype === 'champion') {
733
+ actprofile[act][category][s(dmgtype)][resist] += packCount * hp * 3 * [3, 2.5, 2][diff];
721
734
  }
722
735
  else {
723
- grp = Math.max(1, ((mon['MinGrp'] || 1) + (mon['MaxGrp'] || 1)) / 2);
736
+ actprofile[act][category][s(dmgtype)][resist] += packCount * hp * grp;
724
737
  }
725
- actprofile[act][category][s(dmgtype)][resist] += packCount * hp * [4, 3, 2][diff];
726
- party = 2.5 + diff;
727
- }
728
- else if (montype === 'champion') {
729
- actprofile[act][category][s(dmgtype)][resist] += packCount * hp * 3 * [3, 2.5, 2][diff];
730
- }
731
- else {
732
- actprofile[act][category][s(dmgtype)][resist] += packCount * hp * grp;
733
- }
734
-
735
- if (party > 0 && minions.length > 0) {
736
- for (let minion of minions) {
737
- let mmon = full.monstats[minion],
738
- mresist = mmon[s(dmgtype)] || 0,
739
- mmlvl = (diff ? mmon['Level'] : level[s('MonLvl')]) + (montype === 'superunique' || montype === 'unique' ? 3 : 0),
740
- mhp = full.monlvl[mmlvl][s('HP')] *
741
- (mmon[['minHP', 'MinHP(N)', 'MinHP(H)'][diff]] + mmon[['maxHP', 'MaxHP(N)', 'MaxHP(H)'][diff]]) / 100;
742
-
743
- actprofile[act][category][s(dmgtype)][mresist] = actprofile[act][category][s(dmgtype)][mresist] || 0;
744
- actprofile[act][category][s(dmgtype)][mresist] += packCount * mhp * party / minions.length;
738
+
739
+ if (party > 0 && minions.length > 0) {
740
+ for (let minion of minions) {
741
+ let mmon = full.monstats[minion],
742
+ mresist = mmon[s(dmgtype)] || 0,
743
+ mmlvl = (diff ? mmon['Level'] : level[s('MonLvl')]) + (montype === 'superunique' || montype === 'unique' ? 3 : 0),
744
+ mhp = full.monlvl[mmlvl][s('HP')] *
745
+ (mmon[['minHP', 'MinHP(N)', 'MinHP(H)'][diff]] + mmon[['maxHP', 'MaxHP(N)', 'MaxHP(H)'][diff]]) / 100;
746
+
747
+ actprofile[act][category][s(dmgtype)][mresist] = actprofile[act][category][s(dmgtype)][mresist] || 0;
748
+ actprofile[act][category][s(dmgtype)][mresist] += packCount * mhp * party / minions.length;
749
+ }
745
750
  }
746
751
  }
747
752
  }
748
753
  }
749
754
  }
750
755
  }
751
- }
752
-
753
- (function roundValues (obj) {
754
- for (let key in obj) {
755
- if (typeof obj[key] === 'number') {
756
- obj[key] = Math.round(obj[key]);
756
+
757
+ (function roundValues (obj) {
758
+ for (let key in obj) {
759
+ if (typeof obj[key] === 'number') {
760
+ obj[key] = Math.round(obj[key]);
761
+ }
762
+ else if(typeof obj[key] === 'object') {
763
+ roundValues(obj[key]);
764
+ }
757
765
  }
758
- else if(typeof obj[key] === 'object') {
759
- roundValues(obj[key]);
766
+ })(actprofile);
767
+
768
+ let coldmasterybreakpoints = {}, coldhpbyres = {};
769
+
770
+ for (let act of Object.values(actprofile)) {
771
+ for (let resset of Object.values(act)) {
772
+ if (resset['ResCo(H)']) {
773
+ for (let res in resset['ResCo(H)']) {
774
+ res = Number(res);
775
+ coldhpbyres[res] = coldhpbyres[res] || 0;
776
+ coldhpbyres[res] += resset['ResCo(H)'][res];
777
+ }
778
+ }
760
779
  }
761
780
  }
762
- })(actprofile);
763
-
764
- let coldmasterybreakpoints = {}, coldhpbyres = {};
765
-
766
- for (let act of Object.values(actprofile)) {
767
- for (let resset of Object.values(act)) {
768
- if (resset['ResCo(H)']) {
769
- for (let res in resset['ResCo(H)']) {
781
+
782
+ (function () {
783
+ for (let mastery = 0; mastery <= 195; mastery === 0 ? mastery = 20 : mastery ++) {
784
+ let ehp = 0;
785
+
786
+ for (let res in coldhpbyres) {
770
787
  res = Number(res);
771
- coldhpbyres[res] = coldhpbyres[res] || 0;
772
- coldhpbyres[res] += resset['ResCo(H)'][res];
788
+ let pierce = (res >= 100 ? (mastery / 5) : mastery);
789
+ let mod = Math.min(2, 1 - (Math.min(95, res) - pierce) / 100);
790
+ ehp += coldhpbyres[res] / mod;
773
791
  }
792
+
793
+ coldmasterybreakpoints[mastery] = ehp;
774
794
  }
775
- }
776
- }
777
-
778
- (function () {
779
- for (let mastery = 0; mastery <= 195; mastery === 0 ? mastery = 20 : mastery ++) {
780
- let ehp = 0;
781
-
782
- for (let res in coldhpbyres) {
783
- res = Number(res);
784
- let pierce = (res >= 100 ? (mastery / 5) : mastery);
785
- let mod = Math.min(2, 1 - (Math.min(95, res) - pierce) / 100);
786
- ehp += coldhpbyres[res] / mod;
795
+
796
+ let masterylist = Object.keys(coldmasterybreakpoints);
797
+
798
+ for (let i = masterylist.length - 1; i; i--) {
799
+ coldmasterybreakpoints[masterylist[i]] = (coldmasterybreakpoints[masterylist[i - 1]] / coldmasterybreakpoints[masterylist[i]] - 1);
787
800
  }
788
-
789
- coldmasterybreakpoints[mastery] = ehp;
790
- }
791
-
792
- let masterylist = Object.keys(coldmasterybreakpoints);
793
-
794
- for (let i = masterylist.length - 1; i; i--) {
795
- coldmasterybreakpoints[masterylist[i]] = (coldmasterybreakpoints[masterylist[i - 1]] / coldmasterybreakpoints[masterylist[i]] - 1);
796
- }
797
-
798
- coldmasterybreakpoints[0] = 0;
799
-
800
- for (let c = 0; c < 2; c++) {
801
+
802
+ coldmasterybreakpoints[0] = 0;
803
+
804
+ for (let c = 0; c < 2; c++) {
805
+ for (let i = masterylist.length - 1; i; i--) {
806
+ coldmasterybreakpoints[masterylist[i]] = coldmasterybreakpoints[masterylist[i]] - coldmasterybreakpoints[masterylist[i - 1]];
807
+ }
808
+ }
809
+
801
810
  for (let i = masterylist.length - 1; i; i--) {
802
- coldmasterybreakpoints[masterylist[i]] = coldmasterybreakpoints[masterylist[i]] - coldmasterybreakpoints[masterylist[i - 1]];
811
+ coldmasterybreakpoints[masterylist[i]] = coldmasterybreakpoints[masterylist[i]] * 10000;
803
812
  }
804
- }
805
-
806
- for (let i = masterylist.length - 1; i; i--) {
807
- coldmasterybreakpoints[masterylist[i]] = coldmasterybreakpoints[masterylist[i]] * 10000;
808
- }
809
-
810
- let newMasteryList = {}, mmin = Infinity;
811
-
812
- for (let i = masterylist.length; i; i--) {
813
- if (coldmasterybreakpoints[masterylist[i]] <= -0.5) {
814
- mmin = Math.min(mmin, -coldmasterybreakpoints[masterylist[i]]);
815
- newMasteryList[masterylist[i - 1]] = -coldmasterybreakpoints[masterylist[i]];
813
+
814
+ let newMasteryList = {}, mmin = Infinity;
815
+
816
+ for (let i = masterylist.length; i; i--) {
817
+ if (coldmasterybreakpoints[masterylist[i]] <= -0.5) {
818
+ mmin = Math.min(mmin, -coldmasterybreakpoints[masterylist[i]]);
819
+ newMasteryList[masterylist[i - 1]] = -coldmasterybreakpoints[masterylist[i]];
820
+ }
816
821
  }
817
- }
818
-
819
- for (let key in newMasteryList) {
820
- newMasteryList[key] = Math.round(newMasteryList[key] / mmin);
821
- }
822
-
823
- coldmasterybreakpoints = newMasteryList;
824
- })();
825
-
826
- let tcprecalc = {};
827
-
828
- full.treasureclassex.forEach((tc, key) => {
829
- tcprecalc[key] = tcprecalc[key] || {};
830
- tcprecalc[key].droprate = tc.nodropcalc.map(noDrop => noDrop ? tc['*ItemProbTotal'] / (noDrop + tc['*ItemProbTotal']) : 1);
831
- tcprecalc[key].droprateRoot = {};
832
- tcprecalc[key].counts = tc.precalc;
833
- delete tc['nodropcalc'];
834
- delete tc['precalc'];
835
- });
836
-
837
- atomic.forEach((precalc, key) => {
838
- tcprecalc[key] = {
839
- droprate: {
840
- 1: 1,
841
- 2: 1,
842
- 3: 1,
843
- 4: 1,
844
- 5: 1,
845
- 6: 1,
846
- 7: 1,
847
- 8: 1,
848
- },
849
- droprateRoot: {},
850
- counts: precalc,
851
- };
852
- });
853
-
854
- function totalTC (key, debug = []) {
855
- if (key in tcprecalc) {
856
- let tc = tcprecalc[key], total = 0;
857
-
858
- for (let subtc in tc.counts) {
859
- total += tc.counts[subtc] * totalTC(subtc, debug);
822
+
823
+ for (let key in newMasteryList) {
824
+ newMasteryList[key] = Math.round(newMasteryList[key] / mmin);
860
825
  }
861
-
862
- return Math.round(total * 1e15) / 1e15;
826
+
827
+ coldmasterybreakpoints = newMasteryList;
828
+ })();
829
+
830
+ let tcprecalc = {};
831
+
832
+ full.treasureclassex.forEach((tc, key) => {
833
+ tcprecalc[key] = tcprecalc[key] || {};
834
+ tcprecalc[key].droprate = tc.nodropcalc.map(noDrop => noDrop ? tc['*ItemProbTotal'] / (noDrop + tc['*ItemProbTotal']) : 1);
835
+ tcprecalc[key].droprateRoot = {};
836
+ tcprecalc[key].counts = tc.precalc;
837
+ delete tc['nodropcalc'];
838
+ delete tc['precalc'];
839
+ });
840
+
841
+ atomic.forEach((precalc, key) => {
842
+ tcprecalc[key] = {
843
+ droprate: {
844
+ 1: 1,
845
+ 2: 1,
846
+ 3: 1,
847
+ 4: 1,
848
+ 5: 1,
849
+ 6: 1,
850
+ 7: 1,
851
+ 8: 1,
852
+ },
853
+ droprateRoot: {},
854
+ counts: precalc,
855
+ };
856
+ });
857
+
858
+ function totalTC (key, debug = []) {
859
+ if (key in tcprecalc) {
860
+ let tc = tcprecalc[key], total = 0;
861
+
862
+ for (let subtc in tc.counts) {
863
+ total += tc.counts[subtc] * totalTC(subtc, debug);
864
+ }
865
+
866
+ return Math.round(total * 1e15) / 1e15;
867
+ }
868
+
869
+ return 1;
863
870
  }
864
-
865
- return 1;
866
- }
867
-
868
- for (let key in tcprecalc) {
869
- let debug = [], total = totalTC(key, debug), tc = tcprecalc[key];
870
-
871
- tc.droprateRoot = {
872
- ...tc.droprate,
873
- };
874
-
875
- if (total > 6) {
876
- for (let exp in tc.droprateRoot) {
877
- tc.droprateRoot[exp] = tc.droprateRoot[exp] * 6 / total;
871
+
872
+ for (let key in tcprecalc) {
873
+ let debug = [], total = totalTC(key, debug), tc = tcprecalc[key];
874
+
875
+ tc.droprateRoot = {
876
+ ...tc.droprate,
877
+ };
878
+
879
+ if (total > 6) {
880
+ for (let exp in tc.droprateRoot) {
881
+ tc.droprateRoot[exp] = tc.droprateRoot[exp] * 6 / total;
882
+ }
878
883
  }
879
884
  }
880
- }
881
-
882
- files.forEach(fn => {
883
- fs.writeFileSync(outDir + fn + '.json', JSON.stringify(keySort(full[fn]), null, ' '));
885
+
886
+ files.forEach(fn => {
887
+ fs.writeFileSync(outDir + fn + '.json', JSON.stringify(keySort(full[fn]), null, ' '));
888
+ });
889
+
890
+ fs.writeFileSync(outDir + 'items.json', JSON.stringify(items, null, ' '));
891
+ fs.writeFileSync(outDir + 'atomic.json', JSON.stringify(keySort(atomic), null, ' '));
892
+ fs.writeFileSync(outDir + 'treasureclassgroupsex.json', JSON.stringify(groupsEx, null, ' '));
893
+ fs.writeFileSync(outDir + 'monpopulationest.json', JSON.stringify(monpopulation, null, ' '));
894
+ fs.writeFileSync(outDir + 'tcprecalc.json', JSON.stringify(tcprecalc, null, ' '));
895
+ fs.writeFileSync(outDir + 'actprofile.json', JSON.stringify(actprofile, null, ' '));
896
+ fs.writeFileSync(outDir + 'coldmasterybreakpoints.json', JSON.stringify(coldmasterybreakpoints, null, ' '));
884
897
  });
885
898
 
886
- fs.writeFileSync(outDir + 'items.json', JSON.stringify(items, null, ' '));
887
- fs.writeFileSync(outDir + 'atomic.json', JSON.stringify(keySort(atomic), null, ' '));
888
- fs.writeFileSync(outDir + 'treasureclassgroupsex.json', JSON.stringify(groupsEx, null, ' '));
889
- fs.writeFileSync(outDir + 'monpopulationest.json', JSON.stringify(monpopulation, null, ' '));
890
- fs.writeFileSync(outDir + 'tcprecalc.json', JSON.stringify(tcprecalc, null, ' '));
891
- fs.writeFileSync(outDir + 'actprofile.json', JSON.stringify(actprofile, null, ' '));
892
- fs.writeFileSync(outDir + 'coldmasterybreakpoints.json', JSON.stringify(coldmasterybreakpoints, null, ' '));