annotate Main.lua @ 532:4d8f02877b4f

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