jcallahan@0: ----------------------------------------------------------------------- jcallahan@0: -- Upvalued Lua API. jcallahan@0: ----------------------------------------------------------------------- jcallahan@0: local _G = getfenv(0) jcallahan@0: jcallahan@0: local pairs = _G.pairs jcallahan@1: local tonumber = _G.tonumber jcallahan@1: jcallahan@1: local bit = _G.bit jcallahan@1: local math = _G.math jcallahan@1: local table = _G.table jcallahan@1: jcallahan@0: jcallahan@0: ----------------------------------------------------------------------- jcallahan@0: -- AddOn namespace. jcallahan@0: ----------------------------------------------------------------------- jcallahan@0: local ADDON_NAME, private = ... jcallahan@0: jcallahan@0: local LibStub = _G.LibStub jcallahan@0: local WDP = LibStub("AceAddon-3.0"):NewAddon(ADDON_NAME, "AceEvent-3.0", "AceTimer-3.0") jcallahan@0: jcallahan@0: jcallahan@0: ----------------------------------------------------------------------- jcallahan@0: -- Local constants. jcallahan@0: ----------------------------------------------------------------------- jcallahan@0: local DATABASE_DEFAULTS = { jcallahan@0: global = { jcallahan@0: items = {}, jcallahan@0: npcs = {}, jcallahan@0: objects = {}, jcallahan@0: quests = {}, jcallahan@0: } jcallahan@0: } jcallahan@0: jcallahan@0: jcallahan@1: local EVENT_MAPPING = { jcallahan@1: -- ARTIFACT_COMPLETE = true, jcallahan@0: -- ARTIFACT_HISTORY_READY = true, jcallahan@0: -- AUCTION_HOUSE_SHOW = true, jcallahan@0: -- BANKFRAME_OPENED = true, jcallahan@0: -- BATTLEFIELDS_SHOW = true, jcallahan@0: -- CHAT_MSG_ADDON = true, jcallahan@0: -- CHAT_MSG_MONSTER_EMOTE = true, jcallahan@0: -- CHAT_MSG_MONSTER_SAY = true, jcallahan@0: -- CHAT_MSG_MONSTER_WHISPER = true, jcallahan@0: -- CHAT_MSG_MONSTER_YELL = true, jcallahan@0: -- CHAT_MSG_SYSTEM = true, jcallahan@0: -- COMBAT_LOG_EVENT_UNFILTERED = true, jcallahan@0: -- COMBAT_TEXT_UPDATE = true, jcallahan@0: -- CONFIRM_BINDER = true, jcallahan@0: -- CONFIRM_PET_UNLEARN = true, jcallahan@0: -- CONFIRM_TALENT_WIPE = true, jcallahan@0: -- CURRENCY_DISPLAY_UPDATE = true, jcallahan@0: -- GOSSIP_ENTER_CODE = true, jcallahan@0: -- GOSSIP_SHOW = true, jcallahan@0: -- ITEM_TEXT_BEGIN = true, jcallahan@0: -- LOCALPLAYER_PET_RENAMED = true, jcallahan@0: -- LOOT_CLOSED = true, jcallahan@1: LOOT_OPENED = true, jcallahan@0: -- MAIL_SHOW = true, jcallahan@0: -- MERCHANT_SHOW = true, jcallahan@0: -- MERCHANT_UPDATE = true, jcallahan@0: -- OPEN_TABARD_FRAME = true, jcallahan@0: -- PET_BAR_UPDATE = true, jcallahan@0: -- PET_STABLE_SHOW = true, jcallahan@0: -- PLAYER_ALIVE = true, jcallahan@0: -- PLAYER_ENTERING_WORLD = HandleZoneChange, jcallahan@0: -- PLAYER_LOGIN = true, jcallahan@0: -- PLAYER_LOGOUT = true, jcallahan@0: -- PLAYER_TARGET_CHANGED = true, jcallahan@0: -- QUEST_COMPLETE = true, jcallahan@0: -- QUEST_DETAIL = true, jcallahan@0: -- QUEST_LOG_UPDATE = true, jcallahan@0: -- QUEST_PROGRESS = true, jcallahan@0: -- TAXIMAP_OPENED = true, jcallahan@0: -- TRADE_SKILL_SHOW = true, jcallahan@0: -- TRADE_SKILL_UPDATE = true, jcallahan@0: -- TRAINER_SHOW = true, jcallahan@0: -- UNIT_QUEST_LOG_CHANGED = true, jcallahan@1: UNIT_SPELLCAST_FAILED = "HandleSpellFailure", jcallahan@1: UNIT_SPELLCAST_FAILED_QUIET = "HandleSpellFailure", jcallahan@1: UNIT_SPELLCAST_INTERRUPTED = "HandleSpellFailure", jcallahan@1: UNIT_SPELLCAST_SENT = true, jcallahan@1: UNIT_SPELLCAST_SUCCEEDED = true, jcallahan@0: -- ZONE_CHANGED = HandleZoneChange, jcallahan@0: -- ZONE_CHANGED_NEW_AREA = HandleZoneChange, jcallahan@0: } jcallahan@0: jcallahan@1: local AF = private.ACTION_TYPE_FLAGS jcallahan@0: jcallahan@0: ----------------------------------------------------------------------- jcallahan@0: -- Local variables. jcallahan@0: ----------------------------------------------------------------------- jcallahan@0: local db jcallahan@0: local durability_timer_handle jcallahan@1: local action_data = {} jcallahan@0: jcallahan@1: do jcallahan@1: local UNIT_TYPE_BITMASK = 0x007 jcallahan@1: jcallahan@1: function WDP:ParseGUID(guid) jcallahan@1: local types = private.UNIT_TYPES jcallahan@1: local unit_type = _G.bit.band(tonumber(guid:sub(1, 5)), UNIT_TYPE_BITMASK) jcallahan@1: jcallahan@1: if unit_type ~= types.PLAYER or unit_type ~= types.OBJECT or unit_type ~= types.PET then jcallahan@1: return unit_type, tonumber(guid:sub(-12, -9), 16) jcallahan@1: end jcallahan@1: jcallahan@1: return unit_type jcallahan@1: end jcallahan@1: end -- do-block jcallahan@1: jcallahan@1: jcallahan@1: ----------------------------------------------------------------------- jcallahan@1: -- Helper Functions. jcallahan@1: ----------------------------------------------------------------------- jcallahan@1: local function CurrentLocationData() jcallahan@1: local map_level = _G.GetCurrentMapDungeonLevel() or 0 jcallahan@1: local x, y = _G.GetPlayerMapPosition("player") jcallahan@1: jcallahan@1: x = x or 0 jcallahan@1: y = y or 0 jcallahan@1: jcallahan@1: if x == 0 and y == 0 then jcallahan@1: for level_index = 1, _G.GetNumDungeonMapLevels() do jcallahan@1: _G.SetDungeonMapLevel(level_index) jcallahan@1: x, y = _G.GetPlayerMapPosition("player") jcallahan@1: jcallahan@1: if x and y and (x > 0 or y > 0) then jcallahan@1: _G.SetDungeonMapLevel(map_level) jcallahan@1: map_level = level_index jcallahan@1: break jcallahan@1: end jcallahan@1: end jcallahan@1: end jcallahan@1: jcallahan@1: if _G.DungeonUsesTerrainMap() then jcallahan@1: map_level = map_level - 1 jcallahan@1: end jcallahan@1: jcallahan@1: return _G.GetRealZoneText(), math.floor(x * 1000 + 0.5), math.floor(y * 1000 + 0.5), map_level or 0 jcallahan@1: end jcallahan@1: jcallahan@1: jcallahan@1: local function ItemLinkToID(item_link) jcallahan@1: if not item_link then jcallahan@1: return jcallahan@1: end jcallahan@1: local id = item_link:match("item:(%d+)") jcallahan@1: return id and tonumber(id) or nil jcallahan@1: end jcallahan@0: jcallahan@0: ----------------------------------------------------------------------- jcallahan@0: -- Methods. jcallahan@0: ----------------------------------------------------------------------- jcallahan@0: function WDP:OnInitialize() jcallahan@0: db = LibStub("AceDB-3.0"):New("WoWDBProfilerData", DATABASE_DEFAULTS, "Default").global jcallahan@0: end jcallahan@0: jcallahan@0: jcallahan@0: function WDP:OnEnable() jcallahan@0: for event_name, mapping in pairs(EVENT_MAPPING) do jcallahan@1: self:RegisterEvent(event_name, (_G.type(mapping) ~= "boolean") and mapping or nil) jcallahan@0: end jcallahan@0: durability_timer_handle = self:ScheduleRepeatingTimer("ProcessDurability", 30) jcallahan@0: end jcallahan@0: jcallahan@0: jcallahan@0: local function RecordDurability(item_id, durability) jcallahan@0: if not durability or durability <= 0 then jcallahan@0: return jcallahan@0: end jcallahan@0: jcallahan@0: if not db.items[item_id] then jcallahan@0: db.items[item_id] = {} jcallahan@0: end jcallahan@0: db.items[item_id].durability = durability jcallahan@0: end jcallahan@0: jcallahan@0: jcallahan@0: function WDP:ProcessDurability() jcallahan@0: for slot_index = 0, _G.INVSLOT_LAST_EQUIPPED do jcallahan@1: local item_id = _G.GetInventoryItemID("player", slot_index) jcallahan@0: jcallahan@0: if item_id and item_id > 0 then jcallahan@1: local _, max_durability = _G.GetInventoryItemDurability(slot_index) jcallahan@0: RecordDurability(item_id, max_durability) jcallahan@0: end jcallahan@0: end jcallahan@0: jcallahan@0: for bag_index = 0, _G.NUM_BAG_SLOTS do jcallahan@0: for slot_index = 1, _G.GetContainerNumSlots(bag_index) do jcallahan@1: local item_id = _G.GetContainerItemID(bag_index, slot_index) jcallahan@0: jcallahan@0: if item_id and item_id > 0 then jcallahan@1: local _, max_durability = _G.GetContainerItemDurability(bag_index, slot_index) jcallahan@0: RecordDurability(item_id, max_durability) jcallahan@0: end jcallahan@0: end jcallahan@0: end jcallahan@0: end jcallahan@0: jcallahan@0: jcallahan@0: ----------------------------------------------------------------------- jcallahan@0: -- Event handlers. jcallahan@0: ----------------------------------------------------------------------- jcallahan@1: function WDP:CHAT_MSG_SYSTEM(event_name, message, sender_name, language) jcallahan@0: end jcallahan@0: jcallahan@0: jcallahan@1: local re_gold = _G.GOLD_AMOUNT:gsub("%%d", "(%%d+)") jcallahan@1: local re_silver = _G.SILVER_AMOUNT:gsub("%%d", "(%%d+)") jcallahan@1: local re_copper = _G.COPPER_AMOUNT:gsub("%%d", "(%%d+)") jcallahan@1: jcallahan@1: jcallahan@1: local function _moneyMatch(money, re) jcallahan@1: return money:match(re) or 0 jcallahan@0: end jcallahan@0: jcallahan@0: jcallahan@1: local function _toCopper(money) jcallahan@1: if not money then jcallahan@1: return 0 jcallahan@1: end jcallahan@1: jcallahan@1: return _moneyMatch(money, re_gold) * 10000 + _moneyMatch(money, re_silver) * 100 + _moneyMatch(money, re_copper) jcallahan@0: end jcallahan@0: jcallahan@0: jcallahan@1: local LOOT_VERIFY_FUNCS = { jcallahan@1: [AF.NPC] = function() jcallahan@1: local fishing_loot = _G.IsFishingLoot() jcallahan@1: jcallahan@1: if not fishing_loot and _G.UnitExists("target") and not _G.UnitIsFriend("player", "target") and _G.UnitIsDead("target") then jcallahan@1: if _G.UnitIsPlayer("target") or _G.UnitPlayerControlled("target") then jcallahan@1: return false jcallahan@1: end jcallahan@1: local unit_type, id_num = WDP:ParseGUID(_G.UnitGUID("target")) jcallahan@1: action_data.id_num = id_num jcallahan@1: end jcallahan@1: return true jcallahan@1: end, jcallahan@1: } jcallahan@1: jcallahan@1: local LOOT_UPDATE_FUNCS = { jcallahan@1: [AF.NPC] = function() jcallahan@1: local npc = db.npcs[action_data.id_num] jcallahan@1: jcallahan@1: if not npc then jcallahan@1: db.npcs[action_data.id_num] = {} jcallahan@1: npc = db.npcs[action_data.id_num] jcallahan@1: end jcallahan@1: npc.drops = npc.drops or {} jcallahan@1: jcallahan@1: for index = 1, #action_data.drops do jcallahan@1: table.insert(npc.drops, action_data.drops[index]) jcallahan@1: end jcallahan@1: end, jcallahan@1: } jcallahan@1: jcallahan@1: jcallahan@1: function WDP:LOOT_OPENED() jcallahan@1: if not action_data.type then jcallahan@1: action_data.type = AF.NPC jcallahan@1: end jcallahan@1: local verify_func = LOOT_VERIFY_FUNCS[action_data.type] jcallahan@1: local update_func = LOOT_UPDATE_FUNCS[action_data.type] jcallahan@1: jcallahan@1: if not verify_func or not update_func or not verify_func() then jcallahan@1: return jcallahan@1: end jcallahan@1: jcallahan@1: local loot_registry = {} jcallahan@1: action_data.drops = {} jcallahan@1: jcallahan@1: for loot_slot = 1, _G.GetNumLootItems() do jcallahan@1: local texture, item, quantity, quality, locked = _G.GetLootSlotInfo(loot_slot) jcallahan@1: jcallahan@1: if _G.LootSlotIsItem(loot_slot) then jcallahan@1: local item_id = ItemLinkToID(_G.GetLootSlotLink(loot_slot)) jcallahan@1: loot_registry[item_id] = (loot_registry[item_id]) or 0 + quantity jcallahan@1: elseif _G.LootSlotIsCoin(loot_slot) then jcallahan@1: table.insert(action_data.drops, ("money:%d"):format(_toCopper(item))) jcallahan@1: elseif _G.LootSlotIsCurrency(loot_slot) then jcallahan@1: end jcallahan@1: end jcallahan@1: jcallahan@1: for item_id, quantity in pairs(loot_registry) do jcallahan@1: table.insert(action_data.drops, ("%d:%d"):format(item_id, quantity)) jcallahan@1: end jcallahan@1: update_func() jcallahan@0: end jcallahan@0: jcallahan@0: jcallahan@1: function WDP:UNIT_SPELLCAST_SENT(event_name, unit_id, spell_name, spell_rank, target_name, spell_line) jcallahan@1: if private.tracked_line or unit_id ~= "player" then jcallahan@1: return jcallahan@1: end jcallahan@1: local spell_label = private.SPELL_LABELS_BY_NAME[spell_name] jcallahan@1: jcallahan@1: if not spell_label then jcallahan@1: return jcallahan@1: end jcallahan@1: action_data.type = nil -- This will be set as appropriate below jcallahan@1: jcallahan@1: local tt_item_name, tt_item_link = _G.GameTooltip:GetItem() jcallahan@1: local tt_unit_name, tt_unit_id = _G.GameTooltip:GetUnit() jcallahan@1: jcallahan@1: if not tt_unit_name and _G.UnitName("target") == target_name then jcallahan@1: tt_unit_name = target_name jcallahan@1: tt_unit_id = "target" jcallahan@1: end jcallahan@1: local spell_flags = private.SPELL_FLAGS_BY_LABEL[spell_label] jcallahan@1: jcallahan@1: if not tt_item_name and not tt_unit_name then jcallahan@1: if target_name == "" then jcallahan@1: return jcallahan@1: end jcallahan@1: jcallahan@1: local zone_name, x, y, map_level = CurrentLocationData() jcallahan@1: jcallahan@1: if bit.band(spell_flags, AF.OBJECT) == AF.OBJECT then jcallahan@1: action_data.map_level = map_level jcallahan@1: action_data.name = target_name jcallahan@1: action_data.type = AF.OBJECT jcallahan@1: action_data.x = x jcallahan@1: action_data.y = y jcallahan@1: action_data.zone = zone_name jcallahan@1: print("Found spell flagged for OBJECT") jcallahan@1: elseif bit.band(spell_flags, AF.ZONE) == AF.ZONE then jcallahan@1: print("Found spell flagged for ZONE") jcallahan@1: end jcallahan@1: elseif tt_unit_name and not tt_item_name then jcallahan@1: if bit.band(spell_flags, AF.NPC) == AF.NPC then jcallahan@1: print("Found spell flagged for NPC") jcallahan@1: end jcallahan@1: elseif bit.band(spell_flags, AF.ITEM) == AF.ITEM then jcallahan@1: print("Found spell flagged for ITEM") jcallahan@1: else jcallahan@1: print(("%s: We have an issue with types and flags."), event_name) jcallahan@1: end jcallahan@1: jcallahan@1: print(("%s: '%s', '%s', '%s', '%s', '%s'"):format(event_name, unit_id, spell_name, spell_rank, target_name, spell_line)) jcallahan@1: private.tracked_line = spell_line jcallahan@0: end jcallahan@0: jcallahan@0: jcallahan@1: function WDP:UNIT_SPELLCAST_SUCCEEDED(event_name, unit_id, spell_name, spell_rank, spell_line, spell_id) jcallahan@1: if unit_id ~= "player" then jcallahan@1: return jcallahan@1: end jcallahan@1: jcallahan@1: if action_data.type == AF.OBJECT then jcallahan@1: end jcallahan@1: jcallahan@1: if private.SPELL_LABELS_BY_NAME[spell_name] then jcallahan@1: print(("%s: '%s', '%s', '%s', '%s', '%s'"):format(event_name, unit_id, spell_name, spell_rank, spell_line, spell_id)) jcallahan@1: end jcallahan@1: private.tracked_line = nil jcallahan@0: end jcallahan@0: jcallahan@1: function WDP:HandleSpellFailure(event_name, unit_id, spell_name, spell_rank, spell_line, spell_id) jcallahan@1: if unit_id ~= "player" then jcallahan@1: return jcallahan@1: end jcallahan@0: jcallahan@1: if private.tracked_line == spell_line then jcallahan@1: private.tracked_line = nil jcallahan@1: end jcallahan@0: end