Mercurial > wow > wowdb-profiler
comparison Main.lua @ 393:7d0ad2573092
Added support for collecting loot from item containers with no loot window and no spells cast.
author | MMOSimca <MMOSimca@gmail.com> |
---|---|
date | Thu, 18 Dec 2014 21:38:11 -0500 |
parents | b00732fa9352 |
children | 930da8078de7 |
comparison
equal
deleted
inserted
replaced
392:f1952ed33a16 | 393:7d0ad2573092 |
---|---|
191 y = nil, | 191 y = nil, |
192 zone_data = nil, | 192 zone_data = nil, |
193 } | 193 } |
194 | 194 |
195 | 195 |
196 -- Timer prototypes | |
197 local ClearKilledNPC, ClearKilledBossID, ClearLootToastContainerID, ClearLootToastData, ClearChatLootData | |
198 | |
199 | |
196 -- HELPERS ------------------------------------------------------------ | 200 -- HELPERS ------------------------------------------------------------ |
197 | 201 |
198 local function Debug(message, ...) | 202 local function Debug(message, ...) |
199 if not DEBUGGING or not message then | 203 if not DEBUGGING or not message then |
200 return | 204 return |
210 else | 214 else |
211 _G.print(message) | 215 _G.print(message) |
212 end | 216 end |
213 end | 217 end |
214 | 218 |
215 | |
216 local TradeSkillExecutePer | |
217 do | |
218 local header_list = {} | |
219 | |
220 function TradeSkillExecutePer(iter_func) | |
221 if not _G.TradeSkillFrame or not _G.TradeSkillFrame:IsVisible() then | |
222 return | |
223 end | |
224 -- Clear the search box focus so the scan will have correct results. | |
225 local search_box = _G.TradeSkillFrameSearchBox | |
226 search_box:SetText("") | |
227 | |
228 _G.TradeSkillSearch_OnTextChanged(search_box) | |
229 search_box:ClearFocus() | |
230 search_box:GetScript("OnEditFocusLost")(search_box) | |
231 | |
232 table.wipe(header_list) | |
233 | |
234 -- Save the current state of the TradeSkillFrame so it can be restored after we muck with it. | |
235 local have_materials = _G.TradeSkillFrame.filterTbl.hasMaterials | |
236 local have_skillup = _G.TradeSkillFrame.filterTbl.hasSkillUp | |
237 | |
238 if have_materials then | |
239 _G.TradeSkillFrame.filterTbl.hasMaterials = false | |
240 _G.TradeSkillOnlyShowMakeable(false) | |
241 end | |
242 | |
243 if have_skillup then | |
244 _G.TradeSkillFrame.filterTbl.hasSkillUp = false | |
245 _G.TradeSkillOnlyShowSkillUps(false) | |
246 end | |
247 _G.SetTradeSkillInvSlotFilter(0, true, true) | |
248 _G.TradeSkillUpdateFilterBar() | |
249 _G.TradeSkillFrame_Update() | |
250 | |
251 -- Expand all headers so we can see all the recipes there are | |
252 for tradeskill_index = 1, _G.GetNumTradeSkills() do | |
253 local name, tradeskill_type, _, is_expanded = _G.GetTradeSkillInfo(tradeskill_index) | |
254 | |
255 if tradeskill_type == "header" or tradeskill_type == "subheader" then | |
256 if not is_expanded then | |
257 header_list[name] = true | |
258 _G.ExpandTradeSkillSubClass(tradeskill_index) | |
259 end | |
260 elseif iter_func(name, tradeskill_index) then | |
261 break | |
262 end | |
263 end | |
264 | |
265 -- Restore the state of the things we changed. | |
266 for tradeskill_index = 1, _G.GetNumTradeSkills() do | |
267 local name, tradeskill_type, _, is_expanded = _G.GetTradeSkillInfo(tradeskill_index) | |
268 | |
269 if header_list[name] then | |
270 _G.CollapseTradeSkillSubClass(tradeskill_index) | |
271 end | |
272 end | |
273 _G.TradeSkillFrame.filterTbl.hasMaterials = have_materials | |
274 _G.TradeSkillOnlyShowMakeable(have_materials) | |
275 _G.TradeSkillFrame.filterTbl.hasSkillUp = have_skillup | |
276 _G.TradeSkillOnlyShowSkillUps(have_skillup) | |
277 | |
278 _G.TradeSkillUpdateFilterBar() | |
279 _G.TradeSkillFrame_Update() | |
280 end | |
281 end -- do-block | |
282 | |
283 | |
284 local ActualCopperCost | |
285 do | |
286 local BARTERING_SPELL_ID = 83964 | |
287 | |
288 local STANDING_DISCOUNTS = { | |
289 HATED = 0, | |
290 HOSTILE = 0, | |
291 UNFRIENDLY = 0, | |
292 NEUTRAL = 0, | |
293 FRIENDLY = 0.05, | |
294 HONORED = 0.1, | |
295 REVERED = 0.15, | |
296 EXALTED = 0.2, | |
297 } | |
298 | |
299 | |
300 function ActualCopperCost(copper_cost, rep_standing) | |
301 if not copper_cost or copper_cost == 0 then | |
302 return 0 | |
303 end | |
304 local modifier = 1 | |
305 | |
306 if _G.IsSpellKnown(BARTERING_SPELL_ID) then | |
307 modifier = modifier - 0.1 | |
308 end | |
309 | |
310 if rep_standing then | |
311 if PLAYER_RACE == "Goblin" then | |
312 modifier = modifier - STANDING_DISCOUNTS["EXALTED"] | |
313 elseif STANDING_DISCOUNTS[rep_standing] then | |
314 modifier = modifier - STANDING_DISCOUNTS[rep_standing] | |
315 end | |
316 end | |
317 return math.floor(copper_cost / modifier) | |
318 end | |
319 end -- do-block | |
320 | |
321 | |
322 local function InstanceDifficultyToken() | |
323 local _, instance_type, instance_difficulty, _, _, _, is_dynamic = _G.GetInstanceInfo() | |
324 | |
325 if not instance_type or instance_type == "" then | |
326 instance_type = "NONE" | |
327 end | |
328 return ("%s:%d:%s"):format(instance_type:upper(), instance_difficulty, tostring(is_dynamic)) | |
329 end | |
330 | |
331 | |
332 local function DBEntry(data_type, unit_id) | |
333 if not data_type or not unit_id then | |
334 return | |
335 end | |
336 local category = global_db[data_type] | |
337 | |
338 if not category then | |
339 category = {} | |
340 global_db[data_type] = category | |
341 end | |
342 local unit = category[unit_id] | |
343 | |
344 if not unit then | |
345 unit = {} | |
346 category[unit_id] = unit | |
347 end | |
348 return unit | |
349 end | |
350 | |
351 private.DBEntry = DBEntry | |
352 | |
353 local NPCEntry | |
354 do | |
355 local npc_prototype = {} | |
356 local npc_meta = { | |
357 __index = npc_prototype | |
358 } | |
359 | |
360 function NPCEntry(identifier) | |
361 local npc = DBEntry("npcs", identifier) | |
362 return npc and _G.setmetatable(npc, npc_meta) or nil | |
363 end | |
364 | |
365 function npc_prototype:EncounterData(difficulty_token) | |
366 self.encounter_data = self.encounter_data or {} | |
367 self.encounter_data[difficulty_token] = self.encounter_data[difficulty_token] or {} | |
368 self.encounter_data[difficulty_token].stats = self.encounter_data[difficulty_token].stats or {} | |
369 | |
370 return self.encounter_data[difficulty_token] | |
371 end | |
372 end | |
373 | |
374 | |
375 local function CurrentLocationData() | |
376 if _G.GetCurrentMapAreaID() ~= current_area_id then | |
377 return _G.GetRealZoneText(), current_area_id, 0, 0, 0, InstanceDifficultyToken() | |
378 end | |
379 local map_level = _G.GetCurrentMapDungeonLevel() or 0 | |
380 local x, y = _G.GetPlayerMapPosition("player") | |
381 | |
382 x = x or 0 | |
383 y = y or 0 | |
384 | |
385 if x == 0 and y == 0 then | |
386 for level_index = 1, _G.GetNumDungeonMapLevels() do | |
387 _G.SetDungeonMapLevel(level_index) | |
388 x, y = _G.GetPlayerMapPosition("player") | |
389 | |
390 if x and y and (x > 0 or y > 0) then | |
391 _G.SetDungeonMapLevel(map_level) | |
392 map_level = level_index | |
393 break | |
394 end | |
395 end | |
396 end | |
397 | |
398 if _G.DungeonUsesTerrainMap() then | |
399 map_level = map_level - 1 | |
400 end | |
401 local x = _G.floor(x * 1000) | |
402 local y = _G.floor(y * 1000) | |
403 | |
404 if x % 2 ~= 0 then | |
405 x = x + 1 | |
406 end | |
407 | |
408 if y % 2 ~= 0 then | |
409 y = y + 1 | |
410 end | |
411 return _G.GetRealZoneText(), current_area_id, x, y, map_level, InstanceDifficultyToken() | |
412 end | |
413 | |
414 | |
415 local function CurrencyLinkToTexture(currency_link) | |
416 if not currency_link then | |
417 return | |
418 end | |
419 local _, _, texture_path = _G.GetCurrencyInfo(tonumber(currency_link:match("currency:(%d+)"))) | |
420 return texture_path:match("[^\\]+$"):lower() | |
421 end | |
422 | |
423 | |
424 local function ItemLinkToID(item_link) | |
425 if not item_link then | |
426 return | |
427 end | |
428 return tonumber(item_link:match("item:(%d+)")) | |
429 end | |
430 | |
431 private.ItemLinkToID = ItemLinkToID | |
432 | |
433 local function UnitTypeIsNPC(unit_type) | |
434 return unit_type == private.UNIT_TYPES.NPC or unit_type == private.UNIT_TYPES.VEHICLE | |
435 end | |
436 | |
437 | |
438 local ParseGUID | |
439 do | |
440 local UNIT_TYPES = private.UNIT_TYPES | |
441 | |
442 local NPC_ID_MAPPING = { | |
443 [62164] = 63191, -- Garalon | |
444 } | |
445 | |
446 | |
447 local function MatchUnitTypes(unit_type_name) | |
448 if not unit_type_name then | |
449 return UNIT_TYPES.UNKNOWN | |
450 end | |
451 | |
452 for def, text in next, UNIT_TYPES do | |
453 if unit_type_name == text then | |
454 return UNIT_TYPES[def] | |
455 end | |
456 end | |
457 return UNIT_TYPES.UNKNOWN | |
458 end | |
459 | |
460 | |
461 function ParseGUID(guid) | |
462 if not guid then | |
463 return | |
464 end | |
465 | |
466 -- We might want to use some of this new information later, but leaving the returns alone for now | |
467 local unit_type_name, unk_id1, server_id, instance_id, unk_id2, unit_idnum, spawn_id = ("-"):split(guid) | |
468 | |
469 local unit_type = MatchUnitTypes(unit_type_name) | |
470 if unit_type ~= UNIT_TYPES.PLAYER and unit_type ~= UNIT_TYPES.PET and unit_type ~= UNIT_TYPES.ITEM then | |
471 | |
472 local id_mapping = NPC_ID_MAPPING[unit_idnum] | |
473 | |
474 if id_mapping and UnitTypeIsNPC(unit_type) then | |
475 unit_idnum = id_mapping | |
476 end | |
477 return unit_type, unit_idnum | |
478 end | |
479 return unit_type | |
480 end | |
481 | |
482 private.ParseGUID = ParseGUID | |
483 end -- do-block | |
484 | |
485 | |
486 local UpdateDBEntryLocation | |
487 do | |
488 -- Fishing node coordinate code based on code in GatherMate2 with permission from Kagaro. | |
489 local function FishingCoordinates(x, y, yard_width, yard_height) | |
490 local facing = _G.GetPlayerFacing() | |
491 | |
492 if not facing then | |
493 return x, y | |
494 end | |
495 local rad = facing + math.pi | |
496 return x + math.sin(rad) * 15 / yard_width, y + math.cos(rad) * 15 / yard_height | |
497 end | |
498 | |
499 | |
500 function UpdateDBEntryLocation(entry_type, identifier) | |
501 if not identifier then | |
502 return | |
503 end | |
504 local zone_name, area_id, x, y, map_level, difficulty_token = CurrentLocationData() | |
505 if not (zone_name and area_id and x and y and map_level) then | |
506 Debug("UpdateDBEntryLocation: Missing current location data - %s, %d, %d, %d, %d.", zone_name, area_id, x, y, map_level) | |
507 return | |
508 end | |
509 local entry = DBEntry(entry_type, identifier) | |
510 entry[difficulty_token] = entry[difficulty_token] or {} | |
511 entry[difficulty_token].locations = entry[difficulty_token].locations or {} | |
512 | |
513 local zone_token = ("%s:%d"):format(zone_name, area_id) | |
514 local zone_data = entry[difficulty_token].locations[zone_token] | |
515 | |
516 if not zone_data then | |
517 zone_data = {} | |
518 entry[difficulty_token].locations[zone_token] = zone_data | |
519 end | |
520 | |
521 -- Special case for Fishing. | |
522 if current_action.spell_label == "FISHING" then | |
523 local yard_width, yard_height = MapData:MapArea(area_id, map_level) | |
524 | |
525 if yard_width > 0 and yard_height > 0 then | |
526 x, y = FishingCoordinates(x, y, yard_width, yard_height) | |
527 current_action.x = x | |
528 current_action.y = y | |
529 end | |
530 end | |
531 local location_token = ("%d:%d:%d"):format(map_level, x, y) | |
532 | |
533 zone_data[location_token] = zone_data[location_token] or true | |
534 return zone_data | |
535 end | |
536 end -- do-block | |
537 | |
538 | |
539 local function HandleItemUse(item_link, bag_index, slot_index) | |
540 if not item_link then | |
541 return | |
542 end | |
543 local item_id = ItemLinkToID(item_link) | |
544 | |
545 if not bag_index or not slot_index then | |
546 for new_bag_index = 0, _G.NUM_BAG_FRAMES do | |
547 for new_slot_index = 1, _G.GetContainerNumSlots(new_bag_index) do | |
548 if item_id == ItemLinkToID(_G.GetContainerItemLink(new_bag_index, new_slot_index)) then | |
549 bag_index = new_bag_index | |
550 slot_index = new_slot_index | |
551 break | |
552 end | |
553 end | |
554 end | |
555 end | |
556 | |
557 if not bag_index or not slot_index then | |
558 return | |
559 end | |
560 local _, _, _, _, _, is_lootable = _G.GetContainerItemInfo(bag_index, slot_index) | |
561 | |
562 if not is_lootable then | |
563 return | |
564 end | |
565 | |
566 table.wipe(current_action) | |
567 current_loot = nil | |
568 current_action.target_type = AF.ITEM | |
569 current_action.identifier = item_id | |
570 current_action.loot_label = "contains" | |
571 | |
572 --[[DatamineTT:ClearLines() | |
573 DatamineTT:SetBagItem(bag_index, slot_index) | |
574 | |
575 for line_index = 1, DatamineTT:NumLines() do | |
576 local current_line = _G["WDPDatamineTTTextLeft" .. line_index] | |
577 | |
578 if not current_line then | |
579 Debug("HandleItemUse: Item with ID %d and link %s had an invalid tooltip.", item_id, item_link) | |
580 return | |
581 end | |
582 | |
583 if current_line:GetText() == _G.ITEM_OPENABLE then | |
584 table.wipe(current_action) | |
585 current_loot = nil | |
586 | |
587 current_action.target_type = AF.ITEM | |
588 current_action.identifier = item_id | |
589 current_action.loot_label = "contains" | |
590 return | |
591 end | |
592 end | |
593 Debug("HandleItemUse: Item with ID %d and link %s did not have a tooltip that contained the string %s.", item_id, item_link, _G.ITEM_OPENABLE)]]-- | |
594 end | |
595 | |
596 | |
597 local UnitFactionStanding | |
598 local UpdateFactionData | |
599 do | |
600 local MAX_FACTION_INDEX = 1000 | |
601 | |
602 local STANDING_NAMES = { | |
603 "HATED", | |
604 "HOSTILE", | |
605 "UNFRIENDLY", | |
606 "NEUTRAL", | |
607 "FRIENDLY", | |
608 "HONORED", | |
609 "REVERED", | |
610 "EXALTED", | |
611 } | |
612 | |
613 | |
614 function UnitFactionStanding(unit) | |
615 local unit_name = _G.UnitName(unit) | |
616 UpdateFactionData() | |
617 DatamineTT:ClearLines() | |
618 DatamineTT:SetUnit(unit) | |
619 | |
620 for line_index = 1, DatamineTT:NumLines() do | |
621 local faction_name = _G["WDPDatamineTTTextLeft" .. line_index]:GetText():trim() | |
622 | |
623 if faction_name and faction_name ~= unit_name and faction_standings[faction_name] then | |
624 return faction_name, faction_standings[faction_name] | |
625 end | |
626 end | |
627 end | |
628 | |
629 | |
630 function UpdateFactionData() | |
631 for faction_index = 1, MAX_FACTION_INDEX do | |
632 local faction_name, _, current_standing, _, _, _, _, _, is_header = _G.GetFactionInfo(faction_index) | |
633 | |
634 if faction_name then | |
635 faction_standings[faction_name] = STANDING_NAMES[current_standing] | |
636 elseif not faction_name then | |
637 break | |
638 end | |
639 end | |
640 end | |
641 end -- do-block | |
642 | |
643 | |
644 local GenericLootUpdate | |
645 do | |
646 local function LootTable(entry, loot_type, top_field) | |
647 if top_field then | |
648 entry[top_field] = entry[top_field] or {} | |
649 entry[top_field][loot_type] = entry[top_field][loot_type] or {} | |
650 return entry[top_field][loot_type] | |
651 end | |
652 entry[loot_type] = entry[loot_type] or {} | |
653 return entry[loot_type] | |
654 end | |
655 | |
656 function GenericLootUpdate(data_type, top_field) | |
657 local loot_type = current_loot.label | |
658 local loot_count = ("%s_count"):format(loot_type) | |
659 local source_list = {} | |
660 | |
661 if current_loot.sources then | |
662 for source_guid, loot_data in pairs(current_loot.sources) do | |
663 local source_id | |
664 | |
665 if current_loot.target_type == AF.ITEM then | |
666 -- Items return the player as the source, so we need to use the item's ID (disenchant, milling, etc) | |
667 source_id = current_loot.identifier | |
668 else | |
669 local _, unit_ID = ParseGUID(source_guid) | |
670 if unit_ID then | |
671 if current_loot.target_type == AF.OBJECT then | |
672 source_id = ("%s:%s"):format(current_loot.spell_label, unit_ID) | |
673 else | |
674 source_id = unit_ID | |
675 end | |
676 end | |
677 end | |
678 local entry = DBEntry(data_type, source_id) | |
679 | |
680 if entry then | |
681 local loot_table = LootTable(entry, loot_type, top_field) | |
682 | |
683 if not source_list[source_id] then | |
684 if top_field then | |
685 entry[top_field][loot_count] = (entry[top_field][loot_count] or 0) + 1 | |
686 elseif not container_loot_toasting then | |
687 entry[loot_count] = (entry[loot_count] or 0) + 1 | |
688 end | |
689 source_list[source_id] = true | |
690 end | |
691 UpdateDBEntryLocation(data_type, source_id) | |
692 | |
693 if current_loot.target_type == AF.ZONE then | |
694 for item_id, quantity in pairs(loot_data) do | |
695 table.insert(loot_table, ("%d:%d"):format(item_id, quantity)) | |
696 end | |
697 else | |
698 for loot_token, quantity in pairs(loot_data) do | |
699 local label, currency_texture = (":"):split(loot_token) | |
700 | |
701 if label == "currency" and currency_texture then | |
702 table.insert(loot_table, ("currency:%d:%s"):format(quantity, currency_texture)) | |
703 elseif loot_token == "money" then | |
704 table.insert(loot_table, ("money:%d"):format(quantity)) | |
705 else | |
706 table.insert(loot_table, ("%d:%d"):format(loot_token, quantity)) | |
707 end | |
708 end | |
709 end | |
710 end | |
711 end | |
712 end | |
713 | |
714 -- This is used for Gas Extractions. | |
715 if #current_loot.list <= 0 then | |
716 return | |
717 end | |
718 local entry | |
719 | |
720 -- At this point we only have a name if it's an object. | |
721 -- (As of 5.x, the above statement is almost never true, but there are a few cases, like gas extractions.) | |
722 if current_loot.target_type == AF.OBJECT then | |
723 entry = DBEntry(data_type, ("%s:%s"):format(current_loot.spell_label, current_loot.object_name)) | |
724 else | |
725 entry = DBEntry(data_type, current_loot.identifier) | |
726 end | |
727 | |
728 if not entry then | |
729 return | |
730 end | |
731 local loot_table = LootTable(entry, loot_type, top_field) | |
732 | |
733 if current_loot.identifier then | |
734 if not source_list[current_loot.identifier] then | |
735 if top_field then | |
736 entry[top_field][loot_count] = (entry[top_field][loot_count] or 0) + 1 | |
737 else | |
738 entry[loot_count] = (entry[loot_count] or 0) + 1 | |
739 end | |
740 source_list[current_loot.identifier] = true | |
741 end | |
742 end | |
743 | |
744 for index = 1, #current_loot.list do | |
745 table.insert(loot_table, current_loot.list[index]) | |
746 end | |
747 end | |
748 end -- do-block | |
749 | |
750 | |
751 local ReplaceKeywords | |
752 do | |
753 local KEYWORD_SUBSTITUTIONS = { | |
754 class = PLAYER_CLASS, | |
755 name = PLAYER_NAME, | |
756 race = PLAYER_RACE, | |
757 } | |
758 | |
759 | |
760 function ReplaceKeywords(text) | |
761 if not text or text == "" then | |
762 return "" | |
763 end | |
764 | |
765 for category, lookup in pairs(KEYWORD_SUBSTITUTIONS) do | |
766 local category_format = ("<%s>"):format(category) | |
767 text = text:gsub(lookup, category_format):gsub(lookup:lower(), category_format) | |
768 end | |
769 return text | |
770 end | |
771 end -- do-block | |
772 | |
773 | |
774 -- Contains a dirty hack due to Blizzard's strange handling of Micro Dungeons; GetMapInfo() will not return correct information | |
775 -- unless the WorldMapFrame is shown. | |
776 do | |
777 -- MapFileName = MapAreaID | |
778 local MICRO_DUNGEON_IDS = { | |
779 ShrineofTwoMoons = 903, | |
780 ShrineofSevenStars = 905, | |
781 } | |
782 | |
783 local function SetCurrentAreaID() | |
784 if private.in_combat then | |
785 private.set_area_id = true | |
786 return | |
787 end | |
788 local map_area_id = _G.GetCurrentMapAreaID() | |
789 | |
790 if map_area_id == current_area_id then | |
791 return | |
792 end | |
793 local world_map = _G.WorldMapFrame | |
794 local map_visible = world_map:IsVisible() | |
795 local sfx_value = tonumber(_G.GetCVar("Sound_EnableSFX")) | |
796 | |
797 if not map_visible then | |
798 _G.SetCVar("Sound_EnableSFX", 0) | |
799 world_map:Show() | |
800 end | |
801 local _, _, _, _, micro_dungeon_map_name = _G.GetMapInfo() | |
802 local micro_dungeon_id = MICRO_DUNGEON_IDS[micro_dungeon_map_name] | |
803 | |
804 _G.SetMapToCurrentZone() | |
805 | |
806 if micro_dungeon_id then | |
807 current_area_id = micro_dungeon_id | |
808 else | |
809 current_area_id = _G.GetCurrentMapAreaID() | |
810 end | |
811 | |
812 if map_visible then | |
813 _G.SetMapByID(map_area_id) | |
814 else | |
815 world_map:Hide() | |
816 _G.SetCVar("Sound_EnableSFX", sfx_value) | |
817 end | |
818 end | |
819 | |
820 function WDP:HandleZoneChange(event_name) | |
821 in_instance = _G.IsInInstance() | |
822 SetCurrentAreaID() | |
823 end | |
824 end | |
825 | 219 |
826 local function InitializeCurrentLoot() | 220 local function InitializeCurrentLoot() |
827 current_loot = { | 221 current_loot = { |
828 list = {}, | 222 list = {}, |
829 sources = {}, | 223 sources = {}, |
840 | 234 |
841 table.wipe(current_action) | 235 table.wipe(current_action) |
842 end | 236 end |
843 | 237 |
844 | 238 |
239 local TradeSkillExecutePer | |
240 do | |
241 local header_list = {} | |
242 | |
243 function TradeSkillExecutePer(iter_func) | |
244 if not _G.TradeSkillFrame or not _G.TradeSkillFrame:IsVisible() then | |
245 return | |
246 end | |
247 -- Clear the search box focus so the scan will have correct results. | |
248 local search_box = _G.TradeSkillFrameSearchBox | |
249 search_box:SetText("") | |
250 | |
251 _G.TradeSkillSearch_OnTextChanged(search_box) | |
252 search_box:ClearFocus() | |
253 search_box:GetScript("OnEditFocusLost")(search_box) | |
254 | |
255 table.wipe(header_list) | |
256 | |
257 -- Save the current state of the TradeSkillFrame so it can be restored after we muck with it. | |
258 local have_materials = _G.TradeSkillFrame.filterTbl.hasMaterials | |
259 local have_skillup = _G.TradeSkillFrame.filterTbl.hasSkillUp | |
260 | |
261 if have_materials then | |
262 _G.TradeSkillFrame.filterTbl.hasMaterials = false | |
263 _G.TradeSkillOnlyShowMakeable(false) | |
264 end | |
265 | |
266 if have_skillup then | |
267 _G.TradeSkillFrame.filterTbl.hasSkillUp = false | |
268 _G.TradeSkillOnlyShowSkillUps(false) | |
269 end | |
270 _G.SetTradeSkillInvSlotFilter(0, true, true) | |
271 _G.TradeSkillUpdateFilterBar() | |
272 _G.TradeSkillFrame_Update() | |
273 | |
274 -- Expand all headers so we can see all the recipes there are | |
275 for tradeskill_index = 1, _G.GetNumTradeSkills() do | |
276 local name, tradeskill_type, _, is_expanded = _G.GetTradeSkillInfo(tradeskill_index) | |
277 | |
278 if tradeskill_type == "header" or tradeskill_type == "subheader" then | |
279 if not is_expanded then | |
280 header_list[name] = true | |
281 _G.ExpandTradeSkillSubClass(tradeskill_index) | |
282 end | |
283 elseif iter_func(name, tradeskill_index) then | |
284 break | |
285 end | |
286 end | |
287 | |
288 -- Restore the state of the things we changed. | |
289 for tradeskill_index = 1, _G.GetNumTradeSkills() do | |
290 local name, tradeskill_type, _, is_expanded = _G.GetTradeSkillInfo(tradeskill_index) | |
291 | |
292 if header_list[name] then | |
293 _G.CollapseTradeSkillSubClass(tradeskill_index) | |
294 end | |
295 end | |
296 _G.TradeSkillFrame.filterTbl.hasMaterials = have_materials | |
297 _G.TradeSkillOnlyShowMakeable(have_materials) | |
298 _G.TradeSkillFrame.filterTbl.hasSkillUp = have_skillup | |
299 _G.TradeSkillOnlyShowSkillUps(have_skillup) | |
300 | |
301 _G.TradeSkillUpdateFilterBar() | |
302 _G.TradeSkillFrame_Update() | |
303 end | |
304 end -- do-block | |
305 | |
306 | |
307 local ActualCopperCost | |
308 do | |
309 local BARTERING_SPELL_ID = 83964 | |
310 | |
311 local STANDING_DISCOUNTS = { | |
312 HATED = 0, | |
313 HOSTILE = 0, | |
314 UNFRIENDLY = 0, | |
315 NEUTRAL = 0, | |
316 FRIENDLY = 0.05, | |
317 HONORED = 0.1, | |
318 REVERED = 0.15, | |
319 EXALTED = 0.2, | |
320 } | |
321 | |
322 | |
323 function ActualCopperCost(copper_cost, rep_standing) | |
324 if not copper_cost or copper_cost == 0 then | |
325 return 0 | |
326 end | |
327 local modifier = 1 | |
328 | |
329 if _G.IsSpellKnown(BARTERING_SPELL_ID) then | |
330 modifier = modifier - 0.1 | |
331 end | |
332 | |
333 if rep_standing then | |
334 if PLAYER_RACE == "Goblin" then | |
335 modifier = modifier - STANDING_DISCOUNTS["EXALTED"] | |
336 elseif STANDING_DISCOUNTS[rep_standing] then | |
337 modifier = modifier - STANDING_DISCOUNTS[rep_standing] | |
338 end | |
339 end | |
340 return math.floor(copper_cost / modifier) | |
341 end | |
342 end -- do-block | |
343 | |
344 | |
345 local function InstanceDifficultyToken() | |
346 local _, instance_type, instance_difficulty, _, _, _, is_dynamic = _G.GetInstanceInfo() | |
347 | |
348 if not instance_type or instance_type == "" then | |
349 instance_type = "NONE" | |
350 end | |
351 return ("%s:%d:%s"):format(instance_type:upper(), instance_difficulty, tostring(is_dynamic)) | |
352 end | |
353 | |
354 | |
355 local function DBEntry(data_type, unit_id) | |
356 if not data_type or not unit_id then | |
357 return | |
358 end | |
359 local category = global_db[data_type] | |
360 | |
361 if not category then | |
362 category = {} | |
363 global_db[data_type] = category | |
364 end | |
365 local unit = category[unit_id] | |
366 | |
367 if not unit then | |
368 unit = {} | |
369 category[unit_id] = unit | |
370 end | |
371 return unit | |
372 end | |
373 | |
374 private.DBEntry = DBEntry | |
375 | |
376 local NPCEntry | |
377 do | |
378 local npc_prototype = {} | |
379 local npc_meta = { | |
380 __index = npc_prototype | |
381 } | |
382 | |
383 function NPCEntry(identifier) | |
384 local npc = DBEntry("npcs", identifier) | |
385 return npc and _G.setmetatable(npc, npc_meta) or nil | |
386 end | |
387 | |
388 function npc_prototype:EncounterData(difficulty_token) | |
389 self.encounter_data = self.encounter_data or {} | |
390 self.encounter_data[difficulty_token] = self.encounter_data[difficulty_token] or {} | |
391 self.encounter_data[difficulty_token].stats = self.encounter_data[difficulty_token].stats or {} | |
392 | |
393 return self.encounter_data[difficulty_token] | |
394 end | |
395 end | |
396 | |
397 | |
398 local function CurrentLocationData() | |
399 if _G.GetCurrentMapAreaID() ~= current_area_id then | |
400 return _G.GetRealZoneText(), current_area_id, 0, 0, 0, InstanceDifficultyToken() | |
401 end | |
402 local map_level = _G.GetCurrentMapDungeonLevel() or 0 | |
403 local x, y = _G.GetPlayerMapPosition("player") | |
404 | |
405 x = x or 0 | |
406 y = y or 0 | |
407 | |
408 if x == 0 and y == 0 then | |
409 for level_index = 1, _G.GetNumDungeonMapLevels() do | |
410 _G.SetDungeonMapLevel(level_index) | |
411 x, y = _G.GetPlayerMapPosition("player") | |
412 | |
413 if x and y and (x > 0 or y > 0) then | |
414 _G.SetDungeonMapLevel(map_level) | |
415 map_level = level_index | |
416 break | |
417 end | |
418 end | |
419 end | |
420 | |
421 if _G.DungeonUsesTerrainMap() then | |
422 map_level = map_level - 1 | |
423 end | |
424 local x = _G.floor(x * 1000) | |
425 local y = _G.floor(y * 1000) | |
426 | |
427 if x % 2 ~= 0 then | |
428 x = x + 1 | |
429 end | |
430 | |
431 if y % 2 ~= 0 then | |
432 y = y + 1 | |
433 end | |
434 return _G.GetRealZoneText(), current_area_id, x, y, map_level, InstanceDifficultyToken() | |
435 end | |
436 | |
437 | |
438 local function CurrencyLinkToTexture(currency_link) | |
439 if not currency_link then | |
440 return | |
441 end | |
442 local _, _, texture_path = _G.GetCurrencyInfo(tonumber(currency_link:match("currency:(%d+)"))) | |
443 return texture_path:match("[^\\]+$"):lower() | |
444 end | |
445 | |
446 | |
447 local function ItemLinkToID(item_link) | |
448 if not item_link then | |
449 return | |
450 end | |
451 return tonumber(item_link:match("item:(%d+)")) | |
452 end | |
453 | |
454 private.ItemLinkToID = ItemLinkToID | |
455 | |
456 local function UnitTypeIsNPC(unit_type) | |
457 return unit_type == private.UNIT_TYPES.NPC or unit_type == private.UNIT_TYPES.VEHICLE | |
458 end | |
459 | |
460 | |
461 local ParseGUID | |
462 do | |
463 local UNIT_TYPES = private.UNIT_TYPES | |
464 | |
465 local NPC_ID_MAPPING = { | |
466 [62164] = 63191, -- Garalon | |
467 } | |
468 | |
469 | |
470 local function MatchUnitTypes(unit_type_name) | |
471 if not unit_type_name then | |
472 return UNIT_TYPES.UNKNOWN | |
473 end | |
474 | |
475 for def, text in next, UNIT_TYPES do | |
476 if unit_type_name == text then | |
477 return UNIT_TYPES[def] | |
478 end | |
479 end | |
480 return UNIT_TYPES.UNKNOWN | |
481 end | |
482 | |
483 | |
484 function ParseGUID(guid) | |
485 if not guid then | |
486 return | |
487 end | |
488 | |
489 -- We might want to use some of this new information later, but leaving the returns alone for now | |
490 local unit_type_name, unk_id1, server_id, instance_id, unk_id2, unit_idnum, spawn_id = ("-"):split(guid) | |
491 | |
492 local unit_type = MatchUnitTypes(unit_type_name) | |
493 if unit_type ~= UNIT_TYPES.PLAYER and unit_type ~= UNIT_TYPES.PET and unit_type ~= UNIT_TYPES.ITEM then | |
494 | |
495 local id_mapping = NPC_ID_MAPPING[unit_idnum] | |
496 | |
497 if id_mapping and UnitTypeIsNPC(unit_type) then | |
498 unit_idnum = id_mapping | |
499 end | |
500 return unit_type, unit_idnum | |
501 end | |
502 return unit_type | |
503 end | |
504 | |
505 private.ParseGUID = ParseGUID | |
506 end -- do-block | |
507 | |
508 | |
509 local UpdateDBEntryLocation | |
510 do | |
511 -- Fishing node coordinate code based on code in GatherMate2 with permission from Kagaro. | |
512 local function FishingCoordinates(x, y, yard_width, yard_height) | |
513 local facing = _G.GetPlayerFacing() | |
514 | |
515 if not facing then | |
516 return x, y | |
517 end | |
518 local rad = facing + math.pi | |
519 return x + math.sin(rad) * 15 / yard_width, y + math.cos(rad) * 15 / yard_height | |
520 end | |
521 | |
522 | |
523 function UpdateDBEntryLocation(entry_type, identifier) | |
524 if not identifier then | |
525 return | |
526 end | |
527 local zone_name, area_id, x, y, map_level, difficulty_token = CurrentLocationData() | |
528 if not (zone_name and area_id and x and y and map_level) then | |
529 Debug("UpdateDBEntryLocation: Missing current location data - %s, %d, %d, %d, %d.", zone_name, area_id, x, y, map_level) | |
530 return | |
531 end | |
532 local entry = DBEntry(entry_type, identifier) | |
533 entry[difficulty_token] = entry[difficulty_token] or {} | |
534 entry[difficulty_token].locations = entry[difficulty_token].locations or {} | |
535 | |
536 local zone_token = ("%s:%d"):format(zone_name, area_id) | |
537 local zone_data = entry[difficulty_token].locations[zone_token] | |
538 | |
539 if not zone_data then | |
540 zone_data = {} | |
541 entry[difficulty_token].locations[zone_token] = zone_data | |
542 end | |
543 | |
544 -- Special case for Fishing. | |
545 if current_action.spell_label == "FISHING" then | |
546 local yard_width, yard_height = MapData:MapArea(area_id, map_level) | |
547 | |
548 if yard_width > 0 and yard_height > 0 then | |
549 x, y = FishingCoordinates(x, y, yard_width, yard_height) | |
550 current_action.x = x | |
551 current_action.y = y | |
552 end | |
553 end | |
554 local location_token = ("%d:%d:%d"):format(map_level, x, y) | |
555 | |
556 zone_data[location_token] = zone_data[location_token] or true | |
557 return zone_data | |
558 end | |
559 end -- do-block | |
560 | |
561 | |
562 local function HandleItemUse(item_link, bag_index, slot_index) | |
563 if not item_link then | |
564 return | |
565 end | |
566 local item_id = ItemLinkToID(item_link) | |
567 | |
568 if not bag_index or not slot_index then | |
569 for new_bag_index = 0, _G.NUM_BAG_FRAMES do | |
570 for new_slot_index = 1, _G.GetContainerNumSlots(new_bag_index) do | |
571 if item_id == ItemLinkToID(_G.GetContainerItemLink(new_bag_index, new_slot_index)) then | |
572 bag_index = new_bag_index | |
573 slot_index = new_slot_index | |
574 break | |
575 end | |
576 end | |
577 end | |
578 end | |
579 | |
580 if not bag_index or not slot_index then | |
581 return | |
582 end | |
583 local _, _, _, _, _, is_lootable = _G.GetContainerItemInfo(bag_index, slot_index) | |
584 | |
585 if not is_lootable then | |
586 return | |
587 end | |
588 | |
589 table.wipe(current_action) | |
590 current_loot = nil | |
591 current_action.target_type = AF.ITEM | |
592 current_action.identifier = item_id | |
593 current_action.loot_label = "contains" | |
594 | |
595 -- For items that open instantly with no spell cast | |
596 if private.CONTAINER_ITEM_ID_LIST[item_id] == true then | |
597 ClearChatLootData() | |
598 Debug("HandleItemUse: Beginning chat-based loot timer for item with ID %d.", item_id) | |
599 chat_loot_timer_handle = C_Timer.NewTimer(1, ClearChatLootData) | |
600 InitializeCurrentLoot() | |
601 end | |
602 | |
603 --[[DatamineTT:ClearLines() | |
604 DatamineTT:SetBagItem(bag_index, slot_index) | |
605 | |
606 for line_index = 1, DatamineTT:NumLines() do | |
607 local current_line = _G["WDPDatamineTTTextLeft" .. line_index] | |
608 | |
609 if not current_line then | |
610 Debug("HandleItemUse: Item with ID %d and link %s had an invalid tooltip.", item_id, item_link) | |
611 return | |
612 end | |
613 | |
614 if current_line:GetText() == _G.ITEM_OPENABLE then | |
615 table.wipe(current_action) | |
616 current_loot = nil | |
617 | |
618 current_action.target_type = AF.ITEM | |
619 current_action.identifier = item_id | |
620 current_action.loot_label = "contains" | |
621 return | |
622 end | |
623 end | |
624 Debug("HandleItemUse: Item with ID %d and link %s did not have a tooltip that contained the string %s.", item_id, item_link, _G.ITEM_OPENABLE)]]-- | |
625 end | |
626 | |
627 | |
628 local UnitFactionStanding | |
629 local UpdateFactionData | |
630 do | |
631 local MAX_FACTION_INDEX = 1000 | |
632 | |
633 local STANDING_NAMES = { | |
634 "HATED", | |
635 "HOSTILE", | |
636 "UNFRIENDLY", | |
637 "NEUTRAL", | |
638 "FRIENDLY", | |
639 "HONORED", | |
640 "REVERED", | |
641 "EXALTED", | |
642 } | |
643 | |
644 | |
645 function UnitFactionStanding(unit) | |
646 local unit_name = _G.UnitName(unit) | |
647 UpdateFactionData() | |
648 DatamineTT:ClearLines() | |
649 DatamineTT:SetUnit(unit) | |
650 | |
651 for line_index = 1, DatamineTT:NumLines() do | |
652 local faction_name = _G["WDPDatamineTTTextLeft" .. line_index]:GetText():trim() | |
653 | |
654 if faction_name and faction_name ~= unit_name and faction_standings[faction_name] then | |
655 return faction_name, faction_standings[faction_name] | |
656 end | |
657 end | |
658 end | |
659 | |
660 | |
661 function UpdateFactionData() | |
662 for faction_index = 1, MAX_FACTION_INDEX do | |
663 local faction_name, _, current_standing, _, _, _, _, _, is_header = _G.GetFactionInfo(faction_index) | |
664 | |
665 if faction_name then | |
666 faction_standings[faction_name] = STANDING_NAMES[current_standing] | |
667 elseif not faction_name then | |
668 break | |
669 end | |
670 end | |
671 end | |
672 end -- do-block | |
673 | |
674 | |
675 local GenericLootUpdate | |
676 do | |
677 local function LootTable(entry, loot_type, top_field) | |
678 if top_field then | |
679 entry[top_field] = entry[top_field] or {} | |
680 entry[top_field][loot_type] = entry[top_field][loot_type] or {} | |
681 return entry[top_field][loot_type] | |
682 end | |
683 entry[loot_type] = entry[loot_type] or {} | |
684 return entry[loot_type] | |
685 end | |
686 | |
687 function GenericLootUpdate(data_type, top_field) | |
688 local loot_type = current_loot.label | |
689 local loot_count = ("%s_count"):format(loot_type) | |
690 local source_list = {} | |
691 | |
692 if current_loot.sources then | |
693 for source_guid, loot_data in pairs(current_loot.sources) do | |
694 local source_id | |
695 | |
696 if current_loot.target_type == AF.ITEM then | |
697 -- Items return the player as the source, so we need to use the item's ID (disenchant, milling, etc) | |
698 source_id = current_loot.identifier | |
699 else | |
700 local _, unit_ID = ParseGUID(source_guid) | |
701 if unit_ID then | |
702 if current_loot.target_type == AF.OBJECT then | |
703 source_id = ("%s:%s"):format(current_loot.spell_label, unit_ID) | |
704 else | |
705 source_id = unit_ID | |
706 end | |
707 end | |
708 end | |
709 local entry = DBEntry(data_type, source_id) | |
710 | |
711 if entry then | |
712 local loot_table = LootTable(entry, loot_type, top_field) | |
713 | |
714 if not source_list[source_id] then | |
715 if top_field then | |
716 entry[top_field][loot_count] = (entry[top_field][loot_count] or 0) + 1 | |
717 elseif not container_loot_toasting then | |
718 entry[loot_count] = (entry[loot_count] or 0) + 1 | |
719 end | |
720 source_list[source_id] = true | |
721 end | |
722 UpdateDBEntryLocation(data_type, source_id) | |
723 | |
724 if current_loot.target_type == AF.ZONE then | |
725 for item_id, quantity in pairs(loot_data) do | |
726 table.insert(loot_table, ("%d:%d"):format(item_id, quantity)) | |
727 end | |
728 else | |
729 for loot_token, quantity in pairs(loot_data) do | |
730 local label, currency_texture = (":"):split(loot_token) | |
731 | |
732 if label == "currency" and currency_texture then | |
733 table.insert(loot_table, ("currency:%d:%s"):format(quantity, currency_texture)) | |
734 elseif loot_token == "money" then | |
735 table.insert(loot_table, ("money:%d"):format(quantity)) | |
736 else | |
737 table.insert(loot_table, ("%d:%d"):format(loot_token, quantity)) | |
738 end | |
739 end | |
740 end | |
741 end | |
742 end | |
743 end | |
744 | |
745 -- This is used for Gas Extractions. | |
746 if #current_loot.list <= 0 then | |
747 return | |
748 end | |
749 local entry | |
750 | |
751 -- At this point we only have a name if it's an object. | |
752 -- (As of 5.x, the above statement is almost never true, but there are a few cases, like gas extractions.) | |
753 if current_loot.target_type == AF.OBJECT then | |
754 entry = DBEntry(data_type, ("%s:%s"):format(current_loot.spell_label, current_loot.object_name)) | |
755 else | |
756 entry = DBEntry(data_type, current_loot.identifier) | |
757 end | |
758 | |
759 if not entry then | |
760 return | |
761 end | |
762 local loot_table = LootTable(entry, loot_type, top_field) | |
763 | |
764 if current_loot.identifier then | |
765 if not source_list[current_loot.identifier] then | |
766 if top_field then | |
767 entry[top_field][loot_count] = (entry[top_field][loot_count] or 0) + 1 | |
768 else | |
769 entry[loot_count] = (entry[loot_count] or 0) + 1 | |
770 end | |
771 source_list[current_loot.identifier] = true | |
772 end | |
773 end | |
774 | |
775 for index = 1, #current_loot.list do | |
776 table.insert(loot_table, current_loot.list[index]) | |
777 end | |
778 end | |
779 end -- do-block | |
780 | |
781 | |
782 local ReplaceKeywords | |
783 do | |
784 local KEYWORD_SUBSTITUTIONS = { | |
785 class = PLAYER_CLASS, | |
786 name = PLAYER_NAME, | |
787 race = PLAYER_RACE, | |
788 } | |
789 | |
790 | |
791 function ReplaceKeywords(text) | |
792 if not text or text == "" then | |
793 return "" | |
794 end | |
795 | |
796 for category, lookup in pairs(KEYWORD_SUBSTITUTIONS) do | |
797 local category_format = ("<%s>"):format(category) | |
798 text = text:gsub(lookup, category_format):gsub(lookup:lower(), category_format) | |
799 end | |
800 return text | |
801 end | |
802 end -- do-block | |
803 | |
804 | |
805 -- Contains a dirty hack due to Blizzard's strange handling of Micro Dungeons; GetMapInfo() will not return correct information | |
806 -- unless the WorldMapFrame is shown. | |
807 do | |
808 -- MapFileName = MapAreaID | |
809 local MICRO_DUNGEON_IDS = { | |
810 ShrineofTwoMoons = 903, | |
811 ShrineofSevenStars = 905, | |
812 } | |
813 | |
814 local function SetCurrentAreaID() | |
815 if private.in_combat then | |
816 private.set_area_id = true | |
817 return | |
818 end | |
819 local map_area_id = _G.GetCurrentMapAreaID() | |
820 | |
821 if map_area_id == current_area_id then | |
822 return | |
823 end | |
824 local world_map = _G.WorldMapFrame | |
825 local map_visible = world_map:IsVisible() | |
826 local sfx_value = tonumber(_G.GetCVar("Sound_EnableSFX")) | |
827 | |
828 if not map_visible then | |
829 _G.SetCVar("Sound_EnableSFX", 0) | |
830 world_map:Show() | |
831 end | |
832 local _, _, _, _, micro_dungeon_map_name = _G.GetMapInfo() | |
833 local micro_dungeon_id = MICRO_DUNGEON_IDS[micro_dungeon_map_name] | |
834 | |
835 _G.SetMapToCurrentZone() | |
836 | |
837 if micro_dungeon_id then | |
838 current_area_id = micro_dungeon_id | |
839 else | |
840 current_area_id = _G.GetCurrentMapAreaID() | |
841 end | |
842 | |
843 if map_visible then | |
844 _G.SetMapByID(map_area_id) | |
845 else | |
846 world_map:Hide() | |
847 _G.SetCVar("Sound_EnableSFX", sfx_value) | |
848 end | |
849 end | |
850 | |
851 function WDP:HandleZoneChange(event_name) | |
852 in_instance = _G.IsInInstance() | |
853 SetCurrentAreaID() | |
854 end | |
855 end | |
856 | |
857 | |
845 -- TIMERS ------------------------------------------------------------- | 858 -- TIMERS ------------------------------------------------------------- |
846 | 859 |
847 local function ClearKilledNPC() | 860 function ClearKilledNPC() |
848 killed_npc_id = nil | 861 killed_npc_id = nil |
849 end | 862 end |
850 | 863 |
851 | 864 |
852 local function ClearKilledBossID() | 865 function ClearKilledBossID() |
853 if killed_boss_id_timer_handle then | 866 if killed_boss_id_timer_handle then |
854 killed_boss_id_timer_handle:Cancel() | 867 killed_boss_id_timer_handle:Cancel() |
855 killed_boss_id_timer_handle = nil | 868 killed_boss_id_timer_handle = nil |
856 end | 869 end |
857 | 870 |
858 table.wipe(boss_loot_toasting) | 871 table.wipe(boss_loot_toasting) |
859 raid_boss_id = nil | 872 raid_boss_id = nil |
860 end | 873 end |
861 | 874 |
862 | 875 |
863 local function ClearLootToastContainerID() | 876 function ClearLootToastContainerID() |
864 if loot_toast_container_timer_handle then | 877 if loot_toast_container_timer_handle then |
865 loot_toast_container_timer_handle:Cancel() | 878 loot_toast_container_timer_handle:Cancel() |
866 loot_toast_container_timer_handle = nil | 879 loot_toast_container_timer_handle = nil |
867 end | 880 end |
868 | 881 |
869 container_loot_toasting = false | 882 container_loot_toasting = false |
870 loot_toast_container_id = nil | 883 loot_toast_container_id = nil |
871 end | 884 end |
872 | 885 |
873 | 886 |
874 local function ClearLootToastData() | 887 function ClearLootToastData() |
875 if loot_toast_data_timer_handle then | 888 if loot_toast_data_timer_handle then |
876 loot_toast_data_timer_handle:Cancel() | 889 loot_toast_data_timer_handle:Cancel() |
877 loot_toast_data_timer_handle = nil | 890 loot_toast_data_timer_handle = nil |
878 end | 891 end |
879 | 892 |
881 table.wipe(loot_toast_data) | 894 table.wipe(loot_toast_data) |
882 end | 895 end |
883 end | 896 end |
884 | 897 |
885 | 898 |
886 local function ClearChatLootData() | 899 function ClearChatLootData() |
887 Debug("ClearChatLootData: Ending chat-based loot timer.") | |
888 if chat_loot_timer_handle then | 900 if chat_loot_timer_handle then |
901 Debug("ClearChatLootData: Ending chat-based loot timer.") | |
889 chat_loot_timer_handle:Cancel() | 902 chat_loot_timer_handle:Cancel() |
890 chat_loot_timer_handle = nil | 903 chat_loot_timer_handle = nil |
891 end | 904 |
892 | 905 if current_loot and current_loot.identifier and (private.CONTAINER_ITEM_ID_LIST[current_loot.identifier] ~= nil) then |
893 if current_loot and current_loot.identifier and (private.CONTAINER_ITEM_ID_LIST[current_loot.identifier] ~= nil) then | 906 GenericLootUpdate("items") |
894 GenericLootUpdate("items") | 907 end |
895 end | 908 end |
896 current_loot = nil | 909 current_loot = nil |
897 end | 910 end |
898 | 911 |
899 | 912 |
2705 end | 2718 end |
2706 private.tracked_line = spell_line | 2719 private.tracked_line = spell_line |
2707 end | 2720 end |
2708 | 2721 |
2709 | 2722 |
2723 -- Triggered by bonus roll prompts, disenchant prompts, and in a few other rare circumstances | |
2710 function WDP:SPELL_CONFIRMATION_PROMPT(event_name, spell_id, confirm_type, text, duration, currency_id_cost) | 2724 function WDP:SPELL_CONFIRMATION_PROMPT(event_name, spell_id, confirm_type, text, duration, currency_id_cost) |
2711 if private.RAID_BOSS_BONUS_SPELL_ID_TO_NPC_ID_MAP[spell_id] then | 2725 if private.RAID_BOSS_BONUS_SPELL_ID_TO_NPC_ID_MAP[spell_id] then |
2712 ClearKilledBossID() | 2726 ClearKilledBossID() |
2713 ClearLootToastContainerID() | 2727 ClearLootToastContainerID() |
2714 raid_boss_id = private.RAID_BOSS_BONUS_SPELL_ID_TO_NPC_ID_MAP[spell_id] | 2728 raid_boss_id = private.RAID_BOSS_BONUS_SPELL_ID_TO_NPC_ID_MAP[spell_id] |
2783 return | 2797 return |
2784 end | 2798 end |
2785 private.tracked_line = nil | 2799 private.tracked_line = nil |
2786 private.previous_spell_id = spell_id | 2800 private.previous_spell_id = spell_id |
2787 | 2801 |
2788 -- Handle Logging spell casts | 2802 -- For spells cast when Logging |
2789 if private.LOGGING_SPELL_ID_TO_OBJECT_ID_MAP[spell_id] then | 2803 if private.LOGGING_SPELL_ID_TO_OBJECT_ID_MAP[spell_id] then |
2790 last_timber_spell_id = spell_id | 2804 last_timber_spell_id = spell_id |
2791 UpdateDBEntryLocation("objects", ("OPENING:%s"):format(private.LOGGING_SPELL_ID_TO_OBJECT_ID_MAP[spell_id])) | 2805 UpdateDBEntryLocation("objects", ("OPENING:%s"):format(private.LOGGING_SPELL_ID_TO_OBJECT_ID_MAP[spell_id])) |
2792 return | 2806 return |
2793 end | 2807 end |
2794 | 2808 |
2795 -- Handle Loot Toast spell casts | 2809 -- For spells cast by items that always trigger loot toasts |
2796 if private.LOOT_TOAST_CONTAINER_SPELL_ID_TO_ITEM_ID_MAP[spell_id] then | 2810 if private.LOOT_TOAST_CONTAINER_SPELL_ID_TO_ITEM_ID_MAP[spell_id] then |
2797 ClearKilledBossID() | 2811 ClearKilledBossID() |
2798 ClearLootToastContainerID() | 2812 ClearLootToastContainerID() |
2799 ClearLootToastData() | 2813 ClearLootToastData() |
2800 | 2814 |
2801 loot_toast_container_id = private.LOOT_TOAST_CONTAINER_SPELL_ID_TO_ITEM_ID_MAP[spell_id] | 2815 loot_toast_container_id = private.LOOT_TOAST_CONTAINER_SPELL_ID_TO_ITEM_ID_MAP[spell_id] |
2802 loot_toast_container_timer_handle = C_Timer.NewTimer(1, ClearLootToastContainerID) -- we need to assign a handle here to cancel it later | 2816 loot_toast_container_timer_handle = C_Timer.NewTimer(1, ClearLootToastContainerID) -- we need to assign a handle here to cancel it later |
2803 return | 2817 return |
2804 end | 2818 end |
2805 | 2819 |
2806 -- For Crates of Salvage (and potentially other items based on spell casts in the future which need manual handling) | 2820 -- For spells cast by items that don't usually trigger loot toasts |
2807 if private.DELAYED_CONTAINER_SPELL_ID_TO_ITEM_ID_MAP[spell_id] then | 2821 if private.DELAYED_CONTAINER_SPELL_ID_TO_ITEM_ID_MAP[spell_id] then |
2808 -- Set up timer | 2822 -- Set up timer |
2809 Debug("%s: Beginning Salvage loot timer for spellID %d", event_name, spell_id) | 2823 ClearChatLootData() |
2824 Debug("%s: Beginning chat-based loot timer for spellID %d", event_name, spell_id) | |
2810 chat_loot_timer_handle = C_Timer.NewTimer(1, ClearChatLootData) | 2825 chat_loot_timer_handle = C_Timer.NewTimer(1, ClearChatLootData) |
2811 | 2826 |
2812 -- Standard item handling setup | 2827 -- Standard item handling setup |
2813 table.wipe(current_action) | 2828 table.wipe(current_action) |
2814 current_loot = nil | 2829 current_loot = nil |