@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,781 @@
1
+ <script setup>
2
+ import { reactive, computed } from "vue";
3
+
4
+ import "../../../objext.js";
5
+ import runes from "../../../json/runes.json";
6
+ import itemtypes from "../../../json/itemtypes.json";
7
+ import items from "../../../json/items.json";
8
+ import gems from "../../../json/gems.json";
9
+ import skills from "../../../json/skills.json";
10
+ import properties from "../../../json/properties.json";
11
+ import strings from "../../../json/localestrings-eng.json";
12
+ import modOrder from "./modorder.json";
13
+
14
+ itemtypes.pala.ItemType = 'Paladin Shield';
15
+ itemtypes.h2h.ItemType = 'Claw';
16
+ itemtypes.h2h2.ItemType = 'Claw';
17
+ itemtypes.sppl.ItemType = 'Spear or Polearm';
18
+ itemtypes.blde.ItemType = 'Sword or Dagger';
19
+ itemtypes.rod.ItemType = 'Staff or Rod';
20
+
21
+ strings['skillname350'] = 'Delirium';
22
+
23
+ const data = reactive({
24
+ usedRunes: {},
25
+ advanced: false,
26
+ ladder: true,
27
+ search: {
28
+ name: '',
29
+ types: '',
30
+ items: '',
31
+ mods: '',
32
+ },
33
+ sockets: '',
34
+ });
35
+
36
+ const searchTooltip = `
37
+ Wildcards:
38
+
39
+ ? matches any one character
40
+ * matches one or more characters
41
+
42
+ TIP: Use \'or\' to search for different terms at the same time.
43
+ `.trim();
44
+
45
+ function getTypes (types, ret = {}) {
46
+ (Array.isArray(types) ? types : [types]).forEach(type => {
47
+ if (itemtypes[type]) {
48
+ ret[type] = true;
49
+
50
+ for (let c = 0; c < 10; c++) {
51
+ getTypes(itemtypes[type]['Equiv' + c], ret);
52
+ }
53
+ }
54
+ });
55
+
56
+ return ret;
57
+ }
58
+
59
+ function getSubTypes (types, ret = {}) {
60
+ if (Array.isArray(types) && types.length) {
61
+ let tmp = types.slice();
62
+
63
+ for (let type in itemtypes) {
64
+ if (itemtypes[type]) {
65
+ for (let c = 0; c < 10; c++) {
66
+ if (tmp.includes(itemtypes[type]['Equiv' + c])) {
67
+ ret[type] = true;
68
+ getSubTypes([type], ret);
69
+ }
70
+ }
71
+ }
72
+ }
73
+ }
74
+
75
+ return ret;
76
+ }
77
+
78
+ function intersect (a, b) {
79
+ return a.some(elem => b.includes(elem));
80
+ }
81
+
82
+ function stripHtml (html) {
83
+ let tmp = document.createElement("DIV");
84
+ tmp.innerHTML = html;
85
+ return tmp.textContent || tmp.innerText || "";
86
+ }
87
+
88
+ function escapeRegexp (string) {
89
+ return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
90
+ }
91
+
92
+ function escapeRegexpWithoutWildcards (string) {
93
+ return string.replace(/[.+^${}()|[\]\\]/g, '\\$&');
94
+ }
95
+
96
+ function sortMods (a, b) {
97
+ return modOrder.indexOf(a.property.code) - modOrder.indexOf(b.property.code);
98
+ }
99
+
100
+ let modFormatters = {
101
+ 'skilltab': ({min, max, param}) => {
102
+ let tabname = [
103
+ 'Bow & Crossbow Skills (Amazon)',
104
+ 'Passive & Magic Skills (Amazon)',
105
+ 'Spear & Javelin Skills (Amazon)',
106
+ 'Fire Skills (Sorceress)',
107
+ 'Lightning Skills (Sorceress)',
108
+ 'Cold Skills (Sorceress)',
109
+ 'Curses (Necromancer)',
110
+ 'Poison & Bone Skills (Necromancer)',
111
+ 'Summoning Skills (Necromancer)',
112
+ 'Combat Skills (Paladin)',
113
+ 'Offensive Auras (Paladin)',
114
+ 'Defensive Auras (Paladin)',
115
+ 'Combat Skills (Barbarian)',
116
+ 'Masteries (Barbarian)',
117
+ 'Warcries (Barbarian)',
118
+ 'Summoning Skills (Druid)',
119
+ 'Shapeshifting Skills (Druid)',
120
+ 'Elemental Skills (Druid)',
121
+ 'Trap Skills (Assassin)',
122
+ 'Shadow Disciplines (Assassin)',
123
+ 'Martial Arts (Assassin)',
124
+ ][param];
125
+
126
+ return `+${fv(min, max)} to ${tabname}`;
127
+ },
128
+ 'res-fire': ({min, max}) => `Fire Resist +${fv(min, max)}%`,
129
+ 'res-fire-max': ({min, max}) => `+${fv(min, max)}% to Maximum Fire Resist`,
130
+ 'res-ltng': ({min, max}) => `Lightning Resist +${fv(min, max)}%`,
131
+ 'res-ltng-max': ({min, max}) => `+${fv(min, max)}% to Maximum Lightning Resist`,
132
+ 'res-pois': ({min, max}) => `Poison Resist +${fv(min, max)}%`,
133
+ 'res-pois-max': ({min, max}) => `+${fv(min, max)}% to Maximum Poison Resist`,
134
+ 'res-cold': ({min, max}) => `Cold Resist +${fv(min, max)}%`,
135
+ 'res-cold-max': ({min, max}) => `+${fv(min, max)}% to Maximum Cold Resist`,
136
+ 'res-all': ({min, max}) => `All Resistances +${fv(min, max)}%`,
137
+ 'str': ({min, max}) => `+${fv(min, max)} to Strength`,
138
+ 'str/lvl': ({param}) => `${param/8}-${param/8 * 99}% to Strength (Char Level)`,
139
+ 'dex': ({min, max}) => `+${fv(min, max)} to Dexterity`,
140
+ 'vit': ({min, max}) => `+${fv(min, max)} to Vitality`,
141
+ 'vit/lvl': ({param}) => `${param/8}-${param/8 * 99}% to Vitality (Char Level)`,
142
+ 'enr': ({min, max}) => `+${fv(min, max)} to Energy`,
143
+ 'all-stats': ({min, max}) => `+${fv(min, max)} to All Attributes`,
144
+ 'hp': ({min, max}) => `+${fv(min, max)} to Life`,
145
+ 'hp%': ({min, max}) => `Increase Maximum Life ${fv(min, max)}%`,
146
+ 'hp/lvl': ({min}) => `${min/8}-${min/8 * 99} to Life (Char Level)`,
147
+ 'mana': ({min, max}) => `+${fv(min, max)} to Mana`,
148
+ 'mana%': ({min, max}) => `Increase Maximum Mana ${fv(min, max)}%`,
149
+ 'mana/lvl': ({param}) => `${param/8}-${param/8 * 99} to Mana (Char Level)`,
150
+ 'regen-mana': ({min, max}) => `Regenerate Mana ${fv(min, max)}%`,
151
+ 'ac': ({min, max}) => `+${fv(min, max)} Defense`,
152
+ 'ac%': ({min, max}) => `+${fv(min, max)}% Enhanced Defense`,
153
+ 'ac/lvl': ({min}) => `${min/8}-${min/8 * 99} Defense (Char Level)`,
154
+ 'ac-miss': ({min, max}) => `+${fv(min, max)} Defense vs. Missile`,
155
+ 'red-dmg': ({min, max}) => `Damage Reduced by ${fv(min, max)}`,
156
+ 'red-dmg%': ({min, max}) => `Damage Reduced by ${fv(min, max)}%`,
157
+ 'red-mag': ({min, max}) => `Magic Damage Reduced by ${fv(min, max)}`,
158
+ 'dmg%': ({min, max}) => `+${fv(min, max)}% Enhanced Damage`,
159
+ 'dmg': ({min, max}) => `Damage +${fv(min, max)}`,
160
+ 'dmg-demon': ({min, max}) => `+${fv(min, max)}% Damage to Demons`,
161
+ 'dmg-dem/lvl': ({param}) => `${param/8}-${param/8 * 99}% Damage to Demons (Char Level)`,
162
+ 'dmg-undead': ({min, max}) => `+${fv(min, max)}% Damage to Undead`,
163
+ 'dmg-min': ({min, max}) => `+${fv(min, max)} to Minimum Damage`,
164
+ 'dmg-max': ({min, max}) => `+${fv(min, max)} to Maximum Damage`,
165
+ 'att': ({min, max}) => `+${fv(min, max)} to Attack Rating`,
166
+ 'att%': ({min, max}) => `${fv(min, max)}% Bonus to Attack Rating`,
167
+ 'att-demon': ({min, max}) => `+${fv(min, max)} to Attack Rating against Demons`,
168
+ 'att-undead': ({min, max}) => `+${fv(min, max)} to Attack Rating against Undead`,
169
+ 'reduce-ac': ({min, max}) => `-${fv(min, max)}% Target Defense`,
170
+ 'dmg-ac': ({min, max}) => `${fv(min, max)} to Monster Defense Per Hit`,
171
+ 'dmg-to-mana': ({min, max}) => `${fv(min, max)}% Damage Taken Goes to Mana`,
172
+ 'crush': ({min, max}) => `${fv(min, max)}% Chance of Crushing Blow`,
173
+ 'openwounds': ({min, max}) => `${fv(min, max)}% Chance of Open Wounds`,
174
+ 'deadly': ({min, max}) => `${fv(min, max)}% Deadly Strike`,
175
+ 'deadly/lvl': ({param}) => `${param/8}-${param/8 * 99}% Deadly Strike (Char Level)`,
176
+ 'freeze': ({min, max}) => `Freezes Target +${fv(min, max)}`,
177
+ 'noheal': () => `Prevent Monster Heal`,
178
+ 'knock': () => `Knockback`,
179
+ 'ignore-ac': () => `Ignore Target's Defense`,
180
+ 'indestruct': () => `Indestructible`,
181
+ 'ethereal': () => `Ethereal`,
182
+ 'nofreeze': () => `Cannot Be Frozen`,
183
+ 'half-freeze': () => `Half Freeze Duration`,
184
+ 'rip': () => `Slain Monsters Rest in Peace`,
185
+ 'stupidity': ({min, max}) => `Hit Blinds Target +${fv(min, max)}`,
186
+ 'slow': ({min, max}) => `Slows Target by ${fv(min, max)}%`,
187
+ 'howl': ({min, max}) => `Hit Causes Monster to Flee ${fv(min, max)}%`,
188
+ 'pierce': ({min, max}) => `Piercing Attack +${fv(min, max)}%`,
189
+ 'cast2': ({min, max}) => `+${fv(min, max)}% Faster Cast Rate`,
190
+ 'cast3': ({min, max}) => `+${fv(min, max)}% Faster Cast Rate`,
191
+ 'swing1': ({min, max}) => `+${fv(min, max)}% Increased Attack Speed`,
192
+ 'swing2': ({min, max}) => `+${fv(min, max)}% Increased Attack Speed`,
193
+ 'swing3': ({min, max}) => `+${fv(min, max)}% Increased Attack Speed`,
194
+ 'block2': ({min, max}) => `+${fv(min, max)}% Faster Block Rate`,
195
+ 'balance2': ({min, max}) => `+${fv(min, max)}% Faster Hit Recovery`,
196
+ 'balance3': ({min, max}) => `+${fv(min, max)}% Faster Hit Recovery`,
197
+ 'regen': ({min, max}) => `Replenish Life +${fv(min, max)}`,
198
+ 'heal-kill': ({min, max}) => `+${fv(min, max)} To Life After Each Kill`,
199
+ 'demon-heal': ({min, max}) => `+${fv(min, max)} To Life After Each Demon Kill`,
200
+ 'lifesteal': ({min, max}) => `+${fv(min, max)}% Life Stolen per Hit`,
201
+ 'mana-kill': ({min, max}) => `+${fv(min, max)} To Mana After Each Kill`,
202
+ 'manasteal': ({min, max}) => `+${fv(min, max)}% Mana Stolen per Hit`,
203
+ 'aura': ({min, max, param}) => `Level ${fv(min, max)} ${fs(param)} Aura When Equipped`,
204
+ 'charged': ({min, max, param}) => `Level ${max} ${fs(param)} (${min} Charges)`,
205
+ 'oskill': ({min, max, param}) => `+${fv(min, max)} to ${fs(param)}`,
206
+ 'hit-skill': ({min, max, param}) => `${min}% Chance to Cast Level ${max} ${fs(param)} on Striking`,
207
+ 'att-skill': ({min, max, param}) => `${min}% Chance to Cast Level ${max} ${fs(param)} on Attack`,
208
+ 'gethit-skill': ({min, max, param}) => `${min}% Chance to Cast Level ${max} ${fs(param)} when Struck`,
209
+ 'kill-skill': ({min, max, param}) => `${min}% Chance to Cast Level ${max} ${fs(param)} When You Kill an Enemy`,
210
+ 'death-skill': ({min, max, param}) => `${min}% Chance to Cast Level ${max} ${fs(param)} When You Die`,
211
+ 'levelup-skill': ({min, max, param}) => `${min}% Chance to Cast Level ${max} ${fs(param)} When You Level-Up`,
212
+ 'skill': ({min, max, param}) => `+${fv(min, max)} to ${fs(param)} (${fsc(param)} only)`,
213
+ 'allskills': ({min, max}) => `+${fv(min, max)} to All Skills`,
214
+ 'fireskill': ({min, max}) => `+${fv(min, max)} to Fire Skills`,
215
+ 'ama': ({min, max}) => `+${fv(min, max)} to Amazon Skills`,
216
+ 'nec': ({min, max}) => `+${fv(min, max)} to Necromancer Skills`,
217
+ 'sor': ({min, max}) => `+${fv(min, max)} to Sorceress Skills`,
218
+ 'bar': ({min, max}) => `+${fv(min, max)} to Barbarian Skills`,
219
+ 'pal': ({min, max}) => `+${fv(min, max)} to Paladin Skills`,
220
+ 'dru': ({min, max}) => `+${fv(min, max)} to Druid Skills`,
221
+ 'ass': ({min, max}) => `+${fv(min, max)} to Assassin Skills`,
222
+ 'explosivearrow': ({min, max}) => `Fires Level ${fv(min, max)} Explosive Arrow`,
223
+ 'dmg-elem': ({min, max}) => `Adds ${min}-${max} Fire Damage<br>Adds ${min}-${max} Lightning Damage<br>Adds ${min}-${max} Cold Damage`,
224
+ 'dmg-fire': ({min, max}) => `Adds ${min}-${max} Fire Damage`,
225
+ 'dmg-ltng': ({min, max}) => `Adds ${min}-${max} Lightning Damage`,
226
+ 'dmg-cold': ({min, max}) => `Adds ${min}-${max} Cold Damage`,
227
+ 'dmg-mag': ({min, max}) => `Adds ${min}-${max} Magic Damage`,
228
+ 'dmg-pois': ({min, max, param}) => `Adds ${fv(Math.trunc(min * param / 256), Math.trunc(max * param / 256))} Poison Damage Over ${param/25} Seconds`,
229
+ 'extra-fire': ({min, max}) => `+${fv(min, max)}% to Fire Skill Damage`,
230
+ 'extra-ltng': ({min, max}) => `+${fv(min, max)}% to Lightning Skill Damage`,
231
+ 'extra-cold': ({min, max}) => `+${fv(min, max)}% to Cold Skill Damage`,
232
+ 'extra-pois': ({min, max}) => `+${fv(min, max)}% to Poison Skill Damage`,
233
+ 'pierce-fire': ({min, max}) => `-${fv(min, max)}% to Enemy Fire Resistance`,
234
+ 'pierce-ltng': ({min, max}) => `-${fv(min, max)}% to Enemy Lightning Resistance`,
235
+ 'pierce-cold': ({min, max}) => `-${fv(min, max)}% to Enemy Cold Resistance`,
236
+ 'pierce-pois': ({min, max}) => `-${fv(min, max)}% to Enemy Poison Resistance`,
237
+ 'abs-fire': ({min, max}) => `Fire Absorb ${fv(min, max)}`,
238
+ 'abs-ltng': ({min, max}) => `Lightning Absorb ${fv(min, max)}`,
239
+ 'abs-cold': ({min, max}) => `Cold Absorb ${fv(min, max)}`,
240
+ 'abs-mag': ({min, max}) => `Magic Absorb ${fv(min, max)}`,
241
+ 'ease': ({min, max}) => `Requirements ${fv(min, max)}%`,
242
+ 'light': ({min, max}) => `+${fv(min, max)} to Light Radius`,
243
+ 'mag%': ({min, max}) => `${fv(min, max)}% Better Chance of Finding Magic Items`,
244
+ 'mag%/lvl': ({param}) => `${param/8}-${param/8*99}% Better Chance of Finding Magic Items (Char Level)`,
245
+ 'gold%': ({min, max}) => `${fv(min, max)}% Extra Gold from Monsters`,
246
+ 'gold%/lvl': ({param}) => `${param/8}-${param/8 * 99}% Extra Gold from Monsters (Char Level)`,
247
+ 'move2': ({min, max}) => `+${fv(min, max)}% Faster Run/Walk`,
248
+ 'move3': ({min, max}) => `+${fv(min, max)}% Faster Run/Walk`,
249
+ 'stamdrain': ({min, max}) => `${fv(min, max)}% Slower Stamina Drain`,
250
+ 'cheap': ({min, max}) => `Reduces All Vendor Prices ${fv(min, max)}%`,
251
+ 'res-pois-len': ({min, max}) => `Poison Length Reduced by ${fv(min, max)}%`,
252
+ 'reanimate': ({min, max}) => `${fv(min, max)}% Chance to Reanimate as: Returned`,
253
+ 'rep-dur': ({param}) => `Repairs 1 Durability Every ${100/param} Seconds`,
254
+ 'thorns': ({min, max}) => `Attacker Takes Damage of ${fv(min, max)}%`,
255
+ 'block': ({min, max}) => `${fv(min, max)}% Increased Chance of Blocking`,
256
+ 'stam': ({min, max}) => `+${fv(min, max)} Maximum Stamina`,
257
+ };
258
+
259
+ function getModText (mod) {
260
+ if (modFormatters[mod.property.code] && modFormatters[mod.property.code].apply) {
261
+ return `<span>` + modFormatters[mod.property.code](mod) + '</span>';
262
+ }
263
+
264
+ return `<span>` + (mod.property['*Tooltip'] || '(no mod formatter)') + '</span>';
265
+ }
266
+
267
+ function combineMods (a, b) {
268
+ if ([
269
+ 'oskill',
270
+ 'skill',
271
+ ].includes(a.property.code)) {
272
+ if (a.param === b.param) {
273
+ a.min += b.min;
274
+ a.max += b.max;
275
+ return true;
276
+ }
277
+
278
+ return false;
279
+ } else if ([
280
+ 'dmg',
281
+ 'dmg%',
282
+ 'ac',
283
+ 'ac-miss',
284
+ 'ac%',
285
+ 'red-dmg',
286
+ 'red-mag',
287
+ 'res-all',
288
+ 'res-fire',
289
+ 'res-ltng',
290
+ 'res-cold',
291
+ 'res-pois',
292
+ 'res-fire-max',
293
+ 'dmg-undead',
294
+ 'dmg-fire',
295
+ 'dmg-ltng',
296
+ 'dmg-cold',
297
+ 'balance1',
298
+ 'balance2',
299
+ 'balance3',
300
+ 'block1',
301
+ 'block2',
302
+ 'crush',
303
+ 'openwounds',
304
+ 'att',
305
+ 'str',
306
+ 'dex',
307
+ 'vit',
308
+ 'enr',
309
+ 'light',
310
+ 'lifesteal',
311
+ 'manasteal',
312
+ 'gold%',
313
+ 'mag%',
314
+ 'dmg-demon',
315
+ ].includes(a.property.code)) {
316
+ a.min += b.min;
317
+ a.max += b.max;
318
+ return true;
319
+ } else if (a.property.code === 'dmg-pois') {
320
+ a.min += b.min;
321
+ a.max += b.max;
322
+ a.param = (a.param + b.param) / 2;
323
+ return true;
324
+ } else if ([
325
+ 'noheal',
326
+ 'ignore-ac',
327
+ ].includes(a.property.code)) {
328
+ return true;
329
+ } else if ([
330
+ 'hit-skill',
331
+ 'gethit-skill',
332
+ 'charged',
333
+ ].includes(a.property.code)) {
334
+ return false;
335
+ }
336
+
337
+ return false;
338
+ }
339
+
340
+ items.forEach(item => {
341
+ item.types = Object.keys(getTypes(item.type));
342
+ });
343
+
344
+ runes.forEach(runeword => {
345
+ if (!runeword['complete']) {
346
+ return;
347
+ }
348
+
349
+ runeword.runes = [];
350
+ runeword.types = [];
351
+ runeword.weaponMods = [];
352
+ runeword.helmMods = [];
353
+ runeword.shieldMods = [];
354
+
355
+ for (let c = 0; c < 10; c++) {
356
+ if (runeword['itype' + c]) {
357
+ runeword.types.push(runeword['itype' + c]);
358
+ }
359
+ }
360
+
361
+ runeword.subTypes = Object.keys(getSubTypes(runeword.types)).filter(type => !runeword.types.includes(type) && itemtypes[type]['MaxSockets3']);
362
+ runeword.allTypes = [...runeword.types, ...runeword.subTypes];
363
+ runeword.superTypes = Object.keys(getTypes(runeword.types));
364
+ runeword.isWeapon = intersect(runeword.superTypes, ['weap']);
365
+ runeword.isArmor = intersect(runeword.superTypes, ['tors']);
366
+ runeword.isHelm = intersect(runeword.superTypes, ['helm']);
367
+ runeword.isShield = intersect(runeword.superTypes, ['shld', 'pala']);
368
+
369
+ if (!runeword.isWeapon && !runeword.isArmor && !runeword.isHelm && !runeword.isShield) {
370
+ debugger;
371
+ }
372
+
373
+ for (let c = 0; c < 10; c++) {
374
+ if (runeword['Rune' + c]) {
375
+ let runeName = runeword['Rune' + c];
376
+ runeword.runes.push(runeName);
377
+ data.usedRunes[runeword['Rune' + c]] = data.usedRunes[runeword['Rune' + c]] || false;
378
+
379
+ if (gems[runeName]) {
380
+ for (let i = 0; i < 10; i++) {
381
+ if (runeword.isWeapon && gems[runeName]['weaponMod' + i + 'Code']) {
382
+ runeword.weaponMods.push({
383
+ property: properties[gems[runeName]['weaponMod' + i + 'Code']],
384
+ param: gems[runeName]['weaponMod' + i + 'Param'],
385
+ min: gems[runeName]['weaponMod' + i + 'Min'],
386
+ max: gems[runeName]['weaponMod' + i + 'Max'],
387
+ });
388
+ }
389
+
390
+ if ((runeword.isArmor || runeword.isHelm) && gems[runeName]['helmMod' + i + 'Code']) {
391
+ runeword.helmMods.push({
392
+ property: properties[gems[runeName]['helmMod' + i + 'Code']],
393
+ param: gems[runeName]['helmMod' + i + 'Param'],
394
+ min: gems[runeName]['helmMod' + i + 'Min'],
395
+ max: gems[runeName]['helmMod' + i + 'Max'],
396
+ });
397
+ }
398
+
399
+ if (runeword.isShield && gems[runeName]['shieldMod' + i + 'Code']) {
400
+ runeword.shieldMods.push({
401
+ property: properties[gems[runeName]['shieldMod' + i + 'Code']],
402
+ param: gems[runeName]['shieldMod' + i + 'Param'],
403
+ min: gems[runeName]['shieldMod' + i + 'Min'],
404
+ max: gems[runeName]['shieldMod' + i + 'Max'],
405
+ });
406
+ }
407
+ }
408
+ }
409
+ }
410
+
411
+ if (runeword['T1Code' + c]) {
412
+ let mod = {
413
+ property: properties[runeword['T1Code' + c]],
414
+ param: runeword['T1Param' + c],
415
+ min: runeword['T1Min' + c],
416
+ max: runeword['T1Max' + c],
417
+ };
418
+
419
+ if (runeword.isWeapon) {
420
+ runeword.weaponMods.push(mod);
421
+ }
422
+
423
+ if (runeword.isArmor || runeword.isHelm) {
424
+ runeword.helmMods.push(mod);
425
+ }
426
+
427
+ if (runeword.isShield) {
428
+ runeword.shieldMods.push(mod);
429
+ }
430
+ }
431
+ }
432
+
433
+ [
434
+ runeword.weaponMods,
435
+ runeword.helmMods,
436
+ runeword.shieldMods,
437
+ ].forEach(mods => {
438
+ let hasResists = mods.some(mod => ['res-fire', 'res-ltng', 'res-cold', 'res-ltng'].includes(mod.property.code));
439
+
440
+ if (hasResists) {
441
+ mods.forEach(mod => {
442
+ if (mod.property.code === 'res-all') {
443
+ mod.property = properties['res-fire'];
444
+
445
+ mods.push({
446
+ property: properties['res-ltng'],
447
+ param: mod.param,
448
+ min: mod.min,
449
+ max: mod.max,
450
+ });
451
+
452
+ mods.push({
453
+ property: properties['res-cold'],
454
+ param: mod.param,
455
+ min: mod.min,
456
+ max: mod.max,
457
+ });
458
+
459
+ mods.push({
460
+ property: properties['res-pois'],
461
+ param: mod.param,
462
+ min: mod.min,
463
+ max: mod.max,
464
+ });
465
+ }
466
+ });
467
+ }
468
+
469
+ for (let c = 0; c < mods.length; c++) {
470
+ if (mods[c]) {
471
+ for (let d = c + 1; d < mods.length; d++) {
472
+ if (mods[d] && mods[c].property === mods[d].property) {
473
+ if (combineMods(mods[c], mods[d])) {
474
+ mods[d] = null;
475
+ }
476
+ }
477
+ }
478
+ }
479
+ }
480
+ });
481
+
482
+ runeword.weaponMods = runeword.weaponMods.filter(Boolean).sort(sortMods);
483
+ runeword.helmMods = runeword.helmMods.filter(Boolean).sort(sortMods);
484
+ runeword.shieldMods = runeword.shieldMods.filter(Boolean).sort(sortMods);
485
+
486
+
487
+ runeword.validItems = Object.values(items).filter(item => {
488
+ return intersect(runeword.types, item.types) && runeword.runes.length <= item.gemsockets;
489
+ }).sort((a, b) => itemtypes[a.type].lineNumber - itemtypes[b.type].lineNumber || a.level - b.level);
490
+
491
+ runeword.modText = {};
492
+
493
+ if (runeword.isWeapon) {
494
+ runeword.modText.weapon = runeword.weaponMods.map(getModText).join('<br>');
495
+ }
496
+
497
+ if (runeword.isArmor || runeword.isHelm) {
498
+ runeword.modText.helm = runeword.helmMods.map(getModText).join('<br>');
499
+ }
500
+
501
+ if (runeword.isShield) {
502
+ runeword.modText.shield = runeword.shieldMods.map(getModText).join('<br>');
503
+ }
504
+
505
+ runeword.search = {
506
+ name: [
507
+ runeword['*Rune Name'],
508
+ ].join(' '),
509
+ types: [
510
+ ...runeword.types.map(type => itemtypes[type].ItemType),
511
+ ...runeword.subTypes.map(type => itemtypes[type].ItemType),
512
+ ].join(' '),
513
+ items: [
514
+ ...runeword.validItems.map(item => strings[item.code]),
515
+ ].join(' '),
516
+ mods: Object.values(runeword.modText).map(stripHtml).join(' '),
517
+ };
518
+ });
519
+
520
+ const runewords = computed(() => {
521
+ let ret = Object.values(runes).filter(runeword => runeword['complete']);
522
+
523
+ try {
524
+ if (data.usedRunes.reduce((t, v) => t || v, false)) {
525
+ ret = ret.filter(runeword => {
526
+ for (let rune in data.usedRunes) {
527
+ if (data.usedRunes[rune] && !runeword.runes.includes(rune)) {
528
+ return false;
529
+ }
530
+ }
531
+
532
+ return true;
533
+ });
534
+ }
535
+
536
+ let sockets = data.sockets.split(/\s+/gi).map(Number).filter(isFinite).filter(v => v > 0);
537
+
538
+ if (sockets.length) {
539
+ ret = ret.filter(runeword => sockets.includes(runeword.runes.length));
540
+ }
541
+
542
+ if (!data.ladder) {
543
+ ret = ret.filter(runeword => !runeword.server);
544
+ }
545
+
546
+ data.search.forEach((text, key) => {
547
+ if (text.length) {
548
+ ret = ret.filter(runeword => {
549
+ let words = escapeRegexpWithoutWildcards(text).replace(/\?/gi, '\\S').replace(/\*/gi, '\\S+').split(/\s+/gi).filter(Boolean);
550
+
551
+ while (words.length && /^(or|and)$/i.test(words[0])) {
552
+ words.shift();
553
+ }
554
+
555
+ while (words.length && /^(or|and)$/i.test(words[words.length - 1])) {
556
+ words.pop();
557
+ }
558
+
559
+ if (!words.length) {
560
+ return true;
561
+ }
562
+
563
+ let condition = new RegExp('(' + words.map(word => /^(or|and)$/i.test(word) ? ')|(' : '\\b' + word).join('\\s*') + ')', 'gi');
564
+ return condition.test(runeword.search[key]);
565
+ });
566
+ }
567
+ });
568
+ } catch (e) {
569
+ console.error(e);
570
+ }
571
+
572
+ return ret;
573
+ });
574
+
575
+ function fv (a, b) {
576
+ return a === b ? `${a}` : `<span class="text-light">${a}</span><span class="magic-mod-light">-</span><span class="text-light">${b}</span>`;
577
+ }
578
+
579
+ function fs (a) {
580
+ if (a != Number(a)) {
581
+ for (let c in skills) {
582
+ if (skills[c].skill.toLowerCase() === a.toLowerCase()) {
583
+ a = Number(c);
584
+ break;
585
+ }
586
+ }
587
+ }
588
+
589
+ let skillName = strings['skillname' + a] || strings['Skillname' + (a + 1)];
590
+
591
+ if (!skillName) {
592
+ debugger;
593
+ }
594
+
595
+ return `${skillName}`;
596
+ }
597
+
598
+ function fsc (a) {
599
+ if (a != Number(a)) {
600
+ return a;
601
+ }
602
+
603
+ a = Number(a);
604
+
605
+ let className = 'Unknown Class';
606
+
607
+ if (a >= 6 && a <= 35) {
608
+ className = 'Amazon';
609
+ } else if (a >= 36 && a <= 65) {
610
+ className = 'Sorceress';
611
+ } else if (a >= 66 && a <= 95) {
612
+ className = 'Necromancer';
613
+ } else if (a >= 96 && a <= 125) {
614
+ className = 'Paladin';
615
+ } else if (a >= 126 && a <= 155) {
616
+ className = 'Barbarian';
617
+ } else if (a >= 221 && a <= 250) {
618
+ className = 'Druid';
619
+ } else if (a >= 251 && a <= 280) {
620
+ className = 'Assassin';
621
+ }
622
+
623
+ return `${className}`;
624
+ }
625
+
626
+ </script>
627
+
628
+ <template>
629
+ <div class="row">
630
+ <div class="col-auto">
631
+ <div>
632
+ <button :class="{
633
+ 'btn': true,
634
+ 'btn-outline-success': !data.advanced,
635
+ 'btn-outline-secondary': data.advanced,
636
+ 'mb-2': true,
637
+ 'px-1': true,
638
+ }" style="width:4.25em" @click="data.advanced = !data.advanced">Basic</button>
639
+ </div>
640
+ <div>
641
+ <button :class="{
642
+ 'btn': true,
643
+ 'btn-outline-success': data.ladder,
644
+ 'btn-outline-secondary': !data.ladder,
645
+ 'mb-2': true,
646
+ 'px-1': true,
647
+ }" style="width:4.25em" @click="data.ladder = !data.ladder">Ladder</button>
648
+ </div>
649
+ <label class="form-label text-center d-block"><strong>Runes<br>Used</strong></label>
650
+ <div v-for="rune in Object.keys(data.usedRunes).sort().reverse()" :key="rune" class="my-1 avquest">
651
+ <button :class="{
652
+ 'btn': true,
653
+ 'btn-outline-warning': data.usedRunes[rune],
654
+ 'btn-outline-secondary': !data.usedRunes[rune],
655
+ 'fw-bold': true,
656
+ }" style="width:4.25em" @click="() => {
657
+ data.usedRunes[rune] = !data.usedRunes[rune];
658
+ }" @focus="e => e.target.blur()">{{ strings[rune + 'L'] }}</button>
659
+ </div>
660
+ </div>
661
+ <div class="col">
662
+ <div class="row">
663
+ <div :class="data.advanced ? 'col-12' : 'col'">
664
+ <input type="search" :title="searchTooltip" class="form-control bg-secondary" placeholder="Search a runeword name..." v-model="data.search.name">
665
+ </div>
666
+ <div v-if="data.advanced" class="col-12 col-lg mt-3">
667
+ <input type="search" :title="searchTooltip" class="form-control bg-secondary" placeholder="Search an item type..." v-model="data.search.types">
668
+ </div>
669
+ <div v-if="data.advanced" class="col-12 col-lg mt-3">
670
+ <input type="search" :title="searchTooltip" class="form-control bg-secondary" placeholder="Search a base item..." v-model="data.search.items">
671
+ </div>
672
+ <div v-if="data.advanced" class="col-12 col-lg mt-3">
673
+ <input type="search" :title="searchTooltip" class="form-control bg-secondary" placeholder="Search an item modifier..." v-model="data.search.mods">
674
+ </div>
675
+ <div v-if="data.advanced" class="col-12 col-lg-auto mt-3">
676
+ <input type="search" class="form-control bg-secondary" placeholder="Sockets..." v-model="data.sockets">
677
+ </div>
678
+ </div>
679
+ <div class="row">
680
+ <div v-for="runeword in runewords" :key="runeword.Name" class="col-12 col-lg-6 col-xl-4 col-xxl-3 text-center px-2 pt-3 runeword-display">
681
+ <div class="border border-secondary rounded h-100 p-1 position-relative">
682
+ <div class="unique-title">{{ runeword['*Rune Name'] }}</div>
683
+ <div v-if="runeword.server" class="ladder-tag">Ladder<br>Only</div>
684
+ <div class="rune-list">{{ runeword.runes.map(rune => strings[rune + 'L']).join(' ') }}</div>
685
+ <div class="item-type-list mt-2">
686
+ <template v-for="(type, index) in runeword.types"><template v-if="index">, </template>{{ itemtypes[type].ItemType }}</template>
687
+ </div>
688
+ <div class="magic-mod">
689
+ <div v-if="runeword.isWeapon" class="mt-2">
690
+ <div class="header" v-if="runeword.isHelm || runeword.isArmor || runeword.isShield">For Weapons</div>
691
+ <div v-html="runeword.modText.weapon" />
692
+ </div>
693
+ <div v-if="runeword.isArmor" class="mt-2">
694
+ <div class="header" v-if="runeword.isHelm || runeword.isWeapon || runeword.isShield">For Armor</div>
695
+ <div v-html="runeword.modText.helm" />
696
+ </div>
697
+ <div v-if="runeword.isHelm" class="mt-2">
698
+ <div class="header" v-if="runeword.isWeapon || runeword.isArmor || runeword.isShield">For Helms</div>
699
+ <div v-html="runeword.modText.helm" />
700
+ </div>
701
+ <div v-if="runeword.isShield" class="mt-2">
702
+ <div class="header" v-if="runeword.isHelm || runeword.isArmor || runeword.isWeapon">For Shields</div>
703
+ <div v-html="runeword.modText.shield" />
704
+ </div>
705
+ </div>
706
+ </div>
707
+ </div>
708
+ </div>
709
+ </div>
710
+ </div>
711
+ </template>
712
+
713
+ <style>
714
+ @import "https://blizzhackers.github.io/d2data/css/d2font.css";
715
+ @import url('https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap');
716
+
717
+ body {
718
+ font-family: 'Roboto', sans-serif;
719
+ }
720
+
721
+ .bg-secondary {
722
+ background-color: hsl(208, 7%, 60%) !important;
723
+ }
724
+
725
+ .form-control.bg-secondary {
726
+ color: #000;
727
+ font-weight: 700;
728
+ }
729
+
730
+ .form-control.bg-secondary::placeholder {
731
+ color: #555;
732
+ font-weight: 700;
733
+ }
734
+
735
+ .runeword-display {
736
+ line-height: 1.25;
737
+ }
738
+
739
+ .unique-title {
740
+ color: hsl(51, 24%, 51%);
741
+ font-family: 'AvQuest';
742
+ font-size: 1.25em;
743
+ font-weight: 700;
744
+ letter-spacing: 1px;
745
+ }
746
+
747
+ .rune-list {
748
+ color: hsl(45, 100%, 45%);
749
+ font-family: 'AvQuest';
750
+ font-size: 1.1em;
751
+ font-weight: 700;
752
+ letter-spacing: 1px;
753
+ }
754
+
755
+ .item-type-list {
756
+ color: hsl(0, 0%, 76%);
757
+ font-size: 1.1em;
758
+ letter-spacing: 1px;
759
+ }
760
+
761
+ .magic-mod {
762
+ color: hsl(216, 100%, 66%);
763
+ }
764
+
765
+ .magic-mod-light {
766
+ color: hsl(290, 100%, 50%);
767
+ }
768
+
769
+ .magic-mod .header{
770
+ color: hsl(216, 98%, 82%);
771
+ }
772
+
773
+ .ladder-tag {
774
+ position: absolute;
775
+ top: 0.5em;
776
+ left: 0.75em;
777
+ font-size: 0.69em;
778
+ color: hsl(0, 0%, 76%);
779
+ }
780
+
781
+ </style>