annotate Main.lua @ 496:3938b87cfcd3

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