annotate Main.lua @ 553:98eb25ffc0c4 8.0.1-1

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