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@4: local DatamineTT = _G.CreateFrame("GameTooltip", "WDPDatamineTT", _G.UIParent, "GameTooltipTemplate") jcallahan@5: DatamineTT:SetOwner(_G.WorldFrame, "ANCHOR_NONE") jcallahan@5: 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@17: zones = {}, jcallahan@0: } jcallahan@0: } jcallahan@0: jcallahan@0: jcallahan@1: local EVENT_MAPPING = { jcallahan@23: COMBAT_LOG_EVENT_UNFILTERED = true, jcallahan@18: COMBAT_TEXT_UPDATE = true, jcallahan@13: LOOT_CLOSED = true, jcallahan@1: LOOT_OPENED = true, jcallahan@7: MERCHANT_SHOW = "UpdateMerchantItems", jcallahan@7: MERCHANT_UPDATE = "UpdateMerchantItems", jcallahan@25: PET_BAR_UPDATE = true, jcallahan@2: PLAYER_TARGET_CHANGED = true, jcallahan@9: QUEST_COMPLETE = true, jcallahan@9: QUEST_DETAIL = true, jcallahan@9: QUEST_LOG_UPDATE = true, jcallahan@4: 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: } jcallahan@0: jcallahan@4: jcallahan@1: local AF = private.ACTION_TYPE_FLAGS jcallahan@0: jcallahan@4: jcallahan@0: ----------------------------------------------------------------------- jcallahan@0: -- Local variables. jcallahan@0: ----------------------------------------------------------------------- jcallahan@0: local db jcallahan@0: local durability_timer_handle jcallahan@2: local target_location_timer_handle jcallahan@1: local action_data = {} jcallahan@20: local faction_names = {} jcallahan@0: jcallahan@1: jcallahan@1: ----------------------------------------------------------------------- jcallahan@1: -- Helper Functions. jcallahan@1: ----------------------------------------------------------------------- jcallahan@19: local function DBEntry(data_type, unit_id) jcallahan@19: if not data_type or not unit_id then jcallahan@6: return jcallahan@6: end jcallahan@19: local unit = db[data_type][unit_id] jcallahan@6: jcallahan@10: if not unit then jcallahan@19: db[data_type][unit_id] = {} jcallahan@19: unit = db[data_type][unit_id] jcallahan@6: end jcallahan@10: return unit jcallahan@6: end jcallahan@6: jcallahan@6: jcallahan@22: local function InstanceDifficultyToken() jcallahan@22: local _, instance_type, instance_difficulty, difficulty_name, _, _, is_dynamic = _G.GetInstanceInfo() jcallahan@22: if difficulty_name == "" then jcallahan@22: difficulty_name = "NONE" jcallahan@22: end jcallahan@22: return ("%s:%s:%s"):format(instance_type:upper(), difficulty_name:upper():gsub(" ", "_"), _G.tostring(is_dynamic)) jcallahan@22: end jcallahan@22: jcallahan@22: 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@24: return _G.GetRealZoneText(), _G.GetCurrentMapAreaID(), ("%.2f"):format(x * 100), ("%.2f"):format(y * 100), map_level, InstanceDifficultyToken() 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@7: return tonumber(item_link:match("item:(%d+)")) jcallahan@1: end jcallahan@0: jcallahan@4: jcallahan@4: do jcallahan@4: local UNIT_TYPE_BITMASK = 0x007 jcallahan@4: jcallahan@4: function WDP:ParseGUID(guid) jcallahan@5: if not guid then jcallahan@5: return jcallahan@5: end jcallahan@4: local types = private.UNIT_TYPES jcallahan@4: local unit_type = _G.bit.band(tonumber(guid:sub(1, 5)), UNIT_TYPE_BITMASK) jcallahan@4: jcallahan@10: if unit_type ~= types.PLAYER and unit_type ~= types.PET then jcallahan@4: return unit_type, tonumber(guid:sub(-12, -9), 16) jcallahan@4: end jcallahan@4: jcallahan@4: return unit_type jcallahan@4: end jcallahan@4: end -- do-block jcallahan@4: jcallahan@4: jcallahan@10: local function UpdateObjectLocation(identifier) jcallahan@10: if not identifier then jcallahan@10: return jcallahan@10: end jcallahan@24: local zone_name, area_id, x, y, map_level, instance_token = CurrentLocationData() jcallahan@19: local object = DBEntry("objects", identifier) jcallahan@11: object.locations = object.locations or {} jcallahan@10: jcallahan@24: local location_token = ("%s:%d"):format(zone_name, area_id) jcallahan@24: jcallahan@24: if not object.locations[location_token] then jcallahan@24: object.locations[location_token] = {} jcallahan@10: end jcallahan@24: object.locations[location_token][("%s:%s:%s:%s"):format(instance_token, map_level, x, y)] = true jcallahan@10: end jcallahan@10: jcallahan@10: jcallahan@19: local function HandleItemUse(item_link, bag_index, slot_index) jcallahan@19: if not item_link then jcallahan@19: return jcallahan@19: end jcallahan@19: local item_id = ItemLinkToID(item_link) jcallahan@19: jcallahan@19: if not bag_index or not slot_index then jcallahan@19: for new_bag_index = 0, _G.NUM_BAG_FRAMES do jcallahan@19: for new_slot_index = 1, _G.GetContainerNumSlots(new_bag_index) do jcallahan@19: if item_id == ItemLinkToID(_G.GetContainerItemLink(new_bag_index, new_slot_index)) then jcallahan@19: bag_index = new_bag_index jcallahan@19: slot_index = new_slot_index jcallahan@19: break jcallahan@19: end jcallahan@19: end jcallahan@19: end jcallahan@19: end jcallahan@19: jcallahan@19: if not bag_index or not slot_index then jcallahan@19: return jcallahan@19: end jcallahan@19: local _, _, _, _, _, is_lootable = _G.GetContainerItemInfo(bag_index, slot_index) jcallahan@19: jcallahan@19: if not is_lootable then jcallahan@19: return jcallahan@19: end jcallahan@19: DatamineTT:ClearLines() jcallahan@19: DatamineTT:SetBagItem(bag_index, slot_index) jcallahan@19: jcallahan@19: for line_index = 1, DatamineTT:NumLines() do jcallahan@19: local current_line = _G["WDPDatamineTTTextLeft" .. line_index] jcallahan@19: jcallahan@19: if not current_line then jcallahan@19: break jcallahan@19: end jcallahan@19: jcallahan@19: if current_line:GetText() == _G.ITEM_OPENABLE then jcallahan@19: table.wipe(action_data) jcallahan@19: action_data.type = AF.ITEM jcallahan@19: action_data.item_id = item_id jcallahan@25: action_data.label = "contains" jcallahan@19: break jcallahan@19: end jcallahan@19: end jcallahan@19: end jcallahan@19: jcallahan@19: jcallahan@20: local function UpdateFactionNames() jcallahan@20: for faction_index = 1, 1000 do jcallahan@20: local faction_name, _, _, _, _, _, _, _, is_header = _G.GetFactionInfo(faction_index) jcallahan@20: jcallahan@20: if faction_name and not is_header then jcallahan@20: faction_names[faction_name] = true jcallahan@20: elseif not faction_name then jcallahan@20: break jcallahan@20: end jcallahan@20: end jcallahan@20: end jcallahan@20: jcallahan@20: 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@14: jcallahan@14: local raw_db = _G["WoWDBProfilerData"] jcallahan@14: jcallahan@18: local build_num = tonumber(private.build_num) jcallahan@14: jcallahan@14: if raw_db.build_num and raw_db.build_num < build_num then jcallahan@14: for entry in pairs(DATABASE_DEFAULTS.global) do jcallahan@14: db[entry] = {} jcallahan@14: end jcallahan@14: raw_db.build_num = build_num jcallahan@14: elseif not raw_db.build_num then jcallahan@14: raw_db.build_num = build_num jcallahan@14: end 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@2: target_location_timer_handle = self:ScheduleRepeatingTimer("UpdateTargetLocation", 0.2) jcallahan@19: jcallahan@19: _G.hooksecurefunc("UseContainerItem", function(bag_index, slot_index, target_unit) jcallahan@19: if target_unit then jcallahan@19: return jcallahan@19: end jcallahan@19: HandleItemUse(_G.GetContainerItemLink(bag_index, slot_index), bag_index, slot_index) jcallahan@19: end) jcallahan@19: jcallahan@19: _G.hooksecurefunc("UseItemByName", function(identifier, target_unit) jcallahan@19: if target_unit then jcallahan@19: return jcallahan@19: end jcallahan@19: local _, item_link = _G.GetItemInfo(identifier) jcallahan@19: HandleItemUse(item_link) jcallahan@19: end) 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@2: function WDP:UpdateTargetLocation() jcallahan@22: local is_dead = _G.UnitIsDead("target") jcallahan@22: jcallahan@22: if not _G.UnitExists("target") or _G.UnitPlayerControlled("target") or (_G.UnitIsTapped("target") and not is_dead) then jcallahan@2: return jcallahan@2: end jcallahan@2: jcallahan@2: for index = 1, 4 do jcallahan@2: if not _G.CheckInteractDistance("target", index) then jcallahan@2: return jcallahan@2: end jcallahan@2: end jcallahan@22: local target_guid = _G.UnitGUID("target") jcallahan@22: local unit_type, unit_idnum = self:ParseGUID(target_guid) jcallahan@2: jcallahan@2: if unit_type ~= private.UNIT_TYPES.NPC or not unit_idnum then jcallahan@2: return jcallahan@2: end jcallahan@24: local zone_name, area_id, x, y, map_level, instance_token = CurrentLocationData() jcallahan@22: local npc_data = DBEntry("npcs", unit_idnum).encounter_data[("level_%d"):format(_G.UnitLevel("target"))] jcallahan@6: npc_data.locations = npc_data.locations or {} jcallahan@2: jcallahan@24: local location_token = ("%s:%d"):format(zone_name, area_id) jcallahan@24: jcallahan@24: if not npc_data.locations[location_token] then jcallahan@24: npc_data.locations[location_token] = {} jcallahan@2: end jcallahan@22: jcallahan@22: -- Only record corpse location if there is no entry for this GUID. jcallahan@24: if is_dead and npc_data.locations[location_token][target_guid] then jcallahan@22: return jcallahan@22: end jcallahan@24: npc_data.locations[location_token][target_guid] = ("%s:%s:%s:%s"):format(instance_token, map_level, x, y) jcallahan@2: end jcallahan@2: jcallahan@2: jcallahan@0: ----------------------------------------------------------------------- jcallahan@0: -- Event handlers. jcallahan@0: ----------------------------------------------------------------------- jcallahan@23: do jcallahan@23: local EXTRACT_GAS_SPELL_ID = 30427 jcallahan@23: local FLAGS_NPC = bit.bor(_G.COMBATLOG_OBJECT_TYPE_GUARDIAN, _G.COMBATLOG_OBJECT_CONTROL_NPC) jcallahan@23: local FLAGS_NPC_CONTROL = bit.bor(_G.COMBATLOG_OBJECT_AFFILIATION_OUTSIDER, _G.COMBATLOG_OBJECT_CONTROL_NPC) jcallahan@23: jcallahan@23: jcallahan@23: local function RecordNPCSpell(sub_event, source_guid, source_name, source_flags, dest_guid, dest_name, dest_flags, spell_id, spell_name) jcallahan@23: if not spell_id then jcallahan@23: return jcallahan@23: end jcallahan@23: local source_type, source_id = WDP:ParseGUID(source_guid) jcallahan@23: jcallahan@23: if not source_id or source_type ~= private.UNIT_TYPES.NPC then jcallahan@23: return jcallahan@23: end jcallahan@23: jcallahan@23: if bit.band(FLAGS_NPC_CONTROL, source_flags) == FLAGS_NPC_CONTROL and bit.band(FLAGS_NPC, source_flags) ~= 0 then jcallahan@23: local npc = DBEntry("npcs", source_id) jcallahan@23: local instance_token = InstanceDifficultyToken() jcallahan@23: npc.spells = npc.spells or {} jcallahan@23: npc.spells[instance_token] = npc.spells[instance_token] or {} jcallahan@23: npc.spells[instance_token][spell_id] = true jcallahan@23: end jcallahan@23: end jcallahan@23: jcallahan@23: jcallahan@23: local COMBAT_LOG_FUNCS = { jcallahan@23: SPELL_AURA_APPLIED = RecordNPCSpell, jcallahan@23: SPELL_CAST_START = RecordNPCSpell, jcallahan@23: SPELL_CAST_SUCCESS = RecordNPCSpell, jcallahan@23: UNIT_DISSIPATES = function(sub_event, source_guid, source_name, source_flags, dest_guid, dest_name, dest_flags) jcallahan@23: -- TODO: Write this. jcallahan@23: end, jcallahan@23: } jcallahan@23: jcallahan@23: jcallahan@23: function WDP:COMBAT_LOG_EVENT_UNFILTERED(event, time_stamp, sub_event, hide_caster, source_guid, source_name, source_flags, source_raid_flags, dest_guid, dest_name, dest_flags, dest_raid_flags, ...) jcallahan@23: local combat_log_func = COMBAT_LOG_FUNCS[sub_event] jcallahan@23: jcallahan@23: if not combat_log_func then jcallahan@23: return jcallahan@23: end jcallahan@23: combat_log_func(sub_event, source_guid, source_name, source_flags, dest_guid, dest_name, dest_flags, ...) jcallahan@23: end jcallahan@23: end -- do-block jcallahan@23: jcallahan@23: jcallahan@18: function WDP:COMBAT_TEXT_UPDATE(event, message_type, faction_name, amount) jcallahan@21: local npc = DBEntry("npcs", action_data.id_num) jcallahan@21: jcallahan@21: if not npc then jcallahan@21: return jcallahan@21: end jcallahan@22: npc.encounter_data[action_data.npc_level].reputations = npc.encounter_data[action_data.npc_level].reputations or {} jcallahan@22: npc.encounter_data[action_data.npc_level].reputations[faction_name] = amount jcallahan@18: end jcallahan@18: jcallahan@18: jcallahan@13: function WDP:LOOT_CLOSED() jcallahan@18: -- table.wipe(action_data) jcallahan@0: end jcallahan@0: jcallahan@0: jcallahan@13: do jcallahan@13: local re_gold = _G.GOLD_AMOUNT:gsub("%%d", "(%%d+)") jcallahan@13: local re_silver = _G.SILVER_AMOUNT:gsub("%%d", "(%%d+)") jcallahan@13: local re_copper = _G.COPPER_AMOUNT:gsub("%%d", "(%%d+)") jcallahan@13: jcallahan@13: jcallahan@13: local function _moneyMatch(money, re) jcallahan@13: return money:match(re) or 0 jcallahan@1: end jcallahan@1: jcallahan@0: jcallahan@13: local function _toCopper(money) jcallahan@13: if not money then jcallahan@13: return 0 jcallahan@13: end jcallahan@0: jcallahan@13: return _moneyMatch(money, re_gold) * 10000 + _moneyMatch(money, re_silver) * 100 + _moneyMatch(money, re_copper) jcallahan@1: end jcallahan@1: jcallahan@1: jcallahan@13: local LOOT_VERIFY_FUNCS = { jcallahan@16: [AF.ITEM] = function() jcallahan@16: local locked_item_id jcallahan@16: jcallahan@16: for bag_index = 0, _G.NUM_BAG_FRAMES do jcallahan@16: for slot_index = 1, _G.GetContainerNumSlots(bag_index) do jcallahan@16: local _, _, is_locked = _G.GetContainerItemInfo(bag_index, slot_index) jcallahan@16: jcallahan@16: if is_locked then jcallahan@16: locked_item_id = ItemLinkToID(_G.GetContainerItemLink(bag_index, slot_index)) jcallahan@16: end jcallahan@16: end jcallahan@16: end jcallahan@16: jcallahan@16: if not locked_item_id or (action_data.item_id and action_data.item_id ~= locked_item_id) then jcallahan@16: return false jcallahan@16: end jcallahan@16: action_data.item_id = locked_item_id jcallahan@16: return true jcallahan@16: end, jcallahan@13: [AF.NPC] = function() jcallahan@17: if not _G.UnitExists("target") or _G.UnitIsFriend("player", "target") or _G.UnitIsPlayer("target") or _G.UnitPlayerControlled("target") then jcallahan@15: return false jcallahan@13: end jcallahan@15: local unit_type, id_num = WDP:ParseGUID(_G.UnitGUID("target")) jcallahan@15: action_data.id_num = id_num jcallahan@13: return true jcallahan@13: end, jcallahan@14: [AF.OBJECT] = true, jcallahan@17: [AF.ZONE] = function() jcallahan@25: return action_data.label and _G.IsFishingLoot() jcallahan@17: end, jcallahan@13: } jcallahan@13: jcallahan@13: jcallahan@22: local function GenericLootUpdate(data_type) jcallahan@22: local entry = DBEntry(data_type, action_data.id_num) jcallahan@22: jcallahan@22: if not entry then jcallahan@22: return jcallahan@22: end jcallahan@25: local loot_type = action_data.label or "drops" jcallahan@22: entry[loot_type] = entry[loot_type] or {} jcallahan@22: jcallahan@22: for index = 1, #action_data.loot_list do jcallahan@22: table.insert(entry[loot_type], action_data.loot_list[index]) jcallahan@22: end jcallahan@22: end jcallahan@22: jcallahan@22: jcallahan@13: local LOOT_UPDATE_FUNCS = { jcallahan@16: [AF.ITEM] = function() jcallahan@19: local item = DBEntry("items", action_data.item_id) jcallahan@25: local loot_type = action_data.label or "drops" jcallahan@16: item[loot_type] = item[loot_type] or {} jcallahan@16: jcallahan@17: for index = 1, #action_data.loot_list do jcallahan@17: table.insert(item[loot_type], action_data.loot_list[index]) jcallahan@16: end jcallahan@16: end, jcallahan@13: [AF.NPC] = function() jcallahan@22: GenericLootUpdate("npcs") jcallahan@13: end, jcallahan@13: [AF.OBJECT] = function() jcallahan@22: GenericLootUpdate("objects") jcallahan@17: end, jcallahan@17: [AF.ZONE] = function() jcallahan@25: local loot_type = action_data.label or "drops" jcallahan@19: local zone = DBEntry("zones", action_data.zone) jcallahan@17: zone[loot_type] = zone[loot_type] or {} jcallahan@17: jcallahan@22: local location_data = ("%s:%s:%s:%s"):format(action_data.instance_token, action_data.map_level, action_data.x, action_data.y) jcallahan@17: local loot_data = zone[loot_type][location_data] jcallahan@17: jcallahan@17: if not loot_data then jcallahan@17: zone[loot_type][location_data] = {} jcallahan@17: loot_data = zone[loot_type][location_data] jcallahan@17: end jcallahan@17: jcallahan@17: for index = 1, #action_data.loot_list do jcallahan@17: table.insert(loot_data, action_data.loot_list[index]) jcallahan@13: end jcallahan@13: end, jcallahan@13: } jcallahan@13: jcallahan@13: jcallahan@13: function WDP:LOOT_OPENED() jcallahan@18: if action_data.looting then jcallahan@18: return jcallahan@18: end jcallahan@18: jcallahan@13: if not action_data.type then jcallahan@13: action_data.type = AF.NPC jcallahan@1: end jcallahan@13: local verify_func = LOOT_VERIFY_FUNCS[action_data.type] jcallahan@13: local update_func = LOOT_UPDATE_FUNCS[action_data.type] jcallahan@13: jcallahan@14: if not verify_func or not update_func then jcallahan@13: return jcallahan@13: end jcallahan@13: jcallahan@14: if _G.type(verify_func) == "function" and not verify_func() then jcallahan@14: return jcallahan@14: end jcallahan@18: -- TODO: Remove this check once the MoP client goes live jcallahan@18: local wow_version = private.wow_version jcallahan@13: local loot_registry = {} jcallahan@17: action_data.loot_list = {} jcallahan@18: action_data.looting = true jcallahan@13: jcallahan@18: if wow_version == "5.0.1" then jcallahan@18: for loot_slot = 1, _G.GetNumLootItems() do jcallahan@18: local icon_texture, item_text, quantity, quality, locked = _G.GetLootSlotInfo(loot_slot) jcallahan@13: jcallahan@18: local slot_type = _G.GetLootSlotType(loot_slot) jcallahan@18: jcallahan@18: if slot_type == _G.LOOT_SLOT_ITEM then jcallahan@18: local item_id = ItemLinkToID(_G.GetLootSlotLink(loot_slot)) jcallahan@18: loot_registry[item_id] = (loot_registry[item_id]) or 0 + quantity jcallahan@18: elseif slot_type == _G.LOOT_SLOT_MONEY then jcallahan@18: table.insert(action_data.loot_list, ("money:%d"):format(_toCopper(item_text))) jcallahan@18: elseif slot_type == _G.LOOT_SLOT_CURRENCY then jcallahan@18: table.insert(action_data.loot_list, ("currency:%d:%s"):format(quantity, icon_texture:match("[^\\]+$"):lower())) jcallahan@18: end jcallahan@18: end jcallahan@18: else jcallahan@18: for loot_slot = 1, _G.GetNumLootItems() do jcallahan@18: local icon_texture, item_text, quantity, quality, locked = _G.GetLootSlotInfo(loot_slot) jcallahan@18: if _G.LootSlotIsItem(loot_slot) then jcallahan@18: local item_id = ItemLinkToID(_G.GetLootSlotLink(loot_slot)) jcallahan@18: loot_registry[item_id] = (loot_registry[item_id]) or 0 + quantity jcallahan@18: elseif _G.LootSlotIsCoin(loot_slot) then jcallahan@18: table.insert(action_data.loot_list, ("money:%d"):format(_toCopper(item_text))) jcallahan@18: elseif _G.LootSlotIsCurrency(loot_slot) then jcallahan@18: table.insert(action_data.loot_list, ("currency:%d:%s"):format(quantity, icon_texture:match("[^\\]+$"):lower())) jcallahan@18: end jcallahan@13: end jcallahan@13: end jcallahan@13: jcallahan@13: for item_id, quantity in pairs(loot_registry) do jcallahan@17: table.insert(action_data.loot_list, ("%d:%d"):format(item_id, quantity)) jcallahan@13: end jcallahan@13: update_func() jcallahan@1: end jcallahan@13: end -- do-block jcallahan@0: jcallahan@0: jcallahan@5: local POINT_MATCH_PATTERNS = { jcallahan@5: ("^%s$"):format(_G.ITEM_REQ_ARENA_RATING:gsub("%%d", "(%%d+)")), -- May no longer be necessary jcallahan@5: ("^%s$"):format(_G.ITEM_REQ_ARENA_RATING_3V3:gsub("%%d", "(%%d+)")), -- May no longer be necessary jcallahan@5: ("^%s$"):format(_G.ITEM_REQ_ARENA_RATING_5V5:gsub("%%d", "(%%d+)")), -- May no longer be necessary jcallahan@5: ("^%s$"):format(_G.ITEM_REQ_ARENA_RATING_BG:gsub("%%d", "(%%d+)")), jcallahan@5: ("^%s$"):format(_G.ITEM_REQ_ARENA_RATING_3V3_BG:gsub("%%d", "(%%d+)")), jcallahan@5: } jcallahan@5: jcallahan@5: jcallahan@7: function WDP:UpdateMerchantItems() jcallahan@4: local unit_type, unit_idnum = self:ParseGUID(_G.UnitGUID("target")) jcallahan@4: jcallahan@4: if unit_type ~= private.UNIT_TYPES.NPC or not unit_idnum then jcallahan@4: return jcallahan@4: end jcallahan@19: local merchant = DBEntry("npcs", unit_idnum) jcallahan@6: merchant.sells = merchant.sells or {} jcallahan@5: jcallahan@5: for item_index = 1, _G.GetMerchantNumItems() do jcallahan@5: local _, _, copper_price, stack_size, num_available, _, extended_cost = _G.GetMerchantItemInfo(item_index) jcallahan@5: local item_id = ItemLinkToID(_G.GetMerchantItemLink(item_index)) jcallahan@5: jcallahan@5: if item_id and item_id > 0 then jcallahan@5: local price_string = copper_price jcallahan@5: jcallahan@5: if extended_cost then jcallahan@5: local bg_points = 0 jcallahan@5: local personal_points = 0 jcallahan@5: jcallahan@5: DatamineTT:ClearLines() jcallahan@5: DatamineTT:SetMerchantItem(item_index) jcallahan@5: jcallahan@5: for line_index = 1, DatamineTT:NumLines() do jcallahan@5: local current_line = _G["WDPDatamineTTTextLeft" .. line_index] jcallahan@5: jcallahan@5: if not current_line then jcallahan@5: break jcallahan@5: end jcallahan@5: local breakout jcallahan@5: jcallahan@5: for match_index = 1, #POINT_MATCH_PATTERNS do jcallahan@5: local match1, match2 = current_line:GetText():match(POINT_MATCH_PATTERNS[match_index]) jcallahan@5: personal_points = personal_points + (match1 or 0) jcallahan@5: bg_points = bg_points + (match2 or 0) jcallahan@5: jcallahan@5: if match1 or match2 then jcallahan@5: breakout = true jcallahan@5: break jcallahan@5: end jcallahan@5: end jcallahan@5: jcallahan@5: if breakout then jcallahan@5: break jcallahan@5: end jcallahan@5: end jcallahan@5: local currency_list = {} jcallahan@5: jcallahan@5: price_string = ("%s:%s:%s"):format(price_string, bg_points, personal_points) jcallahan@5: jcallahan@5: for cost_index = 1, _G.GetMerchantItemCostInfo(item_index) do jcallahan@5: local icon_texture, amount_required, currency_link = _G.GetMerchantItemCostItem(item_index, cost_index) jcallahan@5: local currency_id = currency_link and ItemLinkToID(currency_link) or nil jcallahan@5: jcallahan@5: if not currency_id or currency_id < 1 then jcallahan@5: if not icon_texture then jcallahan@5: return jcallahan@5: end jcallahan@5: currency_id = icon_texture:match("[^\\]+$"):lower() jcallahan@5: end jcallahan@5: currency_list[#currency_list + 1] = ("(%s:%s)"):format(amount_required, currency_id) jcallahan@5: end jcallahan@5: jcallahan@5: for currency_index = 1, #currency_list do jcallahan@5: price_string = ("%s:%s"):format(price_string, currency_list[currency_index]) jcallahan@5: end jcallahan@5: end jcallahan@6: merchant.sells[("%s:%s:[%s]"):format(item_id, stack_size, price_string)] = num_available jcallahan@5: end jcallahan@4: end jcallahan@14: jcallahan@14: if _G.CanMerchantRepair() then jcallahan@14: merchant.can_repair = true jcallahan@14: end jcallahan@4: end jcallahan@4: jcallahan@4: jcallahan@25: function WDP:PET_BAR_UPDATE() jcallahan@25: if not action_data.label or not action_data.label == "mind_control" then jcallahan@25: return jcallahan@25: end jcallahan@25: local unit_type, unit_idnum = self:ParseGUID(_G.UnitGUID("pet")) jcallahan@25: jcallahan@25: if unit_type ~= private.UNIT_TYPES.NPC or not unit_idnum then jcallahan@25: return jcallahan@25: end jcallahan@25: DBEntry("npcs", unit_idnum).mind_control = true jcallahan@25: table.wipe(action_data) jcallahan@25: end jcallahan@25: jcallahan@25: jcallahan@18: do jcallahan@18: local GENDER_NAMES = { jcallahan@18: "UNKNOWN", jcallahan@18: "MALE", jcallahan@18: "FEMALE", jcallahan@18: } jcallahan@2: jcallahan@2: jcallahan@18: local REACTION_NAMES = { jcallahan@18: "HATED", jcallahan@18: "HOSTILE", jcallahan@18: "UNFRIENDLY", jcallahan@18: "NEUTRAL", jcallahan@18: "FRIENDLY", jcallahan@18: "HONORED", jcallahan@18: "REVERED", jcallahan@18: "EXALTED", jcallahan@18: } jcallahan@2: jcallahan@2: jcallahan@18: local POWER_TYPE_NAMES = { jcallahan@18: ["0"] = "MANA", jcallahan@18: ["1"] = "RAGE", jcallahan@18: ["2"] = "FOCUS", jcallahan@18: ["3"] = "ENERGY", jcallahan@18: ["6"] = "RUNIC_POWER", jcallahan@18: } jcallahan@2: jcallahan@2: jcallahan@18: function WDP:PLAYER_TARGET_CHANGED() jcallahan@18: if not _G.UnitExists("target") or _G.UnitPlayerControlled("target") then jcallahan@18: return jcallahan@18: end jcallahan@18: local unit_type, unit_idnum = self:ParseGUID(_G.UnitGUID("target")) jcallahan@2: jcallahan@18: if unit_type ~= private.UNIT_TYPES.NPC or not unit_idnum then jcallahan@18: return jcallahan@18: end jcallahan@19: local npc = DBEntry("npcs", unit_idnum) jcallahan@18: local _, class_token = _G.UnitClass("target") jcallahan@18: npc.class = class_token jcallahan@20: jcallahan@20: UpdateFactionNames() jcallahan@20: DatamineTT:ClearLines() jcallahan@20: DatamineTT:SetUnit("target") jcallahan@20: jcallahan@20: for line_index = 1, DatamineTT:NumLines() do jcallahan@20: local current_line = _G["WDPDatamineTTTextLeft" .. line_index] jcallahan@20: jcallahan@20: if not current_line then jcallahan@20: break jcallahan@20: end jcallahan@20: local line_text = current_line:GetText() jcallahan@20: jcallahan@20: if faction_names[line_text] then jcallahan@20: npc.faction = line_text jcallahan@20: break jcallahan@20: end jcallahan@20: end jcallahan@26: npc.genders = npc.genders or {} jcallahan@26: npc.genders[GENDER_NAMES[_G.UnitSex("target")] or "UNDEFINED"] = true jcallahan@18: npc.is_pvp = _G.UnitIsPVP("target") and true or nil jcallahan@18: npc.reaction = ("%s:%s:%s"):format(_G.UnitLevel("player"), _G.UnitFactionGroup("player"), REACTION_NAMES[_G.UnitReaction("player", "target")]) jcallahan@22: npc.encounter_data = npc.encounter_data or {} jcallahan@2: jcallahan@18: local npc_level = ("level_%d"):format(_G.UnitLevel("target")) jcallahan@3: jcallahan@22: if not npc.encounter_data[npc_level] then jcallahan@22: npc.encounter_data[npc_level] = { jcallahan@18: max_health = _G.UnitHealthMax("target"), jcallahan@18: } jcallahan@3: jcallahan@18: local max_power = _G.UnitManaMax("target") jcallahan@18: jcallahan@18: if max_power > 0 then jcallahan@18: local power_type = _G.UnitPowerType("target") jcallahan@22: npc.encounter_data[npc_level].power = ("%s:%d"):format(POWER_TYPE_NAMES[_G.tostring(power_type)] or power_type, max_power) jcallahan@18: end jcallahan@3: end jcallahan@21: table.wipe(action_data) jcallahan@21: action_data.type = AF.NPC jcallahan@21: action_data.id_num = unit_idnum jcallahan@21: action_data.npc_level = npc_level jcallahan@2: end jcallahan@18: end -- do-block jcallahan@2: jcallahan@12: do jcallahan@12: local function UpdateQuestJuncture(point) jcallahan@12: local unit_name = _G.UnitName("questnpc") jcallahan@9: jcallahan@12: if not unit_name then jcallahan@12: return jcallahan@12: end jcallahan@12: local unit_type, unit_id = WDP:ParseGUID(_G.UnitGUID("questnpc")) jcallahan@9: jcallahan@12: if unit_type == private.UNIT_TYPES.OBJECT then jcallahan@12: UpdateObjectLocation(unit_id) jcallahan@12: end jcallahan@19: local quest = DBEntry("quests", _G.GetQuestID()) jcallahan@12: quest[point] = quest[point] or {} jcallahan@12: quest[point][("%s:%d"):format(private.UNIT_TYPE_NAMES[unit_type + 1], unit_id)] = true jcallahan@24: jcallahan@24: return quest jcallahan@12: end jcallahan@10: jcallahan@12: jcallahan@12: function WDP:QUEST_COMPLETE() jcallahan@12: UpdateQuestJuncture("end") jcallahan@10: end jcallahan@10: jcallahan@12: jcallahan@12: function WDP:QUEST_DETAIL() jcallahan@24: local quest = UpdateQuestJuncture("begin") jcallahan@24: jcallahan@24: local _, class = _G.UnitClass("player") jcallahan@24: quest.classes = quest.classes or {} jcallahan@24: quest.classes[class] = true jcallahan@24: jcallahan@24: local _, race = _G.UnitRace("player") jcallahan@24: quest.races = quest.races or {} jcallahan@24: quest.races[race] = true jcallahan@10: end jcallahan@12: end -- do-block jcallahan@9: jcallahan@9: jcallahan@4: function WDP:QUEST_LOG_UPDATE() jcallahan@4: self:UnregisterEvent("QUEST_LOG_UPDATE") jcallahan@4: end jcallahan@4: jcallahan@4: jcallahan@4: function WDP:UNIT_QUEST_LOG_CHANGED(event, unit_id) jcallahan@4: if unit_id ~= "player" then jcallahan@4: return jcallahan@4: end jcallahan@4: self:RegisterEvent("QUEST_LOG_UPDATE") jcallahan@4: end jcallahan@4: jcallahan@4: 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@18: table.wipe(action_data) 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@16: if tt_unit_name and not tt_item_name then jcallahan@16: if bit.band(spell_flags, AF.NPC) == AF.NPC then jcallahan@16: if not tt_unit_id or tt_unit_name ~= target_name then jcallahan@16: return jcallahan@16: end jcallahan@16: action_data.type = AF.NPC jcallahan@25: action_data.label = spell_label:lower() jcallahan@25: action_data.unit_name = tt_unit_name jcallahan@16: end jcallahan@16: elseif bit.band(spell_flags, AF.ITEM) == AF.ITEM then jcallahan@16: action_data.type = AF.ITEM jcallahan@25: action_data.label = spell_label:lower() jcallahan@16: jcallahan@16: if tt_item_name and tt_item_name == target_name then jcallahan@16: action_data.item_id = ItemLinkToID(tt_item_link) jcallahan@16: elseif target_name and target_name ~= "" then jcallahan@16: local _, target_item_link = _G.GetItemInfo(target_name) jcallahan@16: action_data.item_id = ItemLinkToID(target_item_link) jcallahan@16: end jcallahan@16: elseif not tt_item_name and not tt_unit_name then jcallahan@24: local zone_name, area_id, x, y, map_level, instance_token = CurrentLocationData() jcallahan@1: jcallahan@22: action_data.instance_token = instance_token jcallahan@17: action_data.map_level = map_level jcallahan@17: action_data.name = target_name jcallahan@17: action_data.x = x jcallahan@17: action_data.y = y jcallahan@24: action_data.zone = ("%s:%d"):format(zone_name, area_id) jcallahan@17: jcallahan@1: if bit.band(spell_flags, AF.OBJECT) == AF.OBJECT then jcallahan@17: if target_name == "" then jcallahan@17: return jcallahan@17: end jcallahan@11: local identifier = ("%s:%s"):format(spell_label, target_name) jcallahan@11: UpdateObjectLocation(identifier) jcallahan@11: jcallahan@1: action_data.type = AF.OBJECT jcallahan@11: action_data.identifier = identifier jcallahan@1: elseif bit.band(spell_flags, AF.ZONE) == AF.ZONE then jcallahan@17: action_data.type = AF.ZONE jcallahan@25: action_data.label = spell_label:lower() jcallahan@1: end jcallahan@1: end jcallahan@1: jcallahan@18: -- 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@18: -- if private.SPELL_LABELS_BY_NAME[spell_name] then jcallahan@18: -- print(("%s: '%s', '%s', '%s', '%s', '%s'"):format(event_name, unit_id, spell_name, spell_rank, spell_line, spell_id)) jcallahan@18: -- 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