annotate Main.lua @ 552:37eca5900db5

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