| jcallahan@0 | 1 ----------------------------------------------------------------------- | 
| jcallahan@0 | 2 -- Upvalued Lua API. | 
| jcallahan@0 | 3 ----------------------------------------------------------------------- | 
| jcallahan@0 | 4 local _G = getfenv(0) | 
| jcallahan@0 | 5 | 
| jcallahan@0 | 6 local pairs = _G.pairs | 
| jcallahan@1 | 7 local tonumber = _G.tonumber | 
| jcallahan@1 | 8 | 
| jcallahan@1 | 9 local bit = _G.bit | 
| jcallahan@1 | 10 local math = _G.math | 
| jcallahan@1 | 11 local table = _G.table | 
| jcallahan@1 | 12 | 
| jcallahan@0 | 13 | 
| jcallahan@0 | 14 ----------------------------------------------------------------------- | 
| jcallahan@0 | 15 -- AddOn namespace. | 
| jcallahan@0 | 16 ----------------------------------------------------------------------- | 
| jcallahan@0 | 17 local ADDON_NAME, private = ... | 
| jcallahan@0 | 18 | 
| jcallahan@0 | 19 local LibStub = _G.LibStub | 
| jcallahan@0 | 20 local WDP = LibStub("AceAddon-3.0"):NewAddon(ADDON_NAME, "AceEvent-3.0", "AceTimer-3.0") | 
| jcallahan@0 | 21 | 
| jcallahan@4 | 22 local DatamineTT = _G.CreateFrame("GameTooltip", "WDPDatamineTT", _G.UIParent, "GameTooltipTemplate") | 
| jcallahan@5 | 23 DatamineTT:SetOwner(_G.WorldFrame, "ANCHOR_NONE") | 
| jcallahan@5 | 24 | 
| jcallahan@0 | 25 | 
| jcallahan@0 | 26 ----------------------------------------------------------------------- | 
| jcallahan@0 | 27 -- Local constants. | 
| jcallahan@0 | 28 ----------------------------------------------------------------------- | 
| jcallahan@0 | 29 local DATABASE_DEFAULTS = { | 
| jcallahan@0 | 30     global = { | 
| jcallahan@0 | 31         items = {}, | 
| jcallahan@0 | 32         npcs = {}, | 
| jcallahan@0 | 33         objects = {}, | 
| jcallahan@0 | 34         quests = {}, | 
| jcallahan@17 | 35         zones = {}, | 
| jcallahan@0 | 36     } | 
| jcallahan@0 | 37 } | 
| jcallahan@0 | 38 | 
| jcallahan@0 | 39 | 
| jcallahan@1 | 40 local EVENT_MAPPING = { | 
| jcallahan@23 | 41     COMBAT_LOG_EVENT_UNFILTERED = true, | 
| jcallahan@18 | 42     COMBAT_TEXT_UPDATE = true, | 
| jcallahan@1 | 43     LOOT_OPENED = true, | 
| jcallahan@7 | 44     MERCHANT_SHOW = "UpdateMerchantItems", | 
| jcallahan@7 | 45     MERCHANT_UPDATE = "UpdateMerchantItems", | 
| jcallahan@25 | 46     PET_BAR_UPDATE = true, | 
| jcallahan@2 | 47     PLAYER_TARGET_CHANGED = true, | 
| jcallahan@9 | 48     QUEST_COMPLETE = true, | 
| jcallahan@9 | 49     QUEST_DETAIL = true, | 
| jcallahan@9 | 50     QUEST_LOG_UPDATE = true, | 
| jcallahan@27 | 51     TRAINER_SHOW = true, | 
| jcallahan@4 | 52     UNIT_QUEST_LOG_CHANGED = true, | 
| jcallahan@1 | 53     UNIT_SPELLCAST_FAILED = "HandleSpellFailure", | 
| jcallahan@1 | 54     UNIT_SPELLCAST_FAILED_QUIET = "HandleSpellFailure", | 
| jcallahan@1 | 55     UNIT_SPELLCAST_INTERRUPTED = "HandleSpellFailure", | 
| jcallahan@1 | 56     UNIT_SPELLCAST_SENT = true, | 
| jcallahan@1 | 57     UNIT_SPELLCAST_SUCCEEDED = true, | 
| jcallahan@0 | 58 } | 
| jcallahan@0 | 59 | 
| jcallahan@4 | 60 | 
| jcallahan@1 | 61 local AF = private.ACTION_TYPE_FLAGS | 
| jcallahan@0 | 62 | 
| jcallahan@4 | 63 | 
| jcallahan@27 | 64 local PLAYER_CLASS = _G.select(2, _G.UnitClass("player")) | 
| jcallahan@27 | 65 | 
| jcallahan@27 | 66 | 
| jcallahan@0 | 67 ----------------------------------------------------------------------- | 
| jcallahan@0 | 68 -- Local variables. | 
| jcallahan@0 | 69 ----------------------------------------------------------------------- | 
| jcallahan@0 | 70 local db | 
| jcallahan@0 | 71 local durability_timer_handle | 
| jcallahan@2 | 72 local target_location_timer_handle | 
| jcallahan@1 | 73 local action_data = {} | 
| jcallahan@20 | 74 local faction_names = {} | 
| jcallahan@0 | 75 | 
| jcallahan@1 | 76 | 
| jcallahan@1 | 77 ----------------------------------------------------------------------- | 
| jcallahan@1 | 78 -- Helper Functions. | 
| jcallahan@1 | 79 ----------------------------------------------------------------------- | 
| jcallahan@19 | 80 local function DBEntry(data_type, unit_id) | 
| jcallahan@19 | 81     if not data_type or not unit_id then | 
| jcallahan@6 | 82         return | 
| jcallahan@6 | 83     end | 
| jcallahan@19 | 84     local unit = db[data_type][unit_id] | 
| jcallahan@6 | 85 | 
| jcallahan@10 | 86     if not unit then | 
| jcallahan@19 | 87         db[data_type][unit_id] = {} | 
| jcallahan@19 | 88         unit = db[data_type][unit_id] | 
| jcallahan@6 | 89     end | 
| jcallahan@10 | 90     return unit | 
| jcallahan@6 | 91 end | 
| jcallahan@6 | 92 | 
| jcallahan@6 | 93 | 
| jcallahan@22 | 94 local function InstanceDifficultyToken() | 
| jcallahan@22 | 95     local _, instance_type, instance_difficulty, difficulty_name, _, _, is_dynamic = _G.GetInstanceInfo() | 
| jcallahan@22 | 96     if difficulty_name == "" then | 
| jcallahan@22 | 97         difficulty_name = "NONE" | 
| jcallahan@22 | 98     end | 
| jcallahan@22 | 99     return ("%s:%s:%s"):format(instance_type:upper(), difficulty_name:upper():gsub(" ", "_"), _G.tostring(is_dynamic)) | 
| jcallahan@22 | 100 end | 
| jcallahan@22 | 101 | 
| jcallahan@22 | 102 | 
| jcallahan@1 | 103 local function CurrentLocationData() | 
| jcallahan@1 | 104     local map_level = _G.GetCurrentMapDungeonLevel() or 0 | 
| jcallahan@1 | 105     local x, y = _G.GetPlayerMapPosition("player") | 
| jcallahan@1 | 106 | 
| jcallahan@1 | 107     x = x or 0 | 
| jcallahan@1 | 108     y = y or 0 | 
| jcallahan@1 | 109 | 
| jcallahan@1 | 110     if x == 0 and y == 0 then | 
| jcallahan@1 | 111         for level_index = 1, _G.GetNumDungeonMapLevels() do | 
| jcallahan@1 | 112             _G.SetDungeonMapLevel(level_index) | 
| jcallahan@1 | 113             x, y = _G.GetPlayerMapPosition("player") | 
| jcallahan@1 | 114 | 
| jcallahan@1 | 115             if x and y and (x > 0 or y > 0) then | 
| jcallahan@1 | 116                 _G.SetDungeonMapLevel(map_level) | 
| jcallahan@1 | 117                 map_level = level_index | 
| jcallahan@1 | 118                 break | 
| jcallahan@1 | 119             end | 
| jcallahan@1 | 120         end | 
| jcallahan@1 | 121     end | 
| jcallahan@1 | 122 | 
| jcallahan@1 | 123     if _G.DungeonUsesTerrainMap() then | 
| jcallahan@1 | 124         map_level = map_level - 1 | 
| jcallahan@1 | 125     end | 
| jcallahan@28 | 126     local x_floor = floor(x * 1000) | 
| jcallahan@28 | 127     local y_floor = floor(y * 1000) | 
| jcallahan@28 | 128 | 
| jcallahan@28 | 129     if x_floor % 2 ~= 0 then | 
| jcallahan@28 | 130         x_floor = x_floor + 1 | 
| jcallahan@28 | 131     end | 
| jcallahan@28 | 132 | 
| jcallahan@28 | 133     if y_floor % 2 ~= 0 then | 
| jcallahan@28 | 134         y_floor = y_floor + 1 | 
| jcallahan@28 | 135     end | 
| jcallahan@28 | 136     --    print(("x: %d - y: %d"):format(x_floor, y_floor)) | 
| jcallahan@28 | 137 | 
| jcallahan@24 | 138     return _G.GetRealZoneText(), _G.GetCurrentMapAreaID(), ("%.2f"):format(x * 100), ("%.2f"):format(y * 100), map_level, InstanceDifficultyToken() | 
| jcallahan@1 | 139 end | 
| jcallahan@1 | 140 | 
| jcallahan@1 | 141 | 
| jcallahan@1 | 142 local function ItemLinkToID(item_link) | 
| jcallahan@1 | 143     if not item_link then | 
| jcallahan@1 | 144         return | 
| jcallahan@1 | 145     end | 
| jcallahan@7 | 146     return tonumber(item_link:match("item:(%d+)")) | 
| jcallahan@1 | 147 end | 
| jcallahan@0 | 148 | 
| jcallahan@4 | 149 | 
| jcallahan@4 | 150 do | 
| jcallahan@4 | 151     local UNIT_TYPE_BITMASK = 0x007 | 
| jcallahan@4 | 152 | 
| jcallahan@4 | 153     function WDP:ParseGUID(guid) | 
| jcallahan@5 | 154         if not guid then | 
| jcallahan@5 | 155             return | 
| jcallahan@5 | 156         end | 
| jcallahan@4 | 157         local types = private.UNIT_TYPES | 
| jcallahan@4 | 158         local unit_type = _G.bit.band(tonumber(guid:sub(1, 5)), UNIT_TYPE_BITMASK) | 
| jcallahan@4 | 159 | 
| jcallahan@10 | 160         if unit_type ~= types.PLAYER and unit_type ~= types.PET then | 
| jcallahan@4 | 161             return unit_type, tonumber(guid:sub(-12, -9), 16) | 
| jcallahan@4 | 162         end | 
| jcallahan@4 | 163 | 
| jcallahan@4 | 164         return unit_type | 
| jcallahan@4 | 165     end | 
| jcallahan@4 | 166 end -- do-block | 
| jcallahan@4 | 167 | 
| jcallahan@4 | 168 | 
| jcallahan@10 | 169 local function UpdateObjectLocation(identifier) | 
| jcallahan@10 | 170     if not identifier then | 
| jcallahan@10 | 171         return | 
| jcallahan@10 | 172     end | 
| jcallahan@24 | 173     local zone_name, area_id, x, y, map_level, instance_token = CurrentLocationData() | 
| jcallahan@19 | 174     local object = DBEntry("objects", identifier) | 
| jcallahan@28 | 175     object[instance_token] = object[instance_token] or {} | 
| jcallahan@28 | 176     object[instance_token].locations = object[instance_token].locations or {} | 
| jcallahan@10 | 177 | 
| jcallahan@24 | 178     local location_token = ("%s:%d"):format(zone_name, area_id) | 
| jcallahan@24 | 179 | 
| jcallahan@28 | 180     if not object[instance_token].locations[location_token] then | 
| jcallahan@28 | 181         object[instance_token].locations[location_token] = {} | 
| jcallahan@10 | 182     end | 
| jcallahan@28 | 183     object[instance_token].locations[location_token][("%s:%s:%s"):format(map_level, x, y)] = true | 
| jcallahan@10 | 184 end | 
| jcallahan@10 | 185 | 
| jcallahan@10 | 186 | 
| jcallahan@19 | 187 local function HandleItemUse(item_link, bag_index, slot_index) | 
| jcallahan@19 | 188     if not item_link then | 
| jcallahan@19 | 189         return | 
| jcallahan@19 | 190     end | 
| jcallahan@19 | 191     local item_id = ItemLinkToID(item_link) | 
| jcallahan@19 | 192 | 
| jcallahan@19 | 193     if not bag_index or not slot_index then | 
| jcallahan@19 | 194         for new_bag_index = 0, _G.NUM_BAG_FRAMES do | 
| jcallahan@19 | 195             for new_slot_index = 1, _G.GetContainerNumSlots(new_bag_index) do | 
| jcallahan@19 | 196                 if item_id == ItemLinkToID(_G.GetContainerItemLink(new_bag_index, new_slot_index)) then | 
| jcallahan@19 | 197                     bag_index = new_bag_index | 
| jcallahan@19 | 198                     slot_index = new_slot_index | 
| jcallahan@19 | 199                     break | 
| jcallahan@19 | 200                 end | 
| jcallahan@19 | 201             end | 
| jcallahan@19 | 202         end | 
| jcallahan@19 | 203     end | 
| jcallahan@19 | 204 | 
| jcallahan@19 | 205     if not bag_index or not slot_index then | 
| jcallahan@19 | 206         return | 
| jcallahan@19 | 207     end | 
| jcallahan@19 | 208     local _, _, _, _, _, is_lootable = _G.GetContainerItemInfo(bag_index, slot_index) | 
| jcallahan@19 | 209 | 
| jcallahan@19 | 210     if not is_lootable then | 
| jcallahan@19 | 211         return | 
| jcallahan@19 | 212     end | 
| jcallahan@19 | 213     DatamineTT:ClearLines() | 
| jcallahan@19 | 214     DatamineTT:SetBagItem(bag_index, slot_index) | 
| jcallahan@19 | 215 | 
| jcallahan@19 | 216     for line_index = 1, DatamineTT:NumLines() do | 
| jcallahan@19 | 217         local current_line = _G["WDPDatamineTTTextLeft" .. line_index] | 
| jcallahan@19 | 218 | 
| jcallahan@19 | 219         if not current_line then | 
| jcallahan@19 | 220             break | 
| jcallahan@19 | 221         end | 
| jcallahan@19 | 222 | 
| jcallahan@19 | 223         if current_line:GetText() == _G.ITEM_OPENABLE then | 
| jcallahan@19 | 224             table.wipe(action_data) | 
| jcallahan@19 | 225             action_data.type = AF.ITEM | 
| jcallahan@28 | 226             action_data.identifier = item_id | 
| jcallahan@25 | 227             action_data.label = "contains" | 
| jcallahan@19 | 228             break | 
| jcallahan@19 | 229         end | 
| jcallahan@19 | 230     end | 
| jcallahan@19 | 231 end | 
| jcallahan@19 | 232 | 
| jcallahan@19 | 233 | 
| jcallahan@20 | 234 local function UpdateFactionNames() | 
| jcallahan@20 | 235     for faction_index = 1, 1000 do | 
| jcallahan@20 | 236         local faction_name, _, _, _, _, _, _, _, is_header = _G.GetFactionInfo(faction_index) | 
| jcallahan@20 | 237 | 
| jcallahan@20 | 238         if faction_name and not is_header then | 
| jcallahan@20 | 239             faction_names[faction_name] = true | 
| jcallahan@20 | 240         elseif not faction_name then | 
| jcallahan@20 | 241             break | 
| jcallahan@20 | 242         end | 
| jcallahan@20 | 243     end | 
| jcallahan@20 | 244 end | 
| jcallahan@20 | 245 | 
| jcallahan@20 | 246 | 
| jcallahan@0 | 247 ----------------------------------------------------------------------- | 
| jcallahan@0 | 248 -- Methods. | 
| jcallahan@0 | 249 ----------------------------------------------------------------------- | 
| jcallahan@0 | 250 function WDP:OnInitialize() | 
| jcallahan@0 | 251     db = LibStub("AceDB-3.0"):New("WoWDBProfilerData", DATABASE_DEFAULTS, "Default").global | 
| jcallahan@14 | 252 | 
| jcallahan@14 | 253     local raw_db = _G["WoWDBProfilerData"] | 
| jcallahan@14 | 254 | 
| jcallahan@18 | 255     local build_num = tonumber(private.build_num) | 
| jcallahan@14 | 256 | 
| jcallahan@14 | 257     if raw_db.build_num and raw_db.build_num < build_num then | 
| jcallahan@14 | 258         for entry in pairs(DATABASE_DEFAULTS.global) do | 
| jcallahan@14 | 259             db[entry] = {} | 
| jcallahan@14 | 260         end | 
| jcallahan@14 | 261         raw_db.build_num = build_num | 
| jcallahan@14 | 262     elseif not raw_db.build_num then | 
| jcallahan@14 | 263         raw_db.build_num = build_num | 
| jcallahan@14 | 264     end | 
| jcallahan@0 | 265 end | 
| jcallahan@0 | 266 | 
| jcallahan@0 | 267 | 
| jcallahan@0 | 268 function WDP:OnEnable() | 
| jcallahan@0 | 269     for event_name, mapping in pairs(EVENT_MAPPING) do | 
| jcallahan@1 | 270         self:RegisterEvent(event_name, (_G.type(mapping) ~= "boolean") and mapping or nil) | 
| jcallahan@0 | 271     end | 
| jcallahan@0 | 272     durability_timer_handle = self:ScheduleRepeatingTimer("ProcessDurability", 30) | 
| jcallahan@2 | 273     target_location_timer_handle = self:ScheduleRepeatingTimer("UpdateTargetLocation", 0.2) | 
| jcallahan@19 | 274 | 
| jcallahan@19 | 275     _G.hooksecurefunc("UseContainerItem", function(bag_index, slot_index, target_unit) | 
| jcallahan@19 | 276         if target_unit then | 
| jcallahan@19 | 277             return | 
| jcallahan@19 | 278         end | 
| jcallahan@19 | 279         HandleItemUse(_G.GetContainerItemLink(bag_index, slot_index), bag_index, slot_index) | 
| jcallahan@19 | 280     end) | 
| jcallahan@19 | 281 | 
| jcallahan@19 | 282     _G.hooksecurefunc("UseItemByName", function(identifier, target_unit) | 
| jcallahan@19 | 283         if target_unit then | 
| jcallahan@19 | 284             return | 
| jcallahan@19 | 285         end | 
| jcallahan@19 | 286         local _, item_link = _G.GetItemInfo(identifier) | 
| jcallahan@19 | 287         HandleItemUse(item_link) | 
| jcallahan@19 | 288     end) | 
| jcallahan@0 | 289 end | 
| jcallahan@0 | 290 | 
| jcallahan@0 | 291 | 
| jcallahan@0 | 292 local function RecordDurability(item_id, durability) | 
| jcallahan@0 | 293     if not durability or durability <= 0 then | 
| jcallahan@0 | 294         return | 
| jcallahan@0 | 295     end | 
| jcallahan@0 | 296 | 
| jcallahan@0 | 297     if not db.items[item_id] then | 
| jcallahan@0 | 298         db.items[item_id] = {} | 
| jcallahan@0 | 299     end | 
| jcallahan@0 | 300     db.items[item_id].durability = durability | 
| jcallahan@0 | 301 end | 
| jcallahan@0 | 302 | 
| jcallahan@0 | 303 | 
| jcallahan@0 | 304 function WDP:ProcessDurability() | 
| jcallahan@0 | 305     for slot_index = 0, _G.INVSLOT_LAST_EQUIPPED do | 
| jcallahan@1 | 306         local item_id = _G.GetInventoryItemID("player", slot_index) | 
| jcallahan@0 | 307 | 
| jcallahan@0 | 308         if item_id and item_id > 0 then | 
| jcallahan@1 | 309             local _, max_durability = _G.GetInventoryItemDurability(slot_index) | 
| jcallahan@0 | 310             RecordDurability(item_id, max_durability) | 
| jcallahan@0 | 311         end | 
| jcallahan@0 | 312     end | 
| jcallahan@0 | 313 | 
| jcallahan@0 | 314     for bag_index = 0, _G.NUM_BAG_SLOTS do | 
| jcallahan@0 | 315         for slot_index = 1, _G.GetContainerNumSlots(bag_index) do | 
| jcallahan@1 | 316             local item_id = _G.GetContainerItemID(bag_index, slot_index) | 
| jcallahan@0 | 317 | 
| jcallahan@0 | 318             if item_id and item_id > 0 then | 
| jcallahan@1 | 319                 local _, max_durability = _G.GetContainerItemDurability(bag_index, slot_index) | 
| jcallahan@0 | 320                 RecordDurability(item_id, max_durability) | 
| jcallahan@0 | 321             end | 
| jcallahan@0 | 322         end | 
| jcallahan@0 | 323     end | 
| jcallahan@0 | 324 end | 
| jcallahan@0 | 325 | 
| jcallahan@0 | 326 | 
| jcallahan@2 | 327 function WDP:UpdateTargetLocation() | 
| jcallahan@22 | 328     local is_dead = _G.UnitIsDead("target") | 
| jcallahan@22 | 329 | 
| jcallahan@22 | 330     if not _G.UnitExists("target") or _G.UnitPlayerControlled("target") or (_G.UnitIsTapped("target") and not is_dead) then | 
| jcallahan@2 | 331         return | 
| jcallahan@2 | 332     end | 
| jcallahan@2 | 333 | 
| jcallahan@2 | 334     for index = 1, 4 do | 
| jcallahan@2 | 335         if not _G.CheckInteractDistance("target", index) then | 
| jcallahan@2 | 336             return | 
| jcallahan@2 | 337         end | 
| jcallahan@2 | 338     end | 
| jcallahan@22 | 339     local target_guid = _G.UnitGUID("target") | 
| jcallahan@22 | 340     local unit_type, unit_idnum = self:ParseGUID(target_guid) | 
| jcallahan@2 | 341 | 
| jcallahan@2 | 342     if unit_type ~= private.UNIT_TYPES.NPC or not unit_idnum then | 
| jcallahan@2 | 343         return | 
| jcallahan@2 | 344     end | 
| jcallahan@24 | 345     local zone_name, area_id, x, y, map_level, instance_token = CurrentLocationData() | 
| jcallahan@28 | 346     local npc_data = DBEntry("npcs", unit_idnum).encounter_data[instance_token][("level_%d"):format(_G.UnitLevel("target"))] | 
| jcallahan@6 | 347     npc_data.locations = npc_data.locations or {} | 
| jcallahan@2 | 348 | 
| jcallahan@24 | 349     local location_token = ("%s:%d"):format(zone_name, area_id) | 
| jcallahan@24 | 350 | 
| jcallahan@24 | 351     if not npc_data.locations[location_token] then | 
| jcallahan@24 | 352         npc_data.locations[location_token] = {} | 
| jcallahan@2 | 353     end | 
| jcallahan@22 | 354 | 
| jcallahan@22 | 355     -- Only record corpse location if there is no entry for this GUID. | 
| jcallahan@24 | 356     if is_dead and npc_data.locations[location_token][target_guid] then | 
| jcallahan@22 | 357         return | 
| jcallahan@22 | 358     end | 
| jcallahan@28 | 359     npc_data.locations[location_token][target_guid] = ("%s:%s:%s"):format(map_level, x, y) | 
| jcallahan@2 | 360 end | 
| jcallahan@2 | 361 | 
| jcallahan@2 | 362 | 
| jcallahan@0 | 363 ----------------------------------------------------------------------- | 
| jcallahan@0 | 364 -- Event handlers. | 
| jcallahan@0 | 365 ----------------------------------------------------------------------- | 
| jcallahan@23 | 366 do | 
| jcallahan@23 | 367     local EXTRACT_GAS_SPELL_ID = 30427 | 
| jcallahan@23 | 368     local FLAGS_NPC = bit.bor(_G.COMBATLOG_OBJECT_TYPE_GUARDIAN, _G.COMBATLOG_OBJECT_CONTROL_NPC) | 
| jcallahan@23 | 369     local FLAGS_NPC_CONTROL = bit.bor(_G.COMBATLOG_OBJECT_AFFILIATION_OUTSIDER, _G.COMBATLOG_OBJECT_CONTROL_NPC) | 
| jcallahan@23 | 370 | 
| jcallahan@23 | 371 | 
| jcallahan@23 | 372     local function RecordNPCSpell(sub_event, source_guid, source_name, source_flags, dest_guid, dest_name, dest_flags, spell_id, spell_name) | 
| jcallahan@23 | 373         if not spell_id then | 
| jcallahan@23 | 374             return | 
| jcallahan@23 | 375         end | 
| jcallahan@23 | 376         local source_type, source_id = WDP:ParseGUID(source_guid) | 
| jcallahan@23 | 377 | 
| jcallahan@23 | 378         if not source_id or source_type ~= private.UNIT_TYPES.NPC then | 
| jcallahan@23 | 379             return | 
| jcallahan@23 | 380         end | 
| jcallahan@23 | 381 | 
| jcallahan@23 | 382         if bit.band(FLAGS_NPC_CONTROL, source_flags) == FLAGS_NPC_CONTROL and bit.band(FLAGS_NPC, source_flags) ~= 0 then | 
| jcallahan@23 | 383             local npc = DBEntry("npcs", source_id) | 
| jcallahan@23 | 384             local instance_token = InstanceDifficultyToken() | 
| jcallahan@28 | 385             npc.encounter_data = npc.encounter_data or {} | 
| jcallahan@28 | 386 | 
| jcallahan@28 | 387             if not npc.encounter_data[instance_token] then | 
| jcallahan@28 | 388                 npc.encounter_data[instance_token] = {} | 
| jcallahan@28 | 389             end | 
| jcallahan@28 | 390             local encounter_data = npc.encounter_data[instance_token] | 
| jcallahan@28 | 391 | 
| jcallahan@28 | 392             encounter_data.spells = encounter_data.spells or {} | 
| jcallahan@28 | 393             encounter_data.spells[spell_id] = (encounter_data.spells[spell_id] or 0) + 1 | 
| jcallahan@23 | 394         end | 
| jcallahan@23 | 395     end | 
| jcallahan@23 | 396 | 
| jcallahan@23 | 397 | 
| jcallahan@23 | 398     local COMBAT_LOG_FUNCS = { | 
| jcallahan@23 | 399         SPELL_AURA_APPLIED = RecordNPCSpell, | 
| jcallahan@23 | 400         SPELL_CAST_START = RecordNPCSpell, | 
| jcallahan@23 | 401         SPELL_CAST_SUCCESS = RecordNPCSpell, | 
| jcallahan@23 | 402         UNIT_DISSIPATES = function(sub_event, source_guid, source_name, source_flags, dest_guid, dest_name, dest_flags) | 
| jcallahan@23 | 403         -- TODO: Write this. | 
| jcallahan@23 | 404         end, | 
| jcallahan@23 | 405     } | 
| jcallahan@23 | 406 | 
| jcallahan@23 | 407 | 
| jcallahan@23 | 408     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 | 409         local combat_log_func = COMBAT_LOG_FUNCS[sub_event] | 
| jcallahan@23 | 410 | 
| jcallahan@23 | 411         if not combat_log_func then | 
| jcallahan@23 | 412             return | 
| jcallahan@23 | 413         end | 
| jcallahan@23 | 414         combat_log_func(sub_event, source_guid, source_name, source_flags, dest_guid, dest_name, dest_flags, ...) | 
| jcallahan@23 | 415     end | 
| jcallahan@23 | 416 end -- do-block | 
| jcallahan@23 | 417 | 
| jcallahan@23 | 418 | 
| jcallahan@18 | 419 function WDP:COMBAT_TEXT_UPDATE(event, message_type, faction_name, amount) | 
| jcallahan@27 | 420     local npc = DBEntry("npcs", action_data.identifier) | 
| jcallahan@21 | 421 | 
| jcallahan@21 | 422     if not npc then | 
| jcallahan@21 | 423         return | 
| jcallahan@21 | 424     end | 
| jcallahan@28 | 425     local instance_token = InstanceDifficultyToken() | 
| jcallahan@28 | 426 | 
| jcallahan@28 | 427     if not npc.encounter_data[instance_token][action_data.npc_level].reputations then | 
| jcallahan@28 | 428         npc.encounter_data[instance_token][action_data.npc_level].reputations = {} | 
| jcallahan@28 | 429     end | 
| jcallahan@28 | 430     npc.encounter_data[instance_token][action_data.npc_level].reputations[faction_name] = amount | 
| jcallahan@18 | 431 end | 
| jcallahan@18 | 432 | 
| jcallahan@18 | 433 | 
| jcallahan@13 | 434 do | 
| jcallahan@13 | 435     local re_gold = _G.GOLD_AMOUNT:gsub("%%d", "(%%d+)") | 
| jcallahan@13 | 436     local re_silver = _G.SILVER_AMOUNT:gsub("%%d", "(%%d+)") | 
| jcallahan@13 | 437     local re_copper = _G.COPPER_AMOUNT:gsub("%%d", "(%%d+)") | 
| jcallahan@13 | 438 | 
| jcallahan@13 | 439 | 
| jcallahan@13 | 440     local function _moneyMatch(money, re) | 
| jcallahan@13 | 441         return money:match(re) or 0 | 
| jcallahan@1 | 442     end | 
| jcallahan@1 | 443 | 
| jcallahan@0 | 444 | 
| jcallahan@13 | 445     local function _toCopper(money) | 
| jcallahan@13 | 446         if not money then | 
| jcallahan@13 | 447             return 0 | 
| jcallahan@13 | 448         end | 
| jcallahan@0 | 449 | 
| jcallahan@13 | 450         return _moneyMatch(money, re_gold) * 10000 + _moneyMatch(money, re_silver) * 100 + _moneyMatch(money, re_copper) | 
| jcallahan@1 | 451     end | 
| jcallahan@1 | 452 | 
| jcallahan@1 | 453 | 
| jcallahan@13 | 454     local LOOT_VERIFY_FUNCS = { | 
| jcallahan@16 | 455         [AF.ITEM] = function() | 
| jcallahan@16 | 456             local locked_item_id | 
| jcallahan@16 | 457 | 
| jcallahan@16 | 458             for bag_index = 0, _G.NUM_BAG_FRAMES do | 
| jcallahan@16 | 459                 for slot_index = 1, _G.GetContainerNumSlots(bag_index) do | 
| jcallahan@16 | 460                     local _, _, is_locked = _G.GetContainerItemInfo(bag_index, slot_index) | 
| jcallahan@16 | 461 | 
| jcallahan@16 | 462                     if is_locked then | 
| jcallahan@16 | 463                         locked_item_id = ItemLinkToID(_G.GetContainerItemLink(bag_index, slot_index)) | 
| jcallahan@16 | 464                     end | 
| jcallahan@16 | 465                 end | 
| jcallahan@16 | 466             end | 
| jcallahan@16 | 467 | 
| jcallahan@28 | 468             if not locked_item_id or (action_data.identifier and action_data.identifier ~= locked_item_id) then | 
| jcallahan@16 | 469                 return false | 
| jcallahan@16 | 470             end | 
| jcallahan@28 | 471             action_data.identifier = locked_item_id | 
| jcallahan@16 | 472             return true | 
| jcallahan@16 | 473         end, | 
| jcallahan@13 | 474         [AF.NPC] = function() | 
| jcallahan@17 | 475             if not _G.UnitExists("target") or _G.UnitIsFriend("player", "target") or _G.UnitIsPlayer("target") or _G.UnitPlayerControlled("target") then | 
| jcallahan@15 | 476                 return false | 
| jcallahan@13 | 477             end | 
| jcallahan@15 | 478             local unit_type, id_num = WDP:ParseGUID(_G.UnitGUID("target")) | 
| jcallahan@27 | 479             action_data.identifier = id_num | 
| jcallahan@13 | 480             return true | 
| jcallahan@13 | 481         end, | 
| jcallahan@14 | 482         [AF.OBJECT] = true, | 
| jcallahan@17 | 483         [AF.ZONE] = function() | 
| jcallahan@25 | 484             return action_data.label and _G.IsFishingLoot() | 
| jcallahan@17 | 485         end, | 
| jcallahan@13 | 486     } | 
| jcallahan@13 | 487 | 
| jcallahan@13 | 488 | 
| jcallahan@28 | 489     local function GenericLootUpdate(data_type, top_field) | 
| jcallahan@27 | 490         local entry = DBEntry(data_type, action_data.identifier) | 
| jcallahan@22 | 491 | 
| jcallahan@22 | 492         if not entry then | 
| jcallahan@22 | 493             return | 
| jcallahan@22 | 494         end | 
| jcallahan@25 | 495         local loot_type = action_data.label or "drops" | 
| jcallahan@28 | 496         local loot_data | 
| jcallahan@28 | 497 | 
| jcallahan@28 | 498         if top_field then | 
| jcallahan@28 | 499             entry[top_field] = entry[top_field] or {} | 
| jcallahan@28 | 500             entry[top_field][loot_type] = entry[top_field][loot_type] or {} | 
| jcallahan@28 | 501 | 
| jcallahan@28 | 502             loot_data = entry[top_field][loot_type] | 
| jcallahan@28 | 503         else | 
| jcallahan@28 | 504             entry[loot_type] = entry[loot_type] or {} | 
| jcallahan@28 | 505 | 
| jcallahan@28 | 506             loot_data = entry[loot_type] | 
| jcallahan@28 | 507         end | 
| jcallahan@22 | 508 | 
| jcallahan@22 | 509         for index = 1, #action_data.loot_list do | 
| jcallahan@28 | 510             table.insert(loot_data, action_data.loot_list[index]) | 
| jcallahan@22 | 511         end | 
| jcallahan@22 | 512     end | 
| jcallahan@22 | 513 | 
| jcallahan@22 | 514 | 
| jcallahan@13 | 515     local LOOT_UPDATE_FUNCS = { | 
| jcallahan@16 | 516         [AF.ITEM] = function() | 
| jcallahan@28 | 517             GenericLootUpdate("items") | 
| jcallahan@28 | 518         end, | 
| jcallahan@28 | 519         [AF.NPC] = function() | 
| jcallahan@28 | 520             local npc = DBEntry("npcs", action_data.identifier) | 
| jcallahan@28 | 521 | 
| jcallahan@28 | 522             if not npc then | 
| jcallahan@28 | 523                 return | 
| jcallahan@28 | 524             end | 
| jcallahan@28 | 525             local instance_token = InstanceDifficultyToken() | 
| jcallahan@28 | 526 | 
| jcallahan@28 | 527             if not npc.encounter_data[instance_token] then | 
| jcallahan@28 | 528                 npc.encounter_data[instance_token] = {} | 
| jcallahan@28 | 529             end | 
| jcallahan@25 | 530             local loot_type = action_data.label or "drops" | 
| jcallahan@28 | 531             npc.encounter_data[instance_token][loot_type] = npc.encounter_data[instance_token][loot_type] or {} | 
| jcallahan@28 | 532 | 
| jcallahan@28 | 533             local loot_data = npc.encounter_data[instance_token][loot_type] | 
| jcallahan@16 | 534 | 
| jcallahan@17 | 535             for index = 1, #action_data.loot_list do | 
| jcallahan@28 | 536                 table.insert(loot_data, action_data.loot_list[index]) | 
| jcallahan@16 | 537             end | 
| jcallahan@16 | 538         end, | 
| jcallahan@13 | 539         [AF.OBJECT] = function() | 
| jcallahan@28 | 540             GenericLootUpdate("objects", InstanceDifficultyToken()) | 
| jcallahan@17 | 541         end, | 
| jcallahan@17 | 542         [AF.ZONE] = function() | 
| jcallahan@25 | 543             local loot_type = action_data.label or "drops" | 
| jcallahan@19 | 544             local zone = DBEntry("zones", action_data.zone) | 
| jcallahan@28 | 545             zone[action_data.instance_token] = zone[action_data.instance_token] or {} | 
| jcallahan@28 | 546             zone[action_data.instance_token][loot_type] = zone[loot_type][action_data.instance_token] or {} | 
| jcallahan@17 | 547 | 
| jcallahan@28 | 548             local location_data = ("%s:%s:%s"):format(action_data.map_level, action_data.x, action_data.y) | 
| jcallahan@28 | 549             local loot_data = zone[action_data.instance_token][loot_type][location_data] | 
| jcallahan@17 | 550 | 
| jcallahan@17 | 551             if not loot_data then | 
| jcallahan@28 | 552                 zone[action_data.instance_token][loot_type][location_data] = {} | 
| jcallahan@28 | 553                 loot_data = zone[action_data.instance_token][loot_type][location_data] | 
| jcallahan@17 | 554             end | 
| jcallahan@17 | 555 | 
| jcallahan@17 | 556             for index = 1, #action_data.loot_list do | 
| jcallahan@17 | 557                 table.insert(loot_data, action_data.loot_list[index]) | 
| jcallahan@13 | 558             end | 
| jcallahan@13 | 559         end, | 
| jcallahan@13 | 560     } | 
| jcallahan@13 | 561 | 
| jcallahan@13 | 562 | 
| jcallahan@13 | 563     function WDP:LOOT_OPENED() | 
| jcallahan@18 | 564         if action_data.looting then | 
| jcallahan@18 | 565             return | 
| jcallahan@18 | 566         end | 
| jcallahan@18 | 567 | 
| jcallahan@13 | 568         if not action_data.type then | 
| jcallahan@13 | 569             action_data.type = AF.NPC | 
| jcallahan@1 | 570         end | 
| jcallahan@13 | 571         local verify_func = LOOT_VERIFY_FUNCS[action_data.type] | 
| jcallahan@13 | 572         local update_func = LOOT_UPDATE_FUNCS[action_data.type] | 
| jcallahan@13 | 573 | 
| jcallahan@14 | 574         if not verify_func or not update_func then | 
| jcallahan@13 | 575             return | 
| jcallahan@13 | 576         end | 
| jcallahan@13 | 577 | 
| jcallahan@14 | 578         if _G.type(verify_func) == "function" and not verify_func() then | 
| jcallahan@14 | 579             return | 
| jcallahan@14 | 580         end | 
| jcallahan@18 | 581         -- TODO: Remove this check once the MoP client goes live | 
| jcallahan@18 | 582         local wow_version = private.wow_version | 
| jcallahan@13 | 583         local loot_registry = {} | 
| jcallahan@17 | 584         action_data.loot_list = {} | 
| jcallahan@18 | 585         action_data.looting = true | 
| jcallahan@13 | 586 | 
| jcallahan@18 | 587         if wow_version == "5.0.1" then | 
| jcallahan@18 | 588             for loot_slot = 1, _G.GetNumLootItems() do | 
| jcallahan@18 | 589                 local icon_texture, item_text, quantity, quality, locked = _G.GetLootSlotInfo(loot_slot) | 
| jcallahan@13 | 590 | 
| jcallahan@18 | 591                 local slot_type = _G.GetLootSlotType(loot_slot) | 
| jcallahan@18 | 592 | 
| jcallahan@18 | 593                 if slot_type == _G.LOOT_SLOT_ITEM then | 
| jcallahan@18 | 594                     local item_id = ItemLinkToID(_G.GetLootSlotLink(loot_slot)) | 
| jcallahan@18 | 595                     loot_registry[item_id] = (loot_registry[item_id]) or 0 + quantity | 
| jcallahan@18 | 596                 elseif slot_type == _G.LOOT_SLOT_MONEY then | 
| jcallahan@18 | 597                     table.insert(action_data.loot_list, ("money:%d"):format(_toCopper(item_text))) | 
| jcallahan@18 | 598                 elseif slot_type == _G.LOOT_SLOT_CURRENCY then | 
| jcallahan@18 | 599                     table.insert(action_data.loot_list, ("currency:%d:%s"):format(quantity, icon_texture:match("[^\\]+$"):lower())) | 
| jcallahan@18 | 600                 end | 
| jcallahan@18 | 601             end | 
| jcallahan@18 | 602         else | 
| jcallahan@18 | 603             for loot_slot = 1, _G.GetNumLootItems() do | 
| jcallahan@18 | 604                 local icon_texture, item_text, quantity, quality, locked = _G.GetLootSlotInfo(loot_slot) | 
| jcallahan@18 | 605                 if _G.LootSlotIsItem(loot_slot) then | 
| jcallahan@18 | 606                     local item_id = ItemLinkToID(_G.GetLootSlotLink(loot_slot)) | 
| jcallahan@18 | 607                     loot_registry[item_id] = (loot_registry[item_id]) or 0 + quantity | 
| jcallahan@18 | 608                 elseif _G.LootSlotIsCoin(loot_slot) then | 
| jcallahan@18 | 609                     table.insert(action_data.loot_list, ("money:%d"):format(_toCopper(item_text))) | 
| jcallahan@18 | 610                 elseif _G.LootSlotIsCurrency(loot_slot) then | 
| jcallahan@18 | 611                     table.insert(action_data.loot_list, ("currency:%d:%s"):format(quantity, icon_texture:match("[^\\]+$"):lower())) | 
| jcallahan@18 | 612                 end | 
| jcallahan@13 | 613             end | 
| jcallahan@13 | 614         end | 
| jcallahan@13 | 615 | 
| jcallahan@13 | 616         for item_id, quantity in pairs(loot_registry) do | 
| jcallahan@17 | 617             table.insert(action_data.loot_list, ("%d:%d"):format(item_id, quantity)) | 
| jcallahan@13 | 618         end | 
| jcallahan@13 | 619         update_func() | 
| jcallahan@1 | 620     end | 
| jcallahan@13 | 621 end -- do-block | 
| jcallahan@0 | 622 | 
| jcallahan@0 | 623 | 
| jcallahan@5 | 624 local POINT_MATCH_PATTERNS = { | 
| jcallahan@5 | 625     ("^%s$"):format(_G.ITEM_REQ_ARENA_RATING:gsub("%%d", "(%%d+)")), -- May no longer be necessary | 
| jcallahan@5 | 626     ("^%s$"):format(_G.ITEM_REQ_ARENA_RATING_3V3:gsub("%%d", "(%%d+)")), -- May no longer be necessary | 
| jcallahan@5 | 627     ("^%s$"):format(_G.ITEM_REQ_ARENA_RATING_5V5:gsub("%%d", "(%%d+)")), -- May no longer be necessary | 
| jcallahan@5 | 628     ("^%s$"):format(_G.ITEM_REQ_ARENA_RATING_BG:gsub("%%d", "(%%d+)")), | 
| jcallahan@5 | 629     ("^%s$"):format(_G.ITEM_REQ_ARENA_RATING_3V3_BG:gsub("%%d", "(%%d+)")), | 
| jcallahan@5 | 630 } | 
| jcallahan@5 | 631 | 
| jcallahan@5 | 632 | 
| jcallahan@7 | 633 function WDP:UpdateMerchantItems() | 
| jcallahan@4 | 634     local unit_type, unit_idnum = self:ParseGUID(_G.UnitGUID("target")) | 
| jcallahan@4 | 635 | 
| jcallahan@4 | 636     if unit_type ~= private.UNIT_TYPES.NPC or not unit_idnum then | 
| jcallahan@4 | 637         return | 
| jcallahan@4 | 638     end | 
| jcallahan@19 | 639     local merchant = DBEntry("npcs", unit_idnum) | 
| jcallahan@6 | 640     merchant.sells = merchant.sells or {} | 
| jcallahan@5 | 641 | 
| jcallahan@5 | 642     for item_index = 1, _G.GetMerchantNumItems() do | 
| jcallahan@5 | 643         local _, _, copper_price, stack_size, num_available, _, extended_cost = _G.GetMerchantItemInfo(item_index) | 
| jcallahan@5 | 644         local item_id = ItemLinkToID(_G.GetMerchantItemLink(item_index)) | 
| jcallahan@5 | 645 | 
| jcallahan@5 | 646         if item_id and item_id > 0 then | 
| jcallahan@5 | 647             local price_string = copper_price | 
| jcallahan@5 | 648 | 
| jcallahan@5 | 649             if extended_cost then | 
| jcallahan@5 | 650                 local bg_points = 0 | 
| jcallahan@5 | 651                 local personal_points = 0 | 
| jcallahan@5 | 652 | 
| jcallahan@5 | 653                 DatamineTT:ClearLines() | 
| jcallahan@5 | 654                 DatamineTT:SetMerchantItem(item_index) | 
| jcallahan@5 | 655 | 
| jcallahan@5 | 656                 for line_index = 1, DatamineTT:NumLines() do | 
| jcallahan@5 | 657                     local current_line = _G["WDPDatamineTTTextLeft" .. line_index] | 
| jcallahan@5 | 658 | 
| jcallahan@5 | 659                     if not current_line then | 
| jcallahan@5 | 660                         break | 
| jcallahan@5 | 661                     end | 
| jcallahan@5 | 662                     local breakout | 
| jcallahan@5 | 663 | 
| jcallahan@5 | 664                     for match_index = 1, #POINT_MATCH_PATTERNS do | 
| jcallahan@5 | 665                         local match1, match2 = current_line:GetText():match(POINT_MATCH_PATTERNS[match_index]) | 
| jcallahan@5 | 666                         personal_points = personal_points + (match1 or 0) | 
| jcallahan@5 | 667                         bg_points = bg_points + (match2 or 0) | 
| jcallahan@5 | 668 | 
| jcallahan@5 | 669                         if match1 or match2 then | 
| jcallahan@5 | 670                             breakout = true | 
| jcallahan@5 | 671                             break | 
| jcallahan@5 | 672                         end | 
| jcallahan@5 | 673                     end | 
| jcallahan@5 | 674 | 
| jcallahan@5 | 675                     if breakout then | 
| jcallahan@5 | 676                         break | 
| jcallahan@5 | 677                     end | 
| jcallahan@5 | 678                 end | 
| jcallahan@5 | 679                 local currency_list = {} | 
| jcallahan@5 | 680 | 
| jcallahan@5 | 681                 price_string = ("%s:%s:%s"):format(price_string, bg_points, personal_points) | 
| jcallahan@5 | 682 | 
| jcallahan@5 | 683                 for cost_index = 1, _G.GetMerchantItemCostInfo(item_index) do | 
| jcallahan@5 | 684                     local icon_texture, amount_required, currency_link = _G.GetMerchantItemCostItem(item_index, cost_index) | 
| jcallahan@5 | 685                     local currency_id = currency_link and ItemLinkToID(currency_link) or nil | 
| jcallahan@5 | 686 | 
| jcallahan@5 | 687                     if not currency_id or currency_id < 1 then | 
| jcallahan@5 | 688                         if not icon_texture then | 
| jcallahan@5 | 689                             return | 
| jcallahan@5 | 690                         end | 
| jcallahan@5 | 691                         currency_id = icon_texture:match("[^\\]+$"):lower() | 
| jcallahan@5 | 692                     end | 
| jcallahan@5 | 693                     currency_list[#currency_list + 1] = ("(%s:%s)"):format(amount_required, currency_id) | 
| jcallahan@5 | 694                 end | 
| jcallahan@5 | 695 | 
| jcallahan@5 | 696                 for currency_index = 1, #currency_list do | 
| jcallahan@5 | 697                     price_string = ("%s:%s"):format(price_string, currency_list[currency_index]) | 
| jcallahan@5 | 698                 end | 
| jcallahan@5 | 699             end | 
| jcallahan@6 | 700             merchant.sells[("%s:%s:[%s]"):format(item_id, stack_size, price_string)] = num_available | 
| jcallahan@5 | 701         end | 
| jcallahan@4 | 702     end | 
| jcallahan@14 | 703 | 
| jcallahan@14 | 704     if _G.CanMerchantRepair() then | 
| jcallahan@14 | 705         merchant.can_repair = true | 
| jcallahan@14 | 706     end | 
| jcallahan@4 | 707 end | 
| jcallahan@4 | 708 | 
| jcallahan@4 | 709 | 
| jcallahan@25 | 710 function WDP:PET_BAR_UPDATE() | 
| jcallahan@25 | 711     if not action_data.label or not action_data.label == "mind_control" then | 
| jcallahan@25 | 712         return | 
| jcallahan@25 | 713     end | 
| jcallahan@25 | 714     local unit_type, unit_idnum = self:ParseGUID(_G.UnitGUID("pet")) | 
| jcallahan@25 | 715 | 
| jcallahan@25 | 716     if unit_type ~= private.UNIT_TYPES.NPC or not unit_idnum then | 
| jcallahan@25 | 717         return | 
| jcallahan@25 | 718     end | 
| jcallahan@25 | 719     DBEntry("npcs", unit_idnum).mind_control = true | 
| jcallahan@25 | 720     table.wipe(action_data) | 
| jcallahan@25 | 721 end | 
| jcallahan@25 | 722 | 
| jcallahan@25 | 723 | 
| jcallahan@18 | 724 do | 
| jcallahan@18 | 725     local GENDER_NAMES = { | 
| jcallahan@18 | 726         "UNKNOWN", | 
| jcallahan@18 | 727         "MALE", | 
| jcallahan@18 | 728         "FEMALE", | 
| jcallahan@18 | 729     } | 
| jcallahan@2 | 730 | 
| jcallahan@2 | 731 | 
| jcallahan@18 | 732     local REACTION_NAMES = { | 
| jcallahan@18 | 733         "HATED", | 
| jcallahan@18 | 734         "HOSTILE", | 
| jcallahan@18 | 735         "UNFRIENDLY", | 
| jcallahan@18 | 736         "NEUTRAL", | 
| jcallahan@18 | 737         "FRIENDLY", | 
| jcallahan@18 | 738         "HONORED", | 
| jcallahan@18 | 739         "REVERED", | 
| jcallahan@18 | 740         "EXALTED", | 
| jcallahan@18 | 741     } | 
| jcallahan@2 | 742 | 
| jcallahan@2 | 743 | 
| jcallahan@18 | 744     local POWER_TYPE_NAMES = { | 
| jcallahan@18 | 745         ["0"] = "MANA", | 
| jcallahan@18 | 746         ["1"] = "RAGE", | 
| jcallahan@18 | 747         ["2"] = "FOCUS", | 
| jcallahan@18 | 748         ["3"] = "ENERGY", | 
| jcallahan@18 | 749         ["6"] = "RUNIC_POWER", | 
| jcallahan@18 | 750     } | 
| jcallahan@2 | 751 | 
| jcallahan@2 | 752 | 
| jcallahan@18 | 753     function WDP:PLAYER_TARGET_CHANGED() | 
| jcallahan@18 | 754         if not _G.UnitExists("target") or _G.UnitPlayerControlled("target") then | 
| jcallahan@18 | 755             return | 
| jcallahan@18 | 756         end | 
| jcallahan@18 | 757         local unit_type, unit_idnum = self:ParseGUID(_G.UnitGUID("target")) | 
| jcallahan@2 | 758 | 
| jcallahan@18 | 759         if unit_type ~= private.UNIT_TYPES.NPC or not unit_idnum then | 
| jcallahan@18 | 760             return | 
| jcallahan@18 | 761         end | 
| jcallahan@19 | 762         local npc = DBEntry("npcs", unit_idnum) | 
| jcallahan@18 | 763         local _, class_token = _G.UnitClass("target") | 
| jcallahan@18 | 764         npc.class = class_token | 
| jcallahan@20 | 765 | 
| jcallahan@20 | 766         UpdateFactionNames() | 
| jcallahan@20 | 767         DatamineTT:ClearLines() | 
| jcallahan@20 | 768         DatamineTT:SetUnit("target") | 
| jcallahan@20 | 769 | 
| jcallahan@20 | 770         for line_index = 1, DatamineTT:NumLines() do | 
| jcallahan@20 | 771             local current_line = _G["WDPDatamineTTTextLeft" .. line_index] | 
| jcallahan@20 | 772 | 
| jcallahan@20 | 773             if not current_line then | 
| jcallahan@20 | 774                 break | 
| jcallahan@20 | 775             end | 
| jcallahan@20 | 776             local line_text = current_line:GetText() | 
| jcallahan@20 | 777 | 
| jcallahan@20 | 778             if faction_names[line_text] then | 
| jcallahan@20 | 779                 npc.faction = line_text | 
| jcallahan@20 | 780                 break | 
| jcallahan@20 | 781             end | 
| jcallahan@20 | 782         end | 
| jcallahan@26 | 783         npc.genders = npc.genders or {} | 
| jcallahan@26 | 784         npc.genders[GENDER_NAMES[_G.UnitSex("target")] or "UNDEFINED"] = true | 
| jcallahan@18 | 785         npc.is_pvp = _G.UnitIsPVP("target") and true or nil | 
| jcallahan@18 | 786         npc.reaction = ("%s:%s:%s"):format(_G.UnitLevel("player"), _G.UnitFactionGroup("player"), REACTION_NAMES[_G.UnitReaction("player", "target")]) | 
| jcallahan@22 | 787         npc.encounter_data = npc.encounter_data or {} | 
| jcallahan@2 | 788 | 
| jcallahan@28 | 789         local instance_token = InstanceDifficultyToken() | 
| jcallahan@28 | 790 | 
| jcallahan@28 | 791         if not npc.encounter_data[instance_token] then | 
| jcallahan@28 | 792             npc.encounter_data[instance_token] = {} | 
| jcallahan@28 | 793         end | 
| jcallahan@28 | 794         local encounter_data = npc.encounter_data[instance_token] | 
| jcallahan@18 | 795         local npc_level = ("level_%d"):format(_G.UnitLevel("target")) | 
| jcallahan@3 | 796 | 
| jcallahan@28 | 797         if not encounter_data[npc_level] then | 
| jcallahan@28 | 798             encounter_data[npc_level] = { | 
| jcallahan@18 | 799                 max_health = _G.UnitHealthMax("target"), | 
| jcallahan@18 | 800             } | 
| jcallahan@3 | 801 | 
| jcallahan@18 | 802             local max_power = _G.UnitManaMax("target") | 
| jcallahan@18 | 803 | 
| jcallahan@18 | 804             if max_power > 0 then | 
| jcallahan@18 | 805                 local power_type = _G.UnitPowerType("target") | 
| jcallahan@28 | 806                 encounter_data[npc_level].power = ("%s:%d"):format(POWER_TYPE_NAMES[_G.tostring(power_type)] or power_type, max_power) | 
| jcallahan@18 | 807             end | 
| jcallahan@3 | 808         end | 
| jcallahan@21 | 809         table.wipe(action_data) | 
| jcallahan@21 | 810         action_data.type = AF.NPC | 
| jcallahan@27 | 811         action_data.identifier = unit_idnum | 
| jcallahan@21 | 812         action_data.npc_level = npc_level | 
| jcallahan@2 | 813     end | 
| jcallahan@18 | 814 end -- do-block | 
| jcallahan@2 | 815 | 
| jcallahan@12 | 816 do | 
| jcallahan@12 | 817     local function UpdateQuestJuncture(point) | 
| jcallahan@12 | 818         local unit_name = _G.UnitName("questnpc") | 
| jcallahan@9 | 819 | 
| jcallahan@12 | 820         if not unit_name then | 
| jcallahan@12 | 821             return | 
| jcallahan@12 | 822         end | 
| jcallahan@12 | 823         local unit_type, unit_id = WDP:ParseGUID(_G.UnitGUID("questnpc")) | 
| jcallahan@9 | 824 | 
| jcallahan@12 | 825         if unit_type == private.UNIT_TYPES.OBJECT then | 
| jcallahan@12 | 826             UpdateObjectLocation(unit_id) | 
| jcallahan@12 | 827         end | 
| jcallahan@19 | 828         local quest = DBEntry("quests", _G.GetQuestID()) | 
| jcallahan@12 | 829         quest[point] = quest[point] or {} | 
| jcallahan@12 | 830         quest[point][("%s:%d"):format(private.UNIT_TYPE_NAMES[unit_type + 1], unit_id)] = true | 
| jcallahan@24 | 831 | 
| jcallahan@24 | 832         return quest | 
| jcallahan@12 | 833     end | 
| jcallahan@10 | 834 | 
| jcallahan@12 | 835 | 
| jcallahan@12 | 836     function WDP:QUEST_COMPLETE() | 
| jcallahan@12 | 837         UpdateQuestJuncture("end") | 
| jcallahan@10 | 838     end | 
| jcallahan@10 | 839 | 
| jcallahan@12 | 840 | 
| jcallahan@12 | 841     function WDP:QUEST_DETAIL() | 
| jcallahan@24 | 842         local quest = UpdateQuestJuncture("begin") | 
| jcallahan@24 | 843 | 
| jcallahan@24 | 844         quest.classes = quest.classes or {} | 
| jcallahan@27 | 845         quest.classes[PLAYER_CLASS] = true | 
| jcallahan@24 | 846 | 
| jcallahan@24 | 847         local _, race = _G.UnitRace("player") | 
| jcallahan@24 | 848         quest.races = quest.races or {} | 
| jcallahan@24 | 849         quest.races[race] = true | 
| jcallahan@10 | 850     end | 
| jcallahan@12 | 851 end -- do-block | 
| jcallahan@9 | 852 | 
| jcallahan@9 | 853 | 
| jcallahan@4 | 854 function WDP:QUEST_LOG_UPDATE() | 
| jcallahan@4 | 855     self:UnregisterEvent("QUEST_LOG_UPDATE") | 
| jcallahan@4 | 856 end | 
| jcallahan@4 | 857 | 
| jcallahan@4 | 858 | 
| jcallahan@4 | 859 function WDP:UNIT_QUEST_LOG_CHANGED(event, unit_id) | 
| jcallahan@4 | 860     if unit_id ~= "player" then | 
| jcallahan@4 | 861         return | 
| jcallahan@4 | 862     end | 
| jcallahan@4 | 863     self:RegisterEvent("QUEST_LOG_UPDATE") | 
| jcallahan@4 | 864 end | 
| jcallahan@4 | 865 | 
| jcallahan@4 | 866 | 
| jcallahan@27 | 867 function WDP:TRAINER_SHOW() | 
| jcallahan@27 | 868     if not _G.IsTradeskillTrainer() then | 
| jcallahan@27 | 869         return | 
| jcallahan@27 | 870     end | 
| jcallahan@27 | 871     local unit_type, unit_idnum = self:ParseGUID(_G.UnitGUID("target")) | 
| jcallahan@27 | 872     local npc = DBEntry("npcs", unit_idnum) | 
| jcallahan@27 | 873     npc.teaches = npc.teaches or {} | 
| jcallahan@27 | 874 | 
| jcallahan@27 | 875     -- Get the initial trainer filters | 
| jcallahan@27 | 876     local available = _G.GetTrainerServiceTypeFilter("available") | 
| jcallahan@27 | 877     local unavailable = _G.GetTrainerServiceTypeFilter("unavailable") | 
| jcallahan@27 | 878     local used = _G.GetTrainerServiceTypeFilter("used") | 
| jcallahan@27 | 879 | 
| jcallahan@27 | 880     -- Clear the trainer filters | 
| jcallahan@27 | 881     _G.SetTrainerServiceTypeFilter("available", 1) | 
| jcallahan@27 | 882     _G.SetTrainerServiceTypeFilter("unavailable", 1) | 
| jcallahan@27 | 883     _G.SetTrainerServiceTypeFilter("used", 1) | 
| jcallahan@27 | 884 | 
| jcallahan@27 | 885     for index = 1, _G.GetNumTrainerServices(), 1 do | 
| jcallahan@27 | 886         local spell_name, rank_name, _, _, required_level = _G.GetTrainerServiceInfo(index) | 
| jcallahan@27 | 887 | 
| jcallahan@27 | 888         if spell_name then | 
| jcallahan@27 | 889             DatamineTT:ClearLines() | 
| jcallahan@27 | 890             DatamineTT:SetTrainerService(index) | 
| jcallahan@27 | 891 | 
| jcallahan@27 | 892             local _, _, spell_id = DatamineTT:GetSpell() | 
| jcallahan@27 | 893             local profession, min_skill = _G.GetTrainerServiceSkillReq(index) | 
| jcallahan@27 | 894             profession = profession or "General" | 
| jcallahan@27 | 895 | 
| jcallahan@27 | 896             local class_professions = npc.teaches[PLAYER_CLASS] | 
| jcallahan@27 | 897             if not class_professions then | 
| jcallahan@27 | 898                 npc.teaches[PLAYER_CLASS] = {} | 
| jcallahan@27 | 899                 class_professions = npc.teaches[PLAYER_CLASS] | 
| jcallahan@27 | 900             end | 
| jcallahan@27 | 901 | 
| jcallahan@27 | 902             local profession_skills = class_professions[profession] | 
| jcallahan@27 | 903             if not profession_skills then | 
| jcallahan@27 | 904                 class_professions[profession] = {} | 
| jcallahan@27 | 905                 profession_skills = class_professions[profession] | 
| jcallahan@27 | 906             end | 
| jcallahan@27 | 907             profession_skills[spell_id] = ("%d:%d"):format(required_level, min_skill) | 
| jcallahan@27 | 908         end | 
| jcallahan@27 | 909     end | 
| jcallahan@27 | 910 | 
| jcallahan@27 | 911     -- Reset the filters to what they were before | 
| jcallahan@27 | 912     _G.SetTrainerServiceTypeFilter("available", available or 0) | 
| jcallahan@27 | 913     _G.SetTrainerServiceTypeFilter("unavailable", unavailable or 0) | 
| jcallahan@27 | 914     _G.SetTrainerServiceTypeFilter("used", used or 0) | 
| jcallahan@27 | 915 end | 
| jcallahan@27 | 916 | 
| jcallahan@27 | 917 | 
| jcallahan@1 | 918 function WDP:UNIT_SPELLCAST_SENT(event_name, unit_id, spell_name, spell_rank, target_name, spell_line) | 
| jcallahan@1 | 919     if private.tracked_line or unit_id ~= "player" then | 
| jcallahan@1 | 920         return | 
| jcallahan@1 | 921     end | 
| jcallahan@1 | 922     local spell_label = private.SPELL_LABELS_BY_NAME[spell_name] | 
| jcallahan@1 | 923 | 
| jcallahan@1 | 924     if not spell_label then | 
| jcallahan@1 | 925         return | 
| jcallahan@1 | 926     end | 
| jcallahan@18 | 927     table.wipe(action_data) | 
| jcallahan@1 | 928 | 
| jcallahan@1 | 929     local tt_item_name, tt_item_link = _G.GameTooltip:GetItem() | 
| jcallahan@1 | 930     local tt_unit_name, tt_unit_id = _G.GameTooltip:GetUnit() | 
| jcallahan@1 | 931 | 
| jcallahan@1 | 932     if not tt_unit_name and _G.UnitName("target") == target_name then | 
| jcallahan@1 | 933         tt_unit_name = target_name | 
| jcallahan@1 | 934         tt_unit_id = "target" | 
| jcallahan@1 | 935     end | 
| jcallahan@1 | 936     local spell_flags = private.SPELL_FLAGS_BY_LABEL[spell_label] | 
| jcallahan@28 | 937     local zone_name, area_id, x, y, map_level, instance_token = CurrentLocationData() | 
| jcallahan@28 | 938 | 
| jcallahan@28 | 939     action_data.instance_token = instance_token | 
| jcallahan@28 | 940     action_data.map_level = map_level | 
| jcallahan@28 | 941     action_data.x = x | 
| jcallahan@28 | 942     action_data.y = y | 
| jcallahan@28 | 943     action_data.zone = ("%s:%d"):format(zone_name, area_id) | 
| jcallahan@1 | 944 | 
| jcallahan@16 | 945     if tt_unit_name and not tt_item_name then | 
| jcallahan@16 | 946         if bit.band(spell_flags, AF.NPC) == AF.NPC then | 
| jcallahan@16 | 947             if not tt_unit_id or tt_unit_name ~= target_name then | 
| jcallahan@16 | 948                 return | 
| jcallahan@16 | 949             end | 
| jcallahan@16 | 950             action_data.type = AF.NPC | 
| jcallahan@25 | 951             action_data.label = spell_label:lower() | 
| jcallahan@25 | 952             action_data.unit_name = tt_unit_name | 
| jcallahan@16 | 953         end | 
| jcallahan@16 | 954     elseif bit.band(spell_flags, AF.ITEM) == AF.ITEM then | 
| jcallahan@16 | 955         action_data.type = AF.ITEM | 
| jcallahan@25 | 956         action_data.label = spell_label:lower() | 
| jcallahan@16 | 957 | 
| jcallahan@16 | 958         if tt_item_name and tt_item_name == target_name then | 
| jcallahan@28 | 959             action_data.identifier = ItemLinkToID(tt_item_link) | 
| jcallahan@16 | 960         elseif target_name and target_name ~= "" then | 
| jcallahan@16 | 961             local _, target_item_link = _G.GetItemInfo(target_name) | 
| jcallahan@28 | 962             action_data.identifier = ItemLinkToID(target_item_link) | 
| jcallahan@16 | 963         end | 
| jcallahan@16 | 964     elseif not tt_item_name and not tt_unit_name then | 
| jcallahan@17 | 965         action_data.name = target_name | 
| jcallahan@17 | 966 | 
| jcallahan@1 | 967         if bit.band(spell_flags, AF.OBJECT) == AF.OBJECT then | 
| jcallahan@17 | 968             if target_name == "" then | 
| jcallahan@17 | 969                 return | 
| jcallahan@17 | 970             end | 
| jcallahan@11 | 971             local identifier = ("%s:%s"):format(spell_label, target_name) | 
| jcallahan@11 | 972             UpdateObjectLocation(identifier) | 
| jcallahan@11 | 973 | 
| jcallahan@1 | 974             action_data.type = AF.OBJECT | 
| jcallahan@11 | 975             action_data.identifier = identifier | 
| jcallahan@1 | 976         elseif bit.band(spell_flags, AF.ZONE) == AF.ZONE then | 
| jcallahan@17 | 977             action_data.type = AF.ZONE | 
| jcallahan@25 | 978             action_data.label = spell_label:lower() | 
| jcallahan@1 | 979         end | 
| jcallahan@1 | 980     end | 
| jcallahan@1 | 981     private.tracked_line = spell_line | 
| jcallahan@0 | 982 end | 
| jcallahan@0 | 983 | 
| jcallahan@0 | 984 | 
| jcallahan@1 | 985 function WDP:UNIT_SPELLCAST_SUCCEEDED(event_name, unit_id, spell_name, spell_rank, spell_line, spell_id) | 
| jcallahan@1 | 986     if unit_id ~= "player" then | 
| jcallahan@1 | 987         return | 
| jcallahan@1 | 988     end | 
| jcallahan@1 | 989 | 
| jcallahan@18 | 990     --    if private.SPELL_LABELS_BY_NAME[spell_name] then | 
| jcallahan@18 | 991     --        print(("%s: '%s', '%s', '%s', '%s', '%s'"):format(event_name, unit_id, spell_name, spell_rank, spell_line, spell_id)) | 
| jcallahan@18 | 992     --    end | 
| jcallahan@1 | 993     private.tracked_line = nil | 
| jcallahan@0 | 994 end | 
| jcallahan@0 | 995 | 
| jcallahan@1 | 996 function WDP:HandleSpellFailure(event_name, unit_id, spell_name, spell_rank, spell_line, spell_id) | 
| jcallahan@1 | 997     if unit_id ~= "player" then | 
| jcallahan@1 | 998         return | 
| jcallahan@1 | 999     end | 
| jcallahan@0 | 1000 | 
| jcallahan@1 | 1001     if private.tracked_line == spell_line then | 
| jcallahan@1 | 1002         private.tracked_line = nil | 
| jcallahan@1 | 1003     end | 
| jcallahan@0 | 1004 end |