@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
@@ -0,0 +1,870 @@
1
+ <script setup>
2
+
3
+ import { reactive, computed, watch } from "vue";
4
+ import "../../../objext.js";
5
+ import _strings from "../../../json/localestrings-eng.json";
6
+ import _levels from "../../../json/levels.json";
7
+ import _count from "../../../json/moncountest.json";
8
+ import _monstats from "../../../json/monstats.json";
9
+ import _superuniques from "../../../json/superuniques.json";
10
+ import _tc from "../../../json/treasureclassex.json";
11
+ import _tcgroups from "../../../json/treasureclassgroupsex.json";
12
+ import _tcprecalc from "../../../json/tcprecalc.json";
13
+ import _atomic from "../../../json/atomic.json";
14
+ import _itemtypes from "../../../json/itemtypes.json";
15
+ import _itemratio from "../../../json/itemratio.json";
16
+ import _allitems from "../../../json/items.json";
17
+ import _uniqueitems from "../../../json/uniqueitems.json";
18
+ import _setitems from "../../../json/setitems.json";
19
+
20
+ function _s (diff) {
21
+ return str => str + ['', '(N)', '(H)'][diff];
22
+ }
23
+
24
+ function int (num) {
25
+ return Math.trunc(num);
26
+ }
27
+
28
+ function sleep (ms) {
29
+ return new Promise(resolve => setTimeout(resolve, ms));
30
+ }
31
+
32
+ function dtoa (dv) {
33
+ let bytes = [];
34
+
35
+ for (let c = 0; c < dv.byteLength; c++) {
36
+ bytes[c] = String.fromCharCode(dv.getUint8(c));
37
+ }
38
+
39
+ return btoa(bytes.join(''));
40
+ }
41
+
42
+ function atod (str64) {
43
+ let bytes = atob(str64), dv = new DataView(new ArrayBuffer(bytes.length));
44
+
45
+ for (let c = 0; c < bytes.length; c++) {
46
+ dv.setUint8(c, bytes.charCodeAt(c));
47
+ }
48
+
49
+ return dv;
50
+ }
51
+
52
+ let baseurl = 'https://raw.githubusercontent.com/blizzhackers/d2data/master/json/'; // https://api.blizzhackers.dev/json/d2/
53
+
54
+ const data = reactive({
55
+ items: [],
56
+ areaResults: [],
57
+ packResults: [],
58
+ visible: false,
59
+ calculating: false,
60
+ progress: 0,
61
+ params: {
62
+ mf: 0,
63
+ rolling: 1,
64
+ players: 1,
65
+ group: 1,
66
+ minilvl: 0,
67
+ maxilvl: 110,
68
+ },
69
+ parammap: [
70
+ {key: 'mf', type: 'Uint', size: 2},
71
+ {key: 'rolling', type: 'Uint', size: 1},
72
+ {key: 'players', type: 'Uint', size: 1},
73
+ {key: 'group', type: 'Uint', size: 1},
74
+ {key: 'minilvl', type: 'Uint', size: 1},
75
+ {key: 'maxilvl', type: 'Uint', size: 1},
76
+ ],
77
+ itemSearch: '',
78
+ cowsets: {
79
+ "Cow King's Hide": true,
80
+ "Cow King's Hoofs": true,
81
+ "Cow King's Horns": true,
82
+ },
83
+ });
84
+
85
+ let exp = computed(() => {
86
+ return (data.params.players / 2 + data.params.group / 2) | 0;
87
+ });
88
+
89
+ function updateHash() {
90
+ let items = [];
91
+ data.items.forEach((item, index) => item.use && items.push(index));
92
+ let paramdv = new DataView(new ArrayBuffer(data.parammap.reduce((t, line) => t + line.size, 0) + items.length * 2)), pos = 0;
93
+
94
+ data.parammap.forEach(line => {
95
+ paramdv['set' + line.type + (line.size * 8)](pos, data.params[line.key]);
96
+ pos += line.size;
97
+ });
98
+
99
+ items.forEach(num => {
100
+ paramdv.setUint16(pos, num);
101
+ pos += 2;
102
+ });
103
+
104
+ window.location.hash = dtoa(paramdv);
105
+ }
106
+
107
+ function makeRatio(chance) {
108
+ let ratio = 1/chance;
109
+ return Math.round(ratio).toString();
110
+ }
111
+
112
+ function dropChance (base, divisor, min, diminishFactor) {
113
+ base = base || 0;
114
+ divisor = divisor || 1;
115
+ min = min || 0;
116
+
117
+ return (mf, ilvl, qlvl, factor) => {
118
+ let difference = ilvl - qlvl;
119
+ let chance = (base - int(difference / divisor)) * 128;
120
+
121
+ if (mf !== 0) {
122
+ let newmf = mf > 10 ? diminishFactor ? int((mf * diminishFactor) / (mf + diminishFactor)) : (mf | 0) : mf;
123
+
124
+ chance = int((chance * 100) / (100 + newmf));
125
+ }
126
+
127
+ chance = Math.max(min, chance);
128
+ chance = (chance - int(chance * factor / 1024));
129
+
130
+ return chance <= 128 ? 1 : 128 / chance;
131
+ };
132
+ }
133
+
134
+ function matches(a, b) {
135
+ if (!a || !a.length) {
136
+ return false;
137
+ }
138
+
139
+ a = a.toLowerCase().split(/[^a-zA-Z0-9']+/i).filter(Boolean);
140
+ b = b.toLowerCase().split(/[^a-zA-Z0-9']+/i).filter(Boolean);
141
+
142
+ return a.every(av => b.some(bv => bv.indexOf(av) === 0));
143
+ }
144
+
145
+ function capWords(a) {
146
+ if (a && a.length) {
147
+ return a.split(/([^a-zA-Z0-9']+)/i).map(word => word.charAt(0).toUpperCase() + word.slice(1)).join('');
148
+ }
149
+
150
+ return a;
151
+ }
152
+
153
+ function getKeywords(type, ret = {}) {
154
+ let itemtype = _itemtypes[type];
155
+
156
+ ret[itemtype.ItemType] = true;
157
+ itemtype['Equiv1'] && getKeywords(itemtype['Equiv1'], ret);
158
+ itemtype['Equiv2'] && getKeywords(itemtype['Equiv2'], ret);
159
+
160
+ return Object.keys(ret);
161
+ }
162
+
163
+ function clearResults() {
164
+ data.areaResults.splice(0);
165
+ data.packResults.splice(0);
166
+ }
167
+
168
+ let adjustedTcCache = {};
169
+
170
+ function adjustTc(name, lvl) {
171
+ let cachekey = `${name}|${lvl}`;
172
+
173
+ if (adjustedTcCache[cachekey]) {
174
+ return adjustedTcCache[cachekey];
175
+ }
176
+
177
+ if (_tc[name].group && _tcgroups[_tc[name].group]) {
178
+ let grp = _tcgroups[_tc[name].group].toArray().map(g => {
179
+ return [Number(g[0]), g[1]];
180
+ }).filter(g => g[0] <= lvl).sort((a, b) => b[0] - a[0]);
181
+
182
+ if (grp.length && grp[0] && grp[0][1]) {
183
+ return adjustedTcCache[cachekey] = grp[0][1];
184
+ }
185
+ }
186
+
187
+ return adjustedTcCache[cachekey] = name;
188
+ }
189
+
190
+ function setValidHere(set, level) {
191
+ return level.Id === 39 || !data.cowsets[set.index];
192
+ }
193
+
194
+ async function doCalc() {
195
+ let areaResults = [], packResults = [], selectedItems = data.items.filter(item => item.use);
196
+
197
+ if (selectedItems.length) {
198
+ if (data.calculating) {
199
+ return;
200
+ }
201
+
202
+ data.calculating = true;
203
+ data.progress = 0;
204
+ clearResults();
205
+ await sleep(0);
206
+
207
+ let progressInterval = 1 / 3 / Object.values(_levels).length;
208
+
209
+ for (let diff = 0; diff < 3; diff++) {
210
+ let s = _s(diff), cache = {};
211
+
212
+ for (let level of Object.values(_levels)) {
213
+ let lchance = 0;
214
+
215
+ if (diff < 2 && level.Id > 132) {
216
+ data.progress += progressInterval;
217
+ continue;
218
+ }
219
+
220
+ level.calc.monsters[diff].forEach(({mon, mlvl, type, superMon, packCount}) => {
221
+ if (mlvl < data.params.minilvl || mlvl > data.params.maxilvl) {
222
+ return;
223
+ }
224
+
225
+ let chance = 0, schance = 0;
226
+
227
+ let calc = (m, count, mtype, isMinion) => {
228
+ let tcname = (superMon && !isMinion) ? superMon[s('TC')] : m[s('TreasureClass' + (Math.min(3, mtype + 1)))];
229
+
230
+ if (tcname) {
231
+ tcname = adjustTc(tcname, mlvl);
232
+
233
+ let tc = _tc[tcname];
234
+
235
+ let cachekey = `${tcname}|${mtype}|${mlvl}|${Number(level.Id === 39)}`;
236
+
237
+ let calcRes = !cachekey || cache[cachekey] === undefined ? calcPicks((pickItem, ...tcpath) => {
238
+ let {uniqueMod, setMod, rareMod, magicMod} = [pickItem, ...tcpath].reduce(({uniqueMod, setMod, rareMod, magicMod}, v) => {
239
+ if (_tc[v]) {
240
+ uniqueMod = Math.max(uniqueMod, _tc[v]['Unique'] || 0);
241
+ setMod = Math.max(setMod, _tc[v]['Set'] || 0);
242
+ rareMod = Math.max(rareMod, _tc[v]['Rare'] || 0);
243
+ magicMod = Math.max(magicMod, _tc[v]['Magic'] || 0);
244
+ }
245
+
246
+ return {uniqueMod, setMod, rareMod, magicMod};
247
+ }, {
248
+ uniqueMod: 0,
249
+ setMod: 0,
250
+ rareMod: 0,
251
+ magicMod: 0,
252
+ }),
253
+ ichance = 0;
254
+
255
+ selectedItems.forEach(item => {
256
+ if (item.set && !setValidHere(item.set, level)) {
257
+ return;
258
+ }
259
+
260
+ if (pickItem === item.code) {
261
+ switch (item.quality) {
262
+ case 'unique':
263
+ if (mlvl >= (item.unique.lvl || 0)) {
264
+ let ucount = _uniqueitems.filter(u => u.enabled && u.code === item.code && mlvl >= (u.lvl || 0)).reduce((t, u) => {
265
+ return t + (u.rarity || 1);
266
+ }, 0);
267
+
268
+ if (!ucount) {
269
+ return;
270
+ }
271
+
272
+ ichance += item.func.unique(data.params.mf, mlvl, item.item.level || 0, uniqueMod) * item.unique.rarity / ucount;
273
+ }
274
+ break;
275
+
276
+ case 'set':
277
+ if (mlvl >= (item.set.lvl || 0)) {
278
+ let scount = _setitems.filter(set => set.item === item.code && mlvl >= (set.lvl || 0) && setValidHere(set, level)).reduce((t, s) => {
279
+ return t + (s.rarity || 1);
280
+ }, 0);
281
+
282
+ if (!scount) {
283
+ return;
284
+ }
285
+
286
+ ichance += (1 - item.func.unique(data.params.mf, mlvl, item.item.level || 0, uniqueMod)) *
287
+ item.func.set(data.params.mf, mlvl, item.item.level || 0, setMod) * item.set.rarity / scount;
288
+ }
289
+ break;
290
+
291
+ case 'rare':
292
+ ichance += (1 - item.func.unique(data.params.mf, mlvl, item.item.level || 0, uniqueMod)) *
293
+ (1 - item.func.set(data.params.mf, mlvl, item.item.level || 0, setMod)) *
294
+ item.func.rare(data.params.mf, mlvl, item.item.level || 0, rareMod);
295
+ break;
296
+ case 'magic':
297
+ ichance += (item.type.Rare ? (1 - item.func.unique(data.params.mf, mlvl, item.item.level || 0, uniqueMod)) : 1) *
298
+ (item.type.Rare ? (1 - item.func.set(data.params.mf, mlvl, item.item.level || 0, setMod)) : 1) *
299
+ (item.type.Rare ? (1 - item.func.rare(data.params.mf, mlvl, item.item.level || 0, rareMod)) : 1) *
300
+ item.func.magic(data.params.mf, mlvl, item.item.level || 0, magicMod);
301
+ break;
302
+ case 'hq':
303
+ ichance += (item.type.Rare ? (1 - item.func.unique(data.params.mf, mlvl, item.item.level || 0, uniqueMod)) : 1) *
304
+ (item.type.Rare ? (1 - item.func.set(data.params.mf, mlvl, item.item.level || 0, setMod)) : 1) *
305
+ (item.type.Rare ? (1 - item.func.rare(data.params.mf, mlvl, item.item.level || 0, rareMod)) : 1) *
306
+ (1 - item.func.magic(data.params.mf, mlvl, item.item.level || 0, magicMod)) *
307
+ item.func.hq(0, mlvl, item.item.level || 0, 0);
308
+ break;
309
+ case 'normal':
310
+ if (item.type.Normal) {
311
+ ichance += 1;
312
+ } else {
313
+ ichance += (item.type.Rare ? (1 - item.func.unique(data.params.mf, mlvl, item.item.level || 0, uniqueMod)) : 1) *
314
+ (item.type.Rare ? (1 - item.func.set(data.params.mf, mlvl, item.item.level || 0, setMod)) : 1) *
315
+ (item.type.Rare ? (1 - item.func.rare(data.params.mf, mlvl, item.item.level || 0, rareMod)) : 1) *
316
+ (1 - item.func.magic(data.params.mf, mlvl, item.item.level || 0, magicMod)) *
317
+ (1 - item.func.hq(0, mlvl, item.item.level || 0, 0)) *
318
+ item.func.normal(0, mlvl, item.item.level || 0, 0);
319
+ }
320
+ break;
321
+ default:
322
+ ichance += (1 - item.func.unique(data.params.mf, mlvl, item.item.level || 0, uniqueMod)) *
323
+ (1 - item.func.set(data.params.mf, mlvl, item.item.level || 0, setMod)) *
324
+ (1 - item.func.rare(data.params.mf, mlvl, item.item.level || 0, rareMod)) *
325
+ (1 - item.func.magic(data.params.mf, mlvl, item.item.level || 0, magicMod)) *
326
+ (1 - item.func.hq(0, mlvl, item.item.level || 0, 0)) *
327
+ (1 - item.func.normal(0, mlvl, item.item.level || 0, 0));
328
+ break;
329
+ }
330
+ }
331
+ });
332
+
333
+ return ichance;
334
+ }, tcname) : cache[cachekey];
335
+
336
+ let pchance = calcRes;
337
+
338
+ cache[cachekey] = pchance;
339
+
340
+ if (!isMinion) {
341
+ schance = pchance;
342
+ }
343
+
344
+ chance += pchance * count;
345
+ }
346
+ };
347
+
348
+ let packSize = superMon ? 1 : avg(mon['MinGrp'] || 0, mon['MaxGrp'] || 0);
349
+
350
+ calc(mon, packSize, type, false);
351
+
352
+ let minionCount = superMon ? avg(superMon['MinGrp'] || 0, superMon['MaxGrp'] || 0) : avg(mon['PartyMin'] || 0, mon['PartyMax'] || 0);
353
+
354
+ if (superMon && minionCount) {
355
+ minionCount += diff;
356
+ }
357
+
358
+ if (minionCount) {
359
+ let minions = [
360
+ mon['minion1'] && _monstats[mon['minion1']],
361
+ mon['minion2'] && _monstats[mon['minion2']],
362
+ ].filter(Boolean);
363
+
364
+ if (superMon && !minions.length) {
365
+ minions[0] = mon;
366
+ }
367
+
368
+ minions.forEach(minion => {
369
+ calc(minion, minionCount / minions.length, 0, true);
370
+ });
371
+ }
372
+
373
+ if (chance) {
374
+ packResults.push({
375
+ mon,
376
+ mlvl,
377
+ superMon,
378
+ color: ['#000000', '#3366ff', '#b8860b', '#cc33ff', '#FF0000'][type],
379
+ name: (superMon ? _strings[superMon.Name] : _strings[mon.NameStr]) + (packSize + minionCount > 1 ? ' Pack ' : ' ') + ['[N]', '[NM]', '[H]'][diff],
380
+ chance,
381
+ level,
382
+ tooltip: [
383
+ 'Type: ' + ['Normal', 'Champion', 'Unique', 'Superunique', 'Boss'][type],
384
+ 'mlvl: ' + mlvl,
385
+ 'Area: [' + level.Id + '] ' + _strings[level.LevelName],
386
+ 'Act: ' + (level.Id >= 109 ? 5 : level.Id >= 103 ? 4 : level.Id >= 75 ? 3 : level.Id >= 40 ? 2 : 1),
387
+ schance !== chance ? 'Individual Chance: 1:' + makeRatio(schance) : null,
388
+ ].filter(Boolean).join('\n'),
389
+ });
390
+
391
+ lchance += chance * packCount;
392
+ }
393
+ });
394
+
395
+ if (lchance) {
396
+ areaResults.push({
397
+ name: _strings[level.LevelName] + [' [N]', ' [NM]', ' [H]'][diff],
398
+ chance: lchance,
399
+ tooltip: [
400
+ 'Id: ' + level.Id,
401
+ 'mlvl: ' + level[['MonLvlEx', 'MonLvlEx(N)', 'MonLvlEx(H)'][diff]] || 0,
402
+ 'Act: ' + (level.Id >= 109 ? 5 : level.Id >= 103 ? 4 : level.Id >= 75 ? 3 : level.Id >= 40 ? 2 : 1),
403
+ ].filter(Boolean).join('\n'),
404
+ });
405
+ }
406
+
407
+ data.progress += progressInterval;
408
+ await sleep(0);
409
+ }
410
+ }
411
+
412
+ areaResults = areaResults.sort((a, b) => {
413
+ return b.chance - a.chance;
414
+ });
415
+
416
+ packResults = packResults.sort((a, b) => {
417
+ return b.chance - a.chance;
418
+ });
419
+
420
+ data.areaResults.push(...areaResults);
421
+ data.packResults.push(...packResults);
422
+ data.calculating = false;
423
+ }
424
+ }
425
+
426
+ function calcPicks(func, tcname, isNested, ...tcpath) {
427
+ if (tcname && _tcprecalc[tcname]) {
428
+ let totalchance = 0;
429
+
430
+ _tcprecalc[tcname].counts.forEach((chance, item) => {
431
+ totalchance += _tcprecalc[tcname][isNested ? 'droprate' : 'droprateRoot'][exp.value] * chance * calcPicks(func, item, true, tcname, ...tcpath);
432
+ });
433
+
434
+ return totalchance;
435
+ }
436
+
437
+ return func(tcname, ...tcpath);
438
+ }
439
+
440
+ function forEachMonster(level, diff, func) {
441
+ let s = _s(diff);
442
+
443
+ [0, 1, 2].forEach(type => {
444
+ if (type && !level[s('MonUMin')] && !level[s('MonUMax')]) {
445
+ return;
446
+ }
447
+
448
+ let m = num => level[(diff ? 'nmon' : type ? 'umon' : 'mon') + num];
449
+
450
+ for (let c = 1; c <= 9; c++) {
451
+ if (m(c)) {
452
+ let mon = _monstats[m(c)];
453
+
454
+ if (mon.enabled && mon.killable) {
455
+ let mlvl = monlevel(mon, level, diff) + [0, 2, 3][type];
456
+
457
+ func(mon, mlvl, type);
458
+ }
459
+ }
460
+ }
461
+ });
462
+ }
463
+
464
+ function avg(...nums) {
465
+ return nums.reduce((t, v) => t + v, 0) / nums.length || 0;
466
+ }
467
+
468
+ function monlevel(mon, level, diff) {
469
+ if (!diff) {
470
+ return mon['Level'] || 0;
471
+ }
472
+
473
+ let s = _s(diff),
474
+ lvl = level[['MonLvl1Ex', 'MonLvl2Ex', 'MonLvl3Ex'][diff]] || 0,
475
+ mlvl = mon[s('Level')] || 0;
476
+
477
+ return lvl > mlvl ? lvl : mlvl;
478
+ }
479
+
480
+ _itemratio.forEach(data => {
481
+ data.func = {
482
+ unique: dropChance(data.Unique, data.UniqueDivisor, data.UniqueMin, 250),
483
+ set: dropChance(data.Set, data.SetDivisor, data.SetMin, 500),
484
+ rare: dropChance(data.Rare, data.RareDivisor, data.RareMin, 600),
485
+ magic: dropChance(data.Magic, data.MagicDivisor, data.MagicMin),
486
+ hq: dropChance(data.HiQuality, data.HiQualityDivisor),
487
+ normal: dropChance(data.Normal, data.NormalDivisor),
488
+ };
489
+ });
490
+
491
+ [0, 1, 2].forEach(diff => {
492
+ let s = _s(diff);
493
+ _levels.forEach(level => {
494
+ let l = key => level[key] || 0;
495
+
496
+ level.calc = level.calc || {};
497
+ level.calc.monsters = level.calc.monsters || [];
498
+ level.calc.monsters[diff] = level.calc.monsters[diff] || [];
499
+
500
+ if (level.Id) {
501
+ let supers = _superuniques.filter(s => s.areaId == level.Id || s.hcIdx === 19 && [66, 67, 68, 69, 70, 71, 72].includes(level.Id | 0)),
502
+ bosses = _monstats.filter(mon => mon.areaId == level.Id),
503
+ acount = (_count[level.Id] && _count[level.Id][diff] || 0),
504
+ scount = supers.reduce((total, sup) => {
505
+ return total + 1 + diff + ((sup['MinGrp'] || 0) + (sup['MaxGrp'] || 0)) / 2;
506
+ }, 0),
507
+ bcount = bosses.reduce((total, mon) => {
508
+ return total + 1 + + ((mon['MinGrp'] || 0) + (mon['MaxGrp'] || 0)) / 2;
509
+ }, 0),
510
+ ucount = avg(l(s('MonUMin')), l(s('MonUMax'))) * 0.8 * 5.5,
511
+ ccount = avg(l(s('MonUMin')), l(s('MonUMax'))) * 0.2 * 3,
512
+ count = acount - ucount - ccount - scount - bcount;
513
+
514
+ if (count > 0) {
515
+ let totalpackssize = 0, udiv = 0;
516
+
517
+ forEachMonster(level, diff, (mon, mlvl, type) => {
518
+ if (!type) {
519
+ let m = key => mon[key] || 0;
520
+ let packsize = avg(m('PartyMin') + m('PartyMax'), m('MinGrp') + m('MaxGrp'));
521
+
522
+ totalpackssize += packsize;
523
+ };
524
+
525
+ if (type === 2) {
526
+ udiv++;
527
+ }
528
+ });
529
+
530
+ forEachMonster(level, diff, (mon, mlvl, type) => {
531
+ let mult = [count / totalpackssize, ccount / 3 / udiv, ucount / 5.5 / udiv][type];
532
+ level.calc.monsters[diff].push({
533
+ mon,
534
+ mlvl,
535
+ type,
536
+ packCount: mult,
537
+ });
538
+ });
539
+ }
540
+
541
+ supers.forEach(sup => {
542
+ let mon = _monstats[sup.Class],
543
+ mlvl = monlevel(mon, level, diff) + 3;
544
+
545
+ level.calc.monsters[diff].push({
546
+ mon,
547
+ mlvl,
548
+ type: 3,
549
+ packCount: sup.hcIdx === 19 ? (1 / 7) : 1,
550
+ superMon: sup,
551
+ });
552
+ });
553
+
554
+ bosses.forEach(mon => {
555
+ let mlvl = monlevel(mon, level, diff);
556
+
557
+ level.calc.monsters[diff].push({
558
+ mon,
559
+ mlvl,
560
+ type: 4,
561
+ packCount: 1,
562
+ });
563
+ });
564
+ }
565
+ });
566
+ });
567
+
568
+ Object.values(_allitems).sort((a, b) => {
569
+ a = a.code;
570
+ b = b.code;
571
+ return a < b ? -1 : a > b ? 1 : 0;
572
+ }).forEach(item => {
573
+ let type = _itemtypes[item.type],
574
+ itemname = _strings[item.code] || item.name,
575
+ isExceptional = item.ubercode && item.code === item.ubercode,
576
+ isElite = item.ultracode && item.code === item.ultracode,
577
+ isuber = item.normcode && item.code !== item.normcode,
578
+ isclass = type.Class,
579
+ ratioFuncs = Object.values(_itemratio).filter(data => data.Version && !isuber === !data.Uber && !isclass === !data['Class Specific'])[0].func;
580
+
581
+ if (['pk1', 'pk2', 'pk3', 'bet', 'ceh', 'fed', 'tes'].includes(item.code)) {
582
+ item['spawnable'] = true;
583
+ type['Normal'] = true;
584
+ }
585
+
586
+ if (!item['spawnable'] || item.code === 'gld') {
587
+ return;
588
+ }
589
+
590
+ _uniqueitems.forEach(unique => {
591
+ if (unique.code === item.code && unique['enabled']) {
592
+ let name = _strings[unique.index] || unique.index, level = Math.max(item['level'] || 0, unique['lvl'] || 0);
593
+ data.items.push({
594
+ quality: 'unique',
595
+ code: unique.code,
596
+ item,
597
+ type,
598
+ level,
599
+ name: name + ' [Unique]',
600
+ searchable: [name, itemname, 'Unique', unique.code, ...getKeywords(type.Code), isExceptional ? 'exceptional' : 'nonexceptional', isElite ? 'elite' : 'nonelite'].join(' '),
601
+ tooltip: [
602
+ 'Type: ' + itemname,
603
+ 'Code: ' + unique.code,
604
+ 'Level: ' + level,
605
+ 'Keywords: ' + [name, itemname, 'Unique', unique.code, ...getKeywords(type.Code), isExceptional ? 'exceptional' : 'nonexceptional', isElite ? 'elite' : 'nonelite'].join(' '),
606
+ ].join('\n'),
607
+ use: false,
608
+ unique,
609
+ func: ratioFuncs,
610
+ });
611
+ }
612
+ });
613
+
614
+ _setitems.forEach(set => {
615
+ if (set.item === item.code) {
616
+ let name = _strings[set.index] || set.index, level = Math.max(item['level'] || 0, set['lvl'] || 0);
617
+
618
+ data.items.push({
619
+ quality: 'set',
620
+ code: set.item,
621
+ item,
622
+ type,
623
+ level,
624
+ name: name + ' [Set]',
625
+ searchable: [name, itemname, 'Set', set.item, ...getKeywords(type.Code), isExceptional ? 'exceptional' : 'nonexceptional', isElite ? 'elite' : 'nonelite'].join(' '),
626
+ tooltip: [
627
+ 'Type: ' + itemname,
628
+ 'Code: ' + set.item,
629
+ 'Level: ' + level,
630
+ 'Keywords: ' + [name, itemname, 'Set', set.item, ...getKeywords(type.Code), isExceptional ? 'exceptional' : 'nonexceptional', isElite ? 'elite' : 'nonelite'].join(' '),
631
+ ].join('\n'),
632
+ use: false,
633
+ set,
634
+ func: ratioFuncs,
635
+ });
636
+ }
637
+ });
638
+
639
+ if (type['Rare']) {
640
+ let level = item['level'] || 0;
641
+ data.items.push({
642
+ quality: 'rare',
643
+ code: item.code,
644
+ item,
645
+ type,
646
+ level,
647
+ name: itemname + ' [Rare]',
648
+ searchable: [itemname, 'Rare', item.code, ...getKeywords(type.Code), isExceptional ? 'exceptional' : 'nonexceptional', isElite ? 'elite' : 'nonelite'].join(' '),
649
+ tooltip: [
650
+ 'Code: ' + item.code,
651
+ 'Level: ' + level,
652
+ 'Keywords: ' + [itemname, 'Rare', item.code, ...getKeywords(type.Code), isExceptional ? 'exceptional' : 'nonexceptional', isElite ? 'elite' : 'nonelite'].join(' '),
653
+ ].join('\n'),
654
+ use: false,
655
+ func: ratioFuncs,
656
+ });
657
+ }
658
+
659
+ if (!type['Normal']) {
660
+ let level = item['level'] || 0;
661
+ data.items.push({
662
+ quality: 'magic',
663
+ code: item.code,
664
+ item,
665
+ type,
666
+ level,
667
+ name: itemname + ' [Magic]',
668
+ searchable: [itemname, 'Magic', item.code, ...getKeywords(type.Code), isExceptional ? 'exceptional' : 'nonexceptional', isElite ? 'elite' : 'nonelite'].join(' '),
669
+ tooltip: [
670
+ 'Code: ' + item.code,
671
+ 'Level: ' + level,
672
+ 'Keywords: ' + [itemname, 'Magic', item.code, ...getKeywords(type.Code), isExceptional ? 'exceptional' : 'nonexceptional', isElite ? 'elite' : 'nonelite'].join(' '),
673
+ ].join('\n'),
674
+ use: false,
675
+ func: ratioFuncs,
676
+ });
677
+ }
678
+
679
+ if (!type['Magic'] && !type['Normal']) {
680
+ let level = item['level'] || 0;
681
+ data.items.push({
682
+ quality: 'hq',
683
+ code: item.code,
684
+ item,
685
+ type,
686
+ level,
687
+ name: itemname + ' [Superior]',
688
+ searchable: [itemname, 'Superior', item.code, ...getKeywords(type.Code), isExceptional ? 'exceptional' : 'nonexceptional', isElite ? 'elite' : 'nonelite'].join(' '),
689
+ tooltip: [
690
+ 'Code: ' + item.code,
691
+ 'Level: ' + level,
692
+ 'Keywords: ' + [itemname, 'Superior', item.code, ...getKeywords(type.Code), isExceptional ? 'exceptional' : 'nonexceptional', isElite ? 'elite' : 'nonelite'].join(' '),
693
+ ].join('\n'),
694
+ use: false,
695
+ func: ratioFuncs,
696
+ });
697
+ }
698
+
699
+ if (!type['Magic']) {
700
+ let level = item['level'] || 0;
701
+ data.items.push({
702
+ quality: 'normal',
703
+ code: item.code,
704
+ item,
705
+ type,
706
+ level,
707
+ name: itemname + ' [Normal]',
708
+ searchable: [itemname, 'Normal', item.code, ...getKeywords(type.Code), isExceptional ? 'exceptional' : 'nonexceptional', isElite ? 'elite' : 'nonelite'].join(' '),
709
+ tooltip: [
710
+ 'Code: ' + item.code,
711
+ 'Level: ' + level,
712
+ 'Keywords: ' + [itemname, 'Normal', item.code, ...getKeywords(type.Code), isExceptional ? 'exceptional' : 'nonexceptional', isElite ? 'elite' : 'nonelite'].join(' '),
713
+ ].join('\n'),
714
+ use: false,
715
+ func: ratioFuncs,
716
+ });
717
+ }
718
+
719
+ if (!type['Magic'] && !type['Normal']) {
720
+ let level = item['level'] || 0;
721
+ data.items.push({
722
+ quality: 'low',
723
+ code: item.code,
724
+ item,
725
+ type,
726
+ level,
727
+ name: itemname + ' [Low Quality]',
728
+ searchable: [itemname, 'Low Quality', item.code, ...getKeywords(type.Code), isExceptional ? 'exceptional' : 'nonexceptional', isElite ? 'elite' : 'nonelite'].join(' '),
729
+ tooltip: [
730
+ 'Code: ' + item.code,
731
+ 'Level: ' + level,
732
+ 'Keywords: ' + [itemname, 'Low Quality', item.code, ...getKeywords(type.Code), isExceptional ? 'exceptional' : 'nonexceptional', isElite ? 'elite' : 'nonelite'].join(' '),
733
+ ].join('\n'),
734
+ use: false,
735
+ func: ratioFuncs,
736
+ });
737
+ }
738
+ });
739
+
740
+ let calc = false;
741
+
742
+ let paramstr = window.location.hash.slice(1), pos = 0;
743
+
744
+ if (paramstr.length) {
745
+ let paramdata = atod(paramstr);
746
+
747
+ calc = true;
748
+
749
+ data.parammap.forEach(line => {
750
+ data.params[line.key] = paramdata['get' + line.type + (line.size * 8)](pos);
751
+ pos += line.size;
752
+ });
753
+
754
+ for (;pos < paramdata.byteLength; pos += 2) {
755
+ let index = paramdata.getUint16(pos);
756
+ data.items[index].use = true;
757
+ }
758
+ }
759
+
760
+ data.visible = true;
761
+
762
+ watch(data.params, () => {
763
+ if (data.params.group > data.params.players) {
764
+ data.params.group = data.params.players;
765
+ }
766
+
767
+ if (data.params.minilvl > data.params.maxilvl) {
768
+ data.params.maxilvl = data.params.minilvl;
769
+ }
770
+
771
+ updateHash();
772
+ }, {
773
+ deep: true,
774
+ });
775
+
776
+ if (calc) {
777
+ doCalc();
778
+ }
779
+
780
+ </script>
781
+
782
+ <template>
783
+ <div>
784
+ <p v-if="!data.visible" class="text-center">Loading...</p>
785
+ <template v-else>
786
+ <div class="row mb-3 text-center">
787
+ <div class="col-12 col-lg">
788
+ <label>Magic Find: {{ data.params.mf }}%</label>
789
+ <input type="range" class="form-range form-range-sm" min="0" max="1167" v-model.number="data.params.mf">
790
+ </div>
791
+ <div class="col-12 col-lg">
792
+ <label>Players in Game: {{ data.params.players }}</label>
793
+ <input type="range" class="form-range form-range-sm" min="1" max="8" v-model.number="data.params.players">
794
+ </div>
795
+ <div class="col-12 col-lg">
796
+ <label>Players in Group: {{ data.params.group }}</label>
797
+ <input type="range" class="form-range form-range-sm" min="1" :max="data.params.players" v-model.number="data.params.group">
798
+ </div>
799
+ </div>
800
+ <div class="row mb-3 text-center">
801
+ <div class="col-12 col-lg">
802
+ <label>Minimum ilvl: {{ data.params.minilvl }}</label>
803
+ <input type="range" class="form-range form-range-sm" min="0" :max="110" v-model.number="data.params.minilvl">
804
+ </div>
805
+ <div class="col-12 col-lg">
806
+ <label>Max ilvl: {{ data.params.maxilvl }}</label>
807
+ <input type="range" class="form-range form-range-sm" :min="data.params.minilvl" max="110" v-model.number="data.params.maxilvl">
808
+ </div>
809
+ <div class="col-12 col-lg-auto d-flex align-items-center">
810
+ <button class="btn btn-primary" @click="doCalc">Do It!</button>
811
+ </div>
812
+ </div>
813
+ <div class="row">
814
+ <div class="col-12 col-lg-4">
815
+ <div class="mb-3">
816
+ <label>Items to include:</label>
817
+ <input type="search" class="form-control" placeholder="Search for an item or item code..." v-model="data.itemSearch">
818
+ </div>
819
+ <div v-for="(item, index) in data.items" :key="index">
820
+ <div v-if="item.use || matches(data.itemSearch, item.searchable)" class="form-check" :title="item.tooltip">
821
+ <input type="checkbox" class="form-check-input" @change="updateHash" v-model="item.use">
822
+ <label class="form-check-label">{{ item.name }}</label>
823
+ </div>
824
+ </div>
825
+ </div>
826
+ <div class="col-12 col-lg-4">
827
+ <div v-if="!data.calculating">
828
+ <table v-if="data.visible" class="table table-hover table-sm m-0 text-right">
829
+ <thead class="table-primary">
830
+ <tr>
831
+ <th class="text-left">Area</th>
832
+ <th class="text-center" title="Chance">Chance</th>
833
+ </tr>
834
+ </thead>
835
+ <tbody>
836
+ <tr v-for="(result, index) in data.areaResults" :title="result.tooltip" :key="index">
837
+ <td class="text-left">{{ result.name }}</td>
838
+ <td class="text-center">1:{{ makeRatio(result.chance) }}</td>
839
+ </tr>
840
+ </tbody>
841
+ </table>
842
+ </div>
843
+ <h4 class="text-center" v-else>Calculating: {{ (data.progress * 100).toFixed(0) }}%</h4>
844
+ </div>
845
+ <div class="col-12 col-lg-4">
846
+ <div v-if="!data.calculating">
847
+ <table v-if="data.visible" class="table table-hover table-sm m-0 text-right">
848
+ <thead class="table-primary">
849
+ <tr>
850
+ <th class="text-left">Monster</th>
851
+ <th class="text-center" title="Chance">Chance</th>
852
+ </tr>
853
+ </thead>
854
+ <tbody>
855
+ <tr v-for="(result, index) in data.packResults" :title="result.tooltip" :key="index">
856
+ <td class="text-left fw-bold" :style="{color: result.color}">{{ result.name }}</td>
857
+ <td class="text-center">1:{{ makeRatio(result.chance) }}</td>
858
+ </tr>
859
+ </tbody>
860
+ </table>
861
+ </div>
862
+ </div>
863
+ </div>
864
+ </template>
865
+ </div>
866
+ </template>
867
+
868
+ <style>
869
+
870
+ </style>