annotate Main.lua @ 534:31d084e76340

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