annotate Main.lua @ 516:36a08bd1523b

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