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@0: } jcallahan@0: } jcallahan@0: jcallahan@0: jcallahan@1: local EVENT_MAPPING = { jcallahan@1: LOOT_OPENED = true, jcallahan@7: MERCHANT_SHOW = "UpdateMerchantItems", jcallahan@7: MERCHANT_UPDATE = "UpdateMerchantItems", jcallahan@2: PLAYER_TARGET_CHANGED = 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@0: jcallahan@1: jcallahan@1: ----------------------------------------------------------------------- jcallahan@1: -- Helper Functions. jcallahan@1: ----------------------------------------------------------------------- jcallahan@6: local function NPCEntry(id_num) jcallahan@6: if not id_num then jcallahan@6: return jcallahan@6: end jcallahan@6: local npc = db.npcs[id_num] jcallahan@6: jcallahan@6: if not npc then jcallahan@6: db.npcs[id_num] = {} jcallahan@6: npc = db.npcs[id_num] jcallahan@6: end jcallahan@6: return npc jcallahan@6: end jcallahan@6: jcallahan@6: 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@2: return _G.GetRealZoneText(), ("%.2f"):format(x * 100), ("%.2f"):format(y * 100), 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@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@4: if unit_type ~= types.PLAYER or unit_type ~= types.OBJECT or 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@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@2: target_location_timer_handle = self:ScheduleRepeatingTimer("UpdateTargetLocation", 0.2) 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@2: if not _G.UnitExists("target") or _G.UnitPlayerControlled("target") or _G.UnitIsTapped("target") 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@2: jcallahan@2: local unit_type, unit_idnum = self:ParseGUID(_G.UnitGUID("target")) jcallahan@2: jcallahan@2: if unit_type ~= private.UNIT_TYPES.NPC or not unit_idnum then jcallahan@2: return jcallahan@2: end jcallahan@2: local zone_name, x, y, map_level = CurrentLocationData() jcallahan@6: local npc_data = NPCEntry(unit_idnum).stats[("level_%d"):format(_G.UnitLevel("target"))] jcallahan@6: npc_data.locations = npc_data.locations or {} jcallahan@2: jcallahan@2: if not npc_data.locations[zone_name] then jcallahan@2: npc_data.locations[zone_name] = {} jcallahan@2: end jcallahan@2: npc_data.locations[zone_name][("%s:%s:%s"):format(map_level, x, y)] = true jcallahan@2: end jcallahan@2: jcallahan@2: jcallahan@0: ----------------------------------------------------------------------- jcallahan@0: -- Event handlers. 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@4: jcallahan@1: local LOOT_UPDATE_FUNCS = { jcallahan@1: [AF.NPC] = function() jcallahan@6: local npc = NPCEntry(action_data.id_num) 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@2: 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@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@6: local merchant = NPCEntry(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@4: end jcallahan@4: jcallahan@4: jcallahan@2: local GENDER_NAMES = { jcallahan@2: "UNKNOWN", jcallahan@2: "MALE", jcallahan@2: "FEMALE", jcallahan@2: } jcallahan@2: jcallahan@2: jcallahan@2: local REACTION_NAMES = { jcallahan@2: "HATED", jcallahan@2: "HOSTILE", jcallahan@2: "UNFRIENDLY", jcallahan@2: "NEUTRAL", jcallahan@2: "FRIENDLY", jcallahan@2: "HONORED", jcallahan@2: "REVERED", jcallahan@2: "EXALTED", jcallahan@2: } jcallahan@2: jcallahan@2: jcallahan@2: local POWER_TYPE_NAMES = { jcallahan@2: ["0"] = "MANA", jcallahan@2: ["1"] = "RAGE", jcallahan@2: ["2"] = "FOCUS", jcallahan@2: ["3"] = "ENERGY", jcallahan@2: ["6"] = "RUNIC_POWER", jcallahan@2: } jcallahan@2: jcallahan@2: jcallahan@2: function WDP:PLAYER_TARGET_CHANGED() jcallahan@2: if not _G.UnitExists("target") or _G.UnitPlayerControlled("target") then jcallahan@2: return jcallahan@2: end jcallahan@2: local unit_type, unit_idnum = self:ParseGUID(_G.UnitGUID("target")) jcallahan@2: jcallahan@2: if unit_type ~= private.UNIT_TYPES.NPC or not unit_idnum then jcallahan@2: return jcallahan@2: end jcallahan@6: local npc = NPCEntry(unit_idnum) jcallahan@2: local _, class_token = _G.UnitClass("target") jcallahan@2: npc.class = class_token jcallahan@2: -- TODO: Add faction here jcallahan@2: npc.gender = GENDER_NAMES[_G.UnitSex("target")] or "UNDEFINED" jcallahan@3: npc.is_pvp = _G.UnitIsPVP("target") and true or nil jcallahan@2: npc.reaction = ("%s:%s:%s"):format(_G.UnitLevel("player"), _G.UnitFactionGroup("player"), REACTION_NAMES[_G.UnitReaction("player", "target")]) jcallahan@2: npc.stats = npc.stats or {} jcallahan@2: jcallahan@2: local npc_level = ("level_%d"):format(_G.UnitLevel("target")) jcallahan@2: jcallahan@2: if not npc.stats[npc_level] then jcallahan@2: npc.stats[npc_level] = { jcallahan@2: max_health = _G.UnitHealthMax("target"), jcallahan@2: } jcallahan@3: jcallahan@3: local max_power = _G.UnitManaMax("target") jcallahan@3: jcallahan@3: if max_power > 0 then jcallahan@3: local power_type = _G.UnitPowerType("target") jcallahan@3: npc.stats[npc_level].power = ("%s:%d"):format(POWER_TYPE_NAMES[_G.tostring(power_type)] or power_type, max_power) jcallahan@3: end jcallahan@2: end jcallahan@2: end jcallahan@2: jcallahan@2: 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@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@2: print(("Found spell flagged for OBJECT: %s (%s, %s)"):format(zone_name, x, y)) 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