Merge branch 'master' of zorba@192.168.100.11:questhelper
[QuestHelper.git] / collect_loot.lua
blobf126b55ff5bd6c7d94e1efab3f7edcc8a9c5cc03
1 QuestHelper_File["collect_loot.lua"] = "Development Version"
2 QuestHelper_Loadtime["collect_loot.lua"] = GetTime()
4 if not UNIT_SKINNABLE_BOLTS then return end -- This just drops us out early if the user is using 2.4.3, otherwise we get a weird error message that isn't the one we're intending to get.
6 local debug_output = false
7 if QuestHelper_File["collect_loot.lua"] == "Development Version" then debug_output = true end
9 local QHC
11 local GetMonsterUID
12 local GetMonsterType
14 local GetItemType
16 local GetLoc
18 local Patterns
20 local members = {}
21 local members_count = 0
22 local members_refs = {} -- "raid6" and the like
24 local function MembersUpdate()
25 -- We want to keep track of exactly who is in a group with this player, so we can watch for combat messages involving them, so we can see who's been tapped, so we can record the right deaths, so we can know who the player should be able to loot.
26 -- I hate my life.
27 -- >:(
29 local alone = false
31 --QuestHelper:TextOut("MU start")
32 members = {} -- we burn a table every time this updates, but whatever
33 members_count = 0
34 if GetNumRaidMembers() > 0 then
35 -- we is in a raid
36 for i = 1, 40 do
37 local ite = string.format("raid%d", i)
38 local gud = UnitGUID(ite)
39 if gud then
40 members[gud] = true
41 table.insert(members_refs, ite)
42 --QuestHelper:TextOut(string.format("raid member %s added", UnitName(ite)))
43 end
44 end
45 elseif GetNumPartyMembers() > 0 then
46 -- we is in a party
47 for i = 1, 4 do
48 local ite = string.format("party%d", i)
49 local gud = UnitGUID(ite)
50 if gud then
51 members[gud] = true
52 table.insert(members_refs, ite)
53 --QuestHelper:TextOut(string.format("party member %s added", UnitName(ite)))
54 end
55 end
56 members[UnitGUID("player")] = true
57 table.insert(members_refs, "player")
58 --QuestHelper:TextOut(string.format("player %s added", UnitName("player")))
59 else
60 -- we is alone ;.;
61 if UnitGUID("player") then members[UnitGUID("player")] = true end -- it's possible that we haven't logged in entirely yet
62 table.insert(members_refs, "player")
63 --QuestHelper:TextOut(string.format("player %s added as %s", UnitName("player"), tostring(UnitGUID("player"))))
64 alone = true
65 end
67 if GetLootMethod() == "master" and not alone then members = {} members_refs = {} end -- We're not going to bother trying to deal with master loot right now - it's just too different and I just don't care enough.
69 for _, _ in pairs(members) do members_count = members_count + 1 end -- lulz
70 end
72 local MS_TAPPED_US = 1
73 local MS_TAPPED_OTHER = 2
74 local MS_TAPPED_LOOTABLE = 3
75 local MS_TAPPED_LOOTED = 4
77 local last_cleanup = GetTime()
79 local monsterstate = {}
80 local monsterrefresh = {}
81 local monstertimeout = {}
83 -- This all does something quite horrible.
84 -- Some monsters don't become lootable when they're killed and didn't drop anything. We need to record this so we can get real numbers for them.
85 -- Unfortunately, we can't just record when "something" is killed. We have to record when "our group" killed it, so we know that there *was* a chance of looting it.
86 -- As such, we need to check for monster deaths that the player may never have actually targeted. It gets, to put it mildly, grim, and unfortunately we'll never be able to solve it entirely.
87 -- Worse, we need to *not* record item drops for things that we never actually "saw" but that were lootable anyway, because if we do, we bias the results towards positive (i.e. if we AOE ten monsters down, and two of them drop, and we loot those, that's 2/2 if we record the drops, and 0/0 if we don't, while what we really want is 2/10. 0/0 is at least "not wrong".)
88 -- On top of this, we want to avoid looting "discarded items", but unfortunately there's no real good way to determine this. Welp.
89 local function CombatLogEvent(_, event, sourceguid, _, _, destguid, _, _, _, spellname)
90 -- There's many things that are handled here.
91 -- First, if there's any damage messages coming either to or from a party member, we check to see if that monster is tapped by us. If it's tapped, we cache the value for 15 seconds, expiring entirely in 30.
92 -- Second, there's the Death message. If it's tapped by us, increases the kill count by 1/partymembers and changes its state to lootable.
93 if event ~= "UNIT_DIED" then
94 -- Something has been attacked by something, maybe.
95 if not string.find(event, "_DAMAGE$") then return end -- We only care about something punching something else.
97 local target
98 if members[sourceguid] then target = destguid elseif members[destguid] then target = sourceguid end -- If one of the items is in our party, the other is our target.
99 if not target then return end -- If we don't have a target, then nobody is in our party, and we don't care.
101 if monsterrefresh[target] and monsterrefresh[target] > GetTime() then return end -- we already have fresh data, so we're good
103 -- Now comes the tricky part. We can't just look at the target because we're not allowed to target by GUID. So we iterate through all the party/raid members, and their pets, and hope *someone* has it targeted. Luckily, we can stop once we find someone who does.
104 local targ
105 for _, v in pairs(members_refs) do
106 targ = v .. "target" if UnitGUID(targ) == target then break end
107 targ = v .. "pettarget" if UnitGUID(targ) == target then break end
108 targ = nil
111 if not targ then return end -- Well, nobody seems to be targeting it. That's . . . odd, and annoying. We'll take a look at it next combat message, I suppose.
113 -- Okay. So we know who's targeting it. Now, let's see who has it tapped, if anyone.
114 if not UnitIsTapped(targ) then
115 -- Great. Nobody is. That is just *great*. Look how exuberant I feel at this moment.
116 monsterstate[target] = nil
117 monsterrefresh[target] = nil
118 monstertimeout[target] = nil
119 --QuestHelper:TextOut(string.format("Monster ignorified"))
120 else
121 -- We know someone is, so we're going to set up our caching . . .
122 monsterrefresh[target] = GetTime() + 15
123 monstertimeout[target] = GetTime() + 30
124 monsterstate[target] = (UnitIsTappedByPlayer(targ) and not UnitIsTrivial(targ)) and MS_TAPPED_US or MS_TAPPED_OTHER -- and figure out if it's us. Or if it's trivial. we ignore it if it's trivial, since it's much less likely to be looted and that could throw off our numbers
125 --QuestHelper:TextOut(string.format("Monster %s set to %s", target, (monsterstate[target] == MS_TAPPED_US) and "MS_TAPPED_US" or "MS_TAPPED_OTHER"))
128 -- DONE
129 else
130 -- It's dead. Hooray!
132 if monsterstate[destguid] and monstertimeout[destguid] > GetTime() and monsterstate[destguid] == MS_TAPPED_US and members_count > 0 then -- yaaay
133 local type = GetMonsterType(destguid)
134 if not QHC.monster[type] then QHC.monster[type] = {} end
135 QHC.monster[type].kills = (QHC.monster[type].kills or 0) + 1 / members_count -- Hopefully, most people loot their kills. Divide by members_count 'cause there's a 1/members chance that we get to loot.
137 monsterstate[destguid] = MS_TAPPED_LOOTABLE
138 monsterrefresh[destguid] = GetTime() + 600
139 monstertimeout[destguid] = GetTime() + 600
140 --QuestHelper:TextOut(string.format("Tapped monster %s slain, set to lootable", destguid))
141 else
142 monsterstate[destguid] = nil
143 monsterrefresh[destguid] = nil
144 monstertimeout[destguid] = nil
145 --QuestHelper:TextOut(string.format("Untapped monster %s slain, cleared", destguid))
150 local skintypes = {}
152 skintypes[UNIT_SKINNABLE_ROCK] = "mine"
153 skintypes[UNIT_SKINNABLE_HERB] = "herb"
154 skintypes[UNIT_SKINNABLE_BOLTS] = "eng"
155 skintypes[UNIT_SKINNABLE_LEATHER] = "skin"
157 local function SkinnableflagsTooltipy(self, ...)
158 --QuestHelper:TextOut("tooltipy")
159 if UnitExists("mouseover") and UnitIsVisible("mouseover") and not UnitIsPlayer("mouseover") and not UnitPlayerControlled("mouseover") and UnitIsDead("mouseover") then
160 local guid = UnitGUID("mouseover")
161 --QuestHelper:TextOut("critar")
162 if not monsterstate[guid] or monsterstate[guid] ~= MS_TAPPED_LOOTED or monsterrefresh[guid] > GetTime() then return end
163 --QuestHelper:TextOut("runin")
165 local cid = GetMonsterType(guid)
167 local skintype = nil
169 local lines = GameTooltip:NumLines()
170 for i = 3, lines do
171 --QuestHelper:TextOut(_G["GameTooltipTextLeft" .. tostring(i)]:GetText())
172 local skeen = skintypes[_G["GameTooltipTextLeft" .. tostring(i)]:GetText()]
173 if skeen then QuestHelper: Assert(not skintype) skintype = skeen end
176 if not QHC.monster[cid] then QHC.monster[cid] = {} end
177 local qhci = QHC.monster[cid]
179 for _, v in pairs(skintypes) do
180 if v == skintype then
181 --QuestHelper:TextOut(v .. "_yes")
182 qhci[v .. "_yes"] = (qhci[v .. "_yes"] or 0) + 1
183 else
184 --QuestHelper:TextOut(v .. "_no")
185 qhci[v .. "_no"] = (qhci[v .. "_no"] or 0) + 1
191 -- Logic behind this module:
192 -- Watch for the spell to be sent
193 -- Watch for it to start
194 -- Check out the combat log and see what GUID we get
195 -- If the GUID is null, we're targeting an object, otherwise, we're targeting a critter
196 -- Wait for spell to succeed
197 -- If anything doesn't synch up, or the spell is interrupted, nil out all these items.
198 -- We've got a little special case for pickpocketing, because people often use macros, so we detect that case specifically.
200 local PP_PHASE_IDLE
201 local PP_PHASE_SENT
202 local PP_PHASE_COMPLETE
204 local pickpocket_phase = PP_PHASE_IDLE
205 local pickpocket_target
206 local pickpocket_otarget_guid
207 local pickpocket_timestamp
209 local pickpocket_name = GetSpellInfo(921) -- this is the pickpocket spell ID
211 local function pp_reset()
212 pickpocket_target, pickpocket_otarget_guid, pickpocket_timestamp, pickpocket_phase = nil, nil, nil, PP_PHASE_IDLE
214 pp_reset()
216 local function PPSent(player, spell, _, target)
217 if player ~= "player" then return end
218 if spell ~= pickpocket_name then return end
219 if UnitName("target") ~= target then return end -- DENY
221 pickpocket_timestamp, pickpocket_target, pickpocket_otarget_guid, pickpocket_phase = GetTime(), target, UnitGUID("target"), PP_PHASE_SENT
224 local function PPSucceed(player, spell, rank)
225 if player ~= "player" then return end
226 if spell ~= pickpocket_name then return end
228 if pickpocket_phase ~= PP_PHASE_SENT and (not pickpocket_otarget_guid or last_timestamp + 1 < GetTime()) then
229 pp_reset()
230 return
233 pickpocket_timestamp, pickpocket_phase = GetTime(), PP_PHASE_COMPLETE
237 -- This segment deals with openable containers
239 local touched_itemid
240 local touched_timestamp
242 local function ItemLock(bag, slot)
243 if not bag or not slot then return end -- probably changing equipment
245 local _, _, locked = GetContainerItemInfo(bag, slot)
246 --QuestHelper:TextOut(string.format("trying lock %s", tostring(locked)))
247 if locked then
248 touched_itemid = GetItemType(GetContainerItemLink(bag, slot))
249 --[[QuestHelper:TextOut(string.format("trying lock %s", tostring(touched_itemid)))
250 QuestHelper:TextOut(string.format("trying lock %s", tostring(type(touched_itemid))))
251 QuestHelper:TextOut(string.format("trying lock %s", tostring(QHC.item[touched_itemid].open_yes)))
252 QuestHelper:TextOut(string.format("trying lock %s", tostring(QHC.item[touched_itemid].open_no)))]]
253 touched_timestamp = GetTime()
254 if not QHC.item[touched_itemid] or (QHC.item[touched_itemid].open_yes or 0) <= (QHC.item[touched_itemid].open_no or 0) then
255 touched_itemid = nil
256 touched_timestamp = nil
258 else
259 touched_itemid = nil
260 touched_timestamp = nil
265 -- Here's the segment for longer spells. There aren't any instant spells we currently care about, besides pickpocketing. This will probably change eventually (arrows in the DK starting zone?)
267 local LAST_PHASE_IDLE = 0
268 local LAST_PHASE_SENT = 1
269 local LAST_PHASE_START = 2
270 local LAST_PHASE_COMBATLOG = 3
271 local LAST_PHASE_COMPLETE = 4
273 local last_phase = LAST_PHASE_IDLE
274 local last_spell
275 local last_rank
276 local last_target
277 local last_target_guid
278 local last_otarget
279 local last_otarget_guid
280 local last_timestamp
281 local last_succeed = false
282 local last_succeed_trade = GetTime()
284 local gathereffects = {}
286 gathereffects[GetSpellInfo(51306)] = {token = "eng", noclog = true}
287 gathereffects[GetSpellInfo(32606)] = {token = "mine"}
288 gathereffects[GetSpellInfo(2366)] = {token = "herb"}
289 gathereffects[GetSpellInfo(8613)] = {token = "skin"}
290 gathereffects[GetSpellInfo(21248)] = {token = "open", noclog = true}
291 gathereffects[GetSpellInfo(30427)] = {token = "extract", noclog = true} -- not a loot window, so it won't really work, but hey
292 gathereffects[GetSpellInfo(13262)] = {token = "de", noclog = true, ignore = true}
293 gathereffects[GetSpellInfo(31252)] = {token = "prospect", noclog = true, ignore = true}
294 gathereffects[GetSpellInfo(51005)] = {token = "mill", noclog = true, ignore = true}
298 local function last_reset()
299 last_timestamp, last_spell, last_rank, last_target, last_target_guid, last_otarget, last_otarget_guid, last_succeed, last_phase = nil, nil, nil, nil, nil, nil, false, LAST_PHASE_IDLE
301 last_reset()
303 -- This all doesn't work with instant spells. Luckily, I don't care about instant spells (yet).
304 local function SpellSent(player, spell, rank, target)
305 if player ~= "player" then return end
307 last_timestamp, last_spell, last_rank, last_target, last_target_guid, last_otarget, last_otarget_guid, last_succeed, last_phase = GetTime(), spell, rank, target, nil, UnitName("target"), UnitGUID("target"), false, LAST_PHASE_SENT
309 if last_otarget and last_otarget ~= last_target then last_reset() return end
311 --QuestHelper:TextOut(string.format("ss %s", spell))
314 local function SpellStart(player, spell, rank)
315 if player ~= "player" then return end
317 if spell ~= last_spell or rank ~= last_rank or last_target_guid or last_phase ~= LAST_PHASE_SENT or last_timestamp + 1 < GetTime() then
318 last_reset()
319 else
320 --QuestHelper:TextOut(string.format("sst %s", spell))
321 last_timestamp, last_phase = GetTime(), LAST_PHASE_START
325 local function SpellCombatLog(_, event, sourceguid, _, _, destguid, _, _, _, spellname)
326 if event ~= "SPELL_CAST_START" then return end
328 if sourceguid ~= UnitGUID("player") then return end
330 --QuestHelper:TextOut(string.format("cle_ss enter %s %s %s %s", tostring(spellname ~= last_spell), tostring(not last_target), tostring(not not last_target_guid), tostring(last_timestamp + 1 < GetTime())))
332 if spellname ~= last_spell or not last_target or last_target_guid or last_timestamp + 1 < GetTime() then
333 last_reset()
334 return
337 --QuestHelper:TextOut("cle_ss enter")
339 if last_phase ~= LAST_PHASE_START then
340 last_reset()
341 return
344 --QuestHelper:TextOut(string.format("cesst %s", spellname))
345 last_timestamp, last_target_guid, last_phase = GetTime(), destguid, LAST_PHASE_COMBATLOG
347 if last_target_guid == "0x0000000000000000" then last_target_guid = nil end
348 if last_target_guid and last_target_guid ~= last_otarget_guid then last_reset() return end
351 local function SpellSucceed(player, spell, rank)
352 if player ~= "player" then return end
354 if gathereffects[spell] then last_succeed_trade = GetTime() end
356 --QuestHelper:TextOut(string.format("sscu enter %s %s %s %s %s", tostring(last_spell), tostring(last_target), tostring(last_rank), tostring(spell), tostring(rank)))
358 if not last_spell or not last_target or last_spell ~= spell or last_rank ~= rank then
359 last_reset()
360 return
363 --QuestHelper:TextOut("sscu enter")
365 if gathereffects[spell] and gathereffects[spell].noclog then
366 if last_phase ~= LAST_PHASE_START or last_timestamp + 10 < GetTime() then
367 last_reset()
368 return
370 else
371 if last_phase ~= LAST_PHASE_COMBATLOG or last_timestamp + 10 < GetTime() then
372 last_reset()
373 return
377 --QuestHelper:TextOut(string.format("sscu %s, %d, %s, %s", spell, last_phase, tostring(last_phase == LAST_PHASE_SENT), tostring((last_phase == LAST_PHASE_SENT) and LAST_PHASE_SHORT_SUCCEEDED)))
378 last_timestamp, last_succeed, last_phase = GetTime(), true, LAST_PHASE_COMPLETE
379 --QuestHelper:TextOut(string.format("last_phase %d", last_phase))
381 --[[if last_phase == LAST_PHASE_COMPLETE then
382 QuestHelper:TextOut(string.format("spell succeeded, casting %s %s on %s/%s", last_spell, last_rank, tostring(last_target), tostring(last_target_guid)))
383 end]]
386 local function SpellInterrupt(player, spell, rank)
387 if player ~= "player" then return end
389 -- I don't care what they were casting, they're certainly not doing it now
390 --QuestHelper:TextOut(string.format("si %s", spell))
391 last_reset()
394 local function LootOpened()
396 -- We're cleaning up the monster charts here, on the theory that if someone is looting, they're okay with a tiny lag spike.
397 if last_cleanup + 300 < GetTime() then
398 local cleanup = {}
399 for k, v in pairs(monstertimeout) do
400 if v < GetTime() then table.insert(cleanup, k) end
403 for _, v in pairs(cleanup) do
404 monsterstate[v] = nil
405 monsterrefresh[v] = nil
406 monstertimeout[v] = nil
410 -- First off, we try to figure out where the hell these items came from.
412 --QuestHelper:TextOut(string.format("%s %s %s", tostring(last_phase == LAST_PHASE_COMPLETE), tostring(last_spell == "Mining"), tostring(last_timestamp + 1 > GetTime())))
413 --QuestHelper:TextOut(string.format("%s %s %s", tostring(last_phase == LAST_PHASE_COMPLETE), tostring(last_spell == "Mining"), tostring(last_timestamp + 1 > GetTime())))
415 --if last_timestamp then QuestHelper:TextOut(string.format("%s %s %s", tostring(last_phase == LAST_PHASE_COMPLETE), tostring(last_spell == "Mining"), tostring(last_timestamp + 1 > GetTime()))) else QuestHelper:TextOut("timmy") end
416 --if pickpocket_phase == PP_PHASE_COMPLETE then QuestHelper:TextOut(string.format("%s", tostring(pickpocket_timestamp))) else QuestHelper:TextOut("nein") end
418 --local beef = string.format("%s/%s %s/%s", tostring(last_target), tostring(last_target_guid), tostring(last_otarget), tostring(last_otarget_guid))
420 local spot = nil
421 local prefix = nil
423 if IsFishingLoot() then
424 -- yaaaaay
425 if debug_output then QuestHelper:TextOut("Fishing loot") end
426 local loc = GetLoc()
427 if not QHC.fishing[loc] then QHC.fishing[loc] = {} end
428 spot = QHC.fishing[loc]
429 prefix = "fish"
431 elseif pickpocket_phase == PP_PHASE_COMPLETE and pickpocket_timestamp and pickpocket_timestamp + 1 > GetTime() and UnitGUID("target") == pickpocket_otarget_guid then
432 if debug_output then QuestHelper:TextOut(string.format("Pickpocketing from %s/%s", pickpocket_target, UnitName("target"), UnitGUID("target"))) end
433 local mid = GetMonsterType(UnitGUID("target"))
434 if not QHC.monster[mid] then QHC.monster[mid] = {} end
435 spot = QHC.monster[mid]
436 prefix = "rob"
438 elseif last_phase == LAST_PHASE_COMPLETE and gathereffects[last_spell] and last_timestamp + 1 > GetTime() then
439 local beef = string.format("%s/%s %s/%s", tostring(last_target), tostring(last_target_guid), tostring(last_otarget), tostring(last_otarget_guid))
441 if gathereffects[last_spell].ignore then return end
443 prefix = gathereffects[last_spell].token
445 -- this one is sort of grim actually
446 -- If we have an last_otarget_guid, it's the right one, and it's a monster
447 -- If we don't, use last_target, and it's an object
448 -- This is probably going to be buggy. Welp.
449 if last_otarget_guid then
450 if debug_output then QuestHelper:TextOut(string.format("%s from monster %s", gathereffects[last_spell].token, beef)) end
451 local mid = GetMonsterType(last_otarget_guid)
452 if not QHC.monster[mid] then QHC.monster[mid] = {} end
453 spot = QHC.monster[mid]
454 else
455 if debug_output then QuestHelper:TextOut(string.format("%s from object %s", gathereffects[last_spell].token, beef)) end
456 if not QHC.object[last_target] then QHC.object[last_target] = {} end
457 spot = QHC.object[last_target]
460 elseif touched_timestamp and touched_timestamp + 1 > GetTime() then
461 -- Opening a container, possibly
462 if debug_output then QuestHelper:TextOut(string.format("Opening container %d", touched_itemid)) end
463 if not QHC.item[touched_itemid] then QHC.item[touched_itemid] = {} end
464 spot = QHC.item[touched_itemid]
465 prefix = "open"
467 elseif UnitGUID("target") and monsterstate[UnitGUID("target")] == MS_TAPPED_LOOTABLE and monstertimeout[UnitGUID("target")] > GetTime() and (not pickpocket_timestamp or pickpocket_timestamp + 5 < GetTime()) and (not last_timestamp or last_timestamp + 5 < GetTime()) and (last_succeed_trade + 5 < GetTime()) then
468 -- Monster is lootable, so we loot the monster
469 if debug_output then QuestHelper:TextOut(string.format("Monsterloot from %s/%s", UnitName("target"), UnitGUID("target"))) end
470 monsterstate[UnitGUID("target")] = MS_TAPPED_LOOTED
471 monstertimeout[UnitGUID("target")] = GetTime() + 300
472 monsterrefresh[UnitGUID("target")] = GetTime() + 2
473 local mid = GetMonsterType(UnitGUID("target"))
474 if not QHC.monster[mid] then QHC.monster[mid] = {} end
475 spot = QHC.monster[mid]
476 prefix = "loot"
478 else
479 if debug_output then QuestHelper:TextOut("Who knows") end -- ugh
480 local loc = GetLoc()
481 if not QHC.worldloot[loc] then QHC.worldloot[loc] = {} end
482 spot = QHC.worldloot[loc]
483 prefix = "loot"
489 local items = {}
490 items.gold = 0
491 for i = 1, GetNumLootItems() do
492 _, name, quant, _ = GetLootSlotInfo(i)
493 link = GetLootSlotLink(i)
494 if quant == 0 then
495 -- moneys
496 local _, _, amount = string.find(name, Patterns.GOLD_AMOUNT)
497 if amount then items.gold = items.gold + tonumber(amount) * 10000 end
499 local _, _, amount = string.find(name, Patterns.SILVER_AMOUNT)
500 if amount then items.gold = items.gold + tonumber(amount) * 100 end
502 local _, _, amount = string.find(name, Patterns.COPPER_AMOUNT)
503 if amount then items.gold = items.gold + tonumber(amount) * 1 end
504 else
505 local itype = GetItemType(link)
506 items[itype] = (items[itype] or 0) + quant
510 spot[prefix .. "_count"] = (spot[prefix .. "_count"] or 0) + 1
511 if not spot[prefix .. "_loot"] then spot[prefix .. "_loot"] = {} end
512 local pt = spot[prefix .. "_loot"]
513 for k, v in pairs(items) do
514 if v > 0 then pt[k] = (pt[k] or 0) + v end
518 function QH_Collect_Loot_Init(QHCData, API)
519 QHC = QHCData
521 if not QHC.monster then QHC.monster = {} end
522 if not QHC.worldloot then QHC.worldloot = {} end
523 if not QHC.fishing then QHC.fishing = {} end
524 if not QHC.item then QHC.item = {} end
526 API.Registrar_EventHook("PLAYER_ENTERING_WORLD", MembersUpdate)
527 API.Registrar_EventHook("RAID_ROSTER_UPDATE", MembersUpdate)
528 API.Registrar_EventHook("PARTY_MEMBERS_CHANGED", MembersUpdate)
529 API.Registrar_EventHook("COMBAT_LOG_EVENT_UNFILTERED", CombatLogEvent)
531 API.Registrar_EventHook("UPDATE_MOUSEOVER_UNIT", SkinnableflagsTooltipy)
533 API.Registrar_EventHook("UNIT_SPELLCAST_SENT", PPSent)
534 API.Registrar_EventHook("UNIT_SPELLCAST_SUCCEEDED", PPSucceed)
536 API.Registrar_EventHook("ITEM_LOCK_CHANGED", ItemLock)
538 API.Registrar_EventHook("UNIT_SPELLCAST_SENT", SpellSent)
539 API.Registrar_EventHook("UNIT_SPELLCAST_START", SpellStart)
540 API.Registrar_EventHook("COMBAT_LOG_EVENT_UNFILTERED", SpellCombatLog)
541 API.Registrar_EventHook("UNIT_SPELLCAST_SUCCEEDED", SpellSucceed)
542 API.Registrar_EventHook("UNIT_SPELLCAST_INTERRUPTED", SpellInterrupt)
544 API.Registrar_EventHook("LOOT_OPENED", LootOpened)
546 MembersUpdate() -- to get self, probably won't work but hey
548 GetMonsterUID = API.Utility_GetMonsterUID
549 GetMonsterType = API.Utility_GetMonsterType
550 GetItemType = API.Utility_GetItemType
551 QuestHelper: Assert(GetMonsterUID)
552 QuestHelper: Assert(GetMonsterType)
553 QuestHelper: Assert(GetItemType)
555 Patterns = API.Patterns
556 API.Patterns_RegisterNumber("GOLD_AMOUNT")
557 API.Patterns_RegisterNumber("SILVER_AMOUNT")
558 API.Patterns_RegisterNumber("COPPER_AMOUNT")
560 GetLoc = API.Callback_LocationBolusCurrent
561 QuestHelper: Assert(GetLoc)
563 -- What I want to know is whether it was tagged by me or my group when dead
564 -- Check target-of-each-groupmember? Once we see him tapped once, and by us, it's probably sufficient.
565 -- Notes:
566 --[[
568 COMBAT_LOG_EVENT_UNFILTERED arg2 UNIT_DIED, PLAYER_TARGET_CHANGED, LOOT_OPENED, (LOOT_CLOSED, [LOOT_SLOT_CLEARED, ITEM_PUSH, CHAT_MSG_LOOT]), PLAYER_TARGET_CHANGED, SPELLCAST_SENT, SPELLCAST_START, SUCCEEDED/INTERRUPTED, STOP, LOOT_OPENED (etc)
570 ITEM_PUSH can happen after LOOT_CLOSED, but it still happens.
571 Between LOOT_OPENED and LOOT_CLOSED, the lootable target is still targeted. Unsure what happens when looting items. LOOT_CLOSED triggers first if we target someone else.
572 ITEM_PUSH happens, then CHAT_MSG_LOOT. CHAT_MSG_LOOT includes quite a lot of potentially useful arguments.
573 PLAYER_TARGET_CHANGED before either looting or skinning.
574 SPELLCAST_SENT, SPELLCAST_START, SUCCEEDED/INTERRUPTED, STOP in that order. Arg4 on SENT seems to be the target's name. Arg4 on the others appears to be a unique identifier.
575 When started, we target the right thing. After that, we don't seem to. Check the combat log.