-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathitems.lua
365 lines (329 loc) · 13.1 KB
/
items.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
---@class AddonEnv
local _addon = select(2, ...);
local ITEM_SLOTS = {
[1] = "head",
[2] = "neck",
[3] = "shoulder",
[5] = "chest",
[6] = "waist",
[7] = "legs",
[8] = "feet",
[9] = "wrist",
[10] = "hands",
[11] = "finger 1",
[12] = "finger 2",
[13] = "trinket 1",
[14] = "trinket 2",
[15] = "back",
[16] = "main hand",
[17] = "off hand",
[18] = "ranged",
};
---@type table<integer,boolean|integer,nil>
local missingItems = { _count = 0 };
---@type table<integer, integer>
local items = {};
---@type table<integer, integer>
local sets = {};
---@type table<string, integer|nil>
local weaponSubClass = {
mainhand = nil,
offhand = nil,
ranged = nil
};
local SetWeaponBaseDmgAndSpeed;
do
local scanTT = CreateFrame("GameTooltip", "SpellCalcItemsScanTT", nil, "GameTooltipTemplate")--[[@as WoWGameTooltip]];
scanTT:SetOwner(WorldFrame, "ANCHOR_CURSOR");
scanTT:AddFontStrings(
scanTT:CreateFontString("$parentTextLeft1", nil, "GameTooltipText"),
scanTT:CreateFontString("$parentTextRight1", nil, "GameTooltipText"));
---Set weapon attack speed and base damage from tooltip.
---@param itemId integer|nil
---@param key string mainhand, offhand or ranged
function SetWeaponBaseDmgAndSpeed(itemId, key)
local speed = 2;
local dmgLow = 1;
local dmgHigh = 1;
if itemId then
scanTT:ClearLines();
scanTT:SetOwner(WorldFrame, "ANCHOR_CURSOR");
scanTT:SetHyperlink("item:" .. itemId .. ":0:0:0:0:0:0:0");
for i = 1, scanTT:NumLines() do
local fstringl = _G["SpellCalcItemsScanTTTextLeft" .. i]--[[@as FontString]];
local fstringr = _G["SpellCalcItemsScanTTTextRight" .. i]--[[@as FontString]];
if fstringl and fstringr then
local textl = fstringl:GetText();
local textr = fstringr:GetText();
if textl and textr then
local dls, dhs = strmatch(textl, "(%d+) %- (%d+)");
local dl = tonumber(dls);
local dh = tonumber(dhs);
local s = tonumber(strmatch(textr, " (%d%.%d%d)"));
if dl and dh and s then
speed = s;
dmgLow = dl;
dmgHigh = dh;
break;
end
end
end
end
scanTT:Hide();
end
local s = _addon.stats;
s.baseAttackSpeed[key] = speed;
s.weaponBaseDamage[key].min = dmgLow;
s.weaponBaseDamage[key].max = dmgHigh;
end
end
--- Update set item count and add/remove buffs as needed
---@param setId integer
---@param change integer
local function UpdateSet(setId, change)
if sets[setId] == nil then
sets[setId] = change;
else
sets[setId] = sets[setId] + change;
end
local newCount, oldCount = sets[setId], sets[setId] - change;
local setData = _addon.itemSetData[setId];
_addon.util.PrintDebug(("Updating set: %s (%d -> %d)"):format(setData.name, oldCount, newCount));
for k, effectData in ipairs(setData.effects) do
local bname = string.format("%s%d-%dp", setData.name, k, effectData.need);
if effectData.need <= newCount then
if effectData.need > oldCount then
_addon.util.PrintDebug(("Add set bonus %s"):format(bname));
_addon:ApplyAuraEffect(bname, effectData, effectData.value, -1, true);
end
else
if effectData.need <= oldCount then
_addon.util.PrintDebug(("Remove set bonus %s"):format(bname));
_addon:RemoveAuraEffect(bname, effectData, effectData.value, -1, true);
end
end
end
end
--- Equip item for slot
---@param itemId integer
---@param slotId integer
local function EquipItem(itemId, slotId)
_addon.util.PrintDebug("Item " .. itemId .. " -> Slot " .. slotId);
local itemName, _, _, _, _, _, itemSubTypeName, _, _, _, _, classID, subclassID = GetItemInfo(itemId);
local setId = _addon.setItemData[itemId];
if itemName == nil then
if not missingItems[itemId] then
missingItems[itemId] = true;
missingItems._count = missingItems._count + 1;
_addon.util.PrintDebug("Don't have data for item " .. itemId .. " slot " .. slotId .. ", waiting for data");
end
return;
end
if slotId >= 16 and slotId <= 18 then
if classID == LE_ITEM_CLASS_WEAPON then
_addon.util.PrintDebug(ITEM_SLOTS[slotId] .. " is now " .. itemSubTypeName);
if slotId == 16 then
weaponSubClass.mainhand = subclassID;
SetWeaponBaseDmgAndSpeed(itemId, "mainhand");
elseif slotId == 17 then
weaponSubClass.offhand = subclassID;
SetWeaponBaseDmgAndSpeed(itemId, "offhand");
elseif slotId == 18 then
weaponSubClass.ranged = subclassID;
SetWeaponBaseDmgAndSpeed(itemId, "ranged");
end
else
_addon.util.PrintDebug(ITEM_SLOTS[slotId] .. " is not a weapon, leaving empty");
if slotId == 16 then
weaponSubClass.mainhand = nil;
SetWeaponBaseDmgAndSpeed(nil, "mainhand");
elseif slotId == 17 then
weaponSubClass.offhand = nil;
SetWeaponBaseDmgAndSpeed(nil, "offhand");
elseif slotId == 18 then
weaponSubClass.ranged = nil;
SetWeaponBaseDmgAndSpeed(nil, "ranged");
end
end
end
if _addon.itemEffects[itemId] then
for _, effect in ipairs(_addon.itemEffects[itemId]) do
_addon:ApplyAuraEffect(itemName, effect, effect.value, -1, true);
end
_addon:TriggerUpdate();
end
if setId then
_addon.util.PrintDebug("Item is in a set");
UpdateSet(setId, 1);
end
items[slotId] = itemId;
end
--- Unequip previously equipped item
---@param slotId integer
local function UnequipItem(slotId)
local itemName = GetItemInfo(items[slotId]);
local setId = _addon.setItemData[items[slotId]];
if _addon.itemEffects[items[slotId]] then
for _, effect in ipairs(_addon.itemEffects[items[slotId]]) do
_addon:RemoveAuraEffect(itemName, effect, effect.value, -1, true);
end
_addon:TriggerUpdate();
end
if setId then
_addon.util.PrintDebug("Item was in a set");
UpdateSet(setId, -1);
end
items[slotId] = nil;
if slotId >= 16 and slotId <= 18 then
_addon.util.PrintDebug(ITEM_SLOTS[slotId] .. " is now unarmed");
if slotId == 16 then
weaponSubClass.mainhand = nil;
SetWeaponBaseDmgAndSpeed(nil, "mainhand");
elseif slotId == 17 then
weaponSubClass.offhand = nil;
SetWeaponBaseDmgAndSpeed(nil, "offhand");
elseif slotId == 18 then
weaponSubClass.ranged = nil;
SetWeaponBaseDmgAndSpeed(nil, "ranged");
end
end
_addon.util.PrintDebug("Slot " .. slotId .. " empty");
end
--- Update all item slots
local function UpdateItems()
_addon.util.PrintDebug("Update items");
for slot, slotName in pairs(ITEM_SLOTS) do
local itemId = GetInventoryItemID("player", slot);
if itemId ~= nil then
local durability = GetInventoryItemDurability(slot);
if items[slot] ~= itemId then
if items[slot] ~= nil then
_addon.util.PrintDebug("Unequip old item from slot " .. slotName);
UnequipItem(slot);
end
if durability == nil or durability > 0 then
_addon.util.PrintDebug("Equip new item " .. itemId .. " in slot " .. slotName);
EquipItem(itemId, slot);
end
else
if durability ~= nil and durability == 0 then
_addon.util.PrintDebug("Unequip item " .. itemId .. " in slot " .. slotName .. " because it is broken");
UnequipItem(slot);
end
end
else
if items[slot] ~= nil then
_addon.util.PrintDebug("Unequip old item from slot " .. slotName);
UnequipItem(slot);
end
end
end
-- Handle meta gems.
-- 99 is used for meta gem item slot ID internally.
if items[1] then
local gem1p, gem2p, gem3p = strmatch(GetInventoryItemLink("player", 1), "Hitem:%d+:%d*:(%d*):(%d*):(%d*):");
local gem1 = tonumber(gem1p)--[[@as integer]];
local gem2 = tonumber(gem2p)--[[@as integer]];
local gem3 = tonumber(gem3p)--[[@as integer]];
local gemId;
-- Only meta gems should ever have item effects defined.
if _addon.itemEffects[gem1] then
gemId = gem1;
elseif _addon.itemEffects[gem2] then
gemId = gem2;
elseif _addon.itemEffects[gem3] then
gemId = gem3;
end
if items[99] ~= gemId then
if items[99] ~= nil then
UnequipItem(99);
end
if gemId then
EquipItem(gemId, 99);
end
end
elseif items[99] then
UnequipItem(99);
end
end
_addon.events.Register("UPDATE_INVENTORY_DURABILITY", function () UpdateItems() end);
_addon.events.Register("PLAYER_ENTERING_WORLD", function() UpdateItems() end);
_addon.events.Register("UNIT_INVENTORY_CHANGED", function(unit) if unit == "player" then UpdateItems() end end);
---Handle item update after recieved item data.
---@param itemId integer
_addon.events.Register("GET_ITEM_INFO_RECEIVED", function (itemId)
if missingItems[itemId] then
missingItems[itemId] = 0;
missingItems._count = missingItems._count - 1;
if missingItems._count == 0 then
_addon.util.PrintDebug("Data for all items recieved, updating items");
UpdateItems();
end
end
end);
--- Return true if a two handed weapon is in the main hand slot
function _addon:IsTwoHandEquipped()
return weaponSubClass.mainhand and
bit.band(bit.lshift(1, weaponSubClass.mainhand), self.CONST.WEAPON_TYPES_MASK.TWO_HAND) > 0;
end
--- Return true if a one handed weapon is in the main hand slot
function _addon:IsOneHandEquipped()
return weaponSubClass.mainhand and
bit.band(bit.lshift(1, weaponSubClass.mainhand), self.CONST.WEAPON_TYPES_MASK.ONE_HAND) > 0;
end
--- Return true if a weapon is in the mainhand slot
function _addon:IsMainHandWeaponEquipped()
return weaponSubClass.mainhand and bit.band(bit.lshift(1, weaponSubClass.mainhand), self.CONST.WEAPON_TYPES_MASK.MELEE) > 0;
end
--- Return true if a weapon is in the offhand slot
function _addon:IsOffHandWeaponEquipped()
return weaponSubClass.offhand and bit.band(bit.lshift(1, weaponSubClass.offhand), self.CONST.WEAPON_TYPES_MASK.MELEE) > 0;
end
--- Return true if the given weapon class is equipped
---@param weaponSubClassId integer
---@param slot string mainhand, offhand or ranged
function _addon:IsWeaponTypeEquipped(weaponSubClassId, slot)
assert(slot == "mainhand" or slot == "offhand" or slot == "ranged", "Invalid weapon slot!");
return weaponSubClass[slot] == weaponSubClassId;
end
---Check if weapon types are equipped.
---@param weaponSubClassMask integer The mask of possible weapontypes
---@param slot string mainhand, offhand or ranged
---@return boolean
function _addon:IsWeaponTypeMaskEquipped(weaponSubClassMask, slot)
if slot then
assert(slot == "mainhand" or slot == "offhand" or slot == "ranged", "Invalid weapon slot!");
return weaponSubClass[slot] ~= nil and bit.band(bit.lshift(1, weaponSubClass[slot]), weaponSubClassMask) > 0;
end
for _, subClass in pairs(weaponSubClass) do
if bit.band(bit.lshift(1, subClass), weaponSubClassMask) > 0 then
return true;
end
end
return false;
end
--- Return true if both weapon slots have a weapon equipped
function _addon:IsDualWieldEquipped()
return self:IsMainHandWeaponEquipped() and self:IsOffHandWeaponEquipped();
end
--- Return WEAPON_SUBCLASS for weapon in given slot if a weapon is equipped
---@param slot string @mainhand, offhand or ranged
---@return integer|nil
function _addon:GetWeaponType(slot)
assert(slot == "mainhand" or slot == "offhand" or slot == "ranged", "Invalid weapon slot!");
return weaponSubClass[slot];
end
--- Return WEAPON_TYPES_MASK for weapon in given slot if a weapon is equipped
---@param slot string @mainhand, offhand or ranged
---@return integer|nil
function _addon:GetWeaponTypeMask(slot)
assert(slot == "mainhand" or slot == "offhand" or slot == "ranged", "Invalid weapon slot!");
if not weaponSubClass[slot] then return end
return bit.lshift(1, weaponSubClass[slot]);
end
--- (DEBUG) Equip item to test effects. It will just overwrite the slot!
---@param itemId integer
---@param slotId integer
function _addon:DebugEquipItem(itemId, slotId)
EquipItem(itemId, slotId)
end