annotate Main.lua @ 527:09fb00b6325e 7.2.0-2

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