annotate Main.lua @ 575:89fab01eaba0

Fixed WoWDBProfiler for Patch 8.1.0.
author Caleb Atherton <atcaleb@twitch.tv>
date Tue, 11 Dec 2018 18:13:33 -0500
parents 444d15b2091e
children 78e9cbbf3d58
rev   line source
jcallahan@246 1 -- LUA API ------------------------------------------------------------
jcallahan@246 2
jcallahan@0 3 local _G = getfenv(0)
jcallahan@0 4
jcallahan@0 5 local pairs = _G.pairs
jcallahan@312 6 local tostring = _G.tostring
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@334 13 local next = _G.next
jcallahan@78 14 local select = _G.select
mmosimca@485 15 local type = _G.type
jcallahan@306 16 local unpack = _G.unpack
jcallahan@78 17
MMOSimca@383 18 local C_Timer = _G.C_Timer
mmosimca@496 19 local GetCurrencyInfo = _G.GetCurrencyInfo
MMOSimca@383 20
jcallahan@0 21
jcallahan@246 22 -- ADDON NAMESPACE ----------------------------------------------------
jcallahan@246 23
jcallahan@0 24 local ADDON_NAME, private = ...
jcallahan@0 25
jcallahan@0 26 local LibStub = _G.LibStub
MMOSimca@383 27 local WDP = LibStub("AceAddon-3.0"):NewAddon(ADDON_NAME, "AceConsole-3.0", "AceEvent-3.0")
jcallahan@0 28
jcallahan@48 29 local deformat = LibStub("LibDeformat-3.0")
atcaleb@558 30 local HereBeDragons = LibStub("HereBeDragons-2.0")
jcallahan@48 31
jcallahan@4 32 local DatamineTT = _G.CreateFrame("GameTooltip", "WDPDatamineTT", _G.UIParent, "GameTooltipTemplate")
jcallahan@5 33 DatamineTT:SetOwner(_G.WorldFrame, "ANCHOR_NONE")
jcallahan@5 34
jcallahan@0 35
atcaleb@566 36 -- SIMPLE CONSTANTS ----------------------------------------------------------
jcallahan@246 37
jcallahan@246 38 local AF = private.ACTION_TYPE_FLAGS
jcallahan@246 39 local CLIENT_LOCALE = _G.GetLocale()
atcaleb@565 40 local DB_VERSION = 19
MMOSimca@346 41 local DEBUGGING = false
jcallahan@156 42 local EVENT_DEBUG = false
jcallahan@322 43
mmosimca@485 44 -- Timer durations in seconds
mmosimca@485 45 local DELAY_PROCESS_ITEMS = 30
atcaleb@566 46 local DELAY_PROCESS_WORLD_QUESTS = 90
mmosimca@485 47 local DELAY_UPDATE_TARGET_LOCATION = 0.5
mmosimca@485 48
MMOSimca@405 49 local ITEM_ID_TIMBER = 114781
MMOSimca@405 50
mmosimca@520 51 local LOOT_SLOT_CURRENCY = _G.LOOT_SLOT_CURRENCY
mmosimca@520 52 local LOOT_SLOT_ITEM = _G.LOOT_SLOT_ITEM
mmosimca@520 53 local LOOT_SLOT_MONEY = _G.LOOT_SLOT_MONEY
mmosimca@520 54
MMOSimca@539 55 --local LOOT_SOURCE_ID_UNKNOWN = 1 -- Technically unused right now, but has future use potential
MMOSimca@539 56 --local LOOT_SOURCE_ID_REDUNDANT = 3 -- Technically unused right now, but has future use potential
MMOSimca@422 57 local LOOT_SOURCE_ID_GARRISON_CACHE = 10
MMOSimca@422 58
jcallahan@246 59 local OBJECT_ID_ANVIL = 192628
jcallahan@322 60 local OBJECT_ID_FISHING_BOBBER = 35591
jcallahan@246 61 local OBJECT_ID_FORGE = 1685
jcallahan@322 62
MMOSimca@454 63 local PLAYER_CLASS, PLAYER_CLASS_ID = _G.select(2, _G.UnitClass("player"))
jcallahan@246 64 local PLAYER_FACTION = _G.UnitFactionGroup("player")
jcallahan@300 65 local PLAYER_GUID
mmosimca@485 66 local PLAYER_LEVEL = _G.UnitLevel("player")
jcallahan@246 67 local PLAYER_NAME = _G.UnitName("player")
jcallahan@246 68 local PLAYER_RACE = _G.select(2, _G.UnitRace("player"))
jcallahan@246 69
mmosimca@520 70 local SPELL_ID_UPDATE_INTERACTIONS = 161006
MMOSimca@377 71
atcaleb@566 72 local UI_MAP_COSMIC = 946
atcaleb@566 73
jcallahan@246 74 local ALLOWED_LOCALES = {
jcallahan@246 75 enUS = true,
jcallahan@246 76 enGB = true,
MMOSimca@336 77 enTW = true,
MMOSimca@336 78 enCN = true,
jcallahan@246 79 }
jcallahan@157 80
jcallahan@0 81 local DATABASE_DEFAULTS = {
jcallahan@128 82 char = {},
jcallahan@0 83 global = {
jcallahan@270 84 config = {
jcallahan@270 85 minimap_icon = {
jcallahan@270 86 hide = true,
jcallahan@270 87 },
jcallahan@270 88 },
jcallahan@0 89 items = {},
jcallahan@0 90 npcs = {},
jcallahan@0 91 objects = {},
jcallahan@0 92 quests = {},
jcallahan@167 93 spells = {},
MMOSimca@550 94 world_quests = {},
jcallahan@17 95 zones = {},
jcallahan@0 96 }
jcallahan@0 97 }
jcallahan@0 98
jcallahan@1 99 local EVENT_MAPPING = {
MMOSimca@436 100 AUCTION_HOUSE_CLOSED = "ResumeChatLootRecording",
MMOSimca@436 101 AUCTION_HOUSE_SHOW = true, -- also triggers StopChatLootRecording
MMOSimca@436 102 BANKFRAME_CLOSED = "ResumeChatLootRecording",
MMOSimca@436 103 BANKFRAME_OPENED = true, -- also triggers StopChatLootRecording
jcallahan@90 104 BATTLEFIELDS_SHOW = true,
jcallahan@56 105 BLACK_MARKET_ITEM_UPDATE = true,
MMOSimca@408 106 BONUS_ROLL_RESULT = true,
MMOSimca@388 107 CHAT_MSG_CURRENCY = true,
jcallahan@48 108 CHAT_MSG_LOOT = true,
jcallahan@95 109 CHAT_MSG_MONSTER_SAY = "RecordQuote",
jcallahan@95 110 CHAT_MSG_MONSTER_WHISPER = "RecordQuote",
jcallahan@95 111 CHAT_MSG_MONSTER_YELL = "RecordQuote",
jcallahan@40 112 CHAT_MSG_SYSTEM = true,
jcallahan@23 113 COMBAT_LOG_EVENT_UNFILTERED = true,
jcallahan@18 114 COMBAT_TEXT_UPDATE = true,
jcallahan@140 115 CURSOR_UPDATE = true,
MMOSimca@436 116 GARRISON_MISSION_NPC_CLOSED = "ResumeChatLootRecording",
MMOSimca@436 117 GARRISON_MISSION_NPC_OPENED = "StopChatLootRecording",
MMOSimca@450 118 GARRISON_SHIPYARD_NPC_CLOSED = "ResumeChatLootRecording",
MMOSimca@450 119 GARRISON_SHIPYARD_NPC_OPENED = "StopChatLootRecording",
MMOSimca@436 120 GOSSIP_CLOSED = "ResumeChatLootRecording",
MMOSimca@436 121 GOSSIP_SHOW = true, -- also triggers StopChatLootRecording
jcallahan@290 122 GROUP_ROSTER_UPDATE = true,
MMOSimca@436 123 GUILDBANKFRAME_CLOSED = "ResumeChatLootRecording",
MMOSimca@436 124 GUILDBANKFRAME_OPENED = true, -- also triggers StopChatLootRecording
atcaleb@573 125 ISLAND_AZERITE_GAIN = true,
atcaleb@573 126 ISLAND_COMPLETED = true,
jcallahan@42 127 ITEM_TEXT_BEGIN = true,
jcallahan@189 128 ITEM_UPGRADE_MASTER_OPENED = true,
jcallahan@124 129 LOOT_CLOSED = true,
MMOSimca@343 130 LOOT_OPENED = true,
MMOSimca@412 131 LOOT_SLOT_CLEARED = "HandleBadChatLootData",
MMOSimca@436 132 MAIL_CLOSED = "ResumeChatLootRecording",
MMOSimca@436 133 MAIL_SHOW = true, -- also triggers StopChatLootRecording
MMOSimca@436 134 MERCHANT_CLOSED = true, -- also triggers ResumeChatLootRecording
MMOSimca@436 135 MERCHANT_SHOW = "UpdateMerchantItems", -- also triggers StopChatLootRecording
jcallahan@61 136 MERCHANT_UPDATE = "UpdateMerchantItems",
jcallahan@25 137 PET_BAR_UPDATE = true,
MMOSimca@368 138 --PET_JOURNAL_LIST_UPDATE = true,
jcallahan@156 139 PLAYER_REGEN_DISABLED = true,
jcallahan@156 140 PLAYER_REGEN_ENABLED = true,
jcallahan@2 141 PLAYER_TARGET_CHANGED = true,
jcallahan@9 142 QUEST_COMPLETE = true,
jcallahan@9 143 QUEST_DETAIL = true,
jcallahan@9 144 QUEST_LOG_UPDATE = true,
jcallahan@97 145 QUEST_PROGRESS = true,
jcallahan@178 146 SHOW_LOOT_TOAST = true,
jcallahan@306 147 SPELL_CONFIRMATION_PROMPT = true,
jcallahan@88 148 TAXIMAP_OPENED = true,
MMOSimca@437 149 TRADE_CLOSED = "ResumeChatLootRecording",
MMOSimca@437 150 TRADE_SHOW = "StopChatLootRecording",
jcallahan@92 151 TRADE_SKILL_SHOW = true,
jcallahan@167 152 TRAINER_CLOSED = true,
jcallahan@27 153 TRAINER_SHOW = true,
jcallahan@90 154 TRANSMOGRIFY_OPEN = true,
jcallahan@246 155 UNIT_PET = true,
jcallahan@4 156 UNIT_QUEST_LOG_CHANGED = true,
jcallahan@1 157 UNIT_SPELLCAST_FAILED = "HandleSpellFailure",
jcallahan@1 158 UNIT_SPELLCAST_FAILED_QUIET = "HandleSpellFailure",
jcallahan@1 159 UNIT_SPELLCAST_INTERRUPTED = "HandleSpellFailure",
jcallahan@1 160 UNIT_SPELLCAST_SENT = true,
jcallahan@1 161 UNIT_SPELLCAST_SUCCEEDED = true,
jcallahan@90 162 VOID_STORAGE_OPEN = true,
jcallahan@0 163 }
jcallahan@0 164
jcallahan@4 165
jcallahan@246 166 -- VARIABLES ----------------------------------------------------------
jcallahan@246 167
jcallahan@92 168 local anvil_spell_ids = {}
jcallahan@92 169 local currently_drunk
jcallahan@128 170 local char_db
jcallahan@128 171 local global_db
jcallahan@299 172 local group_member_guids = {}
jcallahan@246 173 local group_owner_guids_to_pet_guids = {}
jcallahan@246 174 local group_pet_guids = {}
jcallahan@299 175 local in_instance
jcallahan@187 176 local item_process_timer_handle
jcallahan@92 177 local faction_standings = {}
jcallahan@92 178 local forge_spell_ids = {}
jcallahan@95 179 local languages_known = {}
jcallahan@317 180 local boss_loot_toasting = {}
MMOSimca@387 181 local container_loot_toasting
MMOSimca@387 182 local loot_toast_container_id
MMOSimca@387 183 local raid_boss_id
jcallahan@306 184 local loot_toast_container_timer_handle
jcallahan@307 185 local loot_toast_data
jcallahan@307 186 local loot_toast_data_timer_handle
jcallahan@95 187 local name_to_id_map = {}
jcallahan@306 188 local killed_boss_id_timer_handle
jcallahan@177 189 local killed_npc_id
jcallahan@2 190 local target_location_timer_handle
MMOSimca@345 191 local last_timber_spell_id
MMOSimca@355 192 local last_garrison_cache_object_id
MMOSimca@436 193 local block_chat_loot_data
MMOSimca@435 194 local chat_loot_data = {}
MMOSimca@347 195 local chat_loot_timer_handle
mmosimca@485 196 local world_quest_timer_handle
mmosimca@485 197 local world_quest_event_timestamp = 0
atcaleb@573 198 local killed_npcs_in_island = {}
atcaleb@573 199 local island_difficulty_token
jcallahan@86 200 local current_target_id
jcallahan@131 201 local current_loot
jcallahan@1 202
jcallahan@312 203
jcallahan@121 204 -- Data for our current action. Including possible values as a reference.
jcallahan@122 205 local current_action = {
jcallahan@121 206 identifier = nil,
jcallahan@121 207 loot_label = nil,
jcallahan@121 208 loot_list = nil,
jcallahan@121 209 loot_sources = nil,
jcallahan@121 210 map_level = nil,
jcallahan@121 211 spell_label = nil,
jcallahan@123 212 target_type = nil,
jcallahan@121 213 x = nil,
jcallahan@121 214 y = nil,
jcallahan@121 215 zone_data = nil,
jcallahan@121 216 }
jcallahan@92 217
jcallahan@246 218
MMOSimca@393 219 -- Timer prototypes
MMOSimca@393 220 local ClearKilledNPC, ClearKilledBossID, ClearLootToastContainerID, ClearLootToastData, ClearChatLootData
MMOSimca@393 221
MMOSimca@393 222
jcallahan@246 223 -- HELPERS ------------------------------------------------------------
jcallahan@246 224
jcallahan@245 225 local function Debug(message, ...)
MMOSimca@350 226 if not DEBUGGING or not message then
jcallahan@151 227 return
jcallahan@151 228 end
catherton@465 229
MMOSimca@350 230 if ... then
MMOSimca@350 231 local args = { ... }
MMOSimca@350 232
MMOSimca@350 233 for index = 1, #args do
MMOSimca@377 234 args[index] = tostring(args[index])
jcallahan@306 235 end
MMOSimca@350 236 _G.print(message:format(unpack(args)))
MMOSimca@350 237 else
MMOSimca@350 238 _G.print(message)
jcallahan@306 239 end
jcallahan@151 240 end
jcallahan@151 241
jcallahan@151 242
MMOSimca@393 243 local function InitializeCurrentLoot()
MMOSimca@393 244 current_loot = {
MMOSimca@393 245 list = {},
MMOSimca@393 246 sources = {},
MMOSimca@393 247 identifier = current_action.identifier,
MMOSimca@393 248 label = current_action.loot_label or "drops",
MMOSimca@393 249 map_level = current_action.map_level,
MMOSimca@393 250 object_name = current_action.object_name,
MMOSimca@393 251 spell_label = current_action.spell_label,
MMOSimca@393 252 target_type = current_action.target_type,
MMOSimca@393 253 x = current_action.x,
MMOSimca@393 254 y = current_action.y,
MMOSimca@393 255 zone_data = current_action.zone_data,
MMOSimca@393 256 }
MMOSimca@393 257
MMOSimca@393 258 table.wipe(current_action)
MMOSimca@393 259 end
MMOSimca@393 260
MMOSimca@393 261
jcallahan@169 262 local TradeSkillExecutePer
jcallahan@169 263 do
jcallahan@169 264 local header_list = {}
jcallahan@169 265
jcallahan@169 266 function TradeSkillExecutePer(iter_func)
jcallahan@169 267 if not _G.TradeSkillFrame or not _G.TradeSkillFrame:IsVisible() then
jcallahan@169 268 return
jcallahan@169 269 end
catherton@465 270
catherton@479 271 local recipes = _G.C_TradeSkillUI.GetAllRecipeIDs()
catherton@479 272
catherton@479 273 if recipes and (#recipes > 0) then
catherton@479 274 for i = 1, #recipes do
catherton@479 275 if iter_func(_G.C_TradeSkillUI.GetRecipeInfo(recipes[i]).name, recipes[i]) then
catherton@465 276 break
catherton@465 277 end
catherton@465 278 end
catherton@465 279 end
jcallahan@167 280 end
jcallahan@169 281 end -- do-block
jcallahan@167 282
jcallahan@167 283
jcallahan@39 284 local ActualCopperCost
jcallahan@39 285 do
jcallahan@39 286 local BARTERING_SPELL_ID = 83964
jcallahan@39 287
jcallahan@39 288 function ActualCopperCost(copper_cost, rep_standing)
jcallahan@39 289 if not copper_cost or copper_cost == 0 then
jcallahan@39 290 return 0
jcallahan@39 291 end
jcallahan@39 292 local modifier = 1
jcallahan@39 293
jcallahan@39 294 if _G.IsSpellKnown(BARTERING_SPELL_ID) then
jcallahan@39 295 modifier = modifier - 0.1
jcallahan@39 296 end
jcallahan@39 297
jcallahan@39 298 if rep_standing then
jcallahan@39 299 if PLAYER_RACE == "Goblin" then
atcaleb@558 300 modifier = modifier - private.STANDING_DISCOUNTS["EXALTED"]
atcaleb@558 301 elseif private.STANDING_DISCOUNTS[rep_standing] then
atcaleb@558 302 modifier = modifier - private.STANDING_DISCOUNTS[rep_standing]
jcallahan@39 303 end
jcallahan@39 304 end
jcallahan@39 305 return math.floor(copper_cost / modifier)
jcallahan@39 306 end
jcallahan@39 307 end -- do-block
jcallahan@39 308
jcallahan@39 309
jcallahan@29 310 local function InstanceDifficultyToken()
MMOSimca@440 311 -- Sometimes, instance information is returned when not in an instance. This check protects against that.
MMOSimca@440 312 if _G.IsInInstance() then
MMOSimca@440 313 local _, instance_type, instance_difficulty, _, _, _, is_dynamic = _G.GetInstanceInfo()
MMOSimca@440 314
MMOSimca@440 315 if not instance_type or instance_type == "" then
MMOSimca@440 316 instance_type = "NONE"
MMOSimca@440 317 end
MMOSimca@440 318 return ("%s:%d:%s"):format(instance_type:upper(), instance_difficulty, tostring(is_dynamic))
jcallahan@59 319 end
MMOSimca@440 320 return "NONE:0:false"
jcallahan@29 321 end
jcallahan@29 322
jcallahan@29 323
jcallahan@1 324 local function CurrentLocationData()
mmosimca@488 325 local x, y, current_area_id, map_level, map_file, is_micro_dungeon = HereBeDragons:GetPlayerZonePosition(false)
catherton@468 326 local zone_name = _G.GetRealZoneText()
catherton@465 327
mmosimca@488 328 -- Remove micro-dungeon-ness by translating back to the parent world map (at floor 0) if possible
mmosimca@488 329 if (is_micro_dungeon and x and y and current_area_id and map_level and map_level > 0) then
mmosimca@488 330 x, y = HereBeDragons:TranslateZoneCoordinates(x, y, current_area_id, map_level, current_area_id, 0, false)
mmosimca@488 331 map_level = 0
mmosimca@488 332 end
mmosimca@488 333
catherton@465 334 -- Put coordinates into expected format (as integers, they don't get a billion decimals output in the SavedVariables)
catherton@468 335 local x_int = nil
catherton@468 336 if (x and type(x) == "number") then
catherton@468 337 x_int = _G.floor(x * 1000)
mmosimca@485 338
mmosimca@482 339 -- Limit precision to 0.2
catherton@468 340 if x_int % 2 ~= 0 then
catherton@468 341 x_int = x_int + 1
catherton@468 342 end
mmosimca@485 343
mmosimca@482 344 -- Prevent out of bounds coordinates
mmosimca@482 345 if (x_int < 0 or x_int > 1000) then
mmosimca@482 346 x_int = nil
mmosimca@482 347 end
jcallahan@145 348 end
catherton@468 349 local y_int = nil
catherton@468 350 if (y and type(y) == "number") then
catherton@468 351 y_int = _G.floor(y * 1000)
mmosimca@485 352
mmosimca@482 353 -- Limit precision to 0.2
catherton@468 354 if y_int % 2 ~= 0 then
catherton@468 355 y_int = y_int + 1
catherton@468 356 end
mmosimca@485 357
mmosimca@482 358 -- Prevent out of bounds coordinates
mmosimca@482 359 if (y_int < 0 or y_int > 1000) then
mmosimca@482 360 y_int = nil
mmosimca@482 361 end
jcallahan@1 362 end
jcallahan@1 363
catherton@468 364 return zone_name, current_area_id, x_int, y_int, map_level, InstanceDifficultyToken()
jcallahan@1 365 end
jcallahan@1 366
jcallahan@1 367
MMOSimca@441 368 local function DBEntry(data_type, unit_id)
MMOSimca@441 369 if not data_type or not unit_id then
jcallahan@312 370 return
jcallahan@312 371 end
MMOSimca@441 372 local category = global_db[data_type]
MMOSimca@441 373
MMOSimca@441 374 if not category then
MMOSimca@441 375 category = {}
MMOSimca@441 376 global_db[data_type] = category
MMOSimca@441 377 end
MMOSimca@441 378 local unit = category[unit_id]
MMOSimca@441 379
MMOSimca@441 380 if not unit then
MMOSimca@441 381 unit = {}
MMOSimca@441 382 category[unit_id] = unit
MMOSimca@441 383 end
MMOSimca@441 384 return unit
jcallahan@312 385 end
jcallahan@312 386
MMOSimca@441 387 private.DBEntry = DBEntry
MMOSimca@441 388
MMOSimca@441 389 local NPCEntry
MMOSimca@441 390 do
MMOSimca@441 391 local npc_prototype = {}
MMOSimca@441 392 local npc_meta = {
MMOSimca@441 393 __index = npc_prototype
MMOSimca@441 394 }
MMOSimca@441 395
MMOSimca@441 396 function NPCEntry(identifier)
MMOSimca@441 397 local npc = DBEntry("npcs", identifier)
MMOSimca@441 398 return npc and _G.setmetatable(npc, npc_meta) or nil
jcallahan@1 399 end
MMOSimca@441 400
MMOSimca@441 401 function npc_prototype:EncounterData(difficulty_token)
MMOSimca@441 402 self.encounter_data = self.encounter_data or {}
MMOSimca@441 403 self.encounter_data[difficulty_token] = self.encounter_data[difficulty_token] or {}
MMOSimca@441 404 self.encounter_data[difficulty_token].stats = self.encounter_data[difficulty_token].stats or {}
MMOSimca@441 405
MMOSimca@441 406 return self.encounter_data[difficulty_token]
MMOSimca@441 407 end
jcallahan@1 408 end
jcallahan@270 409
jcallahan@4 410
jcallahan@141 411 local UpdateDBEntryLocation
jcallahan@141 412 do
jcallahan@141 413 -- Fishing node coordinate code based on code in GatherMate2 with permission from Kagaro.
jcallahan@141 414 local function FishingCoordinates(x, y, yard_width, yard_height)
jcallahan@141 415 local facing = _G.GetPlayerFacing()
jcallahan@141 416
jcallahan@141 417 if not facing then
jcallahan@141 418 return x, y
jcallahan@141 419 end
jcallahan@246 420 local rad = facing + math.pi
jcallahan@141 421 return x + math.sin(rad) * 15 / yard_width, y + math.cos(rad) * 15 / yard_height
jcallahan@10 422 end
jcallahan@10 423
jcallahan@24 424
jcallahan@141 425 function UpdateDBEntryLocation(entry_type, identifier)
jcallahan@141 426 if not identifier then
jcallahan@141 427 return
jcallahan@141 428 end
jcallahan@141 429 local zone_name, area_id, x, y, map_level, difficulty_token = CurrentLocationData()
MMOSimca@328 430 if not (zone_name and area_id and x and y and map_level) then
mmosimca@508 431 if not (_G.IsInInstance()) then
mmosimca@508 432 Debug("UpdateDBEntryLocation: Missing current location data - %s, %s, %s, %s, %s.", tostring(zone_name), tostring(area_id), tostring(x), tostring(y), tostring(map_level))
mmosimca@508 433 end
MMOSimca@328 434 return
MMOSimca@328 435 end
jcallahan@141 436 local entry = DBEntry(entry_type, identifier)
jcallahan@141 437 entry[difficulty_token] = entry[difficulty_token] or {}
jcallahan@141 438 entry[difficulty_token].locations = entry[difficulty_token].locations or {}
jcallahan@141 439
jcallahan@141 440 local zone_token = ("%s:%d"):format(zone_name, area_id)
jcallahan@141 441 local zone_data = entry[difficulty_token].locations[zone_token]
jcallahan@141 442
jcallahan@141 443 if not zone_data then
jcallahan@141 444 zone_data = {}
jcallahan@141 445 entry[difficulty_token].locations[zone_token] = zone_data
jcallahan@141 446 end
jcallahan@141 447
jcallahan@141 448 -- Special case for Fishing.
jcallahan@141 449 if current_action.spell_label == "FISHING" then
atcaleb@561 450 local _, q_vector = _G.C_Map.GetWorldPosFromMapPos(area_id, _G.CreateVector2D(0,0))
atcaleb@561 451 local _, w_vector = _G.C_Map.GetWorldPosFromMapPos(area_id, _G.CreateVector2D(1,1))
atcaleb@561 452 local yard_width, yard_height = q_vector.y - w_vector.y, q_vector.x - w_vector.x
jcallahan@141 453
jcallahan@141 454 if yard_width > 0 and yard_height > 0 then
jcallahan@141 455 x, y = FishingCoordinates(x, y, yard_width, yard_height)
jcallahan@141 456 current_action.x = x
jcallahan@141 457 current_action.y = y
jcallahan@141 458 end
jcallahan@141 459 end
jcallahan@141 460 local location_token = ("%d:%d:%d"):format(map_level, x, y)
jcallahan@141 461
jcallahan@141 462 zone_data[location_token] = zone_data[location_token] or true
jcallahan@141 463 return zone_data
jcallahan@10 464 end
jcallahan@141 465 end -- do-block
jcallahan@10 466
jcallahan@10 467
mmosimca@496 468 local function CurrencyLinkToID(currency_link)
MMOSimca@441 469 if not currency_link then
mmosimca@496 470 return nil
MMOSimca@441 471 end
MMOSimca@540 472 return tonumber(tostring(currency_link):match("currency:(%d+)"))
MMOSimca@441 473 end
MMOSimca@441 474
MMOSimca@441 475
MMOSimca@441 476 local function ItemLinkToID(item_link)
MMOSimca@441 477 if not item_link then
MMOSimca@441 478 return
MMOSimca@441 479 end
MMOSimca@441 480 return tonumber(tostring(item_link):match("item:(%d+)"))
MMOSimca@441 481 end
MMOSimca@441 482
MMOSimca@441 483 private.ItemLinkToID = ItemLinkToID
MMOSimca@441 484
MMOSimca@441 485 local function UnitTypeIsNPC(unit_type)
MMOSimca@441 486 return unit_type == private.UNIT_TYPES.NPC or unit_type == private.UNIT_TYPES.VEHICLE
MMOSimca@441 487 end
MMOSimca@441 488
MMOSimca@441 489
MMOSimca@441 490 local ParseGUID
MMOSimca@441 491 do
MMOSimca@441 492 local UNIT_TYPES = private.UNIT_TYPES
MMOSimca@441 493
MMOSimca@441 494 local NPC_ID_MAPPING = {
MMOSimca@441 495 [62164] = 63191, -- Garalon
MMOSimca@441 496 }
MMOSimca@441 497
MMOSimca@441 498
MMOSimca@441 499 local function MatchUnitTypes(unit_type_name)
MMOSimca@441 500 if not unit_type_name then
MMOSimca@441 501 return UNIT_TYPES.UNKNOWN
MMOSimca@441 502 end
MMOSimca@441 503
MMOSimca@441 504 for def, text in next, UNIT_TYPES do
MMOSimca@441 505 if unit_type_name == text then
MMOSimca@441 506 return UNIT_TYPES[def]
MMOSimca@441 507 end
MMOSimca@441 508 end
MMOSimca@441 509 return UNIT_TYPES.UNKNOWN
MMOSimca@441 510 end
MMOSimca@441 511
MMOSimca@441 512
MMOSimca@441 513 function ParseGUID(guid)
MMOSimca@441 514 if not guid then
MMOSimca@441 515 return
MMOSimca@441 516 end
MMOSimca@441 517
MMOSimca@441 518 -- We might want to use some of this new information later, but leaving the returns alone for now
mmosimca@497 519 local guid_pieces = { ("-"):split(guid) }
mmosimca@497 520 local unit_type_name, unk_field, server_id, instance_id, zone_uid, unit_id, spawn_uid, player_uid = guid_pieces[1]
MMOSimca@441 521
MMOSimca@441 522 local unit_type = MatchUnitTypes(unit_type_name)
mmosimca@518 523
mmosimca@497 524 -- Creatures, GameObjects, Vehicles, and Vignettes
MMOSimca@441 525 if unit_type ~= UNIT_TYPES.PLAYER and unit_type ~= UNIT_TYPES.PET and unit_type ~= UNIT_TYPES.ITEM then
mmosimca@497 526 unk_field, server_id, instance_id, zone_uid, unit_id, spawn_uid = guid_pieces[2], guid_pieces[3], guid_pieces[4], guid_pieces[5], guid_pieces[6], guid_pieces[7]
mmosimca@497 527
mmosimca@497 528 local id_mapping = NPC_ID_MAPPING[unit_id]
MMOSimca@441 529
MMOSimca@441 530 if id_mapping and UnitTypeIsNPC(unit_type) then
mmosimca@497 531 unit_id = id_mapping
MMOSimca@441 532 end
mmosimca@497 533
mmosimca@497 534 -- Players
mmosimca@497 535 elseif unit_type == UNIT_TYPES.PLAYER then
mmosimca@497 536 server_id, player_uid = guid_pieces[2], guid_pieces[3]
mmosimca@497 537
mmosimca@497 538 -- Items
mmosimca@497 539 elseif unit_type == UNIT_TYPES.ITEM then
mmosimca@497 540 server_id, unk_field, spawn_uid = guid_pieces[2], guid_pieces[3], guid_pieces[4]
MMOSimca@441 541 end
mmosimca@518 542
mmosimca@497 543 -- Pets and other (i.e. do nothing)
mmosimca@497 544 return unit_type, unit_id
MMOSimca@441 545 end
MMOSimca@441 546
MMOSimca@441 547 private.ParseGUID = ParseGUID
MMOSimca@441 548 end -- do-block
MMOSimca@441 549
MMOSimca@441 550
jcallahan@19 551 local function HandleItemUse(item_link, bag_index, slot_index)
jcallahan@19 552 if not item_link then
jcallahan@19 553 return
jcallahan@19 554 end
jcallahan@19 555 local item_id = ItemLinkToID(item_link)
jcallahan@19 556
jcallahan@19 557 if not bag_index or not slot_index then
jcallahan@19 558 for new_bag_index = 0, _G.NUM_BAG_FRAMES do
jcallahan@19 559 for new_slot_index = 1, _G.GetContainerNumSlots(new_bag_index) do
jcallahan@19 560 if item_id == ItemLinkToID(_G.GetContainerItemLink(new_bag_index, new_slot_index)) then
jcallahan@19 561 bag_index = new_bag_index
jcallahan@19 562 slot_index = new_slot_index
jcallahan@19 563 break
jcallahan@19 564 end
jcallahan@19 565 end
jcallahan@19 566 end
jcallahan@19 567 end
jcallahan@19 568
MMOSimca@410 569 local any_loot = false
MMOSimca@410 570
MMOSimca@411 571 -- Check if Blizzard has marked this item as officially having a chance of containing loot
MMOSimca@410 572 if bag_index and slot_index then
MMOSimca@410 573 local _, _, _, _, _, is_lootable = _G.GetContainerItemInfo(bag_index, slot_index)
MMOSimca@410 574 if is_lootable then
MMOSimca@410 575 any_loot = true
MMOSimca@410 576 end
jcallahan@19 577 end
catherton@465 578
MMOSimca@410 579 -- Check if we've marked this item as one Blizzard provides bad is_lootable data for
MMOSimca@410 580 if private.CONTAINER_ITEM_ID_LIST[item_id] ~= nil then
MMOSimca@410 581 any_loot = true
jcallahan@19 582 end
MMOSimca@368 583
MMOSimca@436 584 -- Going to block 'chat-loot data' at this level for now because I don't think we actually want normal item containers being recorded in these scenarios either.
MMOSimca@436 585 if any_loot and not block_chat_loot_data then
MMOSimca@414 586 -- For item containers that open instantly with no spell cast
MMOSimca@410 587 if (private.CONTAINER_ITEM_ID_LIST[item_id] == true) and ((not _G.GetNumLootItems()) or (_G.GetNumLootItems() == 0)) then
MMOSimca@410 588 ClearChatLootData()
MMOSimca@410 589 Debug("HandleItemUse: Beginning chat-based loot timer for item with ID %d.", item_id)
MMOSimca@423 590 chat_loot_timer_handle = C_Timer.NewTimer(1.5, ClearChatLootData)
atcaleb@573 591 chat_loot_data.category = AF.ITEM
MMOSimca@414 592 chat_loot_data.identifier = item_id
MMOSimca@414 593 -- For normal item containers
MMOSimca@414 594 else
MMOSimca@414 595 table.wipe(current_action)
MMOSimca@414 596 current_loot = nil
MMOSimca@414 597 current_action.target_type = AF.ITEM
MMOSimca@414 598 current_action.identifier = item_id
MMOSimca@414 599 current_action.loot_label = "contains"
MMOSimca@410 600 end
MMOSimca@393 601 end
jcallahan@19 602 end
jcallahan@19 603
jcallahan@19 604
jcallahan@39 605 local UnitFactionStanding
jcallahan@32 606 local UpdateFactionData
jcallahan@32 607 do
jcallahan@32 608 local MAX_FACTION_INDEX = 1000
jcallahan@20 609
jcallahan@32 610 local STANDING_NAMES = {
jcallahan@32 611 "HATED",
jcallahan@32 612 "HOSTILE",
jcallahan@32 613 "UNFRIENDLY",
jcallahan@32 614 "NEUTRAL",
jcallahan@32 615 "FRIENDLY",
jcallahan@32 616 "HONORED",
jcallahan@32 617 "REVERED",
jcallahan@32 618 "EXALTED",
jcallahan@32 619 }
jcallahan@32 620
jcallahan@39 621
jcallahan@39 622 function UnitFactionStanding(unit)
jcallahan@135 623 local unit_name = _G.UnitName(unit)
jcallahan@39 624 UpdateFactionData()
jcallahan@39 625 DatamineTT:ClearLines()
jcallahan@39 626 DatamineTT:SetUnit(unit)
jcallahan@39 627
jcallahan@39 628 for line_index = 1, DatamineTT:NumLines() do
jcallahan@64 629 local faction_name = _G["WDPDatamineTTTextLeft" .. line_index]:GetText():trim()
jcallahan@39 630
jcallahan@135 631 if faction_name and faction_name ~= unit_name and faction_standings[faction_name] then
jcallahan@39 632 return faction_name, faction_standings[faction_name]
jcallahan@39 633 end
jcallahan@39 634 end
jcallahan@39 635 end
jcallahan@39 636
jcallahan@39 637
jcallahan@32 638 function UpdateFactionData()
jcallahan@32 639 for faction_index = 1, MAX_FACTION_INDEX do
jcallahan@32 640 local faction_name, _, current_standing, _, _, _, _, _, is_header = _G.GetFactionInfo(faction_index)
jcallahan@32 641
jcallahan@86 642 if faction_name then
jcallahan@32 643 faction_standings[faction_name] = STANDING_NAMES[current_standing]
jcallahan@32 644 elseif not faction_name then
jcallahan@32 645 break
jcallahan@32 646 end
jcallahan@20 647 end
jcallahan@20 648 end
jcallahan@32 649 end -- do-block
jcallahan@20 650
jcallahan@48 651
MMOSimca@429 652 local GenericLootUpdate, LootTable
jcallahan@75 653 do
MMOSimca@429 654 function LootTable(entry, loot_type, top_field)
jcallahan@75 655 if top_field then
jcallahan@75 656 entry[top_field] = entry[top_field] or {}
jcallahan@75 657 entry[top_field][loot_type] = entry[top_field][loot_type] or {}
jcallahan@75 658 return entry[top_field][loot_type]
jcallahan@75 659 end
jcallahan@48 660 entry[loot_type] = entry[loot_type] or {}
jcallahan@75 661 return entry[loot_type]
jcallahan@48 662 end
jcallahan@48 663
jcallahan@75 664 function GenericLootUpdate(data_type, top_field)
jcallahan@132 665 local loot_type = current_loot.label
jcallahan@75 666 local loot_count = ("%s_count"):format(loot_type)
jcallahan@77 667 local source_list = {}
jcallahan@75 668
jcallahan@131 669 if current_loot.sources then
jcallahan@131 670 for source_guid, loot_data in pairs(current_loot.sources) do
jcallahan@304 671 local source_id
jcallahan@78 672
jcallahan@131 673 if current_loot.target_type == AF.ITEM then
jcallahan@119 674 -- Items return the player as the source, so we need to use the item's ID (disenchant, milling, etc)
jcallahan@131 675 source_id = current_loot.identifier
jcallahan@119 676 else
jcallahan@331 677 local _, unit_ID = ParseGUID(source_guid)
MMOSimca@328 678 if unit_ID then
MMOSimca@328 679 if current_loot.target_type == AF.OBJECT then
MMOSimca@328 680 source_id = ("%s:%s"):format(current_loot.spell_label, unit_ID)
MMOSimca@328 681 else
MMOSimca@328 682 source_id = unit_ID
MMOSimca@328 683 end
MMOSimca@328 684 end
jcallahan@119 685 end
jcallahan@304 686 local entry = DBEntry(data_type, source_id)
jcallahan@75 687
jcallahan@119 688 if entry then
jcallahan@119 689 local loot_table = LootTable(entry, loot_type, top_field)
jcallahan@77 690
jcallahan@304 691 if not source_list[source_id] then
jcallahan@119 692 if top_field then
jcallahan@119 693 entry[top_field][loot_count] = (entry[top_field][loot_count] or 0) + 1
MMOSimca@387 694 elseif not container_loot_toasting then
jcallahan@119 695 entry[loot_count] = (entry[loot_count] or 0) + 1
jcallahan@119 696 end
jcallahan@304 697 source_list[source_id] = true
jcallahan@77 698 end
jcallahan@119 699 UpdateDBEntryLocation(data_type, source_id)
jcallahan@75 700
jcallahan@309 701 if current_loot.target_type == AF.ZONE then
jcallahan@309 702 for item_id, quantity in pairs(loot_data) do
jcallahan@309 703 table.insert(loot_table, ("%d:%d"):format(item_id, quantity))
jcallahan@309 704 end
jcallahan@309 705 else
jcallahan@308 706 for loot_token, quantity in pairs(loot_data) do
mmosimca@496 707 local label, currency_id = (":"):split(loot_token)
mmosimca@496 708
mmosimca@496 709 if label == "currency" and currency_id then
mmosimca@496 710 -- Convert currency_id back into number from string
mmosimca@496 711 currency_id = tonumber(currency_id) or 0
mmosimca@496 712 if currency_id ~= 0 then
mmosimca@496 713 table.insert(loot_table, ("currency:%d:%d"):format(quantity, currency_id))
mmosimca@496 714 end
jcallahan@308 715 elseif loot_token == "money" then
jcallahan@308 716 table.insert(loot_table, ("money:%d"):format(quantity))
jcallahan@308 717 else
jcallahan@308 718 table.insert(loot_table, ("%d:%d"):format(loot_token, quantity))
jcallahan@308 719 end
jcallahan@308 720 end
jcallahan@119 721 end
jcallahan@75 722 end
jcallahan@75 723 end
jcallahan@75 724 end
jcallahan@121 725
jcallahan@121 726 -- This is used for Gas Extractions.
jcallahan@131 727 if #current_loot.list <= 0 then
jcallahan@78 728 return
jcallahan@78 729 end
jcallahan@82 730 local entry
jcallahan@82 731
jcallahan@82 732 -- At this point we only have a name if it's an object.
MMOSimca@388 733 -- (As of 5.x, the above statement is almost never true, but there are a few cases, like gas extractions.)
jcallahan@131 734 if current_loot.target_type == AF.OBJECT then
jcallahan@131 735 entry = DBEntry(data_type, ("%s:%s"):format(current_loot.spell_label, current_loot.object_name))
jcallahan@82 736 else
jcallahan@131 737 entry = DBEntry(data_type, current_loot.identifier)
jcallahan@82 738 end
jcallahan@75 739
jcallahan@75 740 if not entry then
jcallahan@75 741 return
jcallahan@75 742 end
jcallahan@77 743 local loot_table = LootTable(entry, loot_type, top_field)
jcallahan@77 744
jcallahan@307 745 if current_loot.identifier then
jcallahan@307 746 if not source_list[current_loot.identifier] then
jcallahan@307 747 if top_field then
jcallahan@307 748 entry[top_field][loot_count] = (entry[top_field][loot_count] or 0) + 1
jcallahan@307 749 else
jcallahan@307 750 entry[loot_count] = (entry[loot_count] or 0) + 1
jcallahan@307 751 end
jcallahan@307 752 source_list[current_loot.identifier] = true
jcallahan@77 753 end
jcallahan@77 754 end
jcallahan@75 755
jcallahan@131 756 for index = 1, #current_loot.list do
jcallahan@131 757 table.insert(loot_table, current_loot.list[index])
jcallahan@75 758 end
jcallahan@48 759 end
jcallahan@75 760 end -- do-block
jcallahan@48 761
jcallahan@97 762
jcallahan@97 763 local ReplaceKeywords
jcallahan@97 764 do
jcallahan@97 765 local KEYWORD_SUBSTITUTIONS = {
jcallahan@97 766 class = PLAYER_CLASS,
jcallahan@97 767 name = PLAYER_NAME,
jcallahan@97 768 race = PLAYER_RACE,
jcallahan@97 769 }
jcallahan@97 770
jcallahan@97 771
jcallahan@97 772 function ReplaceKeywords(text)
jcallahan@97 773 if not text or text == "" then
jcallahan@97 774 return ""
jcallahan@97 775 end
jcallahan@97 776
jcallahan@97 777 for category, lookup in pairs(KEYWORD_SUBSTITUTIONS) do
jcallahan@97 778 local category_format = ("<%s>"):format(category)
jcallahan@97 779 text = text:gsub(lookup, category_format):gsub(lookup:lower(), category_format)
jcallahan@97 780 end
jcallahan@97 781 return text
jcallahan@97 782 end
jcallahan@97 783 end -- do-block
jcallahan@97 784
jcallahan@97 785
MMOSimca@347 786 -- TIMERS -------------------------------------------------------------
MMOSimca@347 787
MMOSimca@393 788 function ClearKilledNPC()
MMOSimca@347 789 killed_npc_id = nil
MMOSimca@347 790 end
MMOSimca@347 791
MMOSimca@347 792
MMOSimca@393 793 function ClearKilledBossID()
MMOSimca@347 794 if killed_boss_id_timer_handle then
MMOSimca@383 795 killed_boss_id_timer_handle:Cancel()
MMOSimca@347 796 killed_boss_id_timer_handle = nil
MMOSimca@347 797 end
MMOSimca@347 798
MMOSimca@347 799 table.wipe(boss_loot_toasting)
MMOSimca@387 800 raid_boss_id = nil
MMOSimca@347 801 end
MMOSimca@347 802
MMOSimca@347 803
MMOSimca@393 804 function ClearLootToastContainerID()
MMOSimca@347 805 if loot_toast_container_timer_handle then
MMOSimca@383 806 loot_toast_container_timer_handle:Cancel()
MMOSimca@347 807 loot_toast_container_timer_handle = nil
MMOSimca@347 808 end
MMOSimca@347 809
MMOSimca@387 810 container_loot_toasting = false
MMOSimca@387 811 loot_toast_container_id = nil
MMOSimca@347 812 end
MMOSimca@347 813
MMOSimca@347 814
MMOSimca@393 815 function ClearLootToastData()
MMOSimca@347 816 if loot_toast_data_timer_handle then
MMOSimca@383 817 loot_toast_data_timer_handle:Cancel()
MMOSimca@347 818 loot_toast_data_timer_handle = nil
MMOSimca@347 819 end
MMOSimca@347 820
MMOSimca@347 821 if loot_toast_data then
MMOSimca@347 822 table.wipe(loot_toast_data)
MMOSimca@347 823 end
MMOSimca@347 824 end
MMOSimca@347 825
MMOSimca@347 826
MMOSimca@393 827 function ClearChatLootData()
MMOSimca@398 828 if not chat_loot_timer_handle then
MMOSimca@435 829 table.wipe(chat_loot_data)
MMOSimca@398 830 return
MMOSimca@398 831 end
MMOSimca@398 832 Debug("ClearChatLootData: Ending chat-based loot timer.")
MMOSimca@398 833 chat_loot_timer_handle:Cancel()
MMOSimca@398 834 chat_loot_timer_handle = nil
atcaleb@573 835
atcaleb@573 836 -- Slimmed down (and more importantly, separate) versions of GenericLootUpdate, specifically for chat_loot_data
atcaleb@573 837 -- First version is for special item containers that 'push' loot without using a loot window
atcaleb@573 838 if chat_loot_data.identifier and chat_loot_data.loot and chat_loot_data.category == AF.ITEM and private.CONTAINER_ITEM_ID_LIST[chat_loot_data.identifier] ~= nil then
MMOSimca@414 839 local entry = DBEntry("items", chat_loot_data.identifier)
MMOSimca@414 840
MMOSimca@414 841 if entry then
MMOSimca@414 842 local loot_table = LootTable(entry, "contains")
MMOSimca@414 843 entry["contains_count"] = (entry["contains_count"] or 0) + 1
MMOSimca@414 844
MMOSimca@435 845 for loot_token, quantity in pairs(chat_loot_data.loot) do
mmosimca@496 846 local label, currency_id = (":"):split(loot_token)
mmosimca@496 847
mmosimca@496 848 if label == "currency" and currency_id then
mmosimca@496 849 -- Convert currency_id back into number from string
mmosimca@496 850 currency_id = tonumber(currency_id) or 0
mmosimca@496 851 if currency_id ~= 0 then
mmosimca@496 852 table.insert(loot_table, ("currency:%d:%d"):format(quantity, currency_id))
mmosimca@496 853 end
MMOSimca@414 854 elseif loot_token == "money" then
MMOSimca@414 855 table.insert(loot_table, ("money:%d"):format(quantity))
MMOSimca@414 856 else
MMOSimca@414 857 table.insert(loot_table, ("%d:%d"):format(loot_token, quantity))
MMOSimca@414 858 end
MMOSimca@414 859 end
MMOSimca@414 860 end
atcaleb@573 861 -- Second version is for Island Expeditions. This code is flawed (by design).
atcaleb@573 862 elseif chat_loot_data.loot and chat_loot_data.category == AF.NPC then
atcaleb@573 863 -- Iterate over all NPCs killed in an island expedition (at least by your team)
atcaleb@573 864 for island_npc_id, kill_count in pairs(killed_npcs_in_island) do
atcaleb@573 865 local npc = NPCEntry(island_npc_id)
atcaleb@573 866 if npc then
atcaleb@573 867 -- Create needed npc fields if required
atcaleb@573 868 local loot_label = "drops"
atcaleb@573 869 local encounter_data = npc:EncounterData(InstanceDifficultyToken())
atcaleb@573 870 encounter_data[loot_label] = encounter_data[loot_label] or {}
atcaleb@573 871 encounter_data.loot_counts = encounter_data.loot_counts or {}
atcaleb@573 872 encounter_data.loot_counts[loot_label] = (encounter_data.loot_counts[loot_label] or 0) + kill_count
atcaleb@573 873
atcaleb@573 874 for loot_token, quantity in pairs(chat_loot_data.loot) do
atcaleb@573 875 local label, currency_id = (":"):split(loot_token)
atcaleb@573 876
atcaleb@573 877 -- Only support items
atcaleb@573 878 if (label ~= "currency" or not currency_id) and (loot_token ~= "money") then
atcaleb@573 879 -- Ignore certain items (dubloon bags)
atcaleb@573 880 if not private.IGNORED_ISLAND_REWARDS[tonumber(loot_token)] then
atcaleb@573 881 table.insert(encounter_data[loot_label], ("%d:%d"):format(loot_token, quantity))
atcaleb@573 882 end
atcaleb@573 883 end
atcaleb@573 884 end
atcaleb@573 885 end
atcaleb@573 886 end
MMOSimca@347 887 end
atcaleb@573 888 table.wipe(killed_npcs_in_island)
MMOSimca@435 889 table.wipe(chat_loot_data)
MMOSimca@347 890 end
MMOSimca@347 891
MMOSimca@347 892
jcallahan@246 893 -- METHODS ------------------------------------------------------------
jcallahan@246 894
jcallahan@0 895 function WDP:OnInitialize()
jcallahan@128 896 local db = LibStub("AceDB-3.0"):New("WoWDBProfilerData", DATABASE_DEFAULTS, "Default")
jcallahan@270 897 private.db = db
jcallahan@128 898 global_db = db.global
jcallahan@128 899 char_db = db.char
jcallahan@14 900
jcallahan@270 901 local raw_db = _G.WoWDBProfilerData
jcallahan@18 902 local build_num = tonumber(private.build_num)
mmosimca@485 903
MMOSimca@533 904 -- Get current region from API (flawed)
MMOSimca@533 905 local current_region = _G.GetCurrentRegionName() or "XX"
mmosimca@484 906
mmosimca@484 907 -- Wipe all data if DB version or build number changed
jcallahan@136 908 if (raw_db.version and raw_db.version < DB_VERSION) or (raw_db.build_num and raw_db.build_num < build_num) then
jcallahan@74 909 for entry in pairs(DATABASE_DEFAULTS.global) do
jcallahan@128 910 global_db[entry] = {}
jcallahan@74 911 end
jcallahan@74 912 end
mmosimca@485 913 -- Wipe World Quest data if region changed
mmosimca@485 914 if raw_db.region and raw_db.region ~= current_region and global_db["world_quests"] then
mmosimca@485 915 global_db["world_quests"] = {}
mmosimca@485 916 end
mmosimca@485 917
jcallahan@35 918 raw_db.build_num = build_num
mmosimca@484 919 raw_db.region = current_region
jcallahan@63 920 raw_db.version = DB_VERSION
jcallahan@249 921
jcallahan@312 922 private.InitializeCommentSystem()
jcallahan@312 923 self:RegisterChatCommand("comment", private.ProcessCommentCommand)
jcallahan@0 924 end
jcallahan@0 925
jcallahan@0 926
jcallahan@153 927 function WDP:EventDispatcher(...)
jcallahan@153 928 local event_name = ...
jcallahan@153 929
MMOSimca@346 930 if DEBUGGING then
jcallahan@154 931 if event_name == "COMBAT_LOG_EVENT_UNFILTERED" then
jcallahan@154 932 Debug(event_name)
jcallahan@154 933 else
jcallahan@154 934 Debug(...)
jcallahan@153 935 end
jcallahan@153 936 end
jcallahan@153 937 local func = EVENT_MAPPING[event_name]
jcallahan@153 938
mmosimca@522 939 if type(func) == "boolean" then
jcallahan@153 940 self[event_name](self, ...)
mmosimca@522 941 elseif type(func) == "function" then
jcallahan@159 942 self[func](self, ...)
jcallahan@153 943 end
jcallahan@153 944 end
jcallahan@153 945
jcallahan@153 946
jcallahan@0 947 function WDP:OnEnable()
jcallahan@300 948 PLAYER_GUID = _G.UnitGUID("player")
jcallahan@300 949
jcallahan@0 950 for event_name, mapping in pairs(EVENT_MAPPING) do
jcallahan@156 951 if EVENT_DEBUG then
jcallahan@153 952 self:RegisterEvent(event_name, "EventDispatcher")
jcallahan@153 953 else
mmosimca@522 954 self:RegisterEvent(event_name, (type(mapping) ~= "boolean") and mapping or nil)
jcallahan@153 955 end
jcallahan@0 956 end
jcallahan@95 957
mmosimca@496 958 -- Gather known languages
jcallahan@95 959 for index = 1, _G.GetNumLanguages() do
jcallahan@95 960 languages_known[_G.GetLanguageByIndex(index)] = true
jcallahan@95 961 end
mmosimca@518 962
mmosimca@485 963 -- These timers loop indefinitely using Lua's infinity constant
atcaleb@562 964 item_process_timer_handle = C_Timer.NewTicker(DELAY_PROCESS_ITEMS, WDP.ProcessItems, nil)
atcaleb@562 965 target_location_timer_handle = C_Timer.NewTicker(DELAY_UPDATE_TARGET_LOCATION, WDP.UpdateTargetLocation, nil)
atcaleb@562 966 world_quest_timer_handle = C_Timer.NewTicker(DELAY_PROCESS_WORLD_QUESTS, WDP.ProcessWorldQuests, nil)
jcallahan@19 967
jcallahan@19 968 _G.hooksecurefunc("UseContainerItem", function(bag_index, slot_index, target_unit)
jcallahan@19 969 if target_unit then
jcallahan@19 970 return
jcallahan@19 971 end
jcallahan@19 972 HandleItemUse(_G.GetContainerItemLink(bag_index, slot_index), bag_index, slot_index)
jcallahan@19 973 end)
jcallahan@19 974
jcallahan@19 975 _G.hooksecurefunc("UseItemByName", function(identifier, target_unit)
jcallahan@19 976 if target_unit then
jcallahan@19 977 return
jcallahan@19 978 end
jcallahan@19 979 local _, item_link = _G.GetItemInfo(identifier)
jcallahan@19 980 HandleItemUse(item_link)
jcallahan@19 981 end)
jcallahan@263 982
jcallahan@290 983 self:GROUP_ROSTER_UPDATE()
jcallahan@0 984 end
jcallahan@0 985
jcallahan@0 986
mmosimca@485 987 -- Record data for a specific quest ID; reward data must be available or nothing will be recorded
mmosimca@485 988 -- When we reach this point, we've already checked for a valid mapID, questID, quest data, and worldQuestType
atcaleb@562 989 local function RecordWorldQuestData(quest_id, api_data_table)
atcaleb@575 990 local xp, _ = _G.GetQuestLogRewardXP(quest_id)
atcaleb@575 991
mmosimca@485 992 -- Ensure we have location data and rewards (barely readable so putting it on multiple lines)
mmosimca@487 993 -- (Honor is built in to the quest; it is not a sign rewards have been loaded)
atcaleb@562 994 if not api_data_table or not api_data_table.x or not api_data_table.y or not api_data_table.mapID or not
atcaleb@575 995 (xp > 0 or _G.GetNumQuestLogRewardCurrencies(quest_id) > 0
atcaleb@562 996 or _G.GetNumQuestLogRewards(quest_id) > 0 or _G.GetQuestLogRewardMoney(quest_id) > 0) then
atcaleb@562 997 --or _G.GetQuestLogRewardArtifactXP(quest_id) > 0)
mmosimca@485 998 return
mmosimca@485 999 end
mmosimca@485 1000
mmosimca@485 1001 local entry = DBEntry("world_quests", quest_id)
mmosimca@485 1002 if entry then
mmosimca@485 1003
mmosimca@485 1004 -- Record location
mmosimca@485 1005 entry["location"] = {}
atcaleb@562 1006 entry["location"]["world_map_id"] = api_data_table.mapID
atcaleb@562 1007 entry["location"]["x"] = api_data_table.x * 100
atcaleb@562 1008 entry["location"]["y"] = api_data_table.y * 100
mmosimca@485 1009
mmosimca@485 1010 -- Record simple rewards (XP, money, artifact XP, honor)
mmosimca@485 1011 entry["rewards"] = {}
atcaleb@575 1012 entry["rewards"]["xp"] = tonumber(xp) or 0
mmosimca@485 1013 entry["rewards"]["money"] = tonumber(_G.GetQuestLogRewardMoney(quest_id)) or 0
atcaleb@562 1014 --local actualXP, scaling = _G.GetQuestLogRewardArtifactXP(quest_id)
atcaleb@562 1015 --entry["rewards"]["artifact_xp"] = ("%d:%d"):format(tonumber(actualXP) or 0, tonumber(scaling) or 0)
mmosimca@485 1016 entry["rewards"]["honor"] = tonumber(_G.GetQuestLogRewardHonor(quest_id)) or 0
mmosimca@485 1017
mmosimca@485 1018 -- Record currencies
mmosimca@485 1019 entry["rewards"]["currency_count"] = tonumber(_G.GetNumQuestLogRewardCurrencies(quest_id)) or 0
mmosimca@485 1020
mmosimca@496 1021 -- Create currency rewards sub-table and fill
mmosimca@485 1022 if entry["rewards"]["currency_count"] > 0 then
mmosimca@485 1023 entry["rewards"]["currencies"] = {}
mmosimca@485 1024 for i = 1, entry["rewards"]["currency_count"] do
mmosimca@503 1025 local name, texture_path, quantity, currency_id = _G.GetQuestLogRewardCurrencyInfo(i, quest_id)
mmosimca@503 1026 table.insert(entry["rewards"]["currencies"], ("%d:%d"):format(quantity, currency_id))
mmosimca@485 1027 end
mmosimca@485 1028 end
mmosimca@485 1029
mmosimca@485 1030 -- Record items
mmosimca@485 1031 entry["rewards"]["item_count"] = tonumber(_G.GetNumQuestLogRewards(quest_id)) or 0
mmosimca@485 1032
mmosimca@496 1033 -- Create item rewards sub-table and fill
mmosimca@485 1034 if entry["rewards"]["item_count"] > 0 then
mmosimca@485 1035 entry["rewards"]["items"] = {}
mmosimca@485 1036 for i = 1, entry["rewards"]["item_count"] do
mmosimca@485 1037 local item_name, item_texture, quantity, quality, is_usable, item_id = _G.GetQuestLogRewardInfo(i, quest_id)
mmosimca@485 1038 table.insert(entry["rewards"]["items"], ("%d:%d"):format(item_id, quantity))
mmosimca@485 1039 end
mmosimca@485 1040 end
mmosimca@485 1041
mmosimca@485 1042 -- Record time remaining
mmosimca@485 1043 entry["estimated_end_time"] = _G.GetServerTime() + ((_G.C_TaskQuest.GetQuestTimeLeftMinutes(quest_id) or 0) * 60)
mmosimca@485 1044 end
mmosimca@485 1045 end
mmosimca@485 1046
mmosimca@485 1047
mmosimca@485 1048 function WDP:ProcessWorldQuests()
MMOSimca@532 1049 -- Ignore if player is low level (there are some world quests before max level now, but we can collect enough data from 110s alone still)
atcaleb@562 1050 if _G.UnitLevel("player") < 110 then return end
atcaleb@559 1051
atcaleb@566 1052 -- Check all first-order continents
atcaleb@566 1053 local continents = C_Map.GetMapChildrenInfo(UI_MAP_COSMIC, Enum.UIMapType.Continent, true);
atcaleb@566 1054 for i, continentInfo in ipairs(continents) do
atcaleb@566 1055
atcaleb@566 1056 -- Get continent data for World Quests
atcaleb@566 1057 local continent_api_data = C_TaskQuest.GetQuestsForPlayerByMapID(continentInfo.mapID)
atcaleb@566 1058
atcaleb@566 1059 -- If the continent has WQ data, continue
atcaleb@566 1060 if continent_api_data and type(continent_api_data) == "table" and #continent_api_data > 0 then
atcaleb@566 1061 -- Iterate over zones on continents
atcaleb@566 1062 local zones = C_Map.GetMapChildrenInfo(continentInfo.mapID, Enum.UIMapType.Zone)
atcaleb@566 1063 for j, zoneInfo in ipairs(zones) do
atcaleb@566 1064
atcaleb@566 1065 -- Get zone data for World Quests
atcaleb@566 1066 local zone_api_data = C_TaskQuest.GetQuestsForPlayerByMapID(zoneInfo.mapID);
atcaleb@566 1067
atcaleb@566 1068 -- Iterate over the questIDs for each zone, doing preload reward requests and creating SavedVariables entries
atcaleb@566 1069 if zone_api_data and type(zone_api_data) == "table" and #zone_api_data > 0 then
atcaleb@566 1070 for k = 1, #zone_api_data do
atcaleb@566 1071
atcaleb@566 1072 -- Check if we had a valid API table returned to us
atcaleb@566 1073 if zone_api_data[k] and type(zone_api_data[k]) == "table" and zone_api_data[k].questId then
atcaleb@566 1074 local quest_id = tonumber(zone_api_data[k].questId) or 0
atcaleb@566 1075
atcaleb@566 1076 -- Check if we have quest data and the quest is within this zone directly
atcaleb@566 1077 if quest_id > 0 and _G.HaveQuestData(quest_id) and QuestUtils_IsQuestWorldQuest(quest_id) and zone_api_data[k].mapID == zoneInfo.mapID then
atcaleb@566 1078 _G.C_TaskQuest.RequestPreloadRewardData(quest_id)
atcaleb@566 1079 RecordWorldQuestData(quest_id, zone_api_data[k])
atcaleb@566 1080 end
atcaleb@566 1081 end
mmosimca@485 1082 end
mmosimca@485 1083 end
mmosimca@485 1084 end
mmosimca@485 1085 end
mmosimca@485 1086 end
mmosimca@485 1087 end
mmosimca@485 1088
mmosimca@485 1089
MMOSimca@340 1090 local function RecordItemData(item_id, item_link, process_bonus_ids, durability)
jcallahan@331 1091 local _, _, item_string = item_link:find("^|%x+|H(.+)|h%[.+%]")
jcallahan@219 1092 local item
jcallahan@0 1093
jcallahan@191 1094 if item_string then
MMOSimca@338 1095 local item_results = { (":"):split(item_string) }
MMOSimca@338 1096
MMOSimca@460 1097 local suffix_id = tonumber(item_results[8]) or 0
MMOSimca@462 1098 local unique_id = tonumber(item_results[9]) or 0
MMOSimca@447 1099 --local level = tonumber(item_results[10])
MMOSimca@460 1100 --local specialization_id = tonumber(item_results[11])
catherton@469 1101 --local upgrade_type_id = tonumber(item_results[12])
MMOSimca@460 1102 local instance_difficulty_id = tonumber(item_results[13]) or 0
MMOSimca@460 1103 local num_bonus_ids = tonumber(item_results[14]) or 0
catherton@471 1104 -- upgrade_value is optional in 6.2! can be detected using upgrade_type_id, but it's just as easy to check like this
catherton@469 1105 local upgrade_value = tonumber(item_results[15 + num_bonus_ids]) or 0
catherton@465 1106
mmosimca@484 1107 local unk_item_field_1 = tonumber(item_results[16 + num_bonus_ids]) or 0
mmosimca@484 1108 local unk_item_field_2 = tonumber(item_results[17 + num_bonus_ids]) or 0
mmosimca@484 1109 --if unk_item_field_1 > 0 then Debug("unk_item_field_1 for %s is non-zero, specifically %d.", item_link, unk_item_field_1) end
mmosimca@484 1110 --if unk_item_field_2 > 0 then Debug("unk_item_field_2 for %s is non-zero, specifically %d.", item_link, unk_item_field_2) end
MMOSimca@460 1111
MMOSimca@460 1112 -- If there is anything special (non-zero) for this item then we need to make note of everything
catherton@471 1113 if math.max(suffix_id, instance_difficulty_id, num_bonus_ids, upgrade_value) ~= 0 then
MMOSimca@460 1114 item = DBEntry("items", item_id)
MMOSimca@460 1115 item.suffix_id = suffix_id
MMOSimca@460 1116 item.unique_id = bit.band(unique_id, 0xFFFF)
MMOSimca@460 1117 item.instance_difficulty_id = instance_difficulty_id
catherton@471 1118 item.upgrade_value = upgrade_value
MMOSimca@460 1119
MMOSimca@460 1120 if process_bonus_ids then
MMOSimca@460 1121
MMOSimca@460 1122 -- Get ready for bonus IDs
MMOSimca@384 1123 if not item.seen_bonuses then
MMOSimca@384 1124 item.seen_bonuses = {}
MMOSimca@372 1125 end
catherton@465 1126
MMOSimca@460 1127 if num_bonus_ids > 0 then
MMOSimca@460 1128 -- We want the bonus ID combo output to be in the form ["bonusID1:bonusID2:bonusID3"] = true
MMOSimca@460 1129 -- And sorted numerically with the smallest bonusID first
MMOSimca@460 1130 local sorted_bonus_string = ""
MMOSimca@460 1131 local min_bonus_id_array = {}
MMOSimca@460 1132 for iterations = 1, num_bonus_ids do
MMOSimca@460 1133 -- Find minimum of this iteration
MMOSimca@460 1134 local min_bonus_id = 100000
MMOSimca@460 1135 for bonus_index = 1, num_bonus_ids do
MMOSimca@460 1136 local temp_bonus_id = tonumber(item_results[14 + bonus_index])
MMOSimca@460 1137 if temp_bonus_id and (not min_bonus_id_array[temp_bonus_id]) and (temp_bonus_id < min_bonus_id) then
MMOSimca@460 1138 min_bonus_id = temp_bonus_id
MMOSimca@460 1139 end
MMOSimca@460 1140 end
MMOSimca@460 1141
MMOSimca@460 1142 -- Keep track of already processed IDs
MMOSimca@460 1143 min_bonus_id_array[min_bonus_id] = true
MMOSimca@460 1144
MMOSimca@460 1145 -- Build string
MMOSimca@460 1146 if iterations == 1 then
MMOSimca@460 1147 sorted_bonus_string = sorted_bonus_string .. tostring(min_bonus_id)
MMOSimca@460 1148 else
MMOSimca@460 1149 sorted_bonus_string = sorted_bonus_string .. ":" .. tostring(min_bonus_id)
MMOSimca@460 1150 end
MMOSimca@384 1151 end
MMOSimca@460 1152
MMOSimca@460 1153 item.seen_bonuses[sorted_bonus_string] = true
MMOSimca@460 1154 Debug("RecordItemData: Recorded bonus IDs %s for item %d.", sorted_bonus_string, item_id)
MMOSimca@384 1155 else
MMOSimca@460 1156 item.seen_bonuses["0"] = true
MMOSimca@384 1157 end
MMOSimca@329 1158 end
jcallahan@191 1159 end
jcallahan@0 1160 end
jcallahan@212 1161
jcallahan@212 1162 if durability and durability > 0 then
jcallahan@219 1163 item = item or DBEntry("items", item_id)
jcallahan@212 1164 item.durability = durability
jcallahan@212 1165 end
jcallahan@0 1166 end
jcallahan@0 1167
jcallahan@0 1168
jcallahan@187 1169 function WDP:ProcessItems()
jcallahan@187 1170 for slot_index = _G.INVSLOT_FIRST_EQUIPPED, _G.INVSLOT_LAST_EQUIPPED do
jcallahan@1 1171 local item_id = _G.GetInventoryItemID("player", slot_index)
jcallahan@0 1172
jcallahan@0 1173 if item_id and item_id > 0 then
jcallahan@1 1174 local _, max_durability = _G.GetInventoryItemDurability(slot_index)
MMOSimca@340 1175 RecordItemData(item_id, _G.GetInventoryItemLink("player", slot_index), false, max_durability)
jcallahan@0 1176 end
jcallahan@0 1177 end
jcallahan@0 1178
jcallahan@0 1179 for bag_index = 0, _G.NUM_BAG_SLOTS do
jcallahan@0 1180 for slot_index = 1, _G.GetContainerNumSlots(bag_index) do
jcallahan@1 1181 local item_id = _G.GetContainerItemID(bag_index, slot_index)
jcallahan@0 1182
jcallahan@0 1183 if item_id and item_id > 0 then
jcallahan@1 1184 local _, max_durability = _G.GetContainerItemDurability(bag_index, slot_index)
MMOSimca@340 1185 RecordItemData(item_id, _G.GetContainerItemLink(bag_index, slot_index), false, max_durability)
jcallahan@0 1186 end
jcallahan@0 1187 end
jcallahan@0 1188 end
jcallahan@0 1189 end
jcallahan@0 1190
jcallahan@118 1191
atcaleb@558 1192 local function TargetedNPC()
atcaleb@558 1193 if not _G.UnitExists("target") or _G.UnitPlayerControlled("target") or currently_drunk then
atcaleb@558 1194 current_target_id = nil
atcaleb@558 1195 return
atcaleb@558 1196 end
atcaleb@558 1197 local unit_type, unit_idnum = ParseGUID(_G.UnitGUID("target"))
atcaleb@558 1198
atcaleb@558 1199 if not unit_idnum or not UnitTypeIsNPC(unit_type) then
atcaleb@558 1200 return
atcaleb@558 1201 end
atcaleb@558 1202 current_target_id = unit_idnum
atcaleb@558 1203
atcaleb@558 1204 local npc = NPCEntry(unit_idnum)
atcaleb@558 1205 local _, class_token = _G.UnitClass("target")
atcaleb@558 1206 npc.class = class_token
atcaleb@558 1207 npc.faction = UnitFactionStanding("target")
atcaleb@558 1208 npc.genders = npc.genders or {}
atcaleb@558 1209 npc.genders[private.GENDER_NAMES[_G.UnitSex("target")] or "UNDEFINED"] = true
atcaleb@558 1210 npc.is_pvp = _G.UnitIsPVP("target") and true or nil
atcaleb@558 1211 npc.reaction = ("%s:%s:%s"):format(_G.UnitLevel("player"), _G.UnitFactionGroup("player"), private.REACTION_NAMES[_G.UnitReaction("player", "target")])
atcaleb@558 1212
atcaleb@558 1213 local encounter_data = npc:EncounterData(InstanceDifficultyToken()).stats
atcaleb@558 1214 local npc_level = ("level_%d"):format(_G.UnitLevel("target"))
atcaleb@558 1215 local level_data = encounter_data[npc_level]
atcaleb@558 1216
atcaleb@558 1217 if not level_data then
atcaleb@558 1218 level_data = {}
atcaleb@558 1219 encounter_data[npc_level] = level_data
atcaleb@558 1220 end
atcaleb@558 1221 level_data.max_health = level_data.max_health or _G.UnitHealthMax("target")
atcaleb@558 1222
atcaleb@558 1223 -- May not capture as much data as it could, since the API changed in Legion to report multiple types of power
atcaleb@558 1224 if not level_data.power then
atcaleb@558 1225 local max_power = _G.UnitPowerMax("target")
atcaleb@558 1226
atcaleb@558 1227 if max_power > 0 then
atcaleb@558 1228 local power_type = _G.UnitPowerType("target")
atcaleb@558 1229 level_data.power = ("%s:%d"):format(private.POWER_TYPE_NAMES[tostring(power_type)] or power_type, max_power)
jcallahan@118 1230 end
jcallahan@118 1231 end
atcaleb@558 1232 name_to_id_map[_G.UnitName("target")] = unit_idnum
atcaleb@558 1233 return npc, unit_idnum
atcaleb@558 1234 end
jcallahan@118 1235
jcallahan@118 1236
jcallahan@113 1237 do
jcallahan@113 1238 local COORD_MAX = 5
jcallahan@0 1239
jcallahan@113 1240 function WDP:UpdateTargetLocation()
catherton@480 1241 if currently_drunk or not _G.UnitExists("target") or _G.UnitPlayerControlled("target") or (_G.UnitIsTapDenied("target") and not _G.UnitIsDead("target")) then
jcallahan@2 1242 return
jcallahan@2 1243 end
jcallahan@113 1244
jcallahan@113 1245 for index = 1, 4 do
jcallahan@113 1246 if not _G.CheckInteractDistance("target", index) then
jcallahan@113 1247 return
jcallahan@113 1248 end
jcallahan@113 1249 end
jcallahan@215 1250 local npc = TargetedNPC()
jcallahan@113 1251
jcallahan@113 1252 if not npc then
jcallahan@113 1253 return
jcallahan@113 1254 end
jcallahan@113 1255 local zone_name, area_id, x, y, map_level, difficulty_token = CurrentLocationData()
MMOSimca@328 1256 if not (zone_name and area_id and x and y and map_level) then
mmosimca@508 1257 if not (_G.IsInInstance()) then
mmosimca@508 1258 Debug("UpdateTargetLocation: Missing current location data - %s, %s, %s, %s, %s.", tostring(zone_name), tostring(area_id), tostring(x), tostring(y), tostring(map_level))
mmosimca@508 1259 end
MMOSimca@328 1260 return
MMOSimca@328 1261 end
jcallahan@248 1262 local npc_data = npc:EncounterData(difficulty_token).stats[("level_%d"):format(_G.UnitLevel("target"))]
jcallahan@113 1263 local zone_token = ("%s:%d"):format(zone_name, area_id)
jcallahan@118 1264 npc_data.locations = npc_data.locations or {} -- TODO: Fix this. It is broken. Possibly something to do with the timed updates.
jcallahan@113 1265
jcallahan@113 1266 local zone_data = npc_data.locations[zone_token]
jcallahan@113 1267
jcallahan@113 1268 if not zone_data then
jcallahan@113 1269 zone_data = {}
jcallahan@113 1270 npc_data.locations[zone_token] = zone_data
jcallahan@113 1271 end
jcallahan@113 1272
jcallahan@113 1273 for location_token in pairs(zone_data) do
jcallahan@113 1274 local loc_level, loc_x, loc_y = (":"):split(location_token)
jcallahan@113 1275 loc_level = tonumber(loc_level)
jcallahan@113 1276
jcallahan@113 1277 if map_level == loc_level and math.abs(x - loc_x) <= COORD_MAX and math.abs(y - loc_y) <= COORD_MAX then
jcallahan@113 1278 return
jcallahan@113 1279 end
jcallahan@113 1280 end
jcallahan@141 1281 zone_data[("%d:%d:%d"):format(map_level, x, y)] = true
jcallahan@2 1282 end
jcallahan@113 1283 end -- do-block
jcallahan@2 1284
jcallahan@118 1285
MMOSimca@412 1286 function WDP:HandleBadChatLootData(...)
MMOSimca@398 1287 ClearChatLootData()
MMOSimca@398 1288 end
MMOSimca@398 1289
MMOSimca@398 1290
MMOSimca@420 1291 -- EVENT HANDLERS -----------------------------------------------------
MMOSimca@420 1292
MMOSimca@436 1293 -- This function (and the following function) are to stop 'HandleItemUse' from triggering when you put an item that would normally be opened into the bank, guild bank, etc.
MMOSimca@436 1294 function WDP:StopChatLootRecording(event_name)
MMOSimca@436 1295 if not block_chat_loot_data then
MMOSimca@439 1296 Debug("%s: Pausing chat-based loot recording.", event_name)
MMOSimca@436 1297 ClearChatLootData()
MMOSimca@436 1298 block_chat_loot_data = true
MMOSimca@436 1299 end
MMOSimca@436 1300 end
MMOSimca@436 1301
MMOSimca@436 1302
MMOSimca@436 1303 function WDP:ResumeChatLootRecording(event_name)
MMOSimca@436 1304 if block_chat_loot_data then
MMOSimca@439 1305 Debug("%s: Resuming chat-based loot recording.", event_name)
MMOSimca@436 1306 block_chat_loot_data = false
MMOSimca@436 1307 end
MMOSimca@436 1308 end
MMOSimca@436 1309
MMOSimca@436 1310
MMOSimca@408 1311 -- For now, bonus roll data only pollutes the true drop percentages. We still want to capture the data from SPELL_CONFIRMATION_PROMPT because of legendary quest items though.
MMOSimca@408 1312 function WDP:BONUS_ROLL_RESULT(event_name)
MMOSimca@408 1313 Debug("%s: Bonus roll detected; stopping loot recording for this boss to avoid recording bonus loot.", event_name)
MMOSimca@408 1314 ClearKilledBossID()
MMOSimca@408 1315 ClearLootToastContainerID()
MMOSimca@408 1316 end
MMOSimca@408 1317
MMOSimca@408 1318
atcaleb@573 1319 -- Store all known killed NPCs during an island in a table
atcaleb@573 1320 function WDP:ISLAND_AZERITE_GAIN(event_name, amount, gained_by_player, faction_index, gained_by, gained_from)
atcaleb@573 1321 -- Exit now if GUID is not for an NPC
atcaleb@573 1322 local unit_type, unit_id = ParseGUID(gained_from)
atcaleb@573 1323 if not UnitTypeIsNPC(unit_type) then
atcaleb@573 1324 return
atcaleb@573 1325 end
atcaleb@573 1326
atcaleb@573 1327 -- Otherwise, store the NPC ID in the island kill table (if it already exists there, increment its count by 1)
atcaleb@573 1328 Debug("%s: Recording killed island NPC with ID #%d.", event_name, unit_id)
atcaleb@573 1329 if killed_npcs_in_island[unit_id] then
atcaleb@573 1330 killed_npcs_in_island[unit_id] = killed_npcs_in_island[unit_id] + 1
atcaleb@573 1331 else
atcaleb@573 1332 killed_npcs_in_island[unit_id] = 1
atcaleb@573 1333 end
atcaleb@573 1334 end
atcaleb@573 1335
atcaleb@573 1336
atcaleb@573 1337 -- Start chat loot timer for NPCs (and store general island information)
atcaleb@573 1338 function WDP:ISLAND_COMPLETED(event_name)
atcaleb@573 1339 island_difficulty_token = InstanceDifficultyToken()
atcaleb@573 1340
atcaleb@573 1341 Debug("%s: Beginning chat-based loot timer for Island Expedition rewards.", event_name, item_id)
atcaleb@573 1342 chat_loot_timer_handle = C_Timer.NewTimer(1.5, ClearChatLootData)
atcaleb@573 1343 chat_loot_data.category = AF.NPC
atcaleb@573 1344 chat_loot_data.identifier = 0
atcaleb@573 1345 end
atcaleb@573 1346
atcaleb@573 1347
jcallahan@90 1348 function WDP:BLACK_MARKET_ITEM_UPDATE(event_name)
jcallahan@243 1349 if not ALLOWED_LOCALES[CLIENT_LOCALE] then
jcallahan@243 1350 return
jcallahan@243 1351 end
jcallahan@282 1352 local num_items = _G.C_BlackMarket.GetNumItems() or 0
jcallahan@56 1353
jcallahan@56 1354 for index = 1, num_items do
jcallahan@56 1355 local name, texture, quantity, item_type, is_usable, level, level_type, seller_name, min_bid, min_increment, current_bid, has_high_bid, num_bids, time_left, item_link, market_id = _G.C_BlackMarket.GetItemInfoByIndex(index);
jcallahan@56 1356
jcallahan@56 1357 if item_link then
jcallahan@56 1358 DBEntry("items", ItemLinkToID(item_link)).black_market = seller_name or "UNKNOWN"
jcallahan@56 1359 end
jcallahan@56 1360 end
jcallahan@56 1361 end
jcallahan@56 1362
jcallahan@56 1363
jcallahan@298 1364 local function UpdateUnitPet(unit_guid, unit_id)
jcallahan@246 1365 local current_pet_guid = group_owner_guids_to_pet_guids[unit_guid]
jcallahan@246 1366
jcallahan@246 1367 if current_pet_guid then
jcallahan@246 1368 group_owner_guids_to_pet_guids[unit_guid] = nil
jcallahan@246 1369 group_pet_guids[current_pet_guid] = nil
jcallahan@246 1370 end
jcallahan@246 1371 local pet_guid = _G.UnitGUID(unit_id .. "pet")
jcallahan@246 1372
jcallahan@246 1373 if pet_guid then
jcallahan@296 1374 group_owner_guids_to_pet_guids[unit_guid] = pet_guid
jcallahan@246 1375 group_pet_guids[pet_guid] = true
jcallahan@246 1376 end
jcallahan@246 1377 end
jcallahan@246 1378
jcallahan@246 1379
jcallahan@298 1380 function WDP:GROUP_ROSTER_UPDATE(event_name)
jcallahan@298 1381 local is_raid = _G.IsInRaid()
jcallahan@298 1382 local unit_type = is_raid and "raid" or "party"
jcallahan@298 1383 local group_size = is_raid and _G.GetNumGroupMembers() or _G.GetNumSubgroupMembers()
jcallahan@298 1384
jcallahan@299 1385 table.wipe(group_member_guids)
jcallahan@298 1386
jcallahan@298 1387 for index = 1, group_size do
jcallahan@298 1388 local unit_id = unit_type .. index
jcallahan@298 1389 local unit_guid = _G.UnitGUID(unit_id)
jcallahan@298 1390
jcallahan@299 1391 group_member_guids[unit_guid] = true
jcallahan@298 1392 UpdateUnitPet(unit_guid, unit_id)
jcallahan@298 1393 end
jcallahan@299 1394 group_member_guids[PLAYER_GUID] = true
jcallahan@298 1395 end
jcallahan@298 1396
jcallahan@298 1397
jcallahan@298 1398 function WDP:UNIT_PET(event_name, unit_id)
jcallahan@298 1399 UpdateUnitPet(_G.UnitGUID(unit_id), unit_id)
jcallahan@298 1400 end
jcallahan@298 1401
jcallahan@298 1402
MMOSimca@375 1403 function WDP:SHOW_LOOT_TOAST(event_name, loot_type, item_link, quantity, spec_ID, sex_ID, is_personal, loot_source)
jcallahan@312 1404 if not loot_type or (loot_type ~= "item" and loot_type ~= "money" and loot_type ~= "currency") then
jcallahan@306 1405 Debug("%s: loot_type is %s. Item link is %s, and quantity is %d.", event_name, loot_type, item_link, quantity)
jcallahan@306 1406 return
jcallahan@306 1407 end
MMOSimca@372 1408
MMOSimca@372 1409 -- Need information on the most recent args, so using this complete debug statement for now
MMOSimca@375 1410 Debug("%s: loot_type: %s, item_link: %s, quantity: %s, spec_ID: %s, sex_ID: %s, is_personal: %s, loot_source: %s", event_name, loot_type, item_link, quantity, spec_ID, sex_ID, is_personal, loot_source)
MMOSimca@372 1411
MMOSimca@355 1412 -- Handle Garrison cache specially
MMOSimca@422 1413 if loot_source and (loot_source == LOOT_SOURCE_ID_GARRISON_CACHE) and last_garrison_cache_object_id then
MMOSimca@355 1414 -- Record location data for cache
MMOSimca@355 1415 UpdateDBEntryLocation("objects", ("OPENING:%d"):format(last_garrison_cache_object_id))
MMOSimca@355 1416
MMOSimca@355 1417 -- Add drop data
mmosimca@496 1418 local currency_id = CurrencyLinkToID(item_link)
mmosimca@496 1419 if currency_id and currency_id ~= 0 then
MMOSimca@355 1420 -- Check for top level object data
MMOSimca@355 1421 local object_entry = DBEntry("objects", ("OPENING:%d"):format(last_garrison_cache_object_id))
MMOSimca@355 1422 local difficulty_token = InstanceDifficultyToken()
MMOSimca@355 1423 if object_entry[difficulty_token] then
MMOSimca@355 1424 -- Increment loot count
MMOSimca@355 1425 object_entry[difficulty_token]["opening_count"] = (object_entry[difficulty_token]["opening_count"] or 0) + 1
MMOSimca@355 1426
mmosimca@496 1427 Debug("%s: %d X %d", event_name, currency_id, quantity)
MMOSimca@355 1428 object_entry[difficulty_token]["opening"] = object_entry[difficulty_token]["opening"] or {}
mmosimca@496 1429 table.insert(object_entry[difficulty_token]["opening"], ("currency:%d:%d"):format(quantity, currency_id))
MMOSimca@355 1430 else
MMOSimca@355 1431 Debug("%s: When handling the Garrison cache, the top level loot data was missing for objectID %d.", event_name, last_garrison_cache_object_id)
MMOSimca@355 1432 end
MMOSimca@355 1433 else
mmosimca@496 1434 Debug("%s: Currency ID is nil or 0, from currency link %s", event_name, item_link)
MMOSimca@355 1435 end
catherton@465 1436
MMOSimca@431 1437 -- Wipe object ID until future mouseover
MMOSimca@431 1438 last_garrison_cache_object_id = nil
MMOSimca@387 1439 elseif raid_boss_id then
MMOSimca@427 1440 local npc = NPCEntry(raid_boss_id)
MMOSimca@427 1441 if npc then
MMOSimca@427 1442 local loot_label = "drops"
MMOSimca@427 1443 local encounter_data = npc:EncounterData(InstanceDifficultyToken())
MMOSimca@427 1444 encounter_data[loot_label] = encounter_data[loot_label] or {}
MMOSimca@427 1445 encounter_data.loot_counts = encounter_data.loot_counts or {}
MMOSimca@427 1446
MMOSimca@427 1447 if loot_type == "item" then
MMOSimca@427 1448 local item_id = ItemLinkToID(item_link)
MMOSimca@427 1449 if item_id then
MMOSimca@427 1450 Debug("%s: %s X %d (%d)", event_name, item_link, quantity, item_id)
MMOSimca@427 1451 RecordItemData(item_id, item_link, true)
MMOSimca@427 1452 table.insert(encounter_data[loot_label], ("%d:%d"):format(item_id, quantity))
MMOSimca@427 1453 else
MMOSimca@427 1454 Debug("%s: ItemID is nil, from item link %s", event_name, item_link)
MMOSimca@427 1455 return
MMOSimca@427 1456 end
MMOSimca@427 1457 elseif loot_type == "money" then
MMOSimca@427 1458 Debug("%s: money X %d", event_name, quantity)
MMOSimca@427 1459 table.insert(encounter_data[loot_label], ("money:%d"):format(quantity))
MMOSimca@427 1460 elseif loot_type == "currency" then
mmosimca@496 1461 local currency_id = CurrencyLinkToID(item_link)
mmosimca@496 1462 if currency_id and currency_id ~= 0 then
mmosimca@496 1463 Debug("%s: %d X %d", event_name, currency_id, quantity)
mmosimca@496 1464 table.insert(encounter_data[loot_label], ("currency:%d:%d"):format(quantity, currency_id))
MMOSimca@427 1465 else
mmosimca@496 1466 Debug("%s: Currency ID is nil or 0, from currency link %s", event_name, item_link)
MMOSimca@427 1467 return
MMOSimca@427 1468 end
jcallahan@312 1469 end
jcallahan@317 1470
MMOSimca@427 1471 if not boss_loot_toasting[raid_boss_id] then
MMOSimca@427 1472 encounter_data.loot_counts[loot_label] = (encounter_data.loot_counts[loot_label] or 0) + 1
MMOSimca@427 1473 boss_loot_toasting[raid_boss_id] = true -- Do not count further loots until timer expires or another boss is killed
jcallahan@312 1474 end
jcallahan@312 1475 end
MMOSimca@387 1476 elseif loot_toast_container_id then
jcallahan@305 1477 InitializeCurrentLoot()
jcallahan@305 1478
jcallahan@306 1479 -- Fake the loot characteristics to match that of an actual container item
MMOSimca@387 1480 current_loot.identifier = loot_toast_container_id
jcallahan@306 1481 current_loot.label = "contains"
jcallahan@306 1482 current_loot.target_type = AF.ITEM
jcallahan@306 1483
MMOSimca@387 1484 current_loot.sources[loot_toast_container_id] = current_loot.sources[loot_toast_container_id] or {}
jcallahan@312 1485
jcallahan@301 1486 if loot_type == "item" then
jcallahan@312 1487 local item_id = ItemLinkToID(item_link)
jcallahan@312 1488 if item_id then
jcallahan@312 1489 Debug("%s: %s X %d (%d)", event_name, item_link, quantity, item_id)
MMOSimca@340 1490 RecordItemData(item_id, item_link, true)
MMOSimca@387 1491 current_loot.sources[loot_toast_container_id][item_id] = (current_loot.sources[loot_toast_container_id][item_id] or 0) + quantity
jcallahan@312 1492 else
jcallahan@301 1493 Debug("%s: ItemID is nil, from item link %s", event_name, item_link)
jcallahan@312 1494 current_loot = nil
jcallahan@301 1495 return
jcallahan@301 1496 end
jcallahan@301 1497 elseif loot_type == "money" then
jcallahan@312 1498 Debug("%s: money X %d", event_name, quantity)
MMOSimca@387 1499 current_loot.sources[loot_toast_container_id]["money"] = (current_loot.sources[loot_toast_container_id]["money"] or 0) + quantity
jcallahan@312 1500 elseif loot_type == "currency" then
mmosimca@496 1501 local currency_id = CurrencyLinkToID(item_link)
mmosimca@496 1502 if currency_id and currency_id ~= 0 then
mmosimca@496 1503 Debug("%s: %d X %d", event_name, currency_id, quantity)
mmosimca@496 1504 local currency_token = ("currency:%d"):format(currency_id)
MMOSimca@387 1505 current_loot.sources[loot_toast_container_id][currency_token] = (current_loot.sources[loot_toast_container_id][currency_token] or 0) + quantity
jcallahan@312 1506 else
mmosimca@496 1507 Debug("%s: Currency ID is nil or 0, from currency link %s", event_name, item_link)
jcallahan@312 1508 current_loot = nil
jcallahan@312 1509 return
jcallahan@312 1510 end
jcallahan@301 1511 end
jcallahan@312 1512
jcallahan@301 1513 GenericLootUpdate("items")
jcallahan@301 1514 current_loot = nil
MMOSimca@387 1515 container_loot_toasting = true -- Do not count further loots until timer expires or another container is opened
atcaleb@573 1516 elseif loot_source and chat_loot_timer_handle and chat_loot_data.category == AF.ITEM then
MMOSimca@444 1517 -- Handle currency loot toasts for chat-based loot (we do this instead of reading currency chat messages because the chat messages are very delayed)
MMOSimca@444 1518 if loot_type == "currency" then
mmosimca@496 1519 local currency_id = CurrencyLinkToID(item_link)
mmosimca@496 1520 if currency_id and currency_id ~= 0 then
MMOSimca@444 1521 -- Verify that we're still assigning data to the right items
atcaleb@573 1522 if chat_loot_data.category == AF.ITEM and chat_loot_data.identifier and (private.CONTAINER_ITEM_ID_LIST[chat_loot_data.identifier] ~= nil) then
mmosimca@496 1523 Debug("%s: Captured currency for chat-based loot recording. %d X %d", event_name, currency_id, quantity)
mmosimca@496 1524 local currency_token = ("currency:%d"):format(currency_id)
MMOSimca@444 1525 chat_loot_data.loot = chat_loot_data.loot or {}
MMOSimca@444 1526 chat_loot_data.loot[currency_token] = (chat_loot_data.loot[currency_token] or 0) + quantity
MMOSimca@444 1527 else -- If not, cancel the timer and wipe the loot table early
MMOSimca@444 1528 Debug("%s: Canceled chat-based loot recording because we would have assigned the wrong loot!", event_name)
MMOSimca@444 1529 ClearChatLootData()
MMOSimca@444 1530 end
MMOSimca@444 1531 else
mmosimca@496 1532 Debug("%s: Currency ID is nil or 0, from currency link %s", event_name, item_link)
MMOSimca@444 1533 end
MMOSimca@444 1534 -- Handle money loot toasts for chat-based loot (we do this instead of reading money chat messages because the chat messages are very delayed)
MMOSimca@444 1535 elseif loot_type == "money" then
MMOSimca@424 1536 -- Verify that we're still assigning data to the right items
atcaleb@573 1537 if chat_loot_data.category == AF.ITEM and chat_loot_data.identifier and (private.CONTAINER_ITEM_ID_LIST[chat_loot_data.identifier] ~= nil) then
MMOSimca@444 1538 Debug("%s: Captured money for chat-based loot recording. money X %d", event_name, quantity)
MMOSimca@435 1539 chat_loot_data.loot = chat_loot_data.loot or {}
MMOSimca@444 1540 chat_loot_data.loot["money"] = (chat_loot_data.loot["money"] or 0) + quantity
MMOSimca@424 1541 else -- If not, cancel the timer and wipe the loot table early
MMOSimca@424 1542 Debug("%s: Canceled chat-based loot recording because we would have assigned the wrong loot!", event_name)
MMOSimca@424 1543 ClearChatLootData()
MMOSimca@424 1544 end
MMOSimca@424 1545 end
jcallahan@301 1546 else
jcallahan@307 1547 Debug("%s: NPC and Container are nil, storing loot toast data for 5 seconds.", event_name)
jcallahan@307 1548
jcallahan@307 1549 loot_toast_data = loot_toast_data or {}
jcallahan@312 1550 loot_toast_data[#loot_toast_data + 1] = { loot_type, item_link, quantity }
jcallahan@307 1551
MMOSimca@340 1552 local item_id = ItemLinkToID(item_link)
MMOSimca@340 1553 if item_id then
MMOSimca@340 1554 RecordItemData(item_id, item_link, true)
MMOSimca@340 1555 end
MMOSimca@340 1556
MMOSimca@383 1557 loot_toast_data_timer_handle = C_Timer.NewTimer(5, ClearLootToastData)
jcallahan@178 1558 end
jcallahan@178 1559 end
jcallahan@178 1560
jcallahan@178 1561
jcallahan@179 1562 do
MMOSimca@388 1563 local CHAT_MSG_CURRENCY_UPDATE_FUNCS = {
mmosimca@496 1564 [AF.NPC] = function(currency_id, quantity)
mmosimca@496 1565 Debug("CHAT_MSG_CURRENCY: AF.NPC currency:%d (%d)", currency_id, quantity)
MMOSimca@388 1566 end,
mmosimca@496 1567 [AF.ZONE] = function(currency_id, quantity)
mmosimca@496 1568 Debug("CHAT_MSG_CURRENCY: AF.ZONE currency:%d (%d)", currency_id, quantity)
MMOSimca@388 1569 InitializeCurrentLoot()
mmosimca@496 1570 current_loot.list[1] = ("currency:%d:%d"):format(quantity, currency_id)
MMOSimca@388 1571 GenericLootUpdate("zones")
MMOSimca@388 1572 current_loot = nil
MMOSimca@388 1573 end,
MMOSimca@388 1574 }
MMOSimca@388 1575
MMOSimca@388 1576
MMOSimca@388 1577 function WDP:CHAT_MSG_CURRENCY(event_name, message)
MMOSimca@388 1578 local category
MMOSimca@388 1579
MMOSimca@388 1580 local currency_link, quantity = deformat(message, _G.CURRENCY_GAINED_MULTIPLE)
MMOSimca@388 1581 if not currency_link then
MMOSimca@388 1582 quantity, currency_link = 1, deformat(message, _G.CURRENCY_GAINED)
MMOSimca@388 1583 end
mmosimca@496 1584 local currency_id = CurrencyLinkToID(currency_link)
mmosimca@496 1585
mmosimca@496 1586 if not currency_id or currency_id == 0 then
MMOSimca@388 1587 return
MMOSimca@388 1588 end
MMOSimca@388 1589
MMOSimca@388 1590 -- Set update category
MMOSimca@388 1591 if current_action.spell_label == "FISHING" then
MMOSimca@388 1592 category = AF.ZONE
MMOSimca@388 1593 elseif raid_boss_id then
MMOSimca@388 1594 category = AF.NPC
MMOSimca@388 1595 end
MMOSimca@388 1596
MMOSimca@388 1597 -- Take action based on update category
MMOSimca@388 1598 local update_func = CHAT_MSG_CURRENCY_UPDATE_FUNCS[category]
MMOSimca@388 1599 if not category or not update_func then
MMOSimca@388 1600 return
MMOSimca@388 1601 end
mmosimca@496 1602 update_func(currency_id, quantity)
MMOSimca@388 1603 end
MMOSimca@388 1604
MMOSimca@388 1605
jcallahan@179 1606 local CHAT_MSG_LOOT_UPDATE_FUNCS = {
atcaleb@573 1607 -- Handle chat loot data from item containers
MMOSimca@347 1608 [AF.ITEM] = function(item_id, quantity)
MMOSimca@347 1609 -- Verify that we're still assigning data to the right items
MMOSimca@435 1610 if chat_loot_data.identifier and (private.CONTAINER_ITEM_ID_LIST[chat_loot_data.identifier] ~= nil) then
MMOSimca@347 1611 Debug("CHAT_MSG_LOOT: AF.ITEM %d (%d)", item_id, quantity)
MMOSimca@435 1612 chat_loot_data.loot = chat_loot_data.loot or {}
MMOSimca@435 1613 chat_loot_data.loot[item_id] = (chat_loot_data.loot[item_id] or 0) + quantity
MMOSimca@347 1614 else -- If not, cancel the timer and wipe the loot table early
MMOSimca@387 1615 Debug("CHAT_MSG_LOOT: We would have assigned the wrong loot!")
MMOSimca@387 1616 ClearChatLootData()
MMOSimca@347 1617 end
MMOSimca@347 1618 end,
atcaleb@573 1619 -- Handle chat loot data from island expedition rewards
jcallahan@179 1620 [AF.NPC] = function(item_id, quantity)
MMOSimca@345 1621 Debug("CHAT_MSG_LOOT: AF.NPC %d (%d)", item_id, quantity)
atcaleb@573 1622 chat_loot_data.loot = chat_loot_data.loot or {}
atcaleb@573 1623 chat_loot_data.loot[item_id] = (chat_loot_data.loot[item_id] or 0) + quantity
MMOSimca@345 1624 end,
atcaleb@573 1625 -- Handle logging spells for objects
MMOSimca@345 1626 [AF.OBJECT] = function(item_id, quantity)
MMOSimca@345 1627 Debug("CHAT_MSG_LOOT: AF.OBJECT %d (%d)", item_id, quantity)
MMOSimca@381 1628 -- Check for top level object data
MMOSimca@381 1629 local object_entry = DBEntry("objects", ("OPENING:%s"):format(private.LOGGING_SPELL_ID_TO_OBJECT_ID_MAP[last_timber_spell_id]))
MMOSimca@381 1630 local difficulty_token = InstanceDifficultyToken()
MMOSimca@381 1631 if object_entry[difficulty_token] then
MMOSimca@381 1632 -- Increment loot count
MMOSimca@381 1633 object_entry[difficulty_token]["opening_count"] = (object_entry[difficulty_token]["opening_count"] or 0) + 1
MMOSimca@381 1634
MMOSimca@381 1635 -- Add drop data
MMOSimca@381 1636 object_entry[difficulty_token]["opening"] = object_entry[difficulty_token]["opening"] or {}
MMOSimca@381 1637 table.insert(object_entry[difficulty_token]["opening"], ("%d:%d"):format(item_id, quantity))
MMOSimca@381 1638 else
MMOSimca@381 1639 Debug("CHAT_MSG_LOOT: When handling timber, the top level data was missing for objectID %s.", private.LOGGING_SPELL_ID_TO_OBJECT_ID_MAP[last_timber_spell_id])
MMOSimca@381 1640 end
jcallahan@179 1641 end,
atcaleb@573 1642 -- Handle fishing loot table population
jcallahan@179 1643 [AF.ZONE] = function(item_id, quantity)
MMOSimca@345 1644 Debug("CHAT_MSG_LOOT: AF.ZONE %d (%d)", item_id, quantity)
jcallahan@312 1645 InitializeCurrentLoot()
jcallahan@312 1646 current_loot.list[1] = ("%d:%d"):format(item_id, quantity)
jcallahan@179 1647 GenericLootUpdate("zones")
jcallahan@312 1648 current_loot = nil
jcallahan@179 1649 end,
jcallahan@179 1650 }
jcallahan@177 1651
jcallahan@177 1652
MMOSimca@388 1653 function WDP:CHAT_MSG_LOOT(event_name, message)
jcallahan@179 1654 local category
jcallahan@177 1655
MMOSimca@345 1656 local item_link, quantity = deformat(message, _G.LOOT_ITEM_PUSHED_SELF_MULTIPLE)
MMOSimca@345 1657 if not item_link then
MMOSimca@345 1658 quantity, item_link = 1, deformat(message, _G.LOOT_ITEM_PUSHED_SELF)
MMOSimca@345 1659 end
MMOSimca@345 1660 local item_id = ItemLinkToID(item_link)
MMOSimca@345 1661
MMOSimca@345 1662 if not item_id then
MMOSimca@345 1663 return
MMOSimca@345 1664 end
MMOSimca@345 1665
MMOSimca@345 1666 -- Set update category
MMOSimca@405 1667 if last_timber_spell_id and item_id == ITEM_ID_TIMBER then
MMOSimca@345 1668 category = AF.OBJECT
atcaleb@573 1669 -- Changed from ~= "EXTRACT_GAS" because of some occassional bad data, and, as far as I know, no benefit.
MMOSimca@345 1670 elseif current_action.spell_label == "FISHING" then
jcallahan@179 1671 category = AF.ZONE
atcaleb@573 1672 elseif not raid_boss_id and chat_loot_timer_handle and chat_loot_data.category == AF.NPC then
jcallahan@179 1673 category = AF.NPC
atcaleb@573 1674 elseif not raid_boss_id and chat_loot_timer_handle and chat_loot_data.category == AF.ITEM then
MMOSimca@347 1675 category = AF.ITEM
jcallahan@179 1676 end
MMOSimca@345 1677
MMOSimca@395 1678 -- We still want to record the item's data, even if it doesn't need its drop location recorded
MMOSimca@395 1679 RecordItemData(item_id, item_link, true)
MMOSimca@395 1680
MMOSimca@345 1681 -- Take action based on update category
jcallahan@179 1682 local update_func = CHAT_MSG_LOOT_UPDATE_FUNCS[category]
atcaleb@558 1683 if not category or not update_func or private.BLACKLISTED_ITEMS[item_id] then
MMOSimca@340 1684 return
MMOSimca@340 1685 end
jcallahan@179 1686 update_func(item_id, quantity)
jcallahan@177 1687 end
MMOSimca@388 1688 end
MMOSimca@388 1689
MMOSimca@388 1690
jcallahan@97 1691 function WDP:RecordQuote(event_name, message, source_name, language_name)
jcallahan@112 1692 if not ALLOWED_LOCALES[CLIENT_LOCALE] or not source_name or not name_to_id_map[source_name] or (language_name ~= "" and not languages_known[language_name]) then
jcallahan@97 1693 return
jcallahan@95 1694 end
jcallahan@97 1695 local npc = NPCEntry(name_to_id_map[source_name])
jcallahan@97 1696 npc.quotes = npc.quotes or {}
jcallahan@97 1697 npc.quotes[event_name] = npc.quotes[event_name] or {}
jcallahan@97 1698 npc.quotes[event_name][ReplaceKeywords(message)] = true
jcallahan@97 1699 end
jcallahan@95 1700
jcallahan@95 1701
jcallahan@95 1702 do
jcallahan@40 1703 local SOBER_MATCH = _G.DRUNK_MESSAGE_ITEM_SELF1:gsub("%%s", ".+")
jcallahan@40 1704
jcallahan@40 1705 local DRUNK_COMPARES = {
jcallahan@40 1706 _G.DRUNK_MESSAGE_SELF2,
jcallahan@40 1707 _G.DRUNK_MESSAGE_SELF3,
jcallahan@40 1708 _G.DRUNK_MESSAGE_SELF4,
jcallahan@40 1709 }
jcallahan@40 1710
jcallahan@40 1711 local DRUNK_MATCHES = {
jcallahan@254 1712 (_G.DRUNK_MESSAGE_SELF2:gsub("%%s", ".+")),
jcallahan@254 1713 (_G.DRUNK_MESSAGE_SELF3:gsub("%%s", ".+")),
jcallahan@254 1714 (_G.DRUNK_MESSAGE_SELF4:gsub("%%s", ".+")),
jcallahan@40 1715 }
jcallahan@40 1716
jcallahan@167 1717 local RECIPE_MATCH = _G.ERR_LEARN_RECIPE_S:gsub("%%s", "(.*)")
jcallahan@167 1718
jcallahan@167 1719
jcallahan@167 1720 local function RecordDiscovery(tradeskill_name, tradeskill_index)
jcallahan@167 1721 if tradeskill_name == private.discovered_recipe_name then
catherton@479 1722 DBEntry("spells", tonumber(_G.C_TradeSkillUI.GetRecipeLink(tradeskill_index):match("^|c%x%x%x%x%x%x%x%x|H%w+:(%d+)"))).discovery = ("%d:%d"):format(private.previous_spell_id, private.profession_level)
jcallahan@167 1723
jcallahan@167 1724 private.discovered_recipe_name = nil
jcallahan@167 1725 private.profession_level = nil
jcallahan@167 1726 private.previous_spell_id = nil
jcallahan@169 1727
jcallahan@169 1728 return true
jcallahan@167 1729 end
jcallahan@167 1730 end
jcallahan@167 1731
jcallahan@167 1732
jcallahan@167 1733 local function IterativeRecordDiscovery()
jcallahan@167 1734 TradeSkillExecutePer(RecordDiscovery)
jcallahan@167 1735 end
jcallahan@167 1736
jcallahan@167 1737
jcallahan@92 1738 function WDP:CHAT_MSG_SYSTEM(event_name, message)
mmosimca@514 1739 -- This code no longer works, as of Patch 7.0.3, because Blizzard unified the text from quest rewards and loot to match (and now there is no way to distinguish between them)
MMOSimca@532 1740 --[[
MMOSimca@532 1741 -- Removed in Patch 7.0.3; previously used to determine if a system message was a quest reward or not
MMOSimca@532 1742 local ERR_QUEST_REWARD_ITEM_MULT_IS = _G.ERR_QUEST_REWARD_ITEM_MULT_IS or "Received %d of item: %s."
MMOSimca@532 1743 local ERR_QUEST_REWARD_ITEM_S = _G.ERR_QUEST_REWARD_ITEM_S or "Received item: %s."
MMOSimca@532 1744
catherton@472 1745 local item_link, quantity = deformat(message, ERR_QUEST_REWARD_ITEM_MULT_IS)
MMOSimca@400 1746 if not item_link then
catherton@472 1747 quantity, item_link = 1, deformat(message, ERR_QUEST_REWARD_ITEM_S)
MMOSimca@400 1748 end
MMOSimca@400 1749 local item_id = ItemLinkToID(item_link)
MMOSimca@400 1750
mmosimca@514 1751 if item_id then
mmosimca@514 1752 -- If it was a quest message (that we can decode), parse its link
mmosimca@514 1753 RecordItemData(item_id, item_link, true)
mmosimca@514 1754 else
mmosimca@514 1755 -- If it isn't a quest message, check the other uses of system messages
MMOSimca@532 1756 end
MMOSimca@532 1757 ]]--
MMOSimca@532 1758
MMOSimca@532 1759 if not private.trainer_shown then
MMOSimca@532 1760 local recipe_name = message:match(RECIPE_MATCH)
MMOSimca@532 1761
MMOSimca@532 1762 if recipe_name and private.previous_spell_id then
MMOSimca@532 1763 local profession_name, prof_level = _G.C_TradeSkillUI.GetTradeSkillLine()
MMOSimca@532 1764
MMOSimca@532 1765 if profession_name == _G.UNKNOWN then
MMOSimca@532 1766 return
jcallahan@167 1767 end
MMOSimca@532 1768 private.discovered_recipe_name = recipe_name
MMOSimca@532 1769 private.profession_level = prof_level
MMOSimca@532 1770
MMOSimca@532 1771 C_Timer.After(0.2, IterativeRecordDiscovery)
jcallahan@167 1772 end
MMOSimca@532 1773 end
MMOSimca@532 1774
MMOSimca@532 1775 if currently_drunk then
MMOSimca@532 1776 if message == _G.DRUNK_MESSAGE_SELF1 or message:match(SOBER_MATCH) then
MMOSimca@532 1777 currently_drunk = nil
MMOSimca@400 1778 end
MMOSimca@532 1779 return
MMOSimca@532 1780 end
MMOSimca@532 1781
MMOSimca@532 1782 for index = 1, #DRUNK_MATCHES do
MMOSimca@532 1783 if message == DRUNK_COMPARES[index] or message:match(DRUNK_MATCHES[index]) then
MMOSimca@532 1784 currently_drunk = true
MMOSimca@532 1785 break
jcallahan@40 1786 end
jcallahan@40 1787 end
jcallahan@40 1788 end
jcallahan@40 1789 end
jcallahan@40 1790
jcallahan@307 1791
jcallahan@331 1792 do
jcallahan@23 1793 local FLAGS_NPC = bit.bor(_G.COMBATLOG_OBJECT_TYPE_GUARDIAN, _G.COMBATLOG_OBJECT_CONTROL_NPC)
jcallahan@23 1794 local FLAGS_NPC_CONTROL = bit.bor(_G.COMBATLOG_OBJECT_AFFILIATION_OUTSIDER, _G.COMBATLOG_OBJECT_CONTROL_NPC)
jcallahan@23 1795
atcaleb@552 1796 local function RecordNPCSpell(sub_event, source_guid, source_name, source_flags, dest_guid, dest_name, dest_flags, spell_id)
atcaleb@558 1797 if not spell_id or private.BLACKLISTED_SPELLS[spell_id] then
jcallahan@23 1798 return
jcallahan@23 1799 end
jcallahan@34 1800 local source_type, source_id = ParseGUID(source_guid)
jcallahan@23 1801
jcallahan@171 1802 if not source_id or not UnitTypeIsNPC(source_type) then
jcallahan@23 1803 return
jcallahan@23 1804 end
jcallahan@23 1805
jcallahan@23 1806 if bit.band(FLAGS_NPC_CONTROL, source_flags) == FLAGS_NPC_CONTROL and bit.band(FLAGS_NPC, source_flags) ~= 0 then
jcallahan@248 1807 local encounter_data = NPCEntry(source_id):EncounterData(InstanceDifficultyToken())
jcallahan@28 1808 encounter_data.spells = encounter_data.spells or {}
jcallahan@28 1809 encounter_data.spells[spell_id] = (encounter_data.spells[spell_id] or 0) + 1
jcallahan@23 1810 end
jcallahan@23 1811 end
jcallahan@23 1812
jcallahan@115 1813 local HEAL_BATTLE_PETS_SPELL_ID = 125801
jcallahan@115 1814
jcallahan@246 1815 local previous_combat_event = {}
jcallahan@246 1816
jcallahan@23 1817 local COMBAT_LOG_FUNCS = {
jcallahan@23 1818 SPELL_AURA_APPLIED = RecordNPCSpell,
jcallahan@23 1819 SPELL_CAST_START = RecordNPCSpell,
atcaleb@552 1820 SPELL_CAST_SUCCESS = function(sub_event, source_guid, source_name, source_flags, dest_guid, dest_name, dest_flags, spell_id)
jcallahan@115 1821 if spell_id == HEAL_BATTLE_PETS_SPELL_ID then
jcallahan@115 1822 local unit_type, unit_idnum = ParseGUID(source_guid)
jcallahan@115 1823
jcallahan@171 1824 if unit_idnum and UnitTypeIsNPC(unit_type) then
jcallahan@115 1825 NPCEntry(unit_idnum).stable_master = true
jcallahan@115 1826 end
jcallahan@115 1827 end
atcaleb@552 1828 RecordNPCSpell(sub_event, source_guid, source_name, source_flags, dest_guid, dest_name, dest_flags, spell_id)
jcallahan@115 1829 end,
atcaleb@552 1830 UNIT_DIED = function(sub_event, source_guid, source_name, source_flags, dest_guid, dest_name, dest_flags, spell_id)
jcallahan@65 1831 local unit_type, unit_idnum = ParseGUID(dest_guid)
jcallahan@65 1832
jcallahan@171 1833 if not unit_idnum or not UnitTypeIsNPC(unit_type) then
mmosimca@515 1834 --Debug("%s: %s is not an NPC, or has no ID.", sub_event, dest_name or _G.UNKNOWN) -- we really don't need to know this
jcallahan@177 1835 ClearKilledNPC()
jcallahan@98 1836 private.harvesting = nil
jcallahan@65 1837 return
jcallahan@65 1838 end
jcallahan@177 1839
jcallahan@246 1840 if source_guid == "" then
jcallahan@246 1841 source_guid = nil
jcallahan@246 1842 end
jcallahan@246 1843 local killer_guid = source_guid or previous_combat_event.source_guid
jcallahan@246 1844 local killer_name = source_name or previous_combat_event.source_name
jcallahan@246 1845
jcallahan@299 1846 if not previous_combat_event.party_damage then
jcallahan@312 1847 --Debug("%s: %s was killed by %s (not group member or pet).", sub_event, dest_name or _G.UNKNOWN, killer_name or _G.UNKNOWN) -- broken in Patch 5.4
jcallahan@299 1848 table.wipe(previous_combat_event)
jcallahan@217 1849 ClearKilledNPC()
jcallahan@306 1850 else
jcallahan@317 1851 --Debug("%s: %s was killed by %s.", sub_event, dest_name or _G.UNKNOWN, killer_name or _G.UNKNOWN) -- broken in Patch 5.4
jcallahan@177 1852 end
jcallahan@177 1853 killed_npc_id = unit_idnum
MMOSimca@383 1854 C_Timer.After(0.1, ClearKilledNPC)
jcallahan@65 1855 end,
jcallahan@23 1856 }
jcallahan@23 1857
jcallahan@23 1858
jcallahan@246 1859 local NON_DAMAGE_EVENTS = {
jcallahan@246 1860 SPELL_AURA_APPLIED = true,
jcallahan@246 1861 SPELL_AURA_REMOVED = true,
jcallahan@246 1862 SPELL_AURA_REMOVED_DOSE = true,
jcallahan@246 1863 SPELL_CAST_FAILED = true,
jcallahan@246 1864 SWING_MISSED = true,
jcallahan@246 1865 }
jcallahan@246 1866
jcallahan@299 1867 local DAMAGE_EVENTS = {
jcallahan@299 1868 RANGE_DAMAGE = true,
jcallahan@299 1869 SPELL_BUILDING_DAMAGE = true,
jcallahan@299 1870 SPELL_DAMAGE = true,
jcallahan@299 1871 SPELL_PERIODIC_DAMAGE = true,
jcallahan@299 1872 SWING_DAMAGE = true,
jcallahan@299 1873 }
jcallahan@299 1874
jcallahan@246 1875
atcaleb@552 1876 function WDP:COMBAT_LOG_EVENT_UNFILTERED(event_name, time_stamp, sub_event, hide_caster, source_guid, source_name, source_flags, source_raid_flags, dest_guid, dest_name, dest_flags, dest_raid_flags, spell_id, ...)
atcaleb@558 1877 time_stamp, sub_event, hide_caster, source_guid, source_name, source_flags, source_raid_flags, dest_guid, dest_name, dest_flags, dest_raid_flags, spell_id = CombatLogGetCurrentEventInfo()
atcaleb@552 1878
jcallahan@23 1879 local combat_log_func = COMBAT_LOG_FUNCS[sub_event]
jcallahan@23 1880
jcallahan@23 1881 if not combat_log_func then
jcallahan@299 1882 if DAMAGE_EVENTS[sub_event] then
jcallahan@299 1883 table.wipe(previous_combat_event)
jcallahan@246 1884 previous_combat_event.source_name = source_name
jcallahan@299 1885
jcallahan@299 1886 if source_guid ~= dest_guid and (in_instance or group_member_guids[source_guid] or group_pet_guids[source_guid]) then
jcallahan@299 1887 previous_combat_event.party_damage = true
jcallahan@299 1888 end
jcallahan@246 1889 end
jcallahan@23 1890 return
jcallahan@23 1891 end
atcaleb@552 1892 combat_log_func(sub_event, source_guid, source_name, source_flags, dest_guid, dest_name, dest_flags, spell_id)
jcallahan@297 1893
jcallahan@297 1894 if NON_DAMAGE_EVENTS[sub_event] then
jcallahan@297 1895 table.wipe(previous_combat_event)
jcallahan@297 1896 end
jcallahan@23 1897 end
jcallahan@23 1898
catherton@465 1899
jcallahan@44 1900 local DIPLOMACY_SPELL_ID = 20599
jcallahan@44 1901 local MR_POP_RANK1_SPELL_ID = 78634
jcallahan@44 1902 local MR_POP_RANK2_SPELL_ID = 78635
MMOSimca@418 1903 local TRADING_PACT_SPELL_ID = 170200
jcallahan@44 1904
jcallahan@44 1905
jcallahan@92 1906 function WDP:COMBAT_TEXT_UPDATE(event_name, message_type, faction_name, amount)
jcallahan@177 1907 if message_type ~= "FACTION" or not killed_npc_id then
jcallahan@44 1908 return
jcallahan@44 1909 end
jcallahan@44 1910 UpdateFactionData()
jcallahan@44 1911
jcallahan@46 1912 if not faction_name or not faction_standings[faction_name] then
jcallahan@46 1913 return
jcallahan@46 1914 end
jcallahan@177 1915 local npc = NPCEntry(killed_npc_id)
jcallahan@177 1916 ClearKilledNPC()
jcallahan@46 1917
jcallahan@44 1918 if not npc then
jcallahan@98 1919 private.harvesting = nil
jcallahan@44 1920 return
jcallahan@44 1921 end
jcallahan@98 1922 npc.harvested = private.harvesting
jcallahan@98 1923 private.harvesting = nil
jcallahan@98 1924
jcallahan@44 1925 local modifier = 1
jcallahan@44 1926
MMOSimca@340 1927 -- Check for modifiers from known spells
jcallahan@44 1928 if _G.IsSpellKnown(DIPLOMACY_SPELL_ID) then
jcallahan@44 1929 modifier = modifier + 0.1
jcallahan@44 1930 end
jcallahan@44 1931 if _G.IsSpellKnown(MR_POP_RANK2_SPELL_ID) then
jcallahan@44 1932 modifier = modifier + 0.1
jcallahan@44 1933 elseif _G.IsSpellKnown(MR_POP_RANK1_SPELL_ID) then
jcallahan@44 1934 modifier = modifier + 0.05
jcallahan@44 1935 end
MMOSimca@418 1936 if _G.IsSpellKnown(TRADING_PACT_SPELL_ID) then
MMOSimca@418 1937 modifier = modifier + 0.2
MMOSimca@418 1938 end
jcallahan@44 1939
MMOSimca@340 1940 -- Determine faction ID
MMOSimca@340 1941 local faction_ID
MMOSimca@418 1942 for pseudo_faction_name, faction_data_table in pairs(private.FACTION_DATA) do
MMOSimca@418 1943 if faction_name == faction_data_table[3] then
MMOSimca@418 1944 -- Check ignore flag
MMOSimca@418 1945 if faction_data_table[2] then
MMOSimca@418 1946 return
MMOSimca@418 1947 end
MMOSimca@340 1948 faction_ID = faction_data_table[1]
MMOSimca@418 1949 break
MMOSimca@340 1950 end
MMOSimca@340 1951 end
MMOSimca@340 1952 if faction_ID and faction_ID > 0 then
MMOSimca@340 1953 -- Check for modifiers from Commendations (applied directly to the faction, account-wide)
MMOSimca@340 1954 local _, _, _, _, _, _, _, _, _, _, _, _, _, _, has_bonus_rep_gain = GetFactionInfoByID(faction_ID)
MMOSimca@340 1955 if has_bonus_rep_gain then
MMOSimca@340 1956 modifier = modifier + 1
MMOSimca@340 1957 end
MMOSimca@340 1958 end
MMOSimca@340 1959
MMOSimca@340 1960 -- Check for modifiers from buffs
MMOSimca@418 1961 for buff_name, buff_data_table in pairs(private.REP_BUFFS) do
jcallahan@44 1962 if _G.UnitBuff("player", buff_name) then
MMOSimca@340 1963 local modded_faction = buff_data_table.faction
jcallahan@44 1964
jcallahan@44 1965 if not modded_faction or faction_name == modded_faction then
MMOSimca@340 1966 if buff_data_table.ignore then
MMOSimca@340 1967 -- Some buffs from tabards convert all rep of other factions into rep for a specific faction.
MMOSimca@340 1968 -- We can't know what faction the rep was orginally from, so we must ignore the data entirely in these cases.
MMOSimca@340 1969 return
MMOSimca@340 1970 else
MMOSimca@340 1971 modifier = modifier + buff_data_table.modifier
MMOSimca@340 1972 end
jcallahan@44 1973 end
jcallahan@44 1974 end
jcallahan@44 1975 end
catherton@465 1976
jcallahan@65 1977 npc.reputations = npc.reputations or {}
jcallahan@65 1978 npc.reputations[("%s:%s"):format(faction_name, faction_standings[faction_name])] = math.floor(amount / modifier)
jcallahan@32 1979 end
jcallahan@44 1980 end -- do-block
jcallahan@18 1981
jcallahan@18 1982
jcallahan@140 1983 function WDP:CURSOR_UPDATE(event_name)
MMOSimca@355 1984 if current_action.fishing_target or _G.Minimap:IsMouseOver() then
jcallahan@140 1985 return
jcallahan@140 1986 end
jcallahan@140 1987 local text = _G["GameTooltipTextLeft1"]:GetText()
jcallahan@140 1988
MMOSimca@355 1989 -- Handle Fishing
MMOSimca@355 1990 if (current_action.spell_label == "FISHING") then
MMOSimca@355 1991 if not text or text == "Fishing Bobber" then
MMOSimca@355 1992 text = "NONE"
MMOSimca@355 1993 else
MMOSimca@355 1994 current_action.fishing_target = true
MMOSimca@355 1995 end
MMOSimca@355 1996 current_action.identifier = ("%s:%s"):format(current_action.spell_label, text)
MMOSimca@355 1997 -- Handle Garrison Cache
MMOSimca@355 1998 elseif private.GARRISON_CACHE_OBJECT_NAME_TO_OBJECT_ID_MAP[text] then
MMOSimca@355 1999 last_garrison_cache_object_id = private.GARRISON_CACHE_OBJECT_NAME_TO_OBJECT_ID_MAP[text]
jcallahan@140 2000 end
jcallahan@140 2001 end
jcallahan@140 2002
jcallahan@140 2003
jcallahan@92 2004 function WDP:ITEM_TEXT_BEGIN(event_name)
jcallahan@42 2005 local unit_type, unit_idnum = ParseGUID(_G.UnitGUID("npc"))
jcallahan@42 2006
jcallahan@42 2007 if not unit_idnum or unit_type ~= private.UNIT_TYPES.OBJECT or _G.UnitName("npc") ~= _G.ItemTextGetItem() then
jcallahan@42 2008 return
jcallahan@42 2009 end
jcallahan@42 2010 UpdateDBEntryLocation("objects", unit_idnum)
jcallahan@42 2011 end
jcallahan@42 2012
jcallahan@42 2013
jcallahan@13 2014 do
MMOSimca@343 2015 local LOOT_OPENED_VERIFY_FUNCS = {
jcallahan@324 2016 -- Item containers can be AOE-looted in Patch 5.4.2 if the user clicks fast enough, but this verification still works as long as they both have loot.
jcallahan@16 2017 [AF.ITEM] = function()
jcallahan@16 2018 local locked_item_id
jcallahan@16 2019
jcallahan@16 2020 for bag_index = 0, _G.NUM_BAG_FRAMES do
jcallahan@16 2021 for slot_index = 1, _G.GetContainerNumSlots(bag_index) do
jcallahan@324 2022 local _, _, is_locked, _, _, is_lootable = _G.GetContainerItemInfo(bag_index, slot_index)
jcallahan@324 2023
jcallahan@324 2024 if is_locked and is_lootable then
jcallahan@16 2025 locked_item_id = ItemLinkToID(_G.GetContainerItemLink(bag_index, slot_index))
jcallahan@165 2026 break
jcallahan@16 2027 end
jcallahan@16 2028 end
jcallahan@165 2029
jcallahan@165 2030 if locked_item_id then
jcallahan@165 2031 break
jcallahan@165 2032 end
jcallahan@16 2033 end
jcallahan@16 2034
MMOSimca@367 2035 if (not current_action.spell_label == "DISENCHANT") and (not locked_item_id or (current_action.identifier and current_action.identifier ~= locked_item_id)) then
jcallahan@16 2036 return false
jcallahan@16 2037 end
jcallahan@122 2038 current_action.identifier = locked_item_id
jcallahan@16 2039 return true
jcallahan@16 2040 end,
MMOSimca@401 2041 [AF.NPC] = function()
MMOSimca@401 2042 return not _G.IsFishingLoot()
MMOSimca@401 2043 end,
MMOSimca@401 2044 [AF.OBJECT] = function()
MMOSimca@401 2045 return not _G.IsFishingLoot()
MMOSimca@401 2046 end,
jcallahan@17 2047 [AF.ZONE] = function()
jcallahan@140 2048 current_action.zone_data = UpdateDBEntryLocation("zones", current_action.identifier)
jcallahan@210 2049 return _G.IsFishingLoot()
jcallahan@17 2050 end,
jcallahan@13 2051 }
jcallahan@13 2052
jcallahan@13 2053
MMOSimca@343 2054 local LOOT_OPENED_UPDATE_FUNCS = {
jcallahan@16 2055 [AF.ITEM] = function()
jcallahan@28 2056 GenericLootUpdate("items")
jcallahan@28 2057 end,
jcallahan@28 2058 [AF.NPC] = function()
jcallahan@75 2059 local difficulty_token = InstanceDifficultyToken()
jcallahan@312 2060 local loot_label = current_loot.label
jcallahan@77 2061 local source_list = {}
jcallahan@75 2062
jcallahan@131 2063 for source_guid, loot_data in pairs(current_loot.sources) do
jcallahan@331 2064 local _, source_id = ParseGUID(source_guid)
jcallahan@75 2065 local npc = NPCEntry(source_id)
jcallahan@75 2066
jcallahan@75 2067 if npc then
jcallahan@248 2068 local encounter_data = npc:EncounterData(difficulty_token)
jcallahan@312 2069 encounter_data[loot_label] = encounter_data[loot_label] or {}
jcallahan@75 2070
jcallahan@78 2071 if not source_list[source_guid] then
jcallahan@77 2072 encounter_data.loot_counts = encounter_data.loot_counts or {}
MMOSimca@426 2073 encounter_data.loot_counts[loot_label] = (encounter_data.loot_counts[loot_label] or 0) + 1
jcallahan@312 2074 source_list[source_guid] = true
jcallahan@77 2075 end
jcallahan@77 2076
jcallahan@309 2077 for loot_token, quantity in pairs(loot_data) do
mmosimca@496 2078 local loot_type, currency_id = (":"):split(loot_token)
mmosimca@496 2079
mmosimca@496 2080 if loot_type == "currency" and currency_id then
mmosimca@496 2081 -- Convert currency_id back into number from string
mmosimca@496 2082 currency_id = tonumber(currency_id) or 0
mmosimca@496 2083 if currency_id ~= 0 then
mmosimca@496 2084 table.insert(encounter_data[loot_label], ("currency:%d:%d"):format(quantity, currency_id))
mmosimca@496 2085 end
jcallahan@309 2086 elseif loot_token == "money" then
jcallahan@312 2087 table.insert(encounter_data[loot_label], ("money:%d"):format(quantity))
jcallahan@309 2088 else
jcallahan@312 2089 table.insert(encounter_data[loot_label], ("%d:%d"):format(loot_token, quantity))
jcallahan@309 2090 end
jcallahan@75 2091 end
jcallahan@75 2092 end
jcallahan@75 2093 end
jcallahan@16 2094 end,
jcallahan@13 2095 [AF.OBJECT] = function()
jcallahan@28 2096 GenericLootUpdate("objects", InstanceDifficultyToken())
jcallahan@17 2097 end,
jcallahan@17 2098 [AF.ZONE] = function()
MMOSimca@328 2099 if not (current_loot.map_level and current_loot.x and current_loot.y and current_loot.zone_data) then
MMOSimca@328 2100 return
MMOSimca@328 2101 end
jcallahan@141 2102 local location_token = ("%d:%d:%d"):format(current_loot.map_level, current_loot.x, current_loot.y)
jcallahan@41 2103
jcallahan@41 2104 -- This will start life as a boolean true.
mmosimca@522 2105 if type(current_loot.zone_data[location_token]) ~= "table" then
jcallahan@131 2106 current_loot.zone_data[location_token] = {
jcallahan@41 2107 drops = {}
jcallahan@41 2108 }
jcallahan@41 2109 end
jcallahan@132 2110 local loot_count = ("%s_count"):format(current_loot.label)
jcallahan@131 2111 current_loot.zone_data[location_token][loot_count] = (current_loot.zone_data[location_token][loot_count] or 0) + 1
jcallahan@41 2112
jcallahan@131 2113 if current_loot.sources then
jcallahan@131 2114 for source_guid, loot_data in pairs(current_loot.sources) do
jcallahan@131 2115 for item_id, quantity in pairs(loot_data) do
jcallahan@131 2116 table.insert(current_loot.zone_data[location_token].drops, ("%d:%d"):format(item_id, quantity))
jcallahan@131 2117 end
jcallahan@131 2118 end
jcallahan@131 2119 end
jcallahan@131 2120
jcallahan@131 2121 if #current_loot.list <= 0 then
jcallahan@131 2122 return
jcallahan@131 2123 end
jcallahan@131 2124
jcallahan@131 2125 for index = 1, #current_loot.list do
MMOSimca@443 2126 table.insert(current_loot.zone_data[location_token].drops, current_loot.list[index])
jcallahan@41 2127 end
jcallahan@13 2128 end,
jcallahan@13 2129 }
jcallahan@13 2130
jcallahan@79 2131 -- Prevent opening the same loot window multiple times from recording data multiple times.
jcallahan@79 2132 local loot_guid_registry = {}
jcallahan@124 2133
jcallahan@124 2134
jcallahan@124 2135 function WDP:LOOT_CLOSED(event_name)
MMOSimca@398 2136 ClearChatLootData()
jcallahan@131 2137 current_loot = nil
jcallahan@131 2138 table.wipe(current_action)
jcallahan@124 2139 end
jcallahan@124 2140
jcallahan@13 2141
jcallahan@322 2142 local function ExtrapolatedCurrentActionFromLootData(event_name)
MMOSimca@402 2143 local log_source = event_name .. "- ExtrapolatedCurrentActionFromLootData"
MMOSimca@402 2144 local previous_spell_label = current_action.spell_label
jcallahan@322 2145 local extrapolated_guid_registry = {}
jcallahan@322 2146 local num_guids = 0
MMOSimca@402 2147 table.wipe(current_action)
MMOSimca@402 2148
MMOSimca@402 2149 if _G.IsFishingLoot() then
MMOSimca@402 2150 -- Set up a proper 'fishing' current_action table
MMOSimca@402 2151 local zone_name, area_id, x, y, map_level, instance_token = CurrentLocationData()
MMOSimca@402 2152 if not (zone_name and area_id and x and y and map_level) then
mmosimca@508 2153 if not (_G.IsInInstance()) then
mmosimca@508 2154 Debug("%s: Missing current location data - %s, %s, %s, %s, %s.", log_source, tostring(zone_name), tostring(area_id), tostring(x), tostring(y), tostring(map_level))
mmosimca@508 2155 end
MMOSimca@402 2156 return
MMOSimca@402 2157 end
MMOSimca@402 2158 current_action.instance_token = instance_token
MMOSimca@402 2159 current_action.map_level = map_level
MMOSimca@402 2160 current_action.x = x
MMOSimca@402 2161 current_action.y = y
MMOSimca@402 2162 current_action.zone_data = ("%s:%d"):format(zone_name, area_id)
MMOSimca@402 2163 current_action.spell_label = "FISHING"
MMOSimca@402 2164 current_action.loot_label = "fishing"
MMOSimca@402 2165 current_action.identifier = "FISHING:NONE"
MMOSimca@402 2166 current_action.target_type = AF.ZONE
MMOSimca@402 2167
MMOSimca@402 2168 Debug("%s: Fishing loot detected.", log_source)
MMOSimca@402 2169 return true
MMOSimca@402 2170 end
jcallahan@322 2171
MMOSimca@344 2172 -- Loot extrapolation cannot handle objects that need special spell labels (like HERBALISM or MINING) (MIND_CONTROL is okay)
MMOSimca@402 2173 if previous_spell_label and private.SPELL_FLAGS_BY_LABEL[previous_spell_label] and not private.NON_LOOT_SPELL_LABELS[previous_spell_label] then
MMOSimca@344 2174 Debug("%s: Problematic spell label %s found. Loot extrapolation for this set of loot would have run an increased risk of introducing bad data into the system.", log_source, private.previous_spell_id)
MMOSimca@344 2175 return false
MMOSimca@344 2176 end
MMOSimca@344 2177
jcallahan@322 2178 for loot_slot = 1, _G.GetNumLootItems() do
jcallahan@322 2179 local loot_info = {
jcallahan@322 2180 _G.GetLootSourceInfo(loot_slot)
jcallahan@322 2181 }
jcallahan@322 2182
jcallahan@322 2183 for loot_index = 1, #loot_info, 2 do
jcallahan@322 2184 local source_guid = loot_info[loot_index]
jcallahan@322 2185
jcallahan@322 2186 if not extrapolated_guid_registry[source_guid] then
jcallahan@322 2187 local unit_type, unit_idnum = ParseGUID(source_guid)
jcallahan@322 2188
jcallahan@322 2189 if unit_type and unit_idnum then
jcallahan@322 2190 extrapolated_guid_registry[source_guid] = {
jcallahan@322 2191 unit_type,
jcallahan@322 2192 unit_idnum
jcallahan@322 2193 }
jcallahan@322 2194
jcallahan@322 2195 num_guids = num_guids + 1
jcallahan@322 2196 end
jcallahan@322 2197 end
jcallahan@322 2198 end
jcallahan@322 2199 end
jcallahan@322 2200
jcallahan@322 2201 if num_guids == 0 then
jcallahan@322 2202 Debug("%s: No GUIDs found in loot. Blank loot window?", log_source)
jcallahan@322 2203 return false
jcallahan@322 2204 end
jcallahan@326 2205
jcallahan@322 2206 local num_npcs = 0
jcallahan@322 2207 local num_objects = 0
jcallahan@324 2208 local num_itemcontainers = 0
jcallahan@322 2209
jcallahan@322 2210 for source_guid, guid_data in pairs(extrapolated_guid_registry) do
jcallahan@322 2211 local unit_type = guid_data[1]
mmosimca@516 2212 local loot_label = (unit_type == private.UNIT_TYPES.OBJECT) and "opening" or (UnitTypeIsNPC(unit_type) and "drops") or ((unit_type == private.UNIT_TYPES.ITEM) and "contains")
jcallahan@322 2213
jcallahan@322 2214 if loot_label then
jcallahan@322 2215 local unit_idnum = guid_data[2]
jcallahan@322 2216
jcallahan@322 2217 if loot_guid_registry[loot_label] and loot_guid_registry[loot_label][source_guid] then
jcallahan@322 2218 Debug("%s: Previously scanned loot for unit with GUID %s and identifier %s.", log_source, source_guid, unit_idnum)
jcallahan@322 2219 elseif unit_type == private.UNIT_TYPES.OBJECT and unit_idnum ~= OBJECT_ID_FISHING_BOBBER then
jcallahan@322 2220 current_action.loot_label = loot_label
jcallahan@322 2221 current_action.spell_label = "OPENING"
jcallahan@322 2222 current_action.target_type = AF.OBJECT
jcallahan@322 2223 current_action.identifier = unit_idnum
jcallahan@322 2224 num_objects = num_objects + 1
jcallahan@322 2225 elseif UnitTypeIsNPC(unit_type) then
jcallahan@322 2226 current_action.loot_label = loot_label
jcallahan@322 2227 current_action.target_type = AF.NPC
jcallahan@322 2228 current_action.identifier = unit_idnum
jcallahan@322 2229 num_npcs = num_npcs + 1
mmosimca@516 2230 elseif unit_type == private.UNIT_TYPES.ITEM then
jcallahan@324 2231 current_action.loot_label = loot_label
jcallahan@324 2232 current_action.target_type = AF.ITEM
jcallahan@324 2233 -- current_action.identifier assigned during loot verification.
jcallahan@324 2234 num_itemcontainers = num_itemcontainers + 1
jcallahan@322 2235 end
jcallahan@322 2236 else
jcallahan@322 2237 -- Bail here; not only do we not know what this unit is, but we don't want to attribute loot to something that doesn't actually drop it.
jcallahan@322 2238 Debug("%s: Unit with GUID %s has unsupported type for looting.", log_source, source_guid)
jcallahan@322 2239 return false
jcallahan@322 2240 end
jcallahan@322 2241 end
jcallahan@322 2242
jcallahan@322 2243 if not current_action.target_type then
jcallahan@322 2244 Debug("%s: Failure to obtain target_type.", log_source)
jcallahan@322 2245 return false
jcallahan@322 2246 end
jcallahan@322 2247
jcallahan@322 2248 -- We can't figure out what dropped the loot. This shouldn't ever happen, but hey - Blizzard does some awesome stuff on occasion.
jcallahan@324 2249 if (num_npcs > 0 and num_objects + num_itemcontainers > 0) or (num_objects > 0 and num_npcs + num_itemcontainers > 0) or (num_itemcontainers > 0 and num_npcs + num_objects > 0) then
jcallahan@324 2250 Debug("%s: Mixed target types are not supported. NPCs - %d, Objects - %d, Item Containers - %d.", log_source, num_npcs, num_objects, num_itemcontainers)
jcallahan@322 2251 return false
jcallahan@322 2252 end
jcallahan@322 2253
jcallahan@322 2254 return true
jcallahan@322 2255 end
jcallahan@322 2256
jcallahan@322 2257
MMOSimca@343 2258 function WDP:LOOT_OPENED(event_name)
MMOSimca@398 2259 ClearChatLootData()
MMOSimca@387 2260
jcallahan@132 2261 if current_loot then
jcallahan@322 2262 current_loot = nil
jcallahan@322 2263 Debug("%s: Previous loot did not process in time for this event. Attempting to extrapolate current_action from loot data.", event_name)
jcallahan@322 2264
jcallahan@322 2265 if not ExtrapolatedCurrentActionFromLootData(event_name) then
jcallahan@322 2266 Debug("%s: Unable to extrapolate current_action. Aborting attempts to handle loot for now.", event_name)
jcallahan@322 2267 return
jcallahan@322 2268 end
jcallahan@18 2269 end
jcallahan@151 2270
jcallahan@151 2271 if not current_action.target_type then
jcallahan@322 2272 if not ExtrapolatedCurrentActionFromLootData(event_name) then
jcallahan@322 2273 Debug("%s: Unable to extrapolate current_action. Aborting attempts to handle loot for now.", event_name)
jcallahan@322 2274 return
jcallahan@322 2275 end
jcallahan@151 2276 end
MMOSimca@343 2277 local verify_func = LOOT_OPENED_VERIFY_FUNCS[current_action.target_type]
MMOSimca@343 2278 local update_func = LOOT_OPENED_UPDATE_FUNCS[current_action.target_type]
jcallahan@13 2279
jcallahan@14 2280 if not verify_func or not update_func then
jcallahan@322 2281 Debug("%s: The current action's target type is unsupported or nil.", event_name)
jcallahan@13 2282 return
jcallahan@13 2283 end
jcallahan@13 2284
mmosimca@522 2285 if type(verify_func) == "function" and not verify_func() then
jcallahan@324 2286 Debug("%s: The current action type, %s, is supported but has failed loot verification.", event_name, private.ACTION_TYPE_NAMES[current_action.target_type])
jcallahan@14 2287 return
jcallahan@14 2288 end
jcallahan@80 2289 local guids_used = {}
jcallahan@132 2290
jcallahan@301 2291 InitializeCurrentLoot()
jcallahan@217 2292 loot_guid_registry[current_loot.label] = loot_guid_registry[current_loot.label] or {}
jcallahan@217 2293
jcallahan@55 2294 for loot_slot = 1, _G.GetNumLootItems() do
mmosimca@496 2295 local texture_filedata_id, item_text, slot_quantity, quality, locked = _G.GetLootSlotInfo(loot_slot)
jcallahan@55 2296 local slot_type = _G.GetLootSlotType(loot_slot)
catherton@463 2297 local loot_info = { _G.GetLootSourceInfo(loot_slot) }
catherton@464 2298 local loot_link = _G.GetLootSlotLink(loot_slot)
jcallahan@13 2299
jcallahan@237 2300 -- Odd index is GUID, even is count.
jcallahan@237 2301 for loot_index = 1, #loot_info, 2 do
jcallahan@237 2302 local source_guid = loot_info[loot_index]
jcallahan@75 2303
jcallahan@237 2304 if not loot_guid_registry[current_loot.label][source_guid] then
jcallahan@237 2305 local loot_quantity = loot_info[loot_index + 1]
jcallahan@324 2306 -- There is a new bug in 5.4.0 that causes GetLootSlotInfo() to (rarely) return nil values for slot_quantity.
jcallahan@324 2307 if slot_quantity then
jcallahan@324 2308 -- We need slot_quantity to account for an old bug where loot_quantity is sometimes '1' for stacks of items, such as cloth.
jcallahan@324 2309 if slot_quantity > loot_quantity then
jcallahan@324 2310 loot_quantity = slot_quantity
jcallahan@324 2311 end
jcallahan@324 2312 local source_type, source_id = ParseGUID(source_guid)
MMOSimca@329 2313 local source_key = ("%s:%d"):format(source_type, source_id)
jcallahan@324 2314
MMOSimca@366 2315 if slot_type == LOOT_SLOT_ITEM then
catherton@464 2316 if loot_link then
catherton@464 2317 local item_id = ItemLinkToID(loot_link)
catherton@464 2318 Debug("GUID: %s - Type:ID: %s - ItemID: %d - Amount: %d (%d)", loot_info[loot_index], source_key, item_id, loot_info[loot_index + 1], slot_quantity)
catherton@464 2319 current_loot.sources[source_guid] = current_loot.sources[source_guid] or {}
catherton@464 2320 current_loot.sources[source_guid][item_id] = (current_loot.sources[source_guid][item_id] or 0) + loot_quantity
catherton@464 2321 guids_used[source_guid] = true
catherton@463 2322 else
catherton@463 2323 Debug("%s: Loot link is nil for loot slot %d of the entity with GUID %s and Type:ID: %s.", event_name, loot_slot, loot_info[loot_index], source_key)
catherton@463 2324 end
MMOSimca@366 2325 elseif slot_type == LOOT_SLOT_MONEY then
jcallahan@324 2326 Debug("GUID: %s - Type:ID: %s - Money - Amount: %d (%d)", loot_info[loot_index], source_key, loot_info[loot_index + 1], slot_quantity)
jcallahan@324 2327 if current_loot.target_type == AF.ZONE then
jcallahan@324 2328 table.insert(current_loot.list, ("money:%d"):format(loot_quantity))
jcallahan@324 2329 else
jcallahan@324 2330 current_loot.sources[source_guid] = current_loot.sources[source_guid] or {}
MMOSimca@367 2331 current_loot.sources[source_guid]["money"] = (current_loot.sources[source_guid]["money"] or 0) + loot_quantity
jcallahan@324 2332 guids_used[source_guid] = true
jcallahan@324 2333 end
MMOSimca@366 2334 elseif slot_type == LOOT_SLOT_CURRENCY then
jcallahan@324 2335 -- Same bug with GetLootSlotInfo() will screw up currency when it happens, so we won't process this slot's loot.
catherton@463 2336 if loot_link then
mmosimca@496 2337 local currency_id = CurrencyLinkToID(loot_link)
mmosimca@496 2338 Debug("GUID: %s - Type:ID: %s - Currency: %d - Amount: %d (%d)", loot_info[loot_index], source_key, currency_id, loot_info[loot_index + 1], slot_quantity)
jcallahan@324 2339 if current_loot.target_type == AF.ZONE then
mmosimca@496 2340 table.insert(current_loot.list, ("currency:%d:%d"):format(loot_quantity, currency_id))
jcallahan@324 2341 else
mmosimca@496 2342 local currency_token = ("currency:%d"):format(currency_id)
jcallahan@324 2343
jcallahan@324 2344 current_loot.sources[source_guid] = current_loot.sources[source_guid] or {}
MMOSimca@367 2345 current_loot.sources[source_guid][currency_token] = (current_loot.sources[source_guid][currency_token] or 0) + loot_quantity
jcallahan@324 2346 guids_used[source_guid] = true
jcallahan@324 2347 end
jcallahan@324 2348 else
catherton@463 2349 Debug("%s: Loot link is nil for loot slot %d of the entity with GUID %s and Type:ID: %s.", event_name, loot_slot, loot_info[loot_index], source_key)
jcallahan@324 2350 end
jcallahan@308 2351 end
jcallahan@324 2352 else
jcallahan@324 2353 -- If this is nil, then the item's quantity could be wrong if loot_quantity is wrong, so we won't process this slot's loot.
catherton@463 2354 Debug("%s: Slot quantity is nil for loot slot %d of the entity with GUID %s and Type:ID: %s.", event_name, loot_slot, loot_info[loot_index], source_key)
jcallahan@79 2355 end
jcallahan@75 2356 end
jcallahan@13 2357 end
jcallahan@13 2358 end
jcallahan@80 2359
jcallahan@81 2360 for guid in pairs(guids_used) do
jcallahan@217 2361 loot_guid_registry[current_loot.label][guid] = true
jcallahan@80 2362 end
jcallahan@13 2363 update_func()
jcallahan@1 2364 end
jcallahan@13 2365 end -- do-block
jcallahan@0 2366
jcallahan@0 2367
jcallahan@89 2368 function WDP:MAIL_SHOW(event_name)
MMOSimca@436 2369 WDP:StopChatLootRecording(event_name)
jcallahan@89 2370 local unit_type, unit_idnum = ParseGUID(_G.UnitGUID("npc"))
jcallahan@89 2371
jcallahan@89 2372 if not unit_idnum or unit_type ~= private.UNIT_TYPES.OBJECT then
jcallahan@89 2373 return
jcallahan@89 2374 end
jcallahan@89 2375 UpdateDBEntryLocation("objects", unit_idnum)
jcallahan@89 2376 end
jcallahan@89 2377
jcallahan@89 2378
jcallahan@44 2379 do
jcallahan@44 2380 local POINT_MATCH_PATTERNS = {
jcallahan@44 2381 ("^%s$"):format(_G.ITEM_REQ_ARENA_RATING:gsub("%%d", "(%%d+)")), -- May no longer be necessary
jcallahan@44 2382 ("^%s$"):format(_G.ITEM_REQ_ARENA_RATING_3V3:gsub("%%d", "(%%d+)")), -- May no longer be necessary
jcallahan@44 2383 ("^%s$"):format(_G.ITEM_REQ_ARENA_RATING_5V5:gsub("%%d", "(%%d+)")), -- May no longer be necessary
jcallahan@44 2384 ("^%s$"):format(_G.ITEM_REQ_ARENA_RATING_BG:gsub("%%d", "(%%d+)")),
jcallahan@44 2385 ("^%s$"):format(_G.ITEM_REQ_ARENA_RATING_3V3_BG:gsub("%%d", "(%%d+)")),
jcallahan@44 2386 }
jcallahan@5 2387
jcallahan@68 2388 local ITEM_REQ_REPUTATION_MATCH = "Requires (.-) %- (.*)"
jcallahan@87 2389 local ITEM_REQ_QUEST_MATCH1 = "Requires: .*"
jcallahan@87 2390 local ITEM_REQ_QUEST_MATCH2 = "Must have completed: .*"
jcallahan@68 2391
jcallahan@133 2392 local current_merchant
jcallahan@133 2393 local merchant_standing
jcallahan@133 2394
jcallahan@133 2395 function WDP:MERCHANT_CLOSED(event_name)
MMOSimca@436 2396 WDP:ResumeChatLootRecording(event_name)
jcallahan@133 2397 current_merchant = nil
jcallahan@133 2398 merchant_standing = nil
jcallahan@133 2399 end
jcallahan@133 2400
jcallahan@133 2401
jcallahan@89 2402 function WDP:UpdateMerchantItems(event_name)
jcallahan@144 2403 if not current_merchant or event_name == "MERCHANT_SHOW" then
MMOSimca@436 2404 WDP:StopChatLootRecording(event_name)
jcallahan@195 2405 local unit_type, unit_idnum = ParseGUID(_G.UnitGUID("npc"))
jcallahan@4 2406
jcallahan@171 2407 if not unit_idnum or not UnitTypeIsNPC(unit_type) then
jcallahan@133 2408 return
jcallahan@133 2409 end
jcallahan@331 2410 local _, faction_standing = UnitFactionStanding("npc")
jcallahan@331 2411 merchant_standing = faction_standing
jcallahan@133 2412 current_merchant = NPCEntry(unit_idnum)
jcallahan@133 2413 current_merchant.sells = current_merchant.sells or {}
jcallahan@44 2414 end
jcallahan@55 2415 local current_filters = _G.GetMerchantFilter()
jcallahan@57 2416 _G.SetMerchantFilter(_G.LE_LOOT_FILTER_ALL)
jcallahan@57 2417 _G.MerchantFrame_Update()
jcallahan@57 2418
jcallahan@150 2419 local num_items = _G.GetMerchantNumItems()
jcallahan@150 2420
jcallahan@44 2421 for item_index = 1, num_items do
jcallahan@44 2422 local _, _, copper_price, stack_size, num_available, _, extended_cost = _G.GetMerchantItemInfo(item_index)
jcallahan@44 2423 local item_id = ItemLinkToID(_G.GetMerchantItemLink(item_index))
jcallahan@5 2424
jcallahan@324 2425 DatamineTT:ClearLines()
jcallahan@324 2426 DatamineTT:SetMerchantItem(item_index)
jcallahan@324 2427
jcallahan@324 2428 if not item_id then
jcallahan@324 2429 local item_name, item_link = DatamineTT:GetItem()
jcallahan@324 2430 item_id = ItemLinkToID(item_link)
MMOSimca@354 2431 -- GetMerchantItemLink() still ocassionally fails as of Patch 6.0.2. It fails so badly that debug functions cause considerable slowdown.
jcallahan@324 2432 end
jcallahan@324 2433
jcallahan@44 2434 if item_id and item_id > 0 then
jcallahan@44 2435 local price_string = ActualCopperCost(copper_price, merchant_standing)
jcallahan@5 2436
jcallahan@68 2437 local num_lines = DatamineTT:NumLines()
jcallahan@68 2438
jcallahan@68 2439 for line_index = 1, num_lines do
jcallahan@68 2440 local current_line = _G["WDPDatamineTTTextLeft" .. line_index]
jcallahan@68 2441
jcallahan@68 2442 if not current_line then
jcallahan@68 2443 break
jcallahan@68 2444 end
jcallahan@68 2445 local faction, reputation = current_line:GetText():match(ITEM_REQ_REPUTATION_MATCH)
jcallahan@68 2446
jcallahan@68 2447 if faction or reputation then
jcallahan@68 2448 DBEntry("items", item_id).req_reputation = ("%s:%s"):format(faction:gsub("-", ""), reputation:upper())
jcallahan@68 2449 break
jcallahan@68 2450 end
jcallahan@68 2451 end
jcallahan@68 2452
jcallahan@87 2453 for line_index = 1, num_lines do
jcallahan@87 2454 local current_line = _G["WDPDatamineTTTextLeft" .. line_index]
jcallahan@87 2455
jcallahan@87 2456 if not current_line then
jcallahan@87 2457 break
jcallahan@87 2458 end
jcallahan@87 2459 local line_text = current_line:GetText()
jcallahan@87 2460 local quest_name = line_text:match(ITEM_REQ_QUEST_MATCH1) or line_text:match(ITEM_REQ_QUEST_MATCH2)
jcallahan@87 2461
jcallahan@87 2462 if quest_name then
jcallahan@87 2463 DBEntry("items", item_id).req_quest = ("%s"):format(quest_name:gsub("(.+): ", ""), quest_name)
jcallahan@87 2464 break
jcallahan@87 2465 end
jcallahan@87 2466 end
jcallahan@87 2467
jcallahan@44 2468 if extended_cost then
jcallahan@53 2469 local battleground_rating = 0
jcallahan@53 2470 local personal_rating = 0
jcallahan@53 2471 local required_season_amount
jcallahan@5 2472
jcallahan@68 2473 for line_index = 1, num_lines do
jcallahan@44 2474 local current_line = _G["WDPDatamineTTTextLeft" .. line_index]
jcallahan@5 2475
jcallahan@44 2476 if not current_line then
jcallahan@44 2477 break
jcallahan@44 2478 end
jcallahan@53 2479 required_season_amount = current_line:GetText():match("Requires earning a total of (%d+)\n(.-) for the season.")
jcallahan@5 2480
jcallahan@44 2481 for match_index = 1, #POINT_MATCH_PATTERNS do
jcallahan@44 2482 local match1, match2 = current_line:GetText():match(POINT_MATCH_PATTERNS[match_index])
jcallahan@53 2483 personal_rating = personal_rating + (match1 or 0)
jcallahan@53 2484 battleground_rating = battleground_rating + (match2 or 0)
jcallahan@5 2485
jcallahan@44 2486 if match1 or match2 then
jcallahan@44 2487 break
jcallahan@44 2488 end
jcallahan@44 2489 end
jcallahan@5 2490 end
jcallahan@44 2491 local currency_list = {}
jcallahan@44 2492 local item_count = _G.GetMerchantItemCostInfo(item_index)
jcallahan@50 2493
mmosimca@518 2494 -- Keeping this around in case Blizzard makes the two ratings (personal and battleground) diverge at some point
mmosimca@518 2495 --price_string = ("%s:%s:%s:%s"):format(price_string, battleground_rating, personal_rating, required_season_amount or 0)
jcallahan@53 2496 price_string = ("%s:%s:%s"):format(price_string, personal_rating, required_season_amount or 0)
jcallahan@5 2497
jcallahan@44 2498 for cost_index = 1, item_count do
jcallahan@324 2499 -- The third return (Blizz calls "currency_link") of GetMerchantItemCostItem only returns item links as of Patch 5.3.0.
MMOSimca@540 2500 local texture_id, amount_required, hyperlink, name = _G.GetMerchantItemCostItem(item_index, cost_index)
mmosimca@496 2501
mmosimca@519 2502 -- Try to get item ID
MMOSimca@540 2503 local item_id = ItemLinkToID(hyperlink)
mmosimca@519 2504
mmosimca@519 2505 -- FUTURE: At some point, we should make the output from these two cases (item_id vs currency_id) slightly different, so that WoWDB doesn't have to guess if it is a currency or item
mmosimca@519 2506 -- Handle cases when the additional cost is another item
mmosimca@519 2507 if item_id and item_id > 0 then
mmosimca@519 2508 currency_list[#currency_list + 1] = ("(%s:%d)"):format(amount_required, item_id)
mmosimca@519 2509 else
MMOSimca@540 2510 -- Try to get currency ID
MMOSimca@540 2511 local currency_id = CurrencyLinkToID(hyperlink)
mmosimca@519 2512 if currency_id and currency_id > 0 then
mmosimca@501 2513 currency_list[#currency_list + 1] = ("(%s:%d)"):format(amount_required, currency_id)
MMOSimca@540 2514 else
MMOSimca@540 2515 Debug("UpdateMerchantItems: Failed to get item ID and failed to get currency ID for item index %d and cost index %d", item_index, cost_index)
mmosimca@501 2516 end
jcallahan@44 2517 end
jcallahan@44 2518 end
jcallahan@44 2519
jcallahan@44 2520 for currency_index = 1, #currency_list do
jcallahan@44 2521 price_string = ("%s:%s"):format(price_string, currency_list[currency_index])
jcallahan@5 2522 end
jcallahan@5 2523 end
jcallahan@133 2524 current_merchant.sells[item_id] = ("%s:%s:[%s]"):format(num_available, stack_size, price_string)
jcallahan@44 2525 end
jcallahan@44 2526 end
jcallahan@5 2527
jcallahan@44 2528 if _G.CanMerchantRepair() then
jcallahan@133 2529 current_merchant.can_repair = true
jcallahan@5 2530 end
jcallahan@57 2531 _G.SetMerchantFilter(current_filters)
jcallahan@57 2532 _G.MerchantFrame_Update()
jcallahan@4 2533 end
jcallahan@44 2534 end -- do-block
jcallahan@4 2535
jcallahan@89 2536
jcallahan@92 2537 function WDP:PET_BAR_UPDATE(event_name)
MMOSimca@336 2538 if not private.NON_LOOT_SPELL_LABELS[current_action.spell_label] then
jcallahan@25 2539 return
jcallahan@25 2540 end
jcallahan@34 2541 local unit_type, unit_idnum = ParseGUID(_G.UnitGUID("pet"))
jcallahan@25 2542
jcallahan@171 2543 if not unit_idnum or not UnitTypeIsNPC(unit_type) then
jcallahan@25 2544 return
jcallahan@25 2545 end
jcallahan@29 2546 NPCEntry(unit_idnum).mind_control = true
jcallahan@122 2547 table.wipe(current_action)
jcallahan@25 2548 end
jcallahan@25 2549
jcallahan@25 2550
MMOSimca@368 2551 -- This function produces data currently unused by wowdb.com, and it causes unneeded bloat in the raw lua DB.
MMOSimca@442 2552 --[[local LPJ = LibStub("LibPetJournal-2.0")
MMOSimca@442 2553 function WDP:PET_JOURNAL_LIST_UPDATE(event_name)
MMOSimca@346 2554 if DEBUGGING then
jcallahan@309 2555 return
jcallahan@309 2556 end
jcallahan@309 2557
jcallahan@115 2558 local num_pets = LPJ:NumPets()
jcallahan@115 2559
jcallahan@115 2560 for index, pet_id in LPJ:IteratePetIDs() do
jcallahan@115 2561 local _, _, is_owned, _, level, _, _, name, icon, pet_type, npc_id, _, _, is_wild = _G.C_PetJournal.GetPetInfoByIndex(index)
jcallahan@115 2562
jcallahan@115 2563 if is_owned then
jcallahan@115 2564 local health, max_health, attack, speed, rarity = _G.C_PetJournal.GetPetStats(pet_id)
jcallahan@115 2565
jcallahan@139 2566 if rarity then
jcallahan@139 2567 local rarity_name = _G["BATTLE_PET_BREED_QUALITY" .. rarity]
jcallahan@139 2568 local npc = NPCEntry(npc_id)
jcallahan@139 2569 npc.wild_pet = is_wild or nil
jcallahan@139 2570 npc.battle_pet_data = npc.battle_pet_data or {}
jcallahan@139 2571 npc.battle_pet_data[rarity_name] = npc.battle_pet_data[rarity_name] or {}
jcallahan@139 2572 npc.battle_pet_data[rarity_name]["level_" .. level] = npc.battle_pet_data[rarity_name]["level_" .. level] or {}
jcallahan@139 2573
jcallahan@139 2574 local data = npc.battle_pet_data[rarity_name]["level_" .. level]
jcallahan@139 2575 data.max_health = max_health
jcallahan@139 2576 data.attack = attack
jcallahan@139 2577 data.speed = speed
jcallahan@139 2578 end
jcallahan@115 2579 end
jcallahan@115 2580 end
MMOSimca@368 2581 end]]--
jcallahan@115 2582
jcallahan@115 2583
jcallahan@156 2584 function WDP:PLAYER_REGEN_DISABLED(event_name)
jcallahan@156 2585 private.in_combat = true
jcallahan@156 2586 end
jcallahan@156 2587
jcallahan@156 2588
jcallahan@156 2589 function WDP:PLAYER_REGEN_ENABLED(event_name)
jcallahan@156 2590 private.in_combat = nil
jcallahan@156 2591 end
jcallahan@156 2592
jcallahan@156 2593
jcallahan@118 2594 function WDP:PLAYER_TARGET_CHANGED(event_name)
jcallahan@215 2595 if not TargetedNPC() then
jcallahan@118 2596 return
jcallahan@2 2597 end
jcallahan@151 2598 current_action.target_type = AF.NPC
jcallahan@118 2599 self:UpdateTargetLocation()
jcallahan@118 2600 end
jcallahan@2 2601
jcallahan@89 2602
jcallahan@12 2603 do
jcallahan@12 2604 local function UpdateQuestJuncture(point)
jcallahan@12 2605 local unit_name = _G.UnitName("questnpc")
jcallahan@9 2606
jcallahan@12 2607 if not unit_name then
jcallahan@12 2608 return
jcallahan@12 2609 end
jcallahan@34 2610 local unit_type, unit_id = ParseGUID(_G.UnitGUID("questnpc"))
MMOSimca@351 2611 Debug("UpdateQuestJuncture: Updating quest juncture for %s.", ("%s:%d"):format(private.UNIT_TYPE_NAMES[unit_type], unit_id))
jcallahan@12 2612 if unit_type == private.UNIT_TYPES.OBJECT then
jcallahan@38 2613 UpdateDBEntryLocation("objects", unit_id)
jcallahan@12 2614 end
jcallahan@19 2615 local quest = DBEntry("quests", _G.GetQuestID())
jcallahan@12 2616 quest[point] = quest[point] or {}
MMOSimca@329 2617 quest[point][("%s:%d"):format(private.UNIT_TYPE_NAMES[unit_type], unit_id)] = true
jcallahan@24 2618
jcallahan@24 2619 return quest
jcallahan@12 2620 end
jcallahan@10 2621
jcallahan@12 2622
jcallahan@92 2623 function WDP:QUEST_COMPLETE(event_name)
jcallahan@97 2624 local quest = UpdateQuestJuncture("end")
catherton@465 2625
MMOSimca@446 2626 if not quest then
MMOSimca@446 2627 return
MMOSimca@446 2628 end
jcallahan@97 2629
jcallahan@112 2630 if ALLOWED_LOCALES[CLIENT_LOCALE] then
jcallahan@112 2631 quest.reward_text = ReplaceKeywords(_G.GetRewardText())
jcallahan@112 2632 end
jcallahan@67 2633 -- Make sure the quest NPC isn't erroneously recorded as giving reputation for quests which award it.
jcallahan@177 2634 ClearKilledNPC()
jcallahan@10 2635 end
jcallahan@10 2636
jcallahan@12 2637
jcallahan@92 2638 function WDP:QUEST_DETAIL(event_name)
jcallahan@24 2639 local quest = UpdateQuestJuncture("begin")
jcallahan@24 2640
jcallahan@46 2641 if not quest then
jcallahan@46 2642 return
jcallahan@46 2643 end
jcallahan@24 2644 quest.classes = quest.classes or {}
jcallahan@27 2645 quest.classes[PLAYER_CLASS] = true
jcallahan@24 2646
jcallahan@24 2647 quest.races = quest.races or {}
jcallahan@100 2648 quest.races[(PLAYER_RACE == "Pandaren") and ("%s_%s"):format(PLAYER_RACE, PLAYER_FACTION or "Neutral") or PLAYER_RACE] = true
jcallahan@10 2649 end
jcallahan@12 2650 end -- do-block
jcallahan@9 2651
jcallahan@9 2652
jcallahan@92 2653 function WDP:QUEST_LOG_UPDATE(event_name)
jcallahan@38 2654 local selected_quest = _G.GetQuestLogSelection() -- Save current selection to be restored when we're done.
jcallahan@36 2655 local entry_index, processed_quests = 1, 0
jcallahan@36 2656 local _, num_quests = _G.GetNumQuestLogEntries()
jcallahan@36 2657
jcallahan@36 2658 while processed_quests <= num_quests do
MMOSimca@329 2659 local _, _, _, is_header, _, _, _, quest_id = _G.GetQuestLogTitle(entry_index)
jcallahan@36 2660
jcallahan@84 2661 if quest_id == 0 then
jcallahan@84 2662 processed_quests = processed_quests + 1
jcallahan@84 2663 elseif not is_header then
jcallahan@36 2664 _G.SelectQuestLogEntry(entry_index);
jcallahan@36 2665
jcallahan@36 2666 local quest = DBEntry("quests", quest_id)
jcallahan@36 2667 quest.timer = _G.GetQuestLogTimeLeft()
jcallahan@37 2668 quest.can_share = _G.GetQuestLogPushable() and true or nil
jcallahan@36 2669 processed_quests = processed_quests + 1
jcallahan@36 2670 end
jcallahan@36 2671 entry_index = entry_index + 1
jcallahan@36 2672 end
jcallahan@36 2673 _G.SelectQuestLogEntry(selected_quest)
jcallahan@4 2674 self:UnregisterEvent("QUEST_LOG_UPDATE")
jcallahan@4 2675 end
jcallahan@4 2676
jcallahan@4 2677
jcallahan@97 2678 function WDP:QUEST_PROGRESS(event_name)
jcallahan@112 2679 if not ALLOWED_LOCALES[CLIENT_LOCALE] then
jcallahan@112 2680 return
jcallahan@112 2681 end
jcallahan@97 2682 DBEntry("quests", _G.GetQuestID()).progress_text = ReplaceKeywords(_G.GetProgressText())
jcallahan@97 2683 end
jcallahan@97 2684
jcallahan@97 2685
jcallahan@92 2686 function WDP:UNIT_QUEST_LOG_CHANGED(event_name, unit_id)
jcallahan@4 2687 if unit_id ~= "player" then
jcallahan@4 2688 return
jcallahan@4 2689 end
jcallahan@4 2690 self:RegisterEvent("QUEST_LOG_UPDATE")
jcallahan@4 2691 end
jcallahan@4 2692
jcallahan@4 2693
jcallahan@92 2694 do
jcallahan@92 2695 local TRADESKILL_TOOLS = {
jcallahan@92 2696 Anvil = anvil_spell_ids,
jcallahan@92 2697 Forge = forge_spell_ids,
jcallahan@92 2698 }
jcallahan@92 2699
jcallahan@92 2700
jcallahan@167 2701 local function RegisterTools(tradeskill_name, tradeskill_index)
catherton@479 2702 local link = _G.C_TradeSkillUI.GetRecipeLink(tradeskill_index)
catherton@465 2703
MMOSimca@352 2704 if link then
MMOSimca@352 2705 local spell_id = tonumber(link:match("^|c%x%x%x%x%x%x%x%x|H%w+:(%d+)"))
catherton@479 2706 local required_tool = _G.C_TradeSkillUI.GetRecipeTools(tradeskill_index)
MMOSimca@352 2707
MMOSimca@352 2708 if required_tool then
MMOSimca@352 2709 for tool_name, registry in pairs(TRADESKILL_TOOLS) do
MMOSimca@352 2710 if required_tool:find(tool_name) then
MMOSimca@352 2711 registry[spell_id] = true
MMOSimca@352 2712 end
jcallahan@167 2713 end
jcallahan@167 2714 end
jcallahan@167 2715 end
jcallahan@167 2716 end
jcallahan@167 2717
jcallahan@167 2718
jcallahan@92 2719 function WDP:TRADE_SKILL_SHOW(event_name)
catherton@479 2720 local profession_name, prof_level = _G.C_TradeSkillUI.GetTradeSkillLine()
jcallahan@92 2721
jcallahan@92 2722 if profession_name == _G.UNKNOWN then
jcallahan@92 2723 return
jcallahan@92 2724 end
jcallahan@167 2725 TradeSkillExecutePer(RegisterTools)
jcallahan@92 2726 end
jcallahan@92 2727 end -- do-block
jcallahan@92 2728
jcallahan@92 2729
jcallahan@167 2730 function WDP:TRAINER_CLOSED(event_name)
jcallahan@167 2731 private.trainer_shown = nil
jcallahan@167 2732 end
jcallahan@167 2733
jcallahan@167 2734
jcallahan@92 2735 function WDP:TRAINER_SHOW(event_name)
jcallahan@232 2736 local unit_type, unit_idnum = ParseGUID(_G.UnitGUID("npc"))
jcallahan@164 2737 local trainer = NPCEntry(unit_idnum)
jcallahan@58 2738
jcallahan@164 2739 if not trainer then
jcallahan@58 2740 return
jcallahan@58 2741 end
jcallahan@331 2742 local _, trainer_standing = UnitFactionStanding("npc")
jcallahan@164 2743 trainer.teaches = trainer.teaches or {}
jcallahan@27 2744
jcallahan@167 2745 private.trainer_shown = true
jcallahan@167 2746
jcallahan@27 2747 -- Get the initial trainer filters
MMOSimca@344 2748 local available = _G.GetTrainerServiceTypeFilter("available") and 1 or 0
MMOSimca@344 2749 local unavailable = _G.GetTrainerServiceTypeFilter("unavailable") and 1 or 0
MMOSimca@344 2750 local used = _G.GetTrainerServiceTypeFilter("used") and 1 or 0
jcallahan@27 2751
jcallahan@27 2752 -- Clear the trainer filters
MMOSimca@344 2753 _G.SetTrainerServiceTypeFilter("available", 1)
MMOSimca@344 2754 _G.SetTrainerServiceTypeFilter("unavailable", 1)
MMOSimca@344 2755 _G.SetTrainerServiceTypeFilter("used", 1)
jcallahan@27 2756
jcallahan@27 2757 for index = 1, _G.GetNumTrainerServices(), 1 do
jcallahan@27 2758 local spell_name, rank_name, _, _, required_level = _G.GetTrainerServiceInfo(index)
jcallahan@27 2759
jcallahan@27 2760 if spell_name then
jcallahan@27 2761 DatamineTT:ClearLines()
jcallahan@27 2762 DatamineTT:SetTrainerService(index)
jcallahan@27 2763
atcaleb@570 2764 local _, spell_id = DatamineTT:GetSpell()
jcallahan@27 2765
jcallahan@43 2766 if spell_id then
jcallahan@164 2767 local class_professions = trainer.teaches[PLAYER_CLASS]
jcallahan@164 2768
jcallahan@164 2769 if not class_professions then
jcallahan@164 2770 trainer.teaches[PLAYER_CLASS] = {}
jcallahan@164 2771 class_professions = trainer.teaches[PLAYER_CLASS]
jcallahan@164 2772 end
jcallahan@43 2773 local profession, min_skill = _G.GetTrainerServiceSkillReq(index)
jcallahan@43 2774 profession = profession or "General"
jcallahan@43 2775
jcallahan@164 2776 local profession_skills = class_professions[profession]
jcallahan@43 2777
jcallahan@43 2778 if not profession_skills then
jcallahan@43 2779 class_professions[profession] = {}
jcallahan@43 2780 profession_skills = class_professions[profession]
jcallahan@43 2781 end
jcallahan@173 2782 profession_skills[spell_id] = ("%d:%d:%d"):format(required_level, min_skill, _G.GetTrainerServiceCost(index))
jcallahan@27 2783 end
jcallahan@27 2784 end
jcallahan@27 2785 end
jcallahan@27 2786 -- Reset the filters to what they were before
MMOSimca@344 2787 _G.SetTrainerServiceTypeFilter("available", available or 0)
MMOSimca@344 2788 _G.SetTrainerServiceTypeFilter("unavailable", unavailable or 0)
MMOSimca@344 2789 _G.SetTrainerServiceTypeFilter("used", used or 0)
jcallahan@27 2790 end
jcallahan@27 2791
jcallahan@27 2792
jcallahan@1 2793 function WDP:UNIT_SPELLCAST_SENT(event_name, unit_id, spell_name, spell_rank, target_name, spell_line)
jcallahan@1 2794 if private.tracked_line or unit_id ~= "player" then
jcallahan@1 2795 return
jcallahan@1 2796 end
jcallahan@1 2797 local spell_label = private.SPELL_LABELS_BY_NAME[spell_name]
jcallahan@1 2798
jcallahan@1 2799 if not spell_label then
jcallahan@1 2800 return
jcallahan@1 2801 end
jcallahan@306 2802
MMOSimca@343 2803 Debug("UNIT_SPELLCAST_SENT: %s was cast.", spell_name)
jcallahan@150 2804 local item_name, item_link = _G.GameTooltip:GetItem()
jcallahan@150 2805 local unit_name, unit_id = _G.GameTooltip:GetUnit()
jcallahan@1 2806
jcallahan@150 2807 if not unit_name and _G.UnitName("target") == target_name then
jcallahan@150 2808 unit_name = target_name
jcallahan@150 2809 unit_id = "target"
jcallahan@1 2810 end
jcallahan@1 2811 local spell_flags = private.SPELL_FLAGS_BY_LABEL[spell_label]
jcallahan@28 2812 local zone_name, area_id, x, y, map_level, instance_token = CurrentLocationData()
MMOSimca@328 2813 if not (zone_name and area_id and x and y and map_level) then
mmosimca@508 2814 if not (_G.IsInInstance()) then
mmosimca@508 2815 Debug("%s: Missing current location data - %s, %s, %s, %s, %s.", event_name, tostring(zone_name), tostring(area_id), tostring(x), tostring(y), tostring(map_level))
mmosimca@508 2816 end
MMOSimca@328 2817 return
MMOSimca@328 2818 end
jcallahan@28 2819
jcallahan@205 2820 table.wipe(current_action)
jcallahan@122 2821 current_action.instance_token = instance_token
jcallahan@122 2822 current_action.map_level = map_level
jcallahan@122 2823 current_action.x = x
jcallahan@122 2824 current_action.y = y
jcallahan@122 2825 current_action.zone_data = ("%s:%d"):format(zone_name, area_id)
jcallahan@122 2826 current_action.spell_label = spell_label
jcallahan@105 2827
jcallahan@105 2828 if not private.NON_LOOT_SPELL_LABELS[spell_label] then
jcallahan@122 2829 current_action.loot_label = spell_label:lower()
jcallahan@105 2830 end
jcallahan@1 2831
jcallahan@151 2832 if unit_name and unit_name == target_name and not item_name then
jcallahan@16 2833 if bit.band(spell_flags, AF.NPC) == AF.NPC then
jcallahan@150 2834 if not unit_id or unit_name ~= target_name then
jcallahan@16 2835 return
jcallahan@16 2836 end
jcallahan@123 2837 current_action.target_type = AF.NPC
jcallahan@16 2838 end
jcallahan@16 2839 elseif bit.band(spell_flags, AF.ITEM) == AF.ITEM then
jcallahan@123 2840 current_action.target_type = AF.ITEM
jcallahan@16 2841
jcallahan@150 2842 if item_name and item_name == target_name then
jcallahan@150 2843 current_action.identifier = ItemLinkToID(item_link)
jcallahan@16 2844 elseif target_name and target_name ~= "" then
jcallahan@331 2845 local _, item_link = _G.GetItemInfo(target_name)
jcallahan@331 2846 current_action.identifier = ItemLinkToID(item_link)
jcallahan@16 2847 end
jcallahan@150 2848 elseif not item_name and not unit_name then
jcallahan@1 2849 if bit.band(spell_flags, AF.OBJECT) == AF.OBJECT then
jcallahan@17 2850 if target_name == "" then
jcallahan@17 2851 return
jcallahan@17 2852 end
jcallahan@122 2853 current_action.object_name = target_name
jcallahan@123 2854 current_action.target_type = AF.OBJECT
jcallahan@1 2855 elseif bit.band(spell_flags, AF.ZONE) == AF.ZONE then
jcallahan@123 2856 current_action.target_type = AF.ZONE
jcallahan@1 2857 end
jcallahan@1 2858 end
jcallahan@1 2859 private.tracked_line = spell_line
jcallahan@0 2860 end
jcallahan@0 2861
jcallahan@94 2862
MMOSimca@393 2863 -- Triggered by bonus roll prompts, disenchant prompts, and in a few other rare circumstances
jcallahan@312 2864 function WDP:SPELL_CONFIRMATION_PROMPT(event_name, spell_id, confirm_type, text, duration, currency_id_cost)
jcallahan@306 2865 if private.RAID_BOSS_BONUS_SPELL_ID_TO_NPC_ID_MAP[spell_id] then
jcallahan@306 2866 ClearKilledBossID()
jcallahan@306 2867 ClearLootToastContainerID()
MMOSimca@387 2868 raid_boss_id = private.RAID_BOSS_BONUS_SPELL_ID_TO_NPC_ID_MAP[spell_id]
jcallahan@306 2869 else
MMOSimca@336 2870 Debug("%s: Spell ID %d is not a known raid boss 'Bonus' spell.", event_name, spell_id)
jcallahan@306 2871 return
jcallahan@1 2872 end
jcallahan@306 2873
jcallahan@324 2874 -- Assign existing loot data to boss if it exists
jcallahan@307 2875 if loot_toast_data then
MMOSimca@427 2876 local npc = NPCEntry(raid_boss_id)
MMOSimca@427 2877 if npc then
MMOSimca@427 2878 -- Create needed npc fields if required
MMOSimca@427 2879 local loot_label = "drops"
MMOSimca@427 2880 local encounter_data = npc:EncounterData(InstanceDifficultyToken())
MMOSimca@427 2881 encounter_data[loot_label] = encounter_data[loot_label] or {}
MMOSimca@427 2882 encounter_data.loot_counts = encounter_data.loot_counts or {}
MMOSimca@427 2883
MMOSimca@427 2884 for index = 1, #loot_toast_data do
MMOSimca@427 2885 local data = loot_toast_data[index]
MMOSimca@427 2886 local loot_type = data[1]
MMOSimca@427 2887 local hyperlink = data[2]
MMOSimca@427 2888 local quantity = data[3]
MMOSimca@427 2889
MMOSimca@427 2890 if loot_type == "item" then
MMOSimca@427 2891 local item_id = ItemLinkToID(hyperlink)
MMOSimca@427 2892 Debug("%s: Assigned stored item loot data - %s - %d:%d", event_name, hyperlink, item_id, quantity)
MMOSimca@427 2893 table.insert(encounter_data[loot_label], ("%d:%d"):format(item_id, quantity))
MMOSimca@427 2894 elseif loot_type == "money" then
MMOSimca@427 2895 Debug("%s: Assigned stored money loot data - money:%d", event_name, quantity)
MMOSimca@427 2896 table.insert(encounter_data[loot_label], ("money:%d"):format(quantity))
MMOSimca@427 2897 elseif loot_type == "currency" then
mmosimca@496 2898 local currency_id = CurrencyLinkToID(hyperlink)
mmosimca@496 2899 Debug("%s: Assigned stored currency loot data - %s - currency:%d (%d)", event_name, hyperlink, currency_id, quantity)
mmosimca@496 2900 table.insert(encounter_data[loot_label], ("currency:%d:%d"):format(quantity, currency_id))
MMOSimca@427 2901 end
jcallahan@317 2902 end
jcallahan@317 2903
MMOSimca@427 2904 if not boss_loot_toasting[raid_boss_id] then
MMOSimca@427 2905 encounter_data.loot_counts[loot_label] = (encounter_data.loot_counts[loot_label] or 0) + 1
MMOSimca@427 2906 boss_loot_toasting[raid_boss_id] = true -- Do not count further loots until timer expires or another boss is killed
jcallahan@307 2907 end
MMOSimca@427 2908 else
MMOSimca@427 2909 Debug("%s: NPC is nil, but we have stored loot data...", event_name)
jcallahan@307 2910 end
jcallahan@307 2911 end
jcallahan@307 2912
jcallahan@307 2913 ClearLootToastData()
MMOSimca@427 2914 killed_boss_id_timer_handle = C_Timer.NewTimer(5, ClearKilledBossID)
jcallahan@306 2915 end
jcallahan@306 2916
jcallahan@306 2917
jcallahan@306 2918 function WDP:UNIT_SPELLCAST_SUCCEEDED(event_name, unit_id, spell_name, spell_rank, spell_line, spell_id)
jcallahan@306 2919 if unit_id ~= "player" then
jcallahan@306 2920 return
jcallahan@306 2921 end
jcallahan@306 2922 private.tracked_line = nil
jcallahan@306 2923 private.previous_spell_id = spell_id
jcallahan@306 2924
MMOSimca@393 2925 -- For spells cast when Logging
MMOSimca@345 2926 if private.LOGGING_SPELL_ID_TO_OBJECT_ID_MAP[spell_id] then
MMOSimca@345 2927 last_timber_spell_id = spell_id
MMOSimca@351 2928 UpdateDBEntryLocation("objects", ("OPENING:%s"):format(private.LOGGING_SPELL_ID_TO_OBJECT_ID_MAP[spell_id]))
MMOSimca@345 2929 return
MMOSimca@345 2930 end
MMOSimca@345 2931
MMOSimca@393 2932 -- For spells cast by items that always trigger loot toasts
MMOSimca@381 2933 if private.LOOT_TOAST_CONTAINER_SPELL_ID_TO_ITEM_ID_MAP[spell_id] then
jcallahan@306 2934 ClearKilledBossID()
jcallahan@306 2935 ClearLootToastContainerID()
jcallahan@307 2936 ClearLootToastData()
jcallahan@306 2937
MMOSimca@387 2938 loot_toast_container_id = private.LOOT_TOAST_CONTAINER_SPELL_ID_TO_ITEM_ID_MAP[spell_id]
MMOSimca@383 2939 loot_toast_container_timer_handle = C_Timer.NewTimer(1, ClearLootToastContainerID) -- we need to assign a handle here to cancel it later
MMOSimca@345 2940 return
jcallahan@306 2941 end
jcallahan@306 2942
MMOSimca@393 2943 -- For spells cast by items that don't usually trigger loot toasts
catherton@473 2944 if not block_chat_loot_data and (private.DELAYED_CONTAINER_SPELL_ID_TO_ITEM_ID_MAP[spell_id] or (private.DELAYED_CONTAINER_SPELL_ID_TO_ITEM_ID_BY_CLASS_ID_MAP[spell_id] and private.DELAYED_CONTAINER_SPELL_ID_TO_ITEM_ID_BY_CLASS_ID_MAP[spell_id][PLAYER_CLASS_ID])) then
MMOSimca@347 2945 -- Set up timer
MMOSimca@393 2946 ClearChatLootData()
MMOSimca@393 2947 Debug("%s: Beginning chat-based loot timer for spellID %d", event_name, spell_id)
MMOSimca@411 2948 chat_loot_timer_handle = C_Timer.NewTimer(1.5, ClearChatLootData)
catherton@473 2949 if (private.DELAYED_CONTAINER_SPELL_ID_TO_ITEM_ID_BY_CLASS_ID_MAP[spell_id] and private.DELAYED_CONTAINER_SPELL_ID_TO_ITEM_ID_BY_CLASS_ID_MAP[spell_id][PLAYER_CLASS_ID]) then
catherton@473 2950 chat_loot_data.identifier = private.DELAYED_CONTAINER_SPELL_ID_TO_ITEM_ID_BY_CLASS_ID_MAP[spell_id][PLAYER_CLASS_ID]
MMOSimca@454 2951 else
MMOSimca@454 2952 chat_loot_data.identifier = private.DELAYED_CONTAINER_SPELL_ID_TO_ITEM_ID_MAP[spell_id]
MMOSimca@454 2953 end
MMOSimca@347 2954 return
MMOSimca@347 2955 end
MMOSimca@347 2956
mmosimca@520 2957 -- For Ephemeral Crystals (uses a combination of mouseover text and a 'Update Interactions' spell cast to detect the object - this is incredibly hacky but there is no alternative)
mmosimca@520 2958 local text = _G["GameTooltipTextLeft1"] and _G["GameTooltipTextLeft1"]:GetText() or nil
mmosimca@520 2959 if spell_id == SPELL_ID_UPDATE_INTERACTIONS and text and text == "Ephemeral Crystal" then
mmosimca@522 2960 Debug("%s: Detected location for an Ephemeral Crystal.", event_name)
mmosimca@520 2961 for index = 1, #private.EPHEMERAL_CRYSTAL_OBJECT_IDS do
mmosimca@520 2962 UpdateDBEntryLocation("objects", private.EPHEMERAL_CRYSTAL_OBJECT_IDS[index])
mmosimca@520 2963 end
mmosimca@520 2964 end
mmosimca@520 2965
jcallahan@306 2966 if anvil_spell_ids[spell_id] then
jcallahan@306 2967 UpdateDBEntryLocation("objects", OBJECT_ID_ANVIL)
jcallahan@306 2968 elseif forge_spell_ids[spell_id] then
jcallahan@306 2969 UpdateDBEntryLocation("objects", OBJECT_ID_FORGE)
jcallahan@306 2970 elseif spell_name:match("^Harvest.+") then
jcallahan@306 2971 killed_npc_id = current_target_id
MMOSimca@343 2972 private.harvesting = true -- Used to track which NPCs can be harvested (can we get this from CreatureCache instead?)
jcallahan@306 2973 end
jcallahan@306 2974 end
jcallahan@0 2975
jcallahan@90 2976
jcallahan@1 2977 function WDP:HandleSpellFailure(event_name, unit_id, spell_name, spell_rank, spell_line, spell_id)
jcallahan@1 2978 if unit_id ~= "player" then
jcallahan@1 2979 return
jcallahan@1 2980 end
jcallahan@0 2981
jcallahan@1 2982 if private.tracked_line == spell_line then
jcallahan@1 2983 private.tracked_line = nil
jcallahan@1 2984 end
jcallahan@147 2985 table.wipe(current_action)
jcallahan@0 2986 end
jcallahan@90 2987
jcallahan@90 2988
jcallahan@90 2989 do
jcallahan@90 2990 local function SetUnitField(field, required_type)
jcallahan@90 2991 local unit_type, unit_idnum = ParseGUID(_G.UnitGUID("npc"))
jcallahan@90 2992
jcallahan@90 2993 if not unit_idnum or (required_type and unit_type ~= required_type) then
jcallahan@90 2994 return
jcallahan@90 2995 end
jcallahan@90 2996
jcallahan@171 2997 if UnitTypeIsNPC(unit_type) then
jcallahan@90 2998 NPCEntry(unit_idnum)[field] = true
jcallahan@90 2999 elseif unit_type == private.UNIT_TYPES.OBJECT then
jcallahan@90 3000 DBEntry("objects", unit_idnum)[field] = true
jcallahan@93 3001 UpdateDBEntryLocation("objects", unit_idnum)
jcallahan@90 3002 end
jcallahan@90 3003 end
jcallahan@90 3004
jcallahan@90 3005
jcallahan@90 3006 function WDP:AUCTION_HOUSE_SHOW(event_name)
MMOSimca@436 3007 WDP:StopChatLootRecording(event_name)
jcallahan@90 3008 SetUnitField("auctioneer", private.UNIT_TYPES.NPC)
jcallahan@90 3009 end
jcallahan@90 3010
jcallahan@90 3011
jcallahan@90 3012 function WDP:BANKFRAME_OPENED(event_name)
MMOSimca@436 3013 WDP:StopChatLootRecording(event_name)
jcallahan@90 3014 SetUnitField("banker", private.UNIT_TYPES.NPC)
jcallahan@90 3015 end
jcallahan@90 3016
jcallahan@90 3017
jcallahan@90 3018 function WDP:BATTLEFIELDS_SHOW(event_name)
jcallahan@90 3019 SetUnitField("battlemaster", private.UNIT_TYPES.NPC)
jcallahan@90 3020 end
jcallahan@90 3021
jcallahan@90 3022
jcallahan@323 3023 local GOSSIP_SHOW_FUNCS = {
jcallahan@323 3024 [private.UNIT_TYPES.NPC] = function(unit_idnum)
jcallahan@323 3025 local gossip_options = { _G.GetGossipOptions() }
jcallahan@323 3026
jcallahan@323 3027 for index = 2, #gossip_options, 2 do
jcallahan@323 3028 if gossip_options[index] == "binder" then
jcallahan@323 3029 SetUnitField("innkeeper", private.UNIT_TYPES.NPC)
jcallahan@323 3030 return
jcallahan@323 3031 end
jcallahan@323 3032 end
jcallahan@323 3033 end,
jcallahan@323 3034 [private.UNIT_TYPES.OBJECT] = function(unit_idnum)
jcallahan@323 3035 UpdateDBEntryLocation("objects", unit_idnum)
jcallahan@323 3036 end,
jcallahan@323 3037 }
jcallahan@323 3038
jcallahan@323 3039
jcallahan@92 3040 function WDP:GOSSIP_SHOW(event_name)
MMOSimca@436 3041 WDP:StopChatLootRecording(event_name)
jcallahan@323 3042 local unit_type, unit_idnum = ParseGUID(_G.UnitGUID("npc"))
jcallahan@323 3043 if not unit_idnum then
jcallahan@323 3044 return
jcallahan@323 3045 end
jcallahan@323 3046
jcallahan@323 3047 if GOSSIP_SHOW_FUNCS[unit_type] then
jcallahan@323 3048 GOSSIP_SHOW_FUNCS[unit_type](unit_idnum)
jcallahan@90 3049 end
jcallahan@90 3050 end
jcallahan@90 3051
jcallahan@90 3052
jcallahan@93 3053 function WDP:GUILDBANKFRAME_OPENED(event_name)
MMOSimca@436 3054 WDP:StopChatLootRecording(event_name)
jcallahan@93 3055 SetUnitField("guild_bank", private.UNIT_TYPES.OBJECT)
jcallahan@93 3056 end
jcallahan@93 3057
jcallahan@93 3058
jcallahan@189 3059 function WDP:ITEM_UPGRADE_MASTER_OPENED(event_name)
jcallahan@189 3060 SetUnitField("item_upgrade_master", private.UNIT_TYPES.NPC)
jcallahan@189 3061 end
jcallahan@189 3062
jcallahan@189 3063
jcallahan@90 3064 function WDP:TAXIMAP_OPENED(event_name)
jcallahan@90 3065 SetUnitField("flight_master", private.UNIT_TYPES.NPC)
jcallahan@90 3066 end
jcallahan@90 3067
jcallahan@90 3068
jcallahan@90 3069 function WDP:TRANSMOGRIFY_OPEN(event_name)
jcallahan@90 3070 SetUnitField("transmogrifier", private.UNIT_TYPES.NPC)
jcallahan@90 3071 end
jcallahan@90 3072
jcallahan@90 3073
jcallahan@90 3074 function WDP:VOID_STORAGE_OPEN(event_name)
jcallahan@90 3075 SetUnitField("void_storage", private.UNIT_TYPES.NPC)
jcallahan@90 3076 end
jcallahan@90 3077 end -- do-block