annotate Main.lua @ 535:4a7989584ae8 7.2.0-5

Rewrote World Quest recording to use a less-stateful (continent-based) method of gathering World Quests.
author MMOSimca
date Tue, 18 Apr 2017 20:30:31 -0400
parents 2bb33dbd3d7c
children ec2ee7b48c21
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@535 1006 local function RecordWorldQuestData(quest_id, continent_world_map_id, zone_world_map_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@535 1017 -- Translate continent-level coordinates to zone-coordinates
MMOSimca@535 1018 local oX, oY, dFloor = tonumber(api_data_table.x) or 0, tonumber(api_data_table.y) or 0, tonumber(api_data_table.floor) or 0
MMOSimca@535 1019 local dX, dY = HereBeDragons:TranslateZoneCoordinates(oX, oY, continent_world_map_id, 0, zone_world_map_id, dFloor, false)
MMOSimca@535 1020
MMOSimca@535 1021 -- If the translation failed, stop here
MMOSimca@535 1022 if not dX or not dY then return end
MMOSimca@535 1023
mmosimca@485 1024 local entry = DBEntry("world_quests", quest_id)
mmosimca@485 1025 if entry then
mmosimca@485 1026
mmosimca@485 1027 -- Record location
mmosimca@485 1028 entry["location"] = {}
MMOSimca@535 1029 entry["location"]["world_map_id"] = zone_world_map_id
MMOSimca@535 1030 entry["location"]["x"] = dX * 100
MMOSimca@535 1031 entry["location"]["y"] = dY * 100
MMOSimca@535 1032 entry["location"]["floor"] = dFloor
mmosimca@485 1033
mmosimca@485 1034 -- Record simple rewards (XP, money, artifact XP, honor)
mmosimca@485 1035 entry["rewards"] = {}
mmosimca@485 1036 entry["rewards"]["xp"] = tonumber(_G.GetQuestLogRewardXP(quest_id)) or 0
mmosimca@485 1037 entry["rewards"]["money"] = tonumber(_G.GetQuestLogRewardMoney(quest_id)) or 0
mmosimca@485 1038 local actualXP, scaling = _G.GetQuestLogRewardArtifactXP(quest_id)
mmosimca@485 1039 entry["rewards"]["artifact_xp"] = ("%d:%d"):format(tonumber(actualXP) or 0, tonumber(scaling) or 0)
mmosimca@485 1040 entry["rewards"]["honor"] = tonumber(_G.GetQuestLogRewardHonor(quest_id)) or 0
mmosimca@485 1041
mmosimca@485 1042 -- Record currencies
mmosimca@485 1043 entry["rewards"]["currency_count"] = tonumber(_G.GetNumQuestLogRewardCurrencies(quest_id)) or 0
mmosimca@485 1044
mmosimca@496 1045 -- Create currency rewards sub-table and fill
mmosimca@485 1046 if entry["rewards"]["currency_count"] > 0 then
mmosimca@485 1047 entry["rewards"]["currencies"] = {}
mmosimca@485 1048 for i = 1, entry["rewards"]["currency_count"] do
mmosimca@503 1049 local name, texture_path, quantity, currency_id = _G.GetQuestLogRewardCurrencyInfo(i, quest_id)
mmosimca@503 1050 table.insert(entry["rewards"]["currencies"], ("%d:%d"):format(quantity, currency_id))
mmosimca@485 1051 end
mmosimca@485 1052 end
mmosimca@485 1053
mmosimca@485 1054 -- Record items
mmosimca@485 1055 entry["rewards"]["item_count"] = tonumber(_G.GetNumQuestLogRewards(quest_id)) or 0
mmosimca@485 1056
mmosimca@496 1057 -- Create item rewards sub-table and fill
mmosimca@485 1058 if entry["rewards"]["item_count"] > 0 then
mmosimca@485 1059 entry["rewards"]["items"] = {}
mmosimca@485 1060 for i = 1, entry["rewards"]["item_count"] do
mmosimca@485 1061 local item_name, item_texture, quantity, quality, is_usable, item_id = _G.GetQuestLogRewardInfo(i, quest_id)
mmosimca@485 1062 table.insert(entry["rewards"]["items"], ("%d:%d"):format(item_id, quantity))
mmosimca@485 1063 end
mmosimca@485 1064 end
mmosimca@485 1065
mmosimca@485 1066 -- Record time remaining
mmosimca@485 1067 entry["estimated_end_time"] = _G.GetServerTime() + ((_G.C_TaskQuest.GetQuestTimeLeftMinutes(quest_id) or 0) * 60)
mmosimca@485 1068 end
mmosimca@485 1069 end
mmosimca@485 1070
mmosimca@485 1071
mmosimca@485 1072 function WDP:ProcessWorldQuests()
MMOSimca@532 1073 -- 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 1074 if _G.UnitLevel("player") ~= 110 then return end
mmosimca@485 1075
MMOSimca@535 1076 -- Get current continent and zones in current continent
MMOSimca@535 1077 local continentIndex, continentID = GetCurrentMapContinent()
MMOSimca@535 1078 local continentMaps = { GetMapZones(continentIndex) }
MMOSimca@535 1079
MMOSimca@535 1080 -- Iterate over zones in continent
MMOSimca@535 1081 for i = 1, #continentMaps, 2 do
MMOSimca@535 1082
MMOSimca@535 1083 -- Get data for World Quests
MMOSimca@535 1084 local api_data = C_TaskQuest.GetQuestsForPlayerByMapID(continentMaps[i], continentID);
MMOSimca@535 1085
MMOSimca@535 1086 -- Iterate over the questIDs for each map, doing preload reward requests and creating SavedVariables entries
MMOSimca@535 1087 if api_data and type(api_data) == "table" and #api_data > 0 then
MMOSimca@535 1088 for _, current_data in ipairs(api_data) do
MMOSimca@535 1089
MMOSimca@535 1090 -- Check if we had a valid API table returned to us
MMOSimca@535 1091 if current_data and type(current_data) == "table" and current_data["questId"] then
MMOSimca@535 1092 local quest_id = tonumber(current_data["questId"]) or 0
MMOSimca@535 1093
MMOSimca@535 1094 -- Check if we have quest data
MMOSimca@535 1095 if _G.HaveQuestData(quest_id) then
MMOSimca@535 1096 local tag_id, tag_name, world_quest_type, rarity, is_elite, tradeskill_line_index = _G.GetQuestTagInfo(quest_id)
MMOSimca@535 1097
MMOSimca@535 1098 -- Check for valid questID and whether or not it is a World Quest
MMOSimca@535 1099 if quest_id > 0 and world_quest_type ~= nil then
MMOSimca@535 1100 _G.C_TaskQuest.RequestPreloadRewardData(quest_id)
MMOSimca@535 1101 RecordWorldQuestData(quest_id, continentID, continentMaps[i], current_data)
mmosimca@485 1102 end
mmosimca@485 1103 end
mmosimca@485 1104 end
mmosimca@485 1105 end
mmosimca@485 1106 end
mmosimca@485 1107 end
mmosimca@485 1108 end
mmosimca@485 1109
mmosimca@485 1110
MMOSimca@340 1111 local function RecordItemData(item_id, item_link, process_bonus_ids, durability)
jcallahan@331 1112 local _, _, item_string = item_link:find("^|%x+|H(.+)|h%[.+%]")
jcallahan@219 1113 local item
jcallahan@0 1114
jcallahan@191 1115 if item_string then
MMOSimca@338 1116 local item_results = { (":"):split(item_string) }
MMOSimca@338 1117
MMOSimca@460 1118 local suffix_id = tonumber(item_results[8]) or 0
MMOSimca@462 1119 local unique_id = tonumber(item_results[9]) or 0
MMOSimca@447 1120 --local level = tonumber(item_results[10])
MMOSimca@460 1121 --local specialization_id = tonumber(item_results[11])
catherton@469 1122 --local upgrade_type_id = tonumber(item_results[12])
MMOSimca@460 1123 local instance_difficulty_id = tonumber(item_results[13]) or 0
MMOSimca@460 1124 local num_bonus_ids = tonumber(item_results[14]) or 0
catherton@471 1125 -- 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 1126 local upgrade_value = tonumber(item_results[15 + num_bonus_ids]) or 0
catherton@465 1127
mmosimca@484 1128 local unk_item_field_1 = tonumber(item_results[16 + num_bonus_ids]) or 0
mmosimca@484 1129 local unk_item_field_2 = tonumber(item_results[17 + num_bonus_ids]) or 0
mmosimca@484 1130 --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 1131 --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 1132
MMOSimca@460 1133 -- If there is anything special (non-zero) for this item then we need to make note of everything
catherton@471 1134 if math.max(suffix_id, instance_difficulty_id, num_bonus_ids, upgrade_value) ~= 0 then
MMOSimca@460 1135 item = DBEntry("items", item_id)
MMOSimca@460 1136 item.suffix_id = suffix_id
MMOSimca@460 1137 item.unique_id = bit.band(unique_id, 0xFFFF)
MMOSimca@460 1138 item.instance_difficulty_id = instance_difficulty_id
catherton@471 1139 item.upgrade_value = upgrade_value
MMOSimca@460 1140
MMOSimca@460 1141 if process_bonus_ids then
MMOSimca@460 1142
MMOSimca@460 1143 -- Get ready for bonus IDs
MMOSimca@384 1144 if not item.seen_bonuses then
MMOSimca@384 1145 item.seen_bonuses = {}
MMOSimca@372 1146 end
catherton@465 1147
MMOSimca@460 1148 if num_bonus_ids > 0 then
MMOSimca@460 1149 -- We want the bonus ID combo output to be in the form ["bonusID1:bonusID2:bonusID3"] = true
MMOSimca@460 1150 -- And sorted numerically with the smallest bonusID first
MMOSimca@460 1151 local sorted_bonus_string = ""
MMOSimca@460 1152 local min_bonus_id_array = {}
MMOSimca@460 1153 for iterations = 1, num_bonus_ids do
MMOSimca@460 1154 -- Find minimum of this iteration
MMOSimca@460 1155 local min_bonus_id = 100000
MMOSimca@460 1156 for bonus_index = 1, num_bonus_ids do
MMOSimca@460 1157 local temp_bonus_id = tonumber(item_results[14 + bonus_index])
MMOSimca@460 1158 if temp_bonus_id and (not min_bonus_id_array[temp_bonus_id]) and (temp_bonus_id < min_bonus_id) then
MMOSimca@460 1159 min_bonus_id = temp_bonus_id
MMOSimca@460 1160 end
MMOSimca@460 1161 end
MMOSimca@460 1162
MMOSimca@460 1163 -- Keep track of already processed IDs
MMOSimca@460 1164 min_bonus_id_array[min_bonus_id] = true
MMOSimca@460 1165
MMOSimca@460 1166 -- Build string
MMOSimca@460 1167 if iterations == 1 then
MMOSimca@460 1168 sorted_bonus_string = sorted_bonus_string .. tostring(min_bonus_id)
MMOSimca@460 1169 else
MMOSimca@460 1170 sorted_bonus_string = sorted_bonus_string .. ":" .. tostring(min_bonus_id)
MMOSimca@460 1171 end
MMOSimca@384 1172 end
MMOSimca@460 1173
MMOSimca@460 1174 item.seen_bonuses[sorted_bonus_string] = true
MMOSimca@460 1175 Debug("RecordItemData: Recorded bonus IDs %s for item %d.", sorted_bonus_string, item_id)
MMOSimca@384 1176 else
MMOSimca@460 1177 item.seen_bonuses["0"] = true
MMOSimca@384 1178 end
MMOSimca@329 1179 end
jcallahan@191 1180 end
jcallahan@0 1181 end
jcallahan@212 1182
jcallahan@212 1183 if durability and durability > 0 then
jcallahan@219 1184 item = item or DBEntry("items", item_id)
jcallahan@212 1185 item.durability = durability
jcallahan@212 1186 end
jcallahan@0 1187 end
jcallahan@0 1188
jcallahan@0 1189
jcallahan@187 1190 function WDP:ProcessItems()
jcallahan@187 1191 for slot_index = _G.INVSLOT_FIRST_EQUIPPED, _G.INVSLOT_LAST_EQUIPPED do
jcallahan@1 1192 local item_id = _G.GetInventoryItemID("player", slot_index)
jcallahan@0 1193
jcallahan@0 1194 if item_id and item_id > 0 then
jcallahan@1 1195 local _, max_durability = _G.GetInventoryItemDurability(slot_index)
MMOSimca@340 1196 RecordItemData(item_id, _G.GetInventoryItemLink("player", slot_index), false, max_durability)
jcallahan@0 1197 end
jcallahan@0 1198 end
jcallahan@0 1199
jcallahan@0 1200 for bag_index = 0, _G.NUM_BAG_SLOTS do
jcallahan@0 1201 for slot_index = 1, _G.GetContainerNumSlots(bag_index) do
jcallahan@1 1202 local item_id = _G.GetContainerItemID(bag_index, slot_index)
jcallahan@0 1203
jcallahan@0 1204 if item_id and item_id > 0 then
jcallahan@1 1205 local _, max_durability = _G.GetContainerItemDurability(bag_index, slot_index)
MMOSimca@340 1206 RecordItemData(item_id, _G.GetContainerItemLink(bag_index, slot_index), false, max_durability)
jcallahan@0 1207 end
jcallahan@0 1208 end
jcallahan@0 1209 end
jcallahan@0 1210 end
jcallahan@0 1211
jcallahan@118 1212
jcallahan@215 1213 local TargetedNPC
jcallahan@118 1214 do
jcallahan@118 1215 local GENDER_NAMES = {
jcallahan@118 1216 "UNKNOWN",
jcallahan@118 1217 "MALE",
jcallahan@118 1218 "FEMALE",
jcallahan@118 1219 }
jcallahan@118 1220
jcallahan@118 1221
jcallahan@118 1222 local REACTION_NAMES = {
jcallahan@118 1223 "HATED",
jcallahan@118 1224 "HOSTILE",
jcallahan@118 1225 "UNFRIENDLY",
jcallahan@118 1226 "NEUTRAL",
jcallahan@118 1227 "FRIENDLY",
jcallahan@118 1228 "HONORED",
jcallahan@118 1229 "REVERED",
jcallahan@118 1230 "EXALTED",
jcallahan@118 1231 }
jcallahan@118 1232
jcallahan@118 1233
mmosimca@496 1234 -- We should just use IDs here someday; WoWDB site knows all about different power types
jcallahan@118 1235 local POWER_TYPE_NAMES = {
jcallahan@118 1236 ["0"] = "MANA",
jcallahan@118 1237 ["1"] = "RAGE",
jcallahan@118 1238 ["2"] = "FOCUS",
jcallahan@118 1239 ["3"] = "ENERGY",
jcallahan@118 1240 ["6"] = "RUNIC_POWER",
jcallahan@118 1241 }
jcallahan@118 1242
jcallahan@118 1243
jcallahan@215 1244 function TargetedNPC()
jcallahan@118 1245 if not _G.UnitExists("target") or _G.UnitPlayerControlled("target") or currently_drunk then
jcallahan@118 1246 current_target_id = nil
jcallahan@118 1247 return
jcallahan@118 1248 end
jcallahan@118 1249 local unit_type, unit_idnum = ParseGUID(_G.UnitGUID("target"))
jcallahan@118 1250
jcallahan@171 1251 if not unit_idnum or not UnitTypeIsNPC(unit_type) then
jcallahan@118 1252 return
jcallahan@118 1253 end
jcallahan@118 1254 current_target_id = unit_idnum
jcallahan@118 1255
jcallahan@118 1256 local npc = NPCEntry(unit_idnum)
jcallahan@118 1257 local _, class_token = _G.UnitClass("target")
jcallahan@118 1258 npc.class = class_token
jcallahan@118 1259 npc.faction = UnitFactionStanding("target")
jcallahan@118 1260 npc.genders = npc.genders or {}
jcallahan@118 1261 npc.genders[GENDER_NAMES[_G.UnitSex("target")] or "UNDEFINED"] = true
jcallahan@118 1262 npc.is_pvp = _G.UnitIsPVP("target") and true or nil
jcallahan@118 1263 npc.reaction = ("%s:%s:%s"):format(_G.UnitLevel("player"), _G.UnitFactionGroup("player"), REACTION_NAMES[_G.UnitReaction("player", "target")])
jcallahan@118 1264
jcallahan@248 1265 local encounter_data = npc:EncounterData(InstanceDifficultyToken()).stats
jcallahan@118 1266 local npc_level = ("level_%d"):format(_G.UnitLevel("target"))
jcallahan@250 1267 local level_data = encounter_data[npc_level]
jcallahan@250 1268
jcallahan@250 1269 if not level_data then
jcallahan@250 1270 level_data = {}
jcallahan@250 1271 encounter_data[npc_level] = level_data
jcallahan@250 1272 end
jcallahan@257 1273 level_data.max_health = level_data.max_health or _G.UnitHealthMax("target")
jcallahan@257 1274
mmosimca@496 1275 -- May not capture as much data as it could, since the API changed in Legion to report multiple types of power
jcallahan@257 1276 if not level_data.power then
MMOSimca@337 1277 local max_power = _G.UnitPowerMax("target")
jcallahan@257 1278
jcallahan@257 1279 if max_power > 0 then
jcallahan@257 1280 local power_type = _G.UnitPowerType("target")
jcallahan@312 1281 level_data.power = ("%s:%d"):format(POWER_TYPE_NAMES[tostring(power_type)] or power_type, max_power)
jcallahan@257 1282 end
jcallahan@118 1283 end
jcallahan@118 1284 name_to_id_map[_G.UnitName("target")] = unit_idnum
jcallahan@118 1285 return npc, unit_idnum
jcallahan@118 1286 end
jcallahan@118 1287 end -- do-block
jcallahan@118 1288
jcallahan@118 1289
jcallahan@113 1290 do
jcallahan@113 1291 local COORD_MAX = 5
jcallahan@0 1292
jcallahan@113 1293 function WDP:UpdateTargetLocation()
catherton@480 1294 if currently_drunk or not _G.UnitExists("target") or _G.UnitPlayerControlled("target") or (_G.UnitIsTapDenied("target") and not _G.UnitIsDead("target")) then
jcallahan@2 1295 return
jcallahan@2 1296 end
jcallahan@113 1297
jcallahan@113 1298 for index = 1, 4 do
jcallahan@113 1299 if not _G.CheckInteractDistance("target", index) then
jcallahan@113 1300 return
jcallahan@113 1301 end
jcallahan@113 1302 end
jcallahan@215 1303 local npc = TargetedNPC()
jcallahan@113 1304
jcallahan@113 1305 if not npc then
jcallahan@113 1306 return
jcallahan@113 1307 end
jcallahan@113 1308 local zone_name, area_id, x, y, map_level, difficulty_token = CurrentLocationData()
MMOSimca@328 1309 if not (zone_name and area_id and x and y and map_level) then
mmosimca@508 1310 if not (_G.IsInInstance()) then
mmosimca@508 1311 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 1312 end
MMOSimca@328 1313 return
MMOSimca@328 1314 end
jcallahan@248 1315 local npc_data = npc:EncounterData(difficulty_token).stats[("level_%d"):format(_G.UnitLevel("target"))]
jcallahan@113 1316 local zone_token = ("%s:%d"):format(zone_name, area_id)
jcallahan@118 1317 npc_data.locations = npc_data.locations or {} -- TODO: Fix this. It is broken. Possibly something to do with the timed updates.
jcallahan@113 1318
jcallahan@113 1319 local zone_data = npc_data.locations[zone_token]
jcallahan@113 1320
jcallahan@113 1321 if not zone_data then
jcallahan@113 1322 zone_data = {}
jcallahan@113 1323 npc_data.locations[zone_token] = zone_data
jcallahan@113 1324 end
jcallahan@113 1325
jcallahan@113 1326 for location_token in pairs(zone_data) do
jcallahan@113 1327 local loc_level, loc_x, loc_y = (":"):split(location_token)
jcallahan@113 1328 loc_level = tonumber(loc_level)
jcallahan@113 1329
jcallahan@113 1330 if map_level == loc_level and math.abs(x - loc_x) <= COORD_MAX and math.abs(y - loc_y) <= COORD_MAX then
jcallahan@113 1331 return
jcallahan@113 1332 end
jcallahan@113 1333 end
jcallahan@141 1334 zone_data[("%d:%d:%d"):format(map_level, x, y)] = true
jcallahan@2 1335 end
jcallahan@113 1336 end -- do-block
jcallahan@2 1337
jcallahan@118 1338
MMOSimca@412 1339 function WDP:HandleBadChatLootData(...)
MMOSimca@398 1340 ClearChatLootData()
MMOSimca@398 1341 end
MMOSimca@398 1342
MMOSimca@398 1343
MMOSimca@420 1344 -- EVENT HANDLERS -----------------------------------------------------
MMOSimca@420 1345
MMOSimca@436 1346 -- 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 1347 function WDP:StopChatLootRecording(event_name)
MMOSimca@436 1348 if not block_chat_loot_data then
MMOSimca@439 1349 Debug("%s: Pausing chat-based loot recording.", event_name)
MMOSimca@436 1350 ClearChatLootData()
MMOSimca@436 1351 block_chat_loot_data = true
MMOSimca@436 1352 end
MMOSimca@436 1353 end
MMOSimca@436 1354
MMOSimca@436 1355
MMOSimca@436 1356 function WDP:ResumeChatLootRecording(event_name)
MMOSimca@436 1357 if block_chat_loot_data then
MMOSimca@439 1358 Debug("%s: Resuming chat-based loot recording.", event_name)
MMOSimca@436 1359 block_chat_loot_data = false
MMOSimca@436 1360 end
MMOSimca@436 1361 end
MMOSimca@436 1362
MMOSimca@436 1363
MMOSimca@408 1364 -- 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 1365 function WDP:BONUS_ROLL_RESULT(event_name)
MMOSimca@408 1366 Debug("%s: Bonus roll detected; stopping loot recording for this boss to avoid recording bonus loot.", event_name)
MMOSimca@408 1367 ClearKilledBossID()
MMOSimca@408 1368 ClearLootToastContainerID()
MMOSimca@408 1369 end
MMOSimca@408 1370
MMOSimca@408 1371
jcallahan@90 1372 function WDP:BLACK_MARKET_ITEM_UPDATE(event_name)
jcallahan@243 1373 if not ALLOWED_LOCALES[CLIENT_LOCALE] then
jcallahan@243 1374 return
jcallahan@243 1375 end
jcallahan@282 1376 local num_items = _G.C_BlackMarket.GetNumItems() or 0
jcallahan@56 1377
jcallahan@56 1378 for index = 1, num_items do
jcallahan@56 1379 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 1380
jcallahan@56 1381 if item_link then
jcallahan@56 1382 DBEntry("items", ItemLinkToID(item_link)).black_market = seller_name or "UNKNOWN"
jcallahan@56 1383 end
jcallahan@56 1384 end
jcallahan@56 1385 end
jcallahan@56 1386
jcallahan@56 1387
jcallahan@298 1388 local function UpdateUnitPet(unit_guid, unit_id)
jcallahan@246 1389 local current_pet_guid = group_owner_guids_to_pet_guids[unit_guid]
jcallahan@246 1390
jcallahan@246 1391 if current_pet_guid then
jcallahan@246 1392 group_owner_guids_to_pet_guids[unit_guid] = nil
jcallahan@246 1393 group_pet_guids[current_pet_guid] = nil
jcallahan@246 1394 end
jcallahan@246 1395 local pet_guid = _G.UnitGUID(unit_id .. "pet")
jcallahan@246 1396
jcallahan@246 1397 if pet_guid then
jcallahan@296 1398 group_owner_guids_to_pet_guids[unit_guid] = pet_guid
jcallahan@246 1399 group_pet_guids[pet_guid] = true
jcallahan@246 1400 end
jcallahan@246 1401 end
jcallahan@246 1402
jcallahan@246 1403
jcallahan@298 1404 function WDP:GROUP_ROSTER_UPDATE(event_name)
jcallahan@298 1405 local is_raid = _G.IsInRaid()
jcallahan@298 1406 local unit_type = is_raid and "raid" or "party"
jcallahan@298 1407 local group_size = is_raid and _G.GetNumGroupMembers() or _G.GetNumSubgroupMembers()
jcallahan@298 1408
jcallahan@299 1409 table.wipe(group_member_guids)
jcallahan@298 1410
jcallahan@298 1411 for index = 1, group_size do
jcallahan@298 1412 local unit_id = unit_type .. index
jcallahan@298 1413 local unit_guid = _G.UnitGUID(unit_id)
jcallahan@298 1414
jcallahan@299 1415 group_member_guids[unit_guid] = true
jcallahan@298 1416 UpdateUnitPet(unit_guid, unit_id)
jcallahan@298 1417 end
jcallahan@299 1418 group_member_guids[PLAYER_GUID] = true
jcallahan@298 1419 end
jcallahan@298 1420
jcallahan@298 1421
jcallahan@298 1422 function WDP:UNIT_PET(event_name, unit_id)
jcallahan@298 1423 UpdateUnitPet(_G.UnitGUID(unit_id), unit_id)
jcallahan@298 1424 end
jcallahan@298 1425
jcallahan@298 1426
MMOSimca@375 1427 function WDP:SHOW_LOOT_TOAST(event_name, loot_type, item_link, quantity, spec_ID, sex_ID, is_personal, loot_source)
jcallahan@312 1428 if not loot_type or (loot_type ~= "item" and loot_type ~= "money" and loot_type ~= "currency") then
jcallahan@306 1429 Debug("%s: loot_type is %s. Item link is %s, and quantity is %d.", event_name, loot_type, item_link, quantity)
jcallahan@306 1430 return
jcallahan@306 1431 end
MMOSimca@372 1432
MMOSimca@372 1433 -- Need information on the most recent args, so using this complete debug statement for now
MMOSimca@375 1434 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 1435
MMOSimca@355 1436 -- Handle Garrison cache specially
MMOSimca@422 1437 if loot_source and (loot_source == LOOT_SOURCE_ID_GARRISON_CACHE) and last_garrison_cache_object_id then
MMOSimca@355 1438 -- Record location data for cache
MMOSimca@355 1439 UpdateDBEntryLocation("objects", ("OPENING:%d"):format(last_garrison_cache_object_id))
MMOSimca@355 1440
MMOSimca@355 1441 -- Add drop data
mmosimca@496 1442 local currency_id = CurrencyLinkToID(item_link)
mmosimca@496 1443 if currency_id and currency_id ~= 0 then
MMOSimca@355 1444 -- Check for top level object data
MMOSimca@355 1445 local object_entry = DBEntry("objects", ("OPENING:%d"):format(last_garrison_cache_object_id))
MMOSimca@355 1446 local difficulty_token = InstanceDifficultyToken()
MMOSimca@355 1447 if object_entry[difficulty_token] then
MMOSimca@355 1448 -- Increment loot count
MMOSimca@355 1449 object_entry[difficulty_token]["opening_count"] = (object_entry[difficulty_token]["opening_count"] or 0) + 1
MMOSimca@355 1450
mmosimca@496 1451 Debug("%s: %d X %d", event_name, currency_id, quantity)
MMOSimca@355 1452 object_entry[difficulty_token]["opening"] = object_entry[difficulty_token]["opening"] or {}
mmosimca@496 1453 table.insert(object_entry[difficulty_token]["opening"], ("currency:%d:%d"):format(quantity, currency_id))
MMOSimca@355 1454 else
MMOSimca@355 1455 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 1456 end
MMOSimca@355 1457 else
mmosimca@496 1458 Debug("%s: Currency ID is nil or 0, from currency link %s", event_name, item_link)
MMOSimca@355 1459 end
catherton@465 1460
MMOSimca@431 1461 -- Wipe object ID until future mouseover
MMOSimca@431 1462 last_garrison_cache_object_id = nil
MMOSimca@387 1463 elseif raid_boss_id then
MMOSimca@427 1464 local npc = NPCEntry(raid_boss_id)
MMOSimca@427 1465 if npc then
MMOSimca@427 1466 local loot_label = "drops"
MMOSimca@427 1467 local encounter_data = npc:EncounterData(InstanceDifficultyToken())
MMOSimca@427 1468 encounter_data[loot_label] = encounter_data[loot_label] or {}
MMOSimca@427 1469 encounter_data.loot_counts = encounter_data.loot_counts or {}
MMOSimca@427 1470
MMOSimca@427 1471 if loot_type == "item" then
MMOSimca@427 1472 local item_id = ItemLinkToID(item_link)
MMOSimca@427 1473 if item_id then
MMOSimca@427 1474 Debug("%s: %s X %d (%d)", event_name, item_link, quantity, item_id)
MMOSimca@427 1475 RecordItemData(item_id, item_link, true)
MMOSimca@427 1476 table.insert(encounter_data[loot_label], ("%d:%d"):format(item_id, quantity))
MMOSimca@427 1477 else
MMOSimca@427 1478 Debug("%s: ItemID is nil, from item link %s", event_name, item_link)
MMOSimca@427 1479 return
MMOSimca@427 1480 end
MMOSimca@427 1481 elseif loot_type == "money" then
MMOSimca@427 1482 Debug("%s: money X %d", event_name, quantity)
MMOSimca@427 1483 table.insert(encounter_data[loot_label], ("money:%d"):format(quantity))
MMOSimca@427 1484 elseif loot_type == "currency" then
mmosimca@496 1485 local currency_id = CurrencyLinkToID(item_link)
mmosimca@496 1486 if currency_id and currency_id ~= 0 then
mmosimca@496 1487 Debug("%s: %d X %d", event_name, currency_id, quantity)
mmosimca@496 1488 table.insert(encounter_data[loot_label], ("currency:%d:%d"):format(quantity, currency_id))
MMOSimca@427 1489 else
mmosimca@496 1490 Debug("%s: Currency ID is nil or 0, from currency link %s", event_name, item_link)
MMOSimca@427 1491 return
MMOSimca@427 1492 end
jcallahan@312 1493 end
jcallahan@317 1494
MMOSimca@427 1495 if not boss_loot_toasting[raid_boss_id] then
MMOSimca@427 1496 encounter_data.loot_counts[loot_label] = (encounter_data.loot_counts[loot_label] or 0) + 1
MMOSimca@427 1497 boss_loot_toasting[raid_boss_id] = true -- Do not count further loots until timer expires or another boss is killed
jcallahan@312 1498 end
jcallahan@312 1499 end
MMOSimca@387 1500 elseif loot_toast_container_id then
jcallahan@305 1501 InitializeCurrentLoot()
jcallahan@305 1502
jcallahan@306 1503 -- Fake the loot characteristics to match that of an actual container item
MMOSimca@387 1504 current_loot.identifier = loot_toast_container_id
jcallahan@306 1505 current_loot.label = "contains"
jcallahan@306 1506 current_loot.target_type = AF.ITEM
jcallahan@306 1507
MMOSimca@387 1508 current_loot.sources[loot_toast_container_id] = current_loot.sources[loot_toast_container_id] or {}
jcallahan@312 1509
jcallahan@301 1510 if loot_type == "item" then
jcallahan@312 1511 local item_id = ItemLinkToID(item_link)
jcallahan@312 1512 if item_id then
jcallahan@312 1513 Debug("%s: %s X %d (%d)", event_name, item_link, quantity, item_id)
MMOSimca@340 1514 RecordItemData(item_id, item_link, true)
MMOSimca@387 1515 current_loot.sources[loot_toast_container_id][item_id] = (current_loot.sources[loot_toast_container_id][item_id] or 0) + quantity
jcallahan@312 1516 else
jcallahan@301 1517 Debug("%s: ItemID is nil, from item link %s", event_name, item_link)
jcallahan@312 1518 current_loot = nil
jcallahan@301 1519 return
jcallahan@301 1520 end
jcallahan@301 1521 elseif loot_type == "money" then
jcallahan@312 1522 Debug("%s: money X %d", event_name, quantity)
MMOSimca@387 1523 current_loot.sources[loot_toast_container_id]["money"] = (current_loot.sources[loot_toast_container_id]["money"] or 0) + quantity
jcallahan@312 1524 elseif loot_type == "currency" then
mmosimca@496 1525 local currency_id = CurrencyLinkToID(item_link)
mmosimca@496 1526 if currency_id and currency_id ~= 0 then
mmosimca@496 1527 Debug("%s: %d X %d", event_name, currency_id, quantity)
mmosimca@496 1528 local currency_token = ("currency:%d"):format(currency_id)
MMOSimca@387 1529 current_loot.sources[loot_toast_container_id][currency_token] = (current_loot.sources[loot_toast_container_id][currency_token] or 0) + quantity
jcallahan@312 1530 else
mmosimca@496 1531 Debug("%s: Currency ID is nil or 0, from currency link %s", event_name, item_link)
jcallahan@312 1532 current_loot = nil
jcallahan@312 1533 return
jcallahan@312 1534 end
jcallahan@301 1535 end
jcallahan@312 1536
jcallahan@301 1537 GenericLootUpdate("items")
jcallahan@301 1538 current_loot = nil
MMOSimca@387 1539 container_loot_toasting = true -- Do not count further loots until timer expires or another container is opened
MMOSimca@444 1540 elseif loot_source and (loot_source == LOOT_SOURCE_ID_REDUNDANT) and chat_loot_timer_handle then
MMOSimca@444 1541 -- 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 1542 if loot_type == "currency" then
mmosimca@496 1543 local currency_id = CurrencyLinkToID(item_link)
mmosimca@496 1544 if currency_id and currency_id ~= 0 then
MMOSimca@444 1545 -- Verify that we're still assigning data to the right items
MMOSimca@444 1546 if chat_loot_data.identifier and (private.CONTAINER_ITEM_ID_LIST[chat_loot_data.identifier] ~= nil) then
mmosimca@496 1547 Debug("%s: Captured currency for chat-based loot recording. %d X %d", event_name, currency_id, quantity)
mmosimca@496 1548 local currency_token = ("currency:%d"):format(currency_id)
MMOSimca@444 1549 chat_loot_data.loot = chat_loot_data.loot or {}
MMOSimca@444 1550 chat_loot_data.loot[currency_token] = (chat_loot_data.loot[currency_token] or 0) + quantity
MMOSimca@444 1551 else -- If not, cancel the timer and wipe the loot table early
MMOSimca@444 1552 Debug("%s: Canceled chat-based loot recording because we would have assigned the wrong loot!", event_name)
MMOSimca@444 1553 ClearChatLootData()
MMOSimca@444 1554 end
MMOSimca@444 1555 else
mmosimca@496 1556 Debug("%s: Currency ID is nil or 0, from currency link %s", event_name, item_link)
MMOSimca@444 1557 end
MMOSimca@444 1558 -- 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 1559 elseif loot_type == "money" then
MMOSimca@424 1560 -- Verify that we're still assigning data to the right items
MMOSimca@435 1561 if chat_loot_data.identifier and (private.CONTAINER_ITEM_ID_LIST[chat_loot_data.identifier] ~= nil) then
MMOSimca@444 1562 Debug("%s: Captured money for chat-based loot recording. money X %d", event_name, quantity)
MMOSimca@435 1563 chat_loot_data.loot = chat_loot_data.loot or {}
MMOSimca@444 1564 chat_loot_data.loot["money"] = (chat_loot_data.loot["money"] or 0) + quantity
MMOSimca@424 1565 else -- If not, cancel the timer and wipe the loot table early
MMOSimca@424 1566 Debug("%s: Canceled chat-based loot recording because we would have assigned the wrong loot!", event_name)
MMOSimca@424 1567 ClearChatLootData()
MMOSimca@424 1568 end
MMOSimca@424 1569 end
jcallahan@301 1570 else
jcallahan@307 1571 Debug("%s: NPC and Container are nil, storing loot toast data for 5 seconds.", event_name)
jcallahan@307 1572
jcallahan@307 1573 loot_toast_data = loot_toast_data or {}
jcallahan@312 1574 loot_toast_data[#loot_toast_data + 1] = { loot_type, item_link, quantity }
jcallahan@307 1575
MMOSimca@340 1576 local item_id = ItemLinkToID(item_link)
MMOSimca@340 1577 if item_id then
MMOSimca@340 1578 RecordItemData(item_id, item_link, true)
MMOSimca@340 1579 end
MMOSimca@340 1580
MMOSimca@383 1581 loot_toast_data_timer_handle = C_Timer.NewTimer(5, ClearLootToastData)
jcallahan@178 1582 end
jcallahan@178 1583 end
jcallahan@178 1584
jcallahan@178 1585
jcallahan@179 1586 do
MMOSimca@388 1587 local CHAT_MSG_CURRENCY_UPDATE_FUNCS = {
mmosimca@496 1588 [AF.NPC] = function(currency_id, quantity)
mmosimca@496 1589 Debug("CHAT_MSG_CURRENCY: AF.NPC currency:%d (%d)", currency_id, quantity)
MMOSimca@388 1590 end,
mmosimca@496 1591 [AF.ZONE] = function(currency_id, quantity)
mmosimca@496 1592 Debug("CHAT_MSG_CURRENCY: AF.ZONE currency:%d (%d)", currency_id, quantity)
MMOSimca@388 1593 InitializeCurrentLoot()
mmosimca@496 1594 current_loot.list[1] = ("currency:%d:%d"):format(quantity, currency_id)
MMOSimca@388 1595 GenericLootUpdate("zones")
MMOSimca@388 1596 current_loot = nil
MMOSimca@388 1597 end,
MMOSimca@388 1598 }
MMOSimca@388 1599
MMOSimca@388 1600
MMOSimca@388 1601 function WDP:CHAT_MSG_CURRENCY(event_name, message)
MMOSimca@388 1602 local category
MMOSimca@388 1603
MMOSimca@388 1604 local currency_link, quantity = deformat(message, _G.CURRENCY_GAINED_MULTIPLE)
MMOSimca@388 1605 if not currency_link then
MMOSimca@388 1606 quantity, currency_link = 1, deformat(message, _G.CURRENCY_GAINED)
MMOSimca@388 1607 end
mmosimca@496 1608 local currency_id = CurrencyLinkToID(currency_link)
mmosimca@496 1609
mmosimca@496 1610 if not currency_id or currency_id == 0 then
MMOSimca@388 1611 return
MMOSimca@388 1612 end
MMOSimca@388 1613
MMOSimca@388 1614 -- Set update category
MMOSimca@388 1615 if current_action.spell_label == "FISHING" then
MMOSimca@388 1616 category = AF.ZONE
MMOSimca@388 1617 elseif raid_boss_id then
MMOSimca@388 1618 category = AF.NPC
MMOSimca@388 1619 end
MMOSimca@388 1620
MMOSimca@388 1621 -- Take action based on update category
MMOSimca@388 1622 local update_func = CHAT_MSG_CURRENCY_UPDATE_FUNCS[category]
MMOSimca@388 1623 if not category or not update_func then
MMOSimca@388 1624 return
MMOSimca@388 1625 end
mmosimca@496 1626 update_func(currency_id, quantity)
MMOSimca@388 1627 end
MMOSimca@388 1628
MMOSimca@388 1629
MMOSimca@412 1630 local BLACKLISTED_ITEMS = {
MMOSimca@412 1631 [114116] = true,
MMOSimca@412 1632 [114119] = true,
MMOSimca@412 1633 [114120] = true,
MMOSimca@412 1634 [116980] = true,
MMOSimca@412 1635 [120319] = true,
MMOSimca@412 1636 [120320] = true,
catherton@475 1637 [139593] = true,
catherton@475 1638 [139594] = true,
catherton@475 1639 [140590] = true,
MMOSimca@412 1640 }
MMOSimca@412 1641
MMOSimca@412 1642
jcallahan@179 1643 local CHAT_MSG_LOOT_UPDATE_FUNCS = {
MMOSimca@347 1644 [AF.ITEM] = function(item_id, quantity)
MMOSimca@347 1645 -- Verify that we're still assigning data to the right items
MMOSimca@435 1646 if chat_loot_data.identifier and (private.CONTAINER_ITEM_ID_LIST[chat_loot_data.identifier] ~= nil) then
MMOSimca@347 1647 Debug("CHAT_MSG_LOOT: AF.ITEM %d (%d)", item_id, quantity)
MMOSimca@435 1648 chat_loot_data.loot = chat_loot_data.loot or {}
MMOSimca@435 1649 chat_loot_data.loot[item_id] = (chat_loot_data.loot[item_id] or 0) + quantity
MMOSimca@347 1650 else -- If not, cancel the timer and wipe the loot table early
MMOSimca@387 1651 Debug("CHAT_MSG_LOOT: We would have assigned the wrong loot!")
MMOSimca@387 1652 ClearChatLootData()
MMOSimca@347 1653 end
MMOSimca@347 1654 end,
jcallahan@179 1655 [AF.NPC] = function(item_id, quantity)
MMOSimca@345 1656 Debug("CHAT_MSG_LOOT: AF.NPC %d (%d)", item_id, quantity)
MMOSimca@345 1657 end,
MMOSimca@345 1658 [AF.OBJECT] = function(item_id, quantity)
MMOSimca@345 1659 Debug("CHAT_MSG_LOOT: AF.OBJECT %d (%d)", item_id, quantity)
MMOSimca@381 1660 -- Check for top level object data
MMOSimca@381 1661 local object_entry = DBEntry("objects", ("OPENING:%s"):format(private.LOGGING_SPELL_ID_TO_OBJECT_ID_MAP[last_timber_spell_id]))
MMOSimca@381 1662 local difficulty_token = InstanceDifficultyToken()
MMOSimca@381 1663 if object_entry[difficulty_token] then
MMOSimca@381 1664 -- Increment loot count
MMOSimca@381 1665 object_entry[difficulty_token]["opening_count"] = (object_entry[difficulty_token]["opening_count"] or 0) + 1
MMOSimca@381 1666
MMOSimca@381 1667 -- Add drop data
MMOSimca@381 1668 object_entry[difficulty_token]["opening"] = object_entry[difficulty_token]["opening"] or {}
MMOSimca@381 1669 table.insert(object_entry[difficulty_token]["opening"], ("%d:%d"):format(item_id, quantity))
MMOSimca@381 1670 else
MMOSimca@381 1671 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 1672 end
jcallahan@179 1673 end,
jcallahan@179 1674 [AF.ZONE] = function(item_id, quantity)
MMOSimca@345 1675 Debug("CHAT_MSG_LOOT: AF.ZONE %d (%d)", item_id, quantity)
jcallahan@312 1676 InitializeCurrentLoot()
jcallahan@312 1677 current_loot.list[1] = ("%d:%d"):format(item_id, quantity)
jcallahan@179 1678 GenericLootUpdate("zones")
jcallahan@312 1679 current_loot = nil
jcallahan@179 1680 end,
jcallahan@179 1681 }
jcallahan@177 1682
jcallahan@177 1683
MMOSimca@388 1684 function WDP:CHAT_MSG_LOOT(event_name, message)
jcallahan@179 1685 local category
jcallahan@177 1686
MMOSimca@345 1687 local item_link, quantity = deformat(message, _G.LOOT_ITEM_PUSHED_SELF_MULTIPLE)
MMOSimca@345 1688 if not item_link then
MMOSimca@345 1689 quantity, item_link = 1, deformat(message, _G.LOOT_ITEM_PUSHED_SELF)
MMOSimca@345 1690 end
MMOSimca@345 1691 local item_id = ItemLinkToID(item_link)
MMOSimca@345 1692
MMOSimca@345 1693 if not item_id then
MMOSimca@345 1694 return
MMOSimca@345 1695 end
MMOSimca@345 1696
MMOSimca@345 1697 -- Set update category
MMOSimca@405 1698 if last_timber_spell_id and item_id == ITEM_ID_TIMBER then
MMOSimca@345 1699 category = AF.OBJECT
MMOSimca@345 1700 -- Recently changed from ~= "EXTRACT_GAS" because of some occassional bad data, and, as far as I know, no benefit.
MMOSimca@345 1701 elseif current_action.spell_label == "FISHING" then
jcallahan@179 1702 category = AF.ZONE
MMOSimca@388 1703 elseif raid_boss_id then
jcallahan@179 1704 category = AF.NPC
MMOSimca@347 1705 elseif chat_loot_timer_handle then
MMOSimca@347 1706 category = AF.ITEM
jcallahan@179 1707 end
MMOSimca@345 1708
MMOSimca@395 1709 -- We still want to record the item's data, even if it doesn't need its drop location recorded
MMOSimca@395 1710 RecordItemData(item_id, item_link, true)
MMOSimca@395 1711
MMOSimca@345 1712 -- Take action based on update category
jcallahan@179 1713 local update_func = CHAT_MSG_LOOT_UPDATE_FUNCS[category]
MMOSimca@412 1714 if not category or not update_func or BLACKLISTED_ITEMS[item_id] then
MMOSimca@340 1715 return
MMOSimca@340 1716 end
jcallahan@179 1717 update_func(item_id, quantity)
jcallahan@177 1718 end
MMOSimca@388 1719 end
MMOSimca@388 1720
MMOSimca@388 1721
jcallahan@97 1722 function WDP:RecordQuote(event_name, message, source_name, language_name)
jcallahan@112 1723 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 1724 return
jcallahan@95 1725 end
jcallahan@97 1726 local npc = NPCEntry(name_to_id_map[source_name])
jcallahan@97 1727 npc.quotes = npc.quotes or {}
jcallahan@97 1728 npc.quotes[event_name] = npc.quotes[event_name] or {}
jcallahan@97 1729 npc.quotes[event_name][ReplaceKeywords(message)] = true
jcallahan@97 1730 end
jcallahan@95 1731
jcallahan@95 1732
jcallahan@95 1733 do
jcallahan@40 1734 local SOBER_MATCH = _G.DRUNK_MESSAGE_ITEM_SELF1:gsub("%%s", ".+")
jcallahan@40 1735
jcallahan@40 1736 local DRUNK_COMPARES = {
jcallahan@40 1737 _G.DRUNK_MESSAGE_SELF2,
jcallahan@40 1738 _G.DRUNK_MESSAGE_SELF3,
jcallahan@40 1739 _G.DRUNK_MESSAGE_SELF4,
jcallahan@40 1740 }
jcallahan@40 1741
jcallahan@40 1742 local DRUNK_MATCHES = {
jcallahan@254 1743 (_G.DRUNK_MESSAGE_SELF2:gsub("%%s", ".+")),
jcallahan@254 1744 (_G.DRUNK_MESSAGE_SELF3:gsub("%%s", ".+")),
jcallahan@254 1745 (_G.DRUNK_MESSAGE_SELF4:gsub("%%s", ".+")),
jcallahan@40 1746 }
jcallahan@40 1747
jcallahan@167 1748 local RECIPE_MATCH = _G.ERR_LEARN_RECIPE_S:gsub("%%s", "(.*)")
jcallahan@167 1749
jcallahan@167 1750
jcallahan@167 1751 local function RecordDiscovery(tradeskill_name, tradeskill_index)
jcallahan@167 1752 if tradeskill_name == private.discovered_recipe_name then
catherton@479 1753 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 1754
jcallahan@167 1755 private.discovered_recipe_name = nil
jcallahan@167 1756 private.profession_level = nil
jcallahan@167 1757 private.previous_spell_id = nil
jcallahan@169 1758
jcallahan@169 1759 return true
jcallahan@167 1760 end
jcallahan@167 1761 end
jcallahan@167 1762
jcallahan@167 1763
jcallahan@167 1764 local function IterativeRecordDiscovery()
jcallahan@167 1765 TradeSkillExecutePer(RecordDiscovery)
jcallahan@167 1766 end
jcallahan@167 1767
jcallahan@167 1768
jcallahan@92 1769 function WDP:CHAT_MSG_SYSTEM(event_name, message)
mmosimca@514 1770 -- 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 1771 --[[
MMOSimca@532 1772 -- Removed in Patch 7.0.3; previously used to determine if a system message was a quest reward or not
MMOSimca@532 1773 local ERR_QUEST_REWARD_ITEM_MULT_IS = _G.ERR_QUEST_REWARD_ITEM_MULT_IS or "Received %d of item: %s."
MMOSimca@532 1774 local ERR_QUEST_REWARD_ITEM_S = _G.ERR_QUEST_REWARD_ITEM_S or "Received item: %s."
MMOSimca@532 1775
catherton@472 1776 local item_link, quantity = deformat(message, ERR_QUEST_REWARD_ITEM_MULT_IS)
MMOSimca@400 1777 if not item_link then
catherton@472 1778 quantity, item_link = 1, deformat(message, ERR_QUEST_REWARD_ITEM_S)
MMOSimca@400 1779 end
MMOSimca@400 1780 local item_id = ItemLinkToID(item_link)
MMOSimca@400 1781
mmosimca@514 1782 if item_id then
mmosimca@514 1783 -- If it was a quest message (that we can decode), parse its link
mmosimca@514 1784 RecordItemData(item_id, item_link, true)
mmosimca@514 1785 else
mmosimca@514 1786 -- If it isn't a quest message, check the other uses of system messages
MMOSimca@532 1787 end
MMOSimca@532 1788 ]]--
MMOSimca@532 1789
MMOSimca@532 1790 if not private.trainer_shown then
MMOSimca@532 1791 local recipe_name = message:match(RECIPE_MATCH)
MMOSimca@532 1792
MMOSimca@532 1793 if recipe_name and private.previous_spell_id then
MMOSimca@532 1794 local profession_name, prof_level = _G.C_TradeSkillUI.GetTradeSkillLine()
MMOSimca@532 1795
MMOSimca@532 1796 if profession_name == _G.UNKNOWN then
MMOSimca@532 1797 return
jcallahan@167 1798 end
MMOSimca@532 1799 private.discovered_recipe_name = recipe_name
MMOSimca@532 1800 private.profession_level = prof_level
MMOSimca@532 1801
MMOSimca@532 1802 C_Timer.After(0.2, IterativeRecordDiscovery)
jcallahan@167 1803 end
MMOSimca@532 1804 end
MMOSimca@532 1805
MMOSimca@532 1806 if currently_drunk then
MMOSimca@532 1807 if message == _G.DRUNK_MESSAGE_SELF1 or message:match(SOBER_MATCH) then
MMOSimca@532 1808 currently_drunk = nil
MMOSimca@400 1809 end
MMOSimca@532 1810 return
MMOSimca@532 1811 end
MMOSimca@532 1812
MMOSimca@532 1813 for index = 1, #DRUNK_MATCHES do
MMOSimca@532 1814 if message == DRUNK_COMPARES[index] or message:match(DRUNK_MATCHES[index]) then
MMOSimca@532 1815 currently_drunk = true
MMOSimca@532 1816 break
jcallahan@40 1817 end
jcallahan@40 1818 end
jcallahan@40 1819 end
jcallahan@40 1820 end
jcallahan@40 1821
jcallahan@307 1822
jcallahan@331 1823 do
jcallahan@23 1824 local FLAGS_NPC = bit.bor(_G.COMBATLOG_OBJECT_TYPE_GUARDIAN, _G.COMBATLOG_OBJECT_CONTROL_NPC)
jcallahan@23 1825 local FLAGS_NPC_CONTROL = bit.bor(_G.COMBATLOG_OBJECT_AFFILIATION_OUTSIDER, _G.COMBATLOG_OBJECT_CONTROL_NPC)
jcallahan@23 1826
MMOSimca@458 1827 -- Spells that are cast by players/NPCs that are mistakely assigned as being cast by the target; must be blacklisted
MMOSimca@405 1828 local BLACKLISTED_SPELLS = {
MMOSimca@458 1829 [117526] = true, -- Binding Shot (cast by Hunters)
MMOSimca@458 1830 [121308] = true, -- Disguise (cast by Rogues)
MMOSimca@458 1831 [132464] = true, -- Chi Wave (cast by Monks)
MMOSimca@458 1832 [132467] = true, -- Chi Wave (cast by Monks)
MMOSimca@460 1833 [167432] = true, -- Savagery (cast by Warsong Commander)
MMOSimca@460 1834 [175077] = true, -- Fearsome Battle Standard (cast by Fearsome Battle Standard item)
MMOSimca@458 1835 [176813] = true, -- Itchy Spores (cast by Marsh Creatures in Ashran)
MMOSimca@458 1836 [183901] = true, -- Stolen Strength (cast by Felblood NPCs in Tanaan Jungle)
MMOSimca@458 1837 [183904] = true, -- Stolen Speed (cast by Felblood NPCs in Tanaan Jungle)
MMOSimca@458 1838 [183907] = true, -- Stolen Fervor (cast by Felblood NPCs in Tanaan Jungle)
mmosimca@495 1839 [195802] = true, -- Moonkin Feather (applied by Moonfeather Statue; first stage buff)
mmosimca@495 1840 [195805] = true, -- Moonkin Molting (applied by Moonfeather Statue; second stage buff)
mmosimca@495 1841 [195810] = true, -- Feeling Moonkin (applied by Moonfeather Statue; third stage buff)
mmosimca@495 1842 [195816] = true, -- Owlvercome wth the Fever (applied by Moonfeather Statue; final stage buff)
catherton@474 1843 [213738] = true, -- Taste of Blood (applied by Fate and Fortune, Combat Rogue artifacts)
mmosimca@483 1844 [213877] = true, -- Vampiric Aura (used by Nathrezim Invasion bosses and transformed players)
catherton@474 1845 [215377] = true, -- The Maw Must Feed (applied by Maw of the Damned, Blood Death Knight artifact)
mmosimca@499 1846 [218136] = true, -- Arcane Invigoration (cast by Duskwatch Rune Scribes in The Arcway)
catherton@470 1847 [224762] = true, -- Leyline Rift (summoned by players with Leyline Mastery in Suramar)
catherton@474 1848 [225832] = true, -- Nightglow Wisp (cast by players using Wisp in a Bottle toy)
MMOSimca@405 1849 }
MMOSimca@405 1850
jcallahan@23 1851 local function RecordNPCSpell(sub_event, source_guid, source_name, source_flags, dest_guid, dest_name, dest_flags, spell_id, spell_name)
MMOSimca@405 1852 if not spell_id or BLACKLISTED_SPELLS[spell_id] then
jcallahan@23 1853 return
jcallahan@23 1854 end
jcallahan@34 1855 local source_type, source_id = ParseGUID(source_guid)
jcallahan@23 1856
jcallahan@171 1857 if not source_id or not UnitTypeIsNPC(source_type) then
jcallahan@23 1858 return
jcallahan@23 1859 end
jcallahan@23 1860
jcallahan@23 1861 if bit.band(FLAGS_NPC_CONTROL, source_flags) == FLAGS_NPC_CONTROL and bit.band(FLAGS_NPC, source_flags) ~= 0 then
jcallahan@248 1862 local encounter_data = NPCEntry(source_id):EncounterData(InstanceDifficultyToken())
jcallahan@28 1863 encounter_data.spells = encounter_data.spells or {}
jcallahan@28 1864 encounter_data.spells[spell_id] = (encounter_data.spells[spell_id] or 0) + 1
jcallahan@23 1865 end
jcallahan@23 1866 end
jcallahan@23 1867
jcallahan@115 1868 local HEAL_BATTLE_PETS_SPELL_ID = 125801
jcallahan@115 1869
jcallahan@246 1870 local previous_combat_event = {}
jcallahan@246 1871
jcallahan@23 1872 local COMBAT_LOG_FUNCS = {
jcallahan@23 1873 SPELL_AURA_APPLIED = RecordNPCSpell,
jcallahan@23 1874 SPELL_CAST_START = RecordNPCSpell,
jcallahan@115 1875 SPELL_CAST_SUCCESS = function(sub_event, source_guid, source_name, source_flags, dest_guid, dest_name, dest_flags, spell_id, spell_name)
jcallahan@115 1876 if spell_id == HEAL_BATTLE_PETS_SPELL_ID then
jcallahan@115 1877 local unit_type, unit_idnum = ParseGUID(source_guid)
jcallahan@115 1878
jcallahan@171 1879 if unit_idnum and UnitTypeIsNPC(unit_type) then
jcallahan@115 1880 NPCEntry(unit_idnum).stable_master = true
jcallahan@115 1881 end
jcallahan@115 1882 end
jcallahan@115 1883 RecordNPCSpell(sub_event, source_guid, source_name, source_flags, dest_guid, dest_name, dest_flags, spell_id, spell_name)
jcallahan@115 1884 end,
jcallahan@65 1885 UNIT_DIED = function(sub_event, source_guid, source_name, source_flags, dest_guid, dest_name, dest_flags, spell_id, spell_name)
jcallahan@65 1886 local unit_type, unit_idnum = ParseGUID(dest_guid)
jcallahan@65 1887
jcallahan@171 1888 if not unit_idnum or not UnitTypeIsNPC(unit_type) then
mmosimca@515 1889 --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 1890 ClearKilledNPC()
jcallahan@98 1891 private.harvesting = nil
jcallahan@65 1892 return
jcallahan@65 1893 end
jcallahan@177 1894
jcallahan@246 1895 if source_guid == "" then
jcallahan@246 1896 source_guid = nil
jcallahan@246 1897 end
jcallahan@246 1898 local killer_guid = source_guid or previous_combat_event.source_guid
jcallahan@246 1899 local killer_name = source_name or previous_combat_event.source_name
jcallahan@246 1900
jcallahan@299 1901 if not previous_combat_event.party_damage then
jcallahan@312 1902 --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 1903 table.wipe(previous_combat_event)
jcallahan@217 1904 ClearKilledNPC()
jcallahan@306 1905 else
jcallahan@317 1906 --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 1907 end
jcallahan@177 1908 killed_npc_id = unit_idnum
MMOSimca@383 1909 C_Timer.After(0.1, ClearKilledNPC)
jcallahan@65 1910 end,
jcallahan@23 1911 }
jcallahan@23 1912
jcallahan@23 1913
jcallahan@246 1914 local NON_DAMAGE_EVENTS = {
jcallahan@246 1915 SPELL_AURA_APPLIED = true,
jcallahan@246 1916 SPELL_AURA_REMOVED = true,
jcallahan@246 1917 SPELL_AURA_REMOVED_DOSE = true,
jcallahan@246 1918 SPELL_CAST_FAILED = true,
jcallahan@246 1919 SWING_MISSED = true,
jcallahan@246 1920 }
jcallahan@246 1921
jcallahan@299 1922 local DAMAGE_EVENTS = {
jcallahan@299 1923 RANGE_DAMAGE = true,
jcallahan@299 1924 SPELL_BUILDING_DAMAGE = true,
jcallahan@299 1925 SPELL_DAMAGE = true,
jcallahan@299 1926 SPELL_PERIODIC_DAMAGE = true,
jcallahan@299 1927 SWING_DAMAGE = true,
jcallahan@299 1928 }
jcallahan@299 1929
jcallahan@246 1930
jcallahan@92 1931 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 1932 local combat_log_func = COMBAT_LOG_FUNCS[sub_event]
jcallahan@23 1933
jcallahan@23 1934 if not combat_log_func then
jcallahan@299 1935 if DAMAGE_EVENTS[sub_event] then
jcallahan@299 1936 table.wipe(previous_combat_event)
jcallahan@246 1937 previous_combat_event.source_name = source_name
jcallahan@299 1938
jcallahan@299 1939 if source_guid ~= dest_guid and (in_instance or group_member_guids[source_guid] or group_pet_guids[source_guid]) then
jcallahan@299 1940 previous_combat_event.party_damage = true
jcallahan@299 1941 end
jcallahan@246 1942 end
jcallahan@23 1943 return
jcallahan@23 1944 end
jcallahan@23 1945 combat_log_func(sub_event, source_guid, source_name, source_flags, dest_guid, dest_name, dest_flags, ...)
jcallahan@297 1946
jcallahan@297 1947 if NON_DAMAGE_EVENTS[sub_event] then
jcallahan@297 1948 table.wipe(previous_combat_event)
jcallahan@297 1949 end
jcallahan@23 1950 end
jcallahan@23 1951
catherton@465 1952
jcallahan@44 1953 local DIPLOMACY_SPELL_ID = 20599
jcallahan@44 1954 local MR_POP_RANK1_SPELL_ID = 78634
jcallahan@44 1955 local MR_POP_RANK2_SPELL_ID = 78635
MMOSimca@418 1956 local TRADING_PACT_SPELL_ID = 170200
jcallahan@44 1957
jcallahan@44 1958
jcallahan@92 1959 function WDP:COMBAT_TEXT_UPDATE(event_name, message_type, faction_name, amount)
jcallahan@177 1960 if message_type ~= "FACTION" or not killed_npc_id then
jcallahan@44 1961 return
jcallahan@44 1962 end
jcallahan@44 1963 UpdateFactionData()
jcallahan@44 1964
jcallahan@46 1965 if not faction_name or not faction_standings[faction_name] then
jcallahan@46 1966 return
jcallahan@46 1967 end
jcallahan@177 1968 local npc = NPCEntry(killed_npc_id)
jcallahan@177 1969 ClearKilledNPC()
jcallahan@46 1970
jcallahan@44 1971 if not npc then
jcallahan@98 1972 private.harvesting = nil
jcallahan@44 1973 return
jcallahan@44 1974 end
jcallahan@98 1975 npc.harvested = private.harvesting
jcallahan@98 1976 private.harvesting = nil
jcallahan@98 1977
jcallahan@44 1978 local modifier = 1
jcallahan@44 1979
MMOSimca@340 1980 -- Check for modifiers from known spells
jcallahan@44 1981 if _G.IsSpellKnown(DIPLOMACY_SPELL_ID) then
jcallahan@44 1982 modifier = modifier + 0.1
jcallahan@44 1983 end
jcallahan@44 1984 if _G.IsSpellKnown(MR_POP_RANK2_SPELL_ID) then
jcallahan@44 1985 modifier = modifier + 0.1
jcallahan@44 1986 elseif _G.IsSpellKnown(MR_POP_RANK1_SPELL_ID) then
jcallahan@44 1987 modifier = modifier + 0.05
jcallahan@44 1988 end
MMOSimca@418 1989 if _G.IsSpellKnown(TRADING_PACT_SPELL_ID) then
MMOSimca@418 1990 modifier = modifier + 0.2
MMOSimca@418 1991 end
jcallahan@44 1992
MMOSimca@340 1993 -- Determine faction ID
MMOSimca@340 1994 local faction_ID
MMOSimca@418 1995 for pseudo_faction_name, faction_data_table in pairs(private.FACTION_DATA) do
MMOSimca@418 1996 if faction_name == faction_data_table[3] then
MMOSimca@418 1997 -- Check ignore flag
MMOSimca@418 1998 if faction_data_table[2] then
MMOSimca@418 1999 return
MMOSimca@418 2000 end
MMOSimca@340 2001 faction_ID = faction_data_table[1]
MMOSimca@418 2002 break
MMOSimca@340 2003 end
MMOSimca@340 2004 end
MMOSimca@340 2005 if faction_ID and faction_ID > 0 then
MMOSimca@340 2006 -- Check for modifiers from Commendations (applied directly to the faction, account-wide)
MMOSimca@340 2007 local _, _, _, _, _, _, _, _, _, _, _, _, _, _, has_bonus_rep_gain = GetFactionInfoByID(faction_ID)
MMOSimca@340 2008 if has_bonus_rep_gain then
MMOSimca@340 2009 modifier = modifier + 1
MMOSimca@340 2010 end
MMOSimca@340 2011 end
MMOSimca@340 2012
MMOSimca@340 2013 -- Check for modifiers from buffs
MMOSimca@418 2014 for buff_name, buff_data_table in pairs(private.REP_BUFFS) do
jcallahan@44 2015 if _G.UnitBuff("player", buff_name) then
MMOSimca@340 2016 local modded_faction = buff_data_table.faction
jcallahan@44 2017
jcallahan@44 2018 if not modded_faction or faction_name == modded_faction then
MMOSimca@340 2019 if buff_data_table.ignore then
MMOSimca@340 2020 -- Some buffs from tabards convert all rep of other factions into rep for a specific faction.
MMOSimca@340 2021 -- We can't know what faction the rep was orginally from, so we must ignore the data entirely in these cases.
MMOSimca@340 2022 return
MMOSimca@340 2023 else
MMOSimca@340 2024 modifier = modifier + buff_data_table.modifier
MMOSimca@340 2025 end
jcallahan@44 2026 end
jcallahan@44 2027 end
jcallahan@44 2028 end
catherton@465 2029
jcallahan@65 2030 npc.reputations = npc.reputations or {}
jcallahan@65 2031 npc.reputations[("%s:%s"):format(faction_name, faction_standings[faction_name])] = math.floor(amount / modifier)
jcallahan@32 2032 end
jcallahan@44 2033 end -- do-block
jcallahan@18 2034
jcallahan@18 2035
jcallahan@140 2036 function WDP:CURSOR_UPDATE(event_name)
MMOSimca@355 2037 if current_action.fishing_target or _G.Minimap:IsMouseOver() then
jcallahan@140 2038 return
jcallahan@140 2039 end
jcallahan@140 2040 local text = _G["GameTooltipTextLeft1"]:GetText()
jcallahan@140 2041
MMOSimca@355 2042 -- Handle Fishing
MMOSimca@355 2043 if (current_action.spell_label == "FISHING") then
MMOSimca@355 2044 if not text or text == "Fishing Bobber" then
MMOSimca@355 2045 text = "NONE"
MMOSimca@355 2046 else
MMOSimca@355 2047 current_action.fishing_target = true
MMOSimca@355 2048 end
MMOSimca@355 2049 current_action.identifier = ("%s:%s"):format(current_action.spell_label, text)
MMOSimca@355 2050 -- Handle Garrison Cache
MMOSimca@355 2051 elseif private.GARRISON_CACHE_OBJECT_NAME_TO_OBJECT_ID_MAP[text] then
MMOSimca@355 2052 last_garrison_cache_object_id = private.GARRISON_CACHE_OBJECT_NAME_TO_OBJECT_ID_MAP[text]
jcallahan@140 2053 end
jcallahan@140 2054 end
jcallahan@140 2055
jcallahan@140 2056
jcallahan@92 2057 function WDP:ITEM_TEXT_BEGIN(event_name)
jcallahan@42 2058 local unit_type, unit_idnum = ParseGUID(_G.UnitGUID("npc"))
jcallahan@42 2059
jcallahan@42 2060 if not unit_idnum or unit_type ~= private.UNIT_TYPES.OBJECT or _G.UnitName("npc") ~= _G.ItemTextGetItem() then
jcallahan@42 2061 return
jcallahan@42 2062 end
jcallahan@42 2063 UpdateDBEntryLocation("objects", unit_idnum)
jcallahan@42 2064 end
jcallahan@42 2065
jcallahan@42 2066
jcallahan@13 2067 do
MMOSimca@343 2068 local LOOT_OPENED_VERIFY_FUNCS = {
jcallahan@324 2069 -- 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 2070 [AF.ITEM] = function()
jcallahan@16 2071 local locked_item_id
jcallahan@16 2072
jcallahan@16 2073 for bag_index = 0, _G.NUM_BAG_FRAMES do
jcallahan@16 2074 for slot_index = 1, _G.GetContainerNumSlots(bag_index) do
jcallahan@324 2075 local _, _, is_locked, _, _, is_lootable = _G.GetContainerItemInfo(bag_index, slot_index)
jcallahan@324 2076
jcallahan@324 2077 if is_locked and is_lootable then
jcallahan@16 2078 locked_item_id = ItemLinkToID(_G.GetContainerItemLink(bag_index, slot_index))
jcallahan@165 2079 break
jcallahan@16 2080 end
jcallahan@16 2081 end
jcallahan@165 2082
jcallahan@165 2083 if locked_item_id then
jcallahan@165 2084 break
jcallahan@165 2085 end
jcallahan@16 2086 end
jcallahan@16 2087
MMOSimca@367 2088 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 2089 return false
jcallahan@16 2090 end
jcallahan@122 2091 current_action.identifier = locked_item_id
jcallahan@16 2092 return true
jcallahan@16 2093 end,
MMOSimca@401 2094 [AF.NPC] = function()
MMOSimca@401 2095 return not _G.IsFishingLoot()
MMOSimca@401 2096 end,
MMOSimca@401 2097 [AF.OBJECT] = function()
MMOSimca@401 2098 return not _G.IsFishingLoot()
MMOSimca@401 2099 end,
jcallahan@17 2100 [AF.ZONE] = function()
jcallahan@140 2101 current_action.zone_data = UpdateDBEntryLocation("zones", current_action.identifier)
jcallahan@210 2102 return _G.IsFishingLoot()
jcallahan@17 2103 end,
jcallahan@13 2104 }
jcallahan@13 2105
jcallahan@13 2106
MMOSimca@343 2107 local LOOT_OPENED_UPDATE_FUNCS = {
jcallahan@16 2108 [AF.ITEM] = function()
jcallahan@28 2109 GenericLootUpdate("items")
jcallahan@28 2110 end,
jcallahan@28 2111 [AF.NPC] = function()
jcallahan@75 2112 local difficulty_token = InstanceDifficultyToken()
jcallahan@312 2113 local loot_label = current_loot.label
jcallahan@77 2114 local source_list = {}
jcallahan@75 2115
jcallahan@131 2116 for source_guid, loot_data in pairs(current_loot.sources) do
jcallahan@331 2117 local _, source_id = ParseGUID(source_guid)
jcallahan@75 2118 local npc = NPCEntry(source_id)
jcallahan@75 2119
jcallahan@75 2120 if npc then
jcallahan@248 2121 local encounter_data = npc:EncounterData(difficulty_token)
jcallahan@312 2122 encounter_data[loot_label] = encounter_data[loot_label] or {}
jcallahan@75 2123
jcallahan@78 2124 if not source_list[source_guid] then
jcallahan@77 2125 encounter_data.loot_counts = encounter_data.loot_counts or {}
MMOSimca@426 2126 encounter_data.loot_counts[loot_label] = (encounter_data.loot_counts[loot_label] or 0) + 1
jcallahan@312 2127 source_list[source_guid] = true
jcallahan@77 2128 end
jcallahan@77 2129
jcallahan@309 2130 for loot_token, quantity in pairs(loot_data) do
mmosimca@496 2131 local loot_type, currency_id = (":"):split(loot_token)
mmosimca@496 2132
mmosimca@496 2133 if loot_type == "currency" and currency_id then
mmosimca@496 2134 -- Convert currency_id back into number from string
mmosimca@496 2135 currency_id = tonumber(currency_id) or 0
mmosimca@496 2136 if currency_id ~= 0 then
mmosimca@496 2137 table.insert(encounter_data[loot_label], ("currency:%d:%d"):format(quantity, currency_id))
mmosimca@496 2138 end
jcallahan@309 2139 elseif loot_token == "money" then
jcallahan@312 2140 table.insert(encounter_data[loot_label], ("money:%d"):format(quantity))
jcallahan@309 2141 else
jcallahan@312 2142 table.insert(encounter_data[loot_label], ("%d:%d"):format(loot_token, quantity))
jcallahan@309 2143 end
jcallahan@75 2144 end
jcallahan@75 2145 end
jcallahan@75 2146 end
jcallahan@16 2147 end,
jcallahan@13 2148 [AF.OBJECT] = function()
jcallahan@28 2149 GenericLootUpdate("objects", InstanceDifficultyToken())
jcallahan@17 2150 end,
jcallahan@17 2151 [AF.ZONE] = function()
MMOSimca@328 2152 if not (current_loot.map_level and current_loot.x and current_loot.y and current_loot.zone_data) then
MMOSimca@328 2153 return
MMOSimca@328 2154 end
jcallahan@141 2155 local location_token = ("%d:%d:%d"):format(current_loot.map_level, current_loot.x, current_loot.y)
jcallahan@41 2156
jcallahan@41 2157 -- This will start life as a boolean true.
mmosimca@522 2158 if type(current_loot.zone_data[location_token]) ~= "table" then
jcallahan@131 2159 current_loot.zone_data[location_token] = {
jcallahan@41 2160 drops = {}
jcallahan@41 2161 }
jcallahan@41 2162 end
jcallahan@132 2163 local loot_count = ("%s_count"):format(current_loot.label)
jcallahan@131 2164 current_loot.zone_data[location_token][loot_count] = (current_loot.zone_data[location_token][loot_count] or 0) + 1
jcallahan@41 2165
jcallahan@131 2166 if current_loot.sources then
jcallahan@131 2167 for source_guid, loot_data in pairs(current_loot.sources) do
jcallahan@131 2168 for item_id, quantity in pairs(loot_data) do
jcallahan@131 2169 table.insert(current_loot.zone_data[location_token].drops, ("%d:%d"):format(item_id, quantity))
jcallahan@131 2170 end
jcallahan@131 2171 end
jcallahan@131 2172 end
jcallahan@131 2173
jcallahan@131 2174 if #current_loot.list <= 0 then
jcallahan@131 2175 return
jcallahan@131 2176 end
jcallahan@131 2177
jcallahan@131 2178 for index = 1, #current_loot.list do
MMOSimca@443 2179 table.insert(current_loot.zone_data[location_token].drops, current_loot.list[index])
jcallahan@41 2180 end
jcallahan@13 2181 end,
jcallahan@13 2182 }
jcallahan@13 2183
jcallahan@79 2184 -- Prevent opening the same loot window multiple times from recording data multiple times.
jcallahan@79 2185 local loot_guid_registry = {}
jcallahan@124 2186
jcallahan@124 2187
jcallahan@124 2188 function WDP:LOOT_CLOSED(event_name)
MMOSimca@398 2189 ClearChatLootData()
jcallahan@131 2190 current_loot = nil
jcallahan@131 2191 table.wipe(current_action)
jcallahan@124 2192 end
jcallahan@124 2193
jcallahan@13 2194
jcallahan@322 2195 local function ExtrapolatedCurrentActionFromLootData(event_name)
MMOSimca@402 2196 local log_source = event_name .. "- ExtrapolatedCurrentActionFromLootData"
MMOSimca@402 2197 local previous_spell_label = current_action.spell_label
jcallahan@322 2198 local extrapolated_guid_registry = {}
jcallahan@322 2199 local num_guids = 0
MMOSimca@402 2200 table.wipe(current_action)
MMOSimca@402 2201
MMOSimca@402 2202 if _G.IsFishingLoot() then
MMOSimca@402 2203 -- Set up a proper 'fishing' current_action table
MMOSimca@402 2204 local zone_name, area_id, x, y, map_level, instance_token = CurrentLocationData()
MMOSimca@402 2205 if not (zone_name and area_id and x and y and map_level) then
mmosimca@508 2206 if not (_G.IsInInstance()) then
mmosimca@508 2207 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 2208 end
MMOSimca@402 2209 return
MMOSimca@402 2210 end
MMOSimca@402 2211 current_action.instance_token = instance_token
MMOSimca@402 2212 current_action.map_level = map_level
MMOSimca@402 2213 current_action.x = x
MMOSimca@402 2214 current_action.y = y
MMOSimca@402 2215 current_action.zone_data = ("%s:%d"):format(zone_name, area_id)
MMOSimca@402 2216 current_action.spell_label = "FISHING"
MMOSimca@402 2217 current_action.loot_label = "fishing"
MMOSimca@402 2218 current_action.identifier = "FISHING:NONE"
MMOSimca@402 2219 current_action.target_type = AF.ZONE
MMOSimca@402 2220
MMOSimca@402 2221 Debug("%s: Fishing loot detected.", log_source)
MMOSimca@402 2222 return true
MMOSimca@402 2223 end
jcallahan@322 2224
MMOSimca@344 2225 -- Loot extrapolation cannot handle objects that need special spell labels (like HERBALISM or MINING) (MIND_CONTROL is okay)
MMOSimca@402 2226 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 2227 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 2228 return false
MMOSimca@344 2229 end
MMOSimca@344 2230
jcallahan@322 2231 for loot_slot = 1, _G.GetNumLootItems() do
jcallahan@322 2232 local loot_info = {
jcallahan@322 2233 _G.GetLootSourceInfo(loot_slot)
jcallahan@322 2234 }
jcallahan@322 2235
jcallahan@322 2236 for loot_index = 1, #loot_info, 2 do
jcallahan@322 2237 local source_guid = loot_info[loot_index]
jcallahan@322 2238
jcallahan@322 2239 if not extrapolated_guid_registry[source_guid] then
jcallahan@322 2240 local unit_type, unit_idnum = ParseGUID(source_guid)
jcallahan@322 2241
jcallahan@322 2242 if unit_type and unit_idnum then
jcallahan@322 2243 extrapolated_guid_registry[source_guid] = {
jcallahan@322 2244 unit_type,
jcallahan@322 2245 unit_idnum
jcallahan@322 2246 }
jcallahan@322 2247
jcallahan@322 2248 num_guids = num_guids + 1
jcallahan@322 2249 end
jcallahan@322 2250 end
jcallahan@322 2251 end
jcallahan@322 2252 end
jcallahan@322 2253
jcallahan@322 2254 if num_guids == 0 then
jcallahan@322 2255 Debug("%s: No GUIDs found in loot. Blank loot window?", log_source)
jcallahan@322 2256 return false
jcallahan@322 2257 end
jcallahan@326 2258
jcallahan@322 2259 local num_npcs = 0
jcallahan@322 2260 local num_objects = 0
jcallahan@324 2261 local num_itemcontainers = 0
jcallahan@322 2262
jcallahan@322 2263 for source_guid, guid_data in pairs(extrapolated_guid_registry) do
jcallahan@322 2264 local unit_type = guid_data[1]
mmosimca@516 2265 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 2266
jcallahan@322 2267 if loot_label then
jcallahan@322 2268 local unit_idnum = guid_data[2]
jcallahan@322 2269
jcallahan@322 2270 if loot_guid_registry[loot_label] and loot_guid_registry[loot_label][source_guid] then
jcallahan@322 2271 Debug("%s: Previously scanned loot for unit with GUID %s and identifier %s.", log_source, source_guid, unit_idnum)
jcallahan@322 2272 elseif unit_type == private.UNIT_TYPES.OBJECT and unit_idnum ~= OBJECT_ID_FISHING_BOBBER then
jcallahan@322 2273 current_action.loot_label = loot_label
jcallahan@322 2274 current_action.spell_label = "OPENING"
jcallahan@322 2275 current_action.target_type = AF.OBJECT
jcallahan@322 2276 current_action.identifier = unit_idnum
jcallahan@322 2277 num_objects = num_objects + 1
jcallahan@322 2278 elseif UnitTypeIsNPC(unit_type) then
jcallahan@322 2279 current_action.loot_label = loot_label
jcallahan@322 2280 current_action.target_type = AF.NPC
jcallahan@322 2281 current_action.identifier = unit_idnum
jcallahan@322 2282 num_npcs = num_npcs + 1
mmosimca@516 2283 elseif unit_type == private.UNIT_TYPES.ITEM then
jcallahan@324 2284 current_action.loot_label = loot_label
jcallahan@324 2285 current_action.target_type = AF.ITEM
jcallahan@324 2286 -- current_action.identifier assigned during loot verification.
jcallahan@324 2287 num_itemcontainers = num_itemcontainers + 1
jcallahan@322 2288 end
jcallahan@322 2289 else
jcallahan@322 2290 -- 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 2291 Debug("%s: Unit with GUID %s has unsupported type for looting.", log_source, source_guid)
jcallahan@322 2292 return false
jcallahan@322 2293 end
jcallahan@322 2294 end
jcallahan@322 2295
jcallahan@322 2296 if not current_action.target_type then
jcallahan@322 2297 Debug("%s: Failure to obtain target_type.", log_source)
jcallahan@322 2298 return false
jcallahan@322 2299 end
jcallahan@322 2300
jcallahan@322 2301 -- 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 2302 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 2303 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 2304 return false
jcallahan@322 2305 end
jcallahan@322 2306
jcallahan@322 2307 return true
jcallahan@322 2308 end
jcallahan@322 2309
jcallahan@322 2310
MMOSimca@343 2311 function WDP:LOOT_OPENED(event_name)
MMOSimca@398 2312 ClearChatLootData()
MMOSimca@387 2313
jcallahan@132 2314 if current_loot then
jcallahan@322 2315 current_loot = nil
jcallahan@322 2316 Debug("%s: Previous loot did not process in time for this event. Attempting to extrapolate current_action from loot data.", event_name)
jcallahan@322 2317
jcallahan@322 2318 if not ExtrapolatedCurrentActionFromLootData(event_name) then
jcallahan@322 2319 Debug("%s: Unable to extrapolate current_action. Aborting attempts to handle loot for now.", event_name)
jcallahan@322 2320 return
jcallahan@322 2321 end
jcallahan@18 2322 end
jcallahan@151 2323
jcallahan@151 2324 if not current_action.target_type then
jcallahan@322 2325 if not ExtrapolatedCurrentActionFromLootData(event_name) then
jcallahan@322 2326 Debug("%s: Unable to extrapolate current_action. Aborting attempts to handle loot for now.", event_name)
jcallahan@322 2327 return
jcallahan@322 2328 end
jcallahan@151 2329 end
MMOSimca@343 2330 local verify_func = LOOT_OPENED_VERIFY_FUNCS[current_action.target_type]
MMOSimca@343 2331 local update_func = LOOT_OPENED_UPDATE_FUNCS[current_action.target_type]
jcallahan@13 2332
jcallahan@14 2333 if not verify_func or not update_func then
jcallahan@322 2334 Debug("%s: The current action's target type is unsupported or nil.", event_name)
jcallahan@13 2335 return
jcallahan@13 2336 end
jcallahan@13 2337
mmosimca@522 2338 if type(verify_func) == "function" and not verify_func() then
jcallahan@324 2339 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 2340 return
jcallahan@14 2341 end
jcallahan@80 2342 local guids_used = {}
jcallahan@132 2343
jcallahan@301 2344 InitializeCurrentLoot()
jcallahan@217 2345 loot_guid_registry[current_loot.label] = loot_guid_registry[current_loot.label] or {}
jcallahan@217 2346
jcallahan@55 2347 for loot_slot = 1, _G.GetNumLootItems() do
mmosimca@496 2348 local texture_filedata_id, item_text, slot_quantity, quality, locked = _G.GetLootSlotInfo(loot_slot)
jcallahan@55 2349 local slot_type = _G.GetLootSlotType(loot_slot)
catherton@463 2350 local loot_info = { _G.GetLootSourceInfo(loot_slot) }
catherton@464 2351 local loot_link = _G.GetLootSlotLink(loot_slot)
jcallahan@13 2352
jcallahan@237 2353 -- Odd index is GUID, even is count.
jcallahan@237 2354 for loot_index = 1, #loot_info, 2 do
jcallahan@237 2355 local source_guid = loot_info[loot_index]
jcallahan@75 2356
jcallahan@237 2357 if not loot_guid_registry[current_loot.label][source_guid] then
jcallahan@237 2358 local loot_quantity = loot_info[loot_index + 1]
jcallahan@324 2359 -- There is a new bug in 5.4.0 that causes GetLootSlotInfo() to (rarely) return nil values for slot_quantity.
jcallahan@324 2360 if slot_quantity then
jcallahan@324 2361 -- 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 2362 if slot_quantity > loot_quantity then
jcallahan@324 2363 loot_quantity = slot_quantity
jcallahan@324 2364 end
jcallahan@324 2365 local source_type, source_id = ParseGUID(source_guid)
MMOSimca@329 2366 local source_key = ("%s:%d"):format(source_type, source_id)
jcallahan@324 2367
MMOSimca@366 2368 if slot_type == LOOT_SLOT_ITEM then
catherton@464 2369 if loot_link then
catherton@464 2370 local item_id = ItemLinkToID(loot_link)
catherton@464 2371 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 2372 current_loot.sources[source_guid] = current_loot.sources[source_guid] or {}
catherton@464 2373 current_loot.sources[source_guid][item_id] = (current_loot.sources[source_guid][item_id] or 0) + loot_quantity
catherton@464 2374 guids_used[source_guid] = true
catherton@463 2375 else
catherton@463 2376 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 2377 end
MMOSimca@366 2378 elseif slot_type == LOOT_SLOT_MONEY then
jcallahan@324 2379 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 2380 if current_loot.target_type == AF.ZONE then
jcallahan@324 2381 table.insert(current_loot.list, ("money:%d"):format(loot_quantity))
jcallahan@324 2382 else
jcallahan@324 2383 current_loot.sources[source_guid] = current_loot.sources[source_guid] or {}
MMOSimca@367 2384 current_loot.sources[source_guid]["money"] = (current_loot.sources[source_guid]["money"] or 0) + loot_quantity
jcallahan@324 2385 guids_used[source_guid] = true
jcallahan@324 2386 end
MMOSimca@366 2387 elseif slot_type == LOOT_SLOT_CURRENCY then
jcallahan@324 2388 -- Same bug with GetLootSlotInfo() will screw up currency when it happens, so we won't process this slot's loot.
catherton@463 2389 if loot_link then
mmosimca@496 2390 local currency_id = CurrencyLinkToID(loot_link)
mmosimca@496 2391 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 2392 if current_loot.target_type == AF.ZONE then
mmosimca@496 2393 table.insert(current_loot.list, ("currency:%d:%d"):format(loot_quantity, currency_id))
jcallahan@324 2394 else
mmosimca@496 2395 local currency_token = ("currency:%d"):format(currency_id)
jcallahan@324 2396
jcallahan@324 2397 current_loot.sources[source_guid] = current_loot.sources[source_guid] or {}
MMOSimca@367 2398 current_loot.sources[source_guid][currency_token] = (current_loot.sources[source_guid][currency_token] or 0) + loot_quantity
jcallahan@324 2399 guids_used[source_guid] = true
jcallahan@324 2400 end
jcallahan@324 2401 else
catherton@463 2402 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 2403 end
jcallahan@308 2404 end
jcallahan@324 2405 else
jcallahan@324 2406 -- 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 2407 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 2408 end
jcallahan@75 2409 end
jcallahan@13 2410 end
jcallahan@13 2411 end
jcallahan@80 2412
jcallahan@81 2413 for guid in pairs(guids_used) do
jcallahan@217 2414 loot_guid_registry[current_loot.label][guid] = true
jcallahan@80 2415 end
jcallahan@13 2416 update_func()
jcallahan@1 2417 end
jcallahan@13 2418 end -- do-block
jcallahan@0 2419
jcallahan@0 2420
jcallahan@89 2421 function WDP:MAIL_SHOW(event_name)
MMOSimca@436 2422 WDP:StopChatLootRecording(event_name)
jcallahan@89 2423 local unit_type, unit_idnum = ParseGUID(_G.UnitGUID("npc"))
jcallahan@89 2424
jcallahan@89 2425 if not unit_idnum or unit_type ~= private.UNIT_TYPES.OBJECT then
jcallahan@89 2426 return
jcallahan@89 2427 end
jcallahan@89 2428 UpdateDBEntryLocation("objects", unit_idnum)
jcallahan@89 2429 end
jcallahan@89 2430
jcallahan@89 2431
jcallahan@44 2432 do
jcallahan@44 2433 local POINT_MATCH_PATTERNS = {
jcallahan@44 2434 ("^%s$"):format(_G.ITEM_REQ_ARENA_RATING:gsub("%%d", "(%%d+)")), -- May no longer be necessary
jcallahan@44 2435 ("^%s$"):format(_G.ITEM_REQ_ARENA_RATING_3V3:gsub("%%d", "(%%d+)")), -- May no longer be necessary
jcallahan@44 2436 ("^%s$"):format(_G.ITEM_REQ_ARENA_RATING_5V5:gsub("%%d", "(%%d+)")), -- May no longer be necessary
jcallahan@44 2437 ("^%s$"):format(_G.ITEM_REQ_ARENA_RATING_BG:gsub("%%d", "(%%d+)")),
jcallahan@44 2438 ("^%s$"):format(_G.ITEM_REQ_ARENA_RATING_3V3_BG:gsub("%%d", "(%%d+)")),
jcallahan@44 2439 }
jcallahan@5 2440
jcallahan@68 2441 local ITEM_REQ_REPUTATION_MATCH = "Requires (.-) %- (.*)"
jcallahan@87 2442 local ITEM_REQ_QUEST_MATCH1 = "Requires: .*"
jcallahan@87 2443 local ITEM_REQ_QUEST_MATCH2 = "Must have completed: .*"
jcallahan@68 2444
jcallahan@133 2445 local current_merchant
jcallahan@133 2446 local merchant_standing
jcallahan@133 2447
jcallahan@133 2448 function WDP:MERCHANT_CLOSED(event_name)
MMOSimca@436 2449 WDP:ResumeChatLootRecording(event_name)
jcallahan@133 2450 current_merchant = nil
jcallahan@133 2451 merchant_standing = nil
jcallahan@133 2452 end
jcallahan@133 2453
jcallahan@133 2454
jcallahan@89 2455 function WDP:UpdateMerchantItems(event_name)
jcallahan@144 2456 if not current_merchant or event_name == "MERCHANT_SHOW" then
MMOSimca@436 2457 WDP:StopChatLootRecording(event_name)
jcallahan@195 2458 local unit_type, unit_idnum = ParseGUID(_G.UnitGUID("npc"))
jcallahan@4 2459
jcallahan@171 2460 if not unit_idnum or not UnitTypeIsNPC(unit_type) then
jcallahan@133 2461 return
jcallahan@133 2462 end
jcallahan@331 2463 local _, faction_standing = UnitFactionStanding("npc")
jcallahan@331 2464 merchant_standing = faction_standing
jcallahan@133 2465 current_merchant = NPCEntry(unit_idnum)
jcallahan@133 2466 current_merchant.sells = current_merchant.sells or {}
jcallahan@44 2467 end
jcallahan@55 2468 local current_filters = _G.GetMerchantFilter()
jcallahan@57 2469 _G.SetMerchantFilter(_G.LE_LOOT_FILTER_ALL)
jcallahan@57 2470 _G.MerchantFrame_Update()
jcallahan@57 2471
jcallahan@150 2472 local num_items = _G.GetMerchantNumItems()
jcallahan@150 2473
jcallahan@44 2474 for item_index = 1, num_items do
jcallahan@44 2475 local _, _, copper_price, stack_size, num_available, _, extended_cost = _G.GetMerchantItemInfo(item_index)
jcallahan@44 2476 local item_id = ItemLinkToID(_G.GetMerchantItemLink(item_index))
jcallahan@5 2477
jcallahan@324 2478 DatamineTT:ClearLines()
jcallahan@324 2479 DatamineTT:SetMerchantItem(item_index)
jcallahan@324 2480
jcallahan@324 2481 if not item_id then
jcallahan@324 2482 local item_name, item_link = DatamineTT:GetItem()
jcallahan@324 2483 item_id = ItemLinkToID(item_link)
MMOSimca@354 2484 -- GetMerchantItemLink() still ocassionally fails as of Patch 6.0.2. It fails so badly that debug functions cause considerable slowdown.
jcallahan@324 2485 end
jcallahan@324 2486
jcallahan@44 2487 if item_id and item_id > 0 then
jcallahan@44 2488 local price_string = ActualCopperCost(copper_price, merchant_standing)
jcallahan@5 2489
jcallahan@68 2490 local num_lines = DatamineTT:NumLines()
jcallahan@68 2491
jcallahan@68 2492 for line_index = 1, num_lines do
jcallahan@68 2493 local current_line = _G["WDPDatamineTTTextLeft" .. line_index]
jcallahan@68 2494
jcallahan@68 2495 if not current_line then
jcallahan@68 2496 break
jcallahan@68 2497 end
jcallahan@68 2498 local faction, reputation = current_line:GetText():match(ITEM_REQ_REPUTATION_MATCH)
jcallahan@68 2499
jcallahan@68 2500 if faction or reputation then
jcallahan@68 2501 DBEntry("items", item_id).req_reputation = ("%s:%s"):format(faction:gsub("-", ""), reputation:upper())
jcallahan@68 2502 break
jcallahan@68 2503 end
jcallahan@68 2504 end
jcallahan@68 2505
jcallahan@87 2506 for line_index = 1, num_lines do
jcallahan@87 2507 local current_line = _G["WDPDatamineTTTextLeft" .. line_index]
jcallahan@87 2508
jcallahan@87 2509 if not current_line then
jcallahan@87 2510 break
jcallahan@87 2511 end
jcallahan@87 2512 local line_text = current_line:GetText()
jcallahan@87 2513 local quest_name = line_text:match(ITEM_REQ_QUEST_MATCH1) or line_text:match(ITEM_REQ_QUEST_MATCH2)
jcallahan@87 2514
jcallahan@87 2515 if quest_name then
jcallahan@87 2516 DBEntry("items", item_id).req_quest = ("%s"):format(quest_name:gsub("(.+): ", ""), quest_name)
jcallahan@87 2517 break
jcallahan@87 2518 end
jcallahan@87 2519 end
jcallahan@87 2520
jcallahan@44 2521 if extended_cost then
jcallahan@53 2522 local battleground_rating = 0
jcallahan@53 2523 local personal_rating = 0
jcallahan@53 2524 local required_season_amount
jcallahan@5 2525
jcallahan@68 2526 for line_index = 1, num_lines do
jcallahan@44 2527 local current_line = _G["WDPDatamineTTTextLeft" .. line_index]
jcallahan@5 2528
jcallahan@44 2529 if not current_line then
jcallahan@44 2530 break
jcallahan@44 2531 end
jcallahan@53 2532 required_season_amount = current_line:GetText():match("Requires earning a total of (%d+)\n(.-) for the season.")
jcallahan@5 2533
jcallahan@44 2534 for match_index = 1, #POINT_MATCH_PATTERNS do
jcallahan@44 2535 local match1, match2 = current_line:GetText():match(POINT_MATCH_PATTERNS[match_index])
jcallahan@53 2536 personal_rating = personal_rating + (match1 or 0)
jcallahan@53 2537 battleground_rating = battleground_rating + (match2 or 0)
jcallahan@5 2538
jcallahan@44 2539 if match1 or match2 then
jcallahan@44 2540 break
jcallahan@44 2541 end
jcallahan@44 2542 end
jcallahan@5 2543 end
jcallahan@44 2544 local currency_list = {}
jcallahan@44 2545 local item_count = _G.GetMerchantItemCostInfo(item_index)
jcallahan@50 2546
mmosimca@518 2547 -- Keeping this around in case Blizzard makes the two ratings (personal and battleground) diverge at some point
mmosimca@518 2548 --price_string = ("%s:%s:%s:%s"):format(price_string, battleground_rating, personal_rating, required_season_amount or 0)
jcallahan@53 2549 price_string = ("%s:%s:%s"):format(price_string, personal_rating, required_season_amount or 0)
jcallahan@5 2550
jcallahan@44 2551 for cost_index = 1, item_count do
jcallahan@324 2552 -- The third return (Blizz calls "currency_link") of GetMerchantItemCostItem only returns item links as of Patch 5.3.0.
mmosimca@496 2553 local texture_path, amount_required, item_link, name = _G.GetMerchantItemCostItem(item_index, cost_index)
mmosimca@496 2554
mmosimca@519 2555 -- Try to get item ID
mmosimca@519 2556 local item_id = ItemLinkToID(item_link)
mmosimca@519 2557
mmosimca@519 2558 -- 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 2559 -- Handle cases when the additional cost is another item
mmosimca@519 2560 if item_id and item_id > 0 then
mmosimca@519 2561 currency_list[#currency_list + 1] = ("(%s:%d)"):format(amount_required, item_id)
mmosimca@519 2562 -- Handle cases when the additional cost is another currency
mmosimca@519 2563 else
mmosimca@496 2564 local currency_id = CurrencyInfoToID(name, texture_path)
mmosimca@519 2565 if currency_id and currency_id > 0 then
mmosimca@501 2566 currency_list[#currency_list + 1] = ("(%s:%d)"):format(amount_required, currency_id)
mmosimca@501 2567 end
jcallahan@44 2568 end
jcallahan@44 2569 end
jcallahan@44 2570
jcallahan@44 2571 for currency_index = 1, #currency_list do
jcallahan@44 2572 price_string = ("%s:%s"):format(price_string, currency_list[currency_index])
jcallahan@5 2573 end
jcallahan@5 2574 end
jcallahan@133 2575 current_merchant.sells[item_id] = ("%s:%s:[%s]"):format(num_available, stack_size, price_string)
jcallahan@44 2576 end
jcallahan@44 2577 end
jcallahan@5 2578
jcallahan@44 2579 if _G.CanMerchantRepair() then
jcallahan@133 2580 current_merchant.can_repair = true
jcallahan@5 2581 end
jcallahan@57 2582 _G.SetMerchantFilter(current_filters)
jcallahan@57 2583 _G.MerchantFrame_Update()
jcallahan@4 2584 end
jcallahan@44 2585 end -- do-block
jcallahan@4 2586
jcallahan@89 2587
jcallahan@92 2588 function WDP:PET_BAR_UPDATE(event_name)
MMOSimca@336 2589 if not private.NON_LOOT_SPELL_LABELS[current_action.spell_label] then
jcallahan@25 2590 return
jcallahan@25 2591 end
jcallahan@34 2592 local unit_type, unit_idnum = ParseGUID(_G.UnitGUID("pet"))
jcallahan@25 2593
jcallahan@171 2594 if not unit_idnum or not UnitTypeIsNPC(unit_type) then
jcallahan@25 2595 return
jcallahan@25 2596 end
jcallahan@29 2597 NPCEntry(unit_idnum).mind_control = true
jcallahan@122 2598 table.wipe(current_action)
jcallahan@25 2599 end
jcallahan@25 2600
jcallahan@25 2601
MMOSimca@368 2602 -- This function produces data currently unused by wowdb.com, and it causes unneeded bloat in the raw lua DB.
MMOSimca@442 2603 --[[local LPJ = LibStub("LibPetJournal-2.0")
MMOSimca@442 2604 function WDP:PET_JOURNAL_LIST_UPDATE(event_name)
MMOSimca@346 2605 if DEBUGGING then
jcallahan@309 2606 return
jcallahan@309 2607 end
jcallahan@309 2608
jcallahan@115 2609 local num_pets = LPJ:NumPets()
jcallahan@115 2610
jcallahan@115 2611 for index, pet_id in LPJ:IteratePetIDs() do
jcallahan@115 2612 local _, _, is_owned, _, level, _, _, name, icon, pet_type, npc_id, _, _, is_wild = _G.C_PetJournal.GetPetInfoByIndex(index)
jcallahan@115 2613
jcallahan@115 2614 if is_owned then
jcallahan@115 2615 local health, max_health, attack, speed, rarity = _G.C_PetJournal.GetPetStats(pet_id)
jcallahan@115 2616
jcallahan@139 2617 if rarity then
jcallahan@139 2618 local rarity_name = _G["BATTLE_PET_BREED_QUALITY" .. rarity]
jcallahan@139 2619 local npc = NPCEntry(npc_id)
jcallahan@139 2620 npc.wild_pet = is_wild or nil
jcallahan@139 2621 npc.battle_pet_data = npc.battle_pet_data or {}
jcallahan@139 2622 npc.battle_pet_data[rarity_name] = npc.battle_pet_data[rarity_name] or {}
jcallahan@139 2623 npc.battle_pet_data[rarity_name]["level_" .. level] = npc.battle_pet_data[rarity_name]["level_" .. level] or {}
jcallahan@139 2624
jcallahan@139 2625 local data = npc.battle_pet_data[rarity_name]["level_" .. level]
jcallahan@139 2626 data.max_health = max_health
jcallahan@139 2627 data.attack = attack
jcallahan@139 2628 data.speed = speed
jcallahan@139 2629 end
jcallahan@115 2630 end
jcallahan@115 2631 end
MMOSimca@368 2632 end]]--
jcallahan@115 2633
jcallahan@115 2634
jcallahan@156 2635 function WDP:PLAYER_REGEN_DISABLED(event_name)
jcallahan@156 2636 private.in_combat = true
jcallahan@156 2637 end
jcallahan@156 2638
jcallahan@156 2639
jcallahan@156 2640 function WDP:PLAYER_REGEN_ENABLED(event_name)
jcallahan@156 2641 private.in_combat = nil
jcallahan@156 2642 end
jcallahan@156 2643
jcallahan@156 2644
jcallahan@118 2645 function WDP:PLAYER_TARGET_CHANGED(event_name)
jcallahan@215 2646 if not TargetedNPC() then
jcallahan@118 2647 return
jcallahan@2 2648 end
jcallahan@151 2649 current_action.target_type = AF.NPC
jcallahan@118 2650 self:UpdateTargetLocation()
jcallahan@118 2651 end
jcallahan@2 2652
jcallahan@89 2653
jcallahan@12 2654 do
jcallahan@12 2655 local function UpdateQuestJuncture(point)
jcallahan@12 2656 local unit_name = _G.UnitName("questnpc")
jcallahan@9 2657
jcallahan@12 2658 if not unit_name then
jcallahan@12 2659 return
jcallahan@12 2660 end
jcallahan@34 2661 local unit_type, unit_id = ParseGUID(_G.UnitGUID("questnpc"))
MMOSimca@351 2662 Debug("UpdateQuestJuncture: Updating quest juncture for %s.", ("%s:%d"):format(private.UNIT_TYPE_NAMES[unit_type], unit_id))
jcallahan@12 2663 if unit_type == private.UNIT_TYPES.OBJECT then
jcallahan@38 2664 UpdateDBEntryLocation("objects", unit_id)
jcallahan@12 2665 end
jcallahan@19 2666 local quest = DBEntry("quests", _G.GetQuestID())
jcallahan@12 2667 quest[point] = quest[point] or {}
MMOSimca@329 2668 quest[point][("%s:%d"):format(private.UNIT_TYPE_NAMES[unit_type], unit_id)] = true
jcallahan@24 2669
jcallahan@24 2670 return quest
jcallahan@12 2671 end
jcallahan@10 2672
jcallahan@12 2673
jcallahan@92 2674 function WDP:QUEST_COMPLETE(event_name)
jcallahan@97 2675 local quest = UpdateQuestJuncture("end")
catherton@465 2676
MMOSimca@446 2677 if not quest then
MMOSimca@446 2678 return
MMOSimca@446 2679 end
jcallahan@97 2680
jcallahan@112 2681 if ALLOWED_LOCALES[CLIENT_LOCALE] then
jcallahan@112 2682 quest.reward_text = ReplaceKeywords(_G.GetRewardText())
jcallahan@112 2683 end
jcallahan@67 2684 -- Make sure the quest NPC isn't erroneously recorded as giving reputation for quests which award it.
jcallahan@177 2685 ClearKilledNPC()
jcallahan@10 2686 end
jcallahan@10 2687
jcallahan@12 2688
jcallahan@92 2689 function WDP:QUEST_DETAIL(event_name)
jcallahan@24 2690 local quest = UpdateQuestJuncture("begin")
jcallahan@24 2691
jcallahan@46 2692 if not quest then
jcallahan@46 2693 return
jcallahan@46 2694 end
jcallahan@24 2695 quest.classes = quest.classes or {}
jcallahan@27 2696 quest.classes[PLAYER_CLASS] = true
jcallahan@24 2697
jcallahan@24 2698 quest.races = quest.races or {}
jcallahan@100 2699 quest.races[(PLAYER_RACE == "Pandaren") and ("%s_%s"):format(PLAYER_RACE, PLAYER_FACTION or "Neutral") or PLAYER_RACE] = true
jcallahan@10 2700 end
jcallahan@12 2701 end -- do-block
jcallahan@9 2702
jcallahan@9 2703
jcallahan@92 2704 function WDP:QUEST_LOG_UPDATE(event_name)
jcallahan@38 2705 local selected_quest = _G.GetQuestLogSelection() -- Save current selection to be restored when we're done.
jcallahan@36 2706 local entry_index, processed_quests = 1, 0
jcallahan@36 2707 local _, num_quests = _G.GetNumQuestLogEntries()
jcallahan@36 2708
jcallahan@36 2709 while processed_quests <= num_quests do
MMOSimca@329 2710 local _, _, _, is_header, _, _, _, quest_id = _G.GetQuestLogTitle(entry_index)
jcallahan@36 2711
jcallahan@84 2712 if quest_id == 0 then
jcallahan@84 2713 processed_quests = processed_quests + 1
jcallahan@84 2714 elseif not is_header then
jcallahan@36 2715 _G.SelectQuestLogEntry(entry_index);
jcallahan@36 2716
jcallahan@36 2717 local quest = DBEntry("quests", quest_id)
jcallahan@36 2718 quest.timer = _G.GetQuestLogTimeLeft()
jcallahan@37 2719 quest.can_share = _G.GetQuestLogPushable() and true or nil
jcallahan@36 2720 processed_quests = processed_quests + 1
jcallahan@36 2721 end
jcallahan@36 2722 entry_index = entry_index + 1
jcallahan@36 2723 end
jcallahan@36 2724 _G.SelectQuestLogEntry(selected_quest)
jcallahan@4 2725 self:UnregisterEvent("QUEST_LOG_UPDATE")
jcallahan@4 2726 end
jcallahan@4 2727
jcallahan@4 2728
jcallahan@97 2729 function WDP:QUEST_PROGRESS(event_name)
jcallahan@112 2730 if not ALLOWED_LOCALES[CLIENT_LOCALE] then
jcallahan@112 2731 return
jcallahan@112 2732 end
jcallahan@97 2733 DBEntry("quests", _G.GetQuestID()).progress_text = ReplaceKeywords(_G.GetProgressText())
jcallahan@97 2734 end
jcallahan@97 2735
jcallahan@97 2736
jcallahan@92 2737 function WDP:UNIT_QUEST_LOG_CHANGED(event_name, unit_id)
jcallahan@4 2738 if unit_id ~= "player" then
jcallahan@4 2739 return
jcallahan@4 2740 end
jcallahan@4 2741 self:RegisterEvent("QUEST_LOG_UPDATE")
jcallahan@4 2742 end
jcallahan@4 2743
jcallahan@4 2744
jcallahan@92 2745 do
jcallahan@92 2746 local TRADESKILL_TOOLS = {
jcallahan@92 2747 Anvil = anvil_spell_ids,
jcallahan@92 2748 Forge = forge_spell_ids,
jcallahan@92 2749 }
jcallahan@92 2750
jcallahan@92 2751
jcallahan@167 2752 local function RegisterTools(tradeskill_name, tradeskill_index)
catherton@479 2753 local link = _G.C_TradeSkillUI.GetRecipeLink(tradeskill_index)
catherton@465 2754
MMOSimca@352 2755 if link then
MMOSimca@352 2756 local spell_id = tonumber(link:match("^|c%x%x%x%x%x%x%x%x|H%w+:(%d+)"))
catherton@479 2757 local required_tool = _G.C_TradeSkillUI.GetRecipeTools(tradeskill_index)
MMOSimca@352 2758
MMOSimca@352 2759 if required_tool then
MMOSimca@352 2760 for tool_name, registry in pairs(TRADESKILL_TOOLS) do
MMOSimca@352 2761 if required_tool:find(tool_name) then
MMOSimca@352 2762 registry[spell_id] = true
MMOSimca@352 2763 end
jcallahan@167 2764 end
jcallahan@167 2765 end
jcallahan@167 2766 end
jcallahan@167 2767 end
jcallahan@167 2768
jcallahan@167 2769
jcallahan@92 2770 function WDP:TRADE_SKILL_SHOW(event_name)
catherton@479 2771 local profession_name, prof_level = _G.C_TradeSkillUI.GetTradeSkillLine()
jcallahan@92 2772
jcallahan@92 2773 if profession_name == _G.UNKNOWN then
jcallahan@92 2774 return
jcallahan@92 2775 end
jcallahan@167 2776 TradeSkillExecutePer(RegisterTools)
jcallahan@92 2777 end
jcallahan@92 2778 end -- do-block
jcallahan@92 2779
jcallahan@92 2780
jcallahan@167 2781 function WDP:TRAINER_CLOSED(event_name)
jcallahan@167 2782 private.trainer_shown = nil
jcallahan@167 2783 end
jcallahan@167 2784
jcallahan@167 2785
jcallahan@92 2786 function WDP:TRAINER_SHOW(event_name)
jcallahan@232 2787 local unit_type, unit_idnum = ParseGUID(_G.UnitGUID("npc"))
jcallahan@164 2788 local trainer = NPCEntry(unit_idnum)
jcallahan@58 2789
jcallahan@164 2790 if not trainer then
jcallahan@58 2791 return
jcallahan@58 2792 end
jcallahan@331 2793 local _, trainer_standing = UnitFactionStanding("npc")
jcallahan@164 2794 trainer.teaches = trainer.teaches or {}
jcallahan@27 2795
jcallahan@167 2796 private.trainer_shown = true
jcallahan@167 2797
jcallahan@27 2798 -- Get the initial trainer filters
MMOSimca@344 2799 local available = _G.GetTrainerServiceTypeFilter("available") and 1 or 0
MMOSimca@344 2800 local unavailable = _G.GetTrainerServiceTypeFilter("unavailable") and 1 or 0
MMOSimca@344 2801 local used = _G.GetTrainerServiceTypeFilter("used") and 1 or 0
jcallahan@27 2802
jcallahan@27 2803 -- Clear the trainer filters
MMOSimca@344 2804 _G.SetTrainerServiceTypeFilter("available", 1)
MMOSimca@344 2805 _G.SetTrainerServiceTypeFilter("unavailable", 1)
MMOSimca@344 2806 _G.SetTrainerServiceTypeFilter("used", 1)
jcallahan@27 2807
jcallahan@27 2808 for index = 1, _G.GetNumTrainerServices(), 1 do
jcallahan@27 2809 local spell_name, rank_name, _, _, required_level = _G.GetTrainerServiceInfo(index)
jcallahan@27 2810
jcallahan@27 2811 if spell_name then
jcallahan@27 2812 DatamineTT:ClearLines()
jcallahan@27 2813 DatamineTT:SetTrainerService(index)
jcallahan@27 2814
jcallahan@27 2815 local _, _, spell_id = DatamineTT:GetSpell()
jcallahan@27 2816
jcallahan@43 2817 if spell_id then
jcallahan@164 2818 local class_professions = trainer.teaches[PLAYER_CLASS]
jcallahan@164 2819
jcallahan@164 2820 if not class_professions then
jcallahan@164 2821 trainer.teaches[PLAYER_CLASS] = {}
jcallahan@164 2822 class_professions = trainer.teaches[PLAYER_CLASS]
jcallahan@164 2823 end
jcallahan@43 2824 local profession, min_skill = _G.GetTrainerServiceSkillReq(index)
jcallahan@43 2825 profession = profession or "General"
jcallahan@43 2826
jcallahan@164 2827 local profession_skills = class_professions[profession]
jcallahan@43 2828
jcallahan@43 2829 if not profession_skills then
jcallahan@43 2830 class_professions[profession] = {}
jcallahan@43 2831 profession_skills = class_professions[profession]
jcallahan@43 2832 end
jcallahan@173 2833 profession_skills[spell_id] = ("%d:%d:%d"):format(required_level, min_skill, _G.GetTrainerServiceCost(index))
jcallahan@27 2834 end
jcallahan@27 2835 end
jcallahan@27 2836 end
jcallahan@27 2837 -- Reset the filters to what they were before
MMOSimca@344 2838 _G.SetTrainerServiceTypeFilter("available", available or 0)
MMOSimca@344 2839 _G.SetTrainerServiceTypeFilter("unavailable", unavailable or 0)
MMOSimca@344 2840 _G.SetTrainerServiceTypeFilter("used", used or 0)
jcallahan@27 2841 end
jcallahan@27 2842
jcallahan@27 2843
jcallahan@1 2844 function WDP:UNIT_SPELLCAST_SENT(event_name, unit_id, spell_name, spell_rank, target_name, spell_line)
jcallahan@1 2845 if private.tracked_line or unit_id ~= "player" then
jcallahan@1 2846 return
jcallahan@1 2847 end
jcallahan@1 2848 local spell_label = private.SPELL_LABELS_BY_NAME[spell_name]
jcallahan@1 2849
jcallahan@1 2850 if not spell_label then
jcallahan@1 2851 return
jcallahan@1 2852 end
jcallahan@306 2853
MMOSimca@343 2854 Debug("UNIT_SPELLCAST_SENT: %s was cast.", spell_name)
jcallahan@150 2855 local item_name, item_link = _G.GameTooltip:GetItem()
jcallahan@150 2856 local unit_name, unit_id = _G.GameTooltip:GetUnit()
jcallahan@1 2857
jcallahan@150 2858 if not unit_name and _G.UnitName("target") == target_name then
jcallahan@150 2859 unit_name = target_name
jcallahan@150 2860 unit_id = "target"
jcallahan@1 2861 end
jcallahan@1 2862 local spell_flags = private.SPELL_FLAGS_BY_LABEL[spell_label]
jcallahan@28 2863 local zone_name, area_id, x, y, map_level, instance_token = CurrentLocationData()
MMOSimca@328 2864 if not (zone_name and area_id and x and y and map_level) then
mmosimca@508 2865 if not (_G.IsInInstance()) then
mmosimca@508 2866 Debug("%s: Missing current location data - %s, %s, %s, %s, %s.", event_name, tostring(zone_name), tostring(area_id), tostring(x), tostring(y), tostring(map_level))
mmosimca@508 2867 end
MMOSimca@328 2868 return
MMOSimca@328 2869 end
jcallahan@28 2870
jcallahan@205 2871 table.wipe(current_action)
jcallahan@122 2872 current_action.instance_token = instance_token
jcallahan@122 2873 current_action.map_level = map_level
jcallahan@122 2874 current_action.x = x
jcallahan@122 2875 current_action.y = y
jcallahan@122 2876 current_action.zone_data = ("%s:%d"):format(zone_name, area_id)
jcallahan@122 2877 current_action.spell_label = spell_label
jcallahan@105 2878
jcallahan@105 2879 if not private.NON_LOOT_SPELL_LABELS[spell_label] then
jcallahan@122 2880 current_action.loot_label = spell_label:lower()
jcallahan@105 2881 end
jcallahan@1 2882
jcallahan@151 2883 if unit_name and unit_name == target_name and not item_name then
jcallahan@16 2884 if bit.band(spell_flags, AF.NPC) == AF.NPC then
jcallahan@150 2885 if not unit_id or unit_name ~= target_name then
jcallahan@16 2886 return
jcallahan@16 2887 end
jcallahan@123 2888 current_action.target_type = AF.NPC
jcallahan@16 2889 end
jcallahan@16 2890 elseif bit.band(spell_flags, AF.ITEM) == AF.ITEM then
jcallahan@123 2891 current_action.target_type = AF.ITEM
jcallahan@16 2892
jcallahan@150 2893 if item_name and item_name == target_name then
jcallahan@150 2894 current_action.identifier = ItemLinkToID(item_link)
jcallahan@16 2895 elseif target_name and target_name ~= "" then
jcallahan@331 2896 local _, item_link = _G.GetItemInfo(target_name)
jcallahan@331 2897 current_action.identifier = ItemLinkToID(item_link)
jcallahan@16 2898 end
jcallahan@150 2899 elseif not item_name and not unit_name then
jcallahan@1 2900 if bit.band(spell_flags, AF.OBJECT) == AF.OBJECT then
jcallahan@17 2901 if target_name == "" then
jcallahan@17 2902 return
jcallahan@17 2903 end
jcallahan@122 2904 current_action.object_name = target_name
jcallahan@123 2905 current_action.target_type = AF.OBJECT
jcallahan@1 2906 elseif bit.band(spell_flags, AF.ZONE) == AF.ZONE then
jcallahan@123 2907 current_action.target_type = AF.ZONE
jcallahan@1 2908 end
jcallahan@1 2909 end
jcallahan@1 2910 private.tracked_line = spell_line
jcallahan@0 2911 end
jcallahan@0 2912
jcallahan@94 2913
MMOSimca@393 2914 -- Triggered by bonus roll prompts, disenchant prompts, and in a few other rare circumstances
jcallahan@312 2915 function WDP:SPELL_CONFIRMATION_PROMPT(event_name, spell_id, confirm_type, text, duration, currency_id_cost)
jcallahan@306 2916 if private.RAID_BOSS_BONUS_SPELL_ID_TO_NPC_ID_MAP[spell_id] then
jcallahan@306 2917 ClearKilledBossID()
jcallahan@306 2918 ClearLootToastContainerID()
MMOSimca@387 2919 raid_boss_id = private.RAID_BOSS_BONUS_SPELL_ID_TO_NPC_ID_MAP[spell_id]
jcallahan@306 2920 else
MMOSimca@336 2921 Debug("%s: Spell ID %d is not a known raid boss 'Bonus' spell.", event_name, spell_id)
jcallahan@306 2922 return
jcallahan@1 2923 end
jcallahan@306 2924
jcallahan@324 2925 -- Assign existing loot data to boss if it exists
jcallahan@307 2926 if loot_toast_data then
MMOSimca@427 2927 local npc = NPCEntry(raid_boss_id)
MMOSimca@427 2928 if npc then
MMOSimca@427 2929 -- Create needed npc fields if required
MMOSimca@427 2930 local loot_label = "drops"
MMOSimca@427 2931 local encounter_data = npc:EncounterData(InstanceDifficultyToken())
MMOSimca@427 2932 encounter_data[loot_label] = encounter_data[loot_label] or {}
MMOSimca@427 2933 encounter_data.loot_counts = encounter_data.loot_counts or {}
MMOSimca@427 2934
MMOSimca@427 2935 for index = 1, #loot_toast_data do
MMOSimca@427 2936 local data = loot_toast_data[index]
MMOSimca@427 2937 local loot_type = data[1]
MMOSimca@427 2938 local hyperlink = data[2]
MMOSimca@427 2939 local quantity = data[3]
MMOSimca@427 2940
MMOSimca@427 2941 if loot_type == "item" then
MMOSimca@427 2942 local item_id = ItemLinkToID(hyperlink)
MMOSimca@427 2943 Debug("%s: Assigned stored item loot data - %s - %d:%d", event_name, hyperlink, item_id, quantity)
MMOSimca@427 2944 table.insert(encounter_data[loot_label], ("%d:%d"):format(item_id, quantity))
MMOSimca@427 2945 elseif loot_type == "money" then
MMOSimca@427 2946 Debug("%s: Assigned stored money loot data - money:%d", event_name, quantity)
MMOSimca@427 2947 table.insert(encounter_data[loot_label], ("money:%d"):format(quantity))
MMOSimca@427 2948 elseif loot_type == "currency" then
mmosimca@496 2949 local currency_id = CurrencyLinkToID(hyperlink)
mmosimca@496 2950 Debug("%s: Assigned stored currency loot data - %s - currency:%d (%d)", event_name, hyperlink, currency_id, quantity)
mmosimca@496 2951 table.insert(encounter_data[loot_label], ("currency:%d:%d"):format(quantity, currency_id))
MMOSimca@427 2952 end
jcallahan@317 2953 end
jcallahan@317 2954
MMOSimca@427 2955 if not boss_loot_toasting[raid_boss_id] then
MMOSimca@427 2956 encounter_data.loot_counts[loot_label] = (encounter_data.loot_counts[loot_label] or 0) + 1
MMOSimca@427 2957 boss_loot_toasting[raid_boss_id] = true -- Do not count further loots until timer expires or another boss is killed
jcallahan@307 2958 end
MMOSimca@427 2959 else
MMOSimca@427 2960 Debug("%s: NPC is nil, but we have stored loot data...", event_name)
jcallahan@307 2961 end
jcallahan@307 2962 end
jcallahan@307 2963
jcallahan@307 2964 ClearLootToastData()
MMOSimca@427 2965 killed_boss_id_timer_handle = C_Timer.NewTimer(5, ClearKilledBossID)
jcallahan@306 2966 end
jcallahan@306 2967
jcallahan@306 2968
jcallahan@306 2969 function WDP:UNIT_SPELLCAST_SUCCEEDED(event_name, unit_id, spell_name, spell_rank, spell_line, spell_id)
jcallahan@306 2970 if unit_id ~= "player" then
jcallahan@306 2971 return
jcallahan@306 2972 end
jcallahan@306 2973 private.tracked_line = nil
jcallahan@306 2974 private.previous_spell_id = spell_id
jcallahan@306 2975
MMOSimca@393 2976 -- For spells cast when Logging
MMOSimca@345 2977 if private.LOGGING_SPELL_ID_TO_OBJECT_ID_MAP[spell_id] then
MMOSimca@345 2978 last_timber_spell_id = spell_id
MMOSimca@351 2979 UpdateDBEntryLocation("objects", ("OPENING:%s"):format(private.LOGGING_SPELL_ID_TO_OBJECT_ID_MAP[spell_id]))
MMOSimca@345 2980 return
MMOSimca@345 2981 end
MMOSimca@345 2982
MMOSimca@393 2983 -- For spells cast by items that always trigger loot toasts
MMOSimca@381 2984 if private.LOOT_TOAST_CONTAINER_SPELL_ID_TO_ITEM_ID_MAP[spell_id] then
jcallahan@306 2985 ClearKilledBossID()
jcallahan@306 2986 ClearLootToastContainerID()
jcallahan@307 2987 ClearLootToastData()
jcallahan@306 2988
MMOSimca@387 2989 loot_toast_container_id = private.LOOT_TOAST_CONTAINER_SPELL_ID_TO_ITEM_ID_MAP[spell_id]
MMOSimca@383 2990 loot_toast_container_timer_handle = C_Timer.NewTimer(1, ClearLootToastContainerID) -- we need to assign a handle here to cancel it later
MMOSimca@345 2991 return
jcallahan@306 2992 end
jcallahan@306 2993
MMOSimca@393 2994 -- For spells cast by items that don't usually trigger loot toasts
catherton@473 2995 if not block_chat_loot_data and (private.DELAYED_CONTAINER_SPELL_ID_TO_ITEM_ID_MAP[spell_id] or (private.DELAYED_CONTAINER_SPELL_ID_TO_ITEM_ID_BY_CLASS_ID_MAP[spell_id] and private.DELAYED_CONTAINER_SPELL_ID_TO_ITEM_ID_BY_CLASS_ID_MAP[spell_id][PLAYER_CLASS_ID])) then
MMOSimca@347 2996 -- Set up timer
MMOSimca@393 2997 ClearChatLootData()
MMOSimca@393 2998 Debug("%s: Beginning chat-based loot timer for spellID %d", event_name, spell_id)
MMOSimca@411 2999 chat_loot_timer_handle = C_Timer.NewTimer(1.5, ClearChatLootData)
catherton@473 3000 if (private.DELAYED_CONTAINER_SPELL_ID_TO_ITEM_ID_BY_CLASS_ID_MAP[spell_id] and private.DELAYED_CONTAINER_SPELL_ID_TO_ITEM_ID_BY_CLASS_ID_MAP[spell_id][PLAYER_CLASS_ID]) then
catherton@473 3001 chat_loot_data.identifier = private.DELAYED_CONTAINER_SPELL_ID_TO_ITEM_ID_BY_CLASS_ID_MAP[spell_id][PLAYER_CLASS_ID]
MMOSimca@454 3002 else
MMOSimca@454 3003 chat_loot_data.identifier = private.DELAYED_CONTAINER_SPELL_ID_TO_ITEM_ID_MAP[spell_id]
MMOSimca@454 3004 end
MMOSimca@347 3005 return
MMOSimca@347 3006 end
MMOSimca@347 3007
mmosimca@520 3008 -- 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 3009 local text = _G["GameTooltipTextLeft1"] and _G["GameTooltipTextLeft1"]:GetText() or nil
mmosimca@520 3010 if spell_id == SPELL_ID_UPDATE_INTERACTIONS and text and text == "Ephemeral Crystal" then
mmosimca@522 3011 Debug("%s: Detected location for an Ephemeral Crystal.", event_name)
mmosimca@520 3012 for index = 1, #private.EPHEMERAL_CRYSTAL_OBJECT_IDS do
mmosimca@520 3013 UpdateDBEntryLocation("objects", private.EPHEMERAL_CRYSTAL_OBJECT_IDS[index])
mmosimca@520 3014 end
mmosimca@520 3015 end
mmosimca@520 3016
jcallahan@306 3017 if anvil_spell_ids[spell_id] then
jcallahan@306 3018 UpdateDBEntryLocation("objects", OBJECT_ID_ANVIL)
jcallahan@306 3019 elseif forge_spell_ids[spell_id] then
jcallahan@306 3020 UpdateDBEntryLocation("objects", OBJECT_ID_FORGE)
jcallahan@306 3021 elseif spell_name:match("^Harvest.+") then
jcallahan@306 3022 killed_npc_id = current_target_id
MMOSimca@343 3023 private.harvesting = true -- Used to track which NPCs can be harvested (can we get this from CreatureCache instead?)
jcallahan@306 3024 end
jcallahan@306 3025 end
jcallahan@0 3026
jcallahan@90 3027
jcallahan@1 3028 function WDP:HandleSpellFailure(event_name, unit_id, spell_name, spell_rank, spell_line, spell_id)
jcallahan@1 3029 if unit_id ~= "player" then
jcallahan@1 3030 return
jcallahan@1 3031 end
jcallahan@0 3032
jcallahan@1 3033 if private.tracked_line == spell_line then
jcallahan@1 3034 private.tracked_line = nil
jcallahan@1 3035 end
jcallahan@147 3036 table.wipe(current_action)
jcallahan@0 3037 end
jcallahan@90 3038
jcallahan@90 3039
jcallahan@90 3040 do
jcallahan@90 3041 local function SetUnitField(field, required_type)
jcallahan@90 3042 local unit_type, unit_idnum = ParseGUID(_G.UnitGUID("npc"))
jcallahan@90 3043
jcallahan@90 3044 if not unit_idnum or (required_type and unit_type ~= required_type) then
jcallahan@90 3045 return
jcallahan@90 3046 end
jcallahan@90 3047
jcallahan@171 3048 if UnitTypeIsNPC(unit_type) then
jcallahan@90 3049 NPCEntry(unit_idnum)[field] = true
jcallahan@90 3050 elseif unit_type == private.UNIT_TYPES.OBJECT then
jcallahan@90 3051 DBEntry("objects", unit_idnum)[field] = true
jcallahan@93 3052 UpdateDBEntryLocation("objects", unit_idnum)
jcallahan@90 3053 end
jcallahan@90 3054 end
jcallahan@90 3055
jcallahan@90 3056
jcallahan@90 3057 function WDP:AUCTION_HOUSE_SHOW(event_name)
MMOSimca@436 3058 WDP:StopChatLootRecording(event_name)
jcallahan@90 3059 SetUnitField("auctioneer", private.UNIT_TYPES.NPC)
jcallahan@90 3060 end
jcallahan@90 3061
jcallahan@90 3062
jcallahan@90 3063 function WDP:BANKFRAME_OPENED(event_name)
MMOSimca@436 3064 WDP:StopChatLootRecording(event_name)
jcallahan@90 3065 SetUnitField("banker", private.UNIT_TYPES.NPC)
jcallahan@90 3066 end
jcallahan@90 3067
jcallahan@90 3068
jcallahan@90 3069 function WDP:BATTLEFIELDS_SHOW(event_name)
jcallahan@90 3070 SetUnitField("battlemaster", private.UNIT_TYPES.NPC)
jcallahan@90 3071 end
jcallahan@90 3072
jcallahan@90 3073
jcallahan@92 3074 function WDP:FORGE_MASTER_OPENED(event_name)
jcallahan@90 3075 SetUnitField("arcane_reforger", private.UNIT_TYPES.NPC)
jcallahan@90 3076 end
jcallahan@90 3077
jcallahan@90 3078
jcallahan@323 3079 local GOSSIP_SHOW_FUNCS = {
jcallahan@323 3080 [private.UNIT_TYPES.NPC] = function(unit_idnum)
jcallahan@323 3081 local gossip_options = { _G.GetGossipOptions() }
jcallahan@323 3082
jcallahan@323 3083 for index = 2, #gossip_options, 2 do
jcallahan@323 3084 if gossip_options[index] == "binder" then
jcallahan@323 3085 SetUnitField("innkeeper", private.UNIT_TYPES.NPC)
jcallahan@323 3086 return
jcallahan@323 3087 end
jcallahan@323 3088 end
jcallahan@323 3089 end,
jcallahan@323 3090 [private.UNIT_TYPES.OBJECT] = function(unit_idnum)
jcallahan@323 3091 UpdateDBEntryLocation("objects", unit_idnum)
jcallahan@323 3092 end,
jcallahan@323 3093 }
jcallahan@323 3094
jcallahan@323 3095
jcallahan@92 3096 function WDP:GOSSIP_SHOW(event_name)
MMOSimca@436 3097 WDP:StopChatLootRecording(event_name)
jcallahan@323 3098 local unit_type, unit_idnum = ParseGUID(_G.UnitGUID("npc"))
jcallahan@323 3099 if not unit_idnum then
jcallahan@323 3100 return
jcallahan@323 3101 end
jcallahan@323 3102
jcallahan@323 3103 if GOSSIP_SHOW_FUNCS[unit_type] then
jcallahan@323 3104 GOSSIP_SHOW_FUNCS[unit_type](unit_idnum)
jcallahan@90 3105 end
jcallahan@90 3106 end
jcallahan@90 3107
jcallahan@90 3108
jcallahan@93 3109 function WDP:GUILDBANKFRAME_OPENED(event_name)
MMOSimca@436 3110 WDP:StopChatLootRecording(event_name)
jcallahan@93 3111 SetUnitField("guild_bank", private.UNIT_TYPES.OBJECT)
jcallahan@93 3112 end
jcallahan@93 3113
jcallahan@93 3114
jcallahan@189 3115 function WDP:ITEM_UPGRADE_MASTER_OPENED(event_name)
jcallahan@189 3116 SetUnitField("item_upgrade_master", private.UNIT_TYPES.NPC)
jcallahan@189 3117 end
jcallahan@189 3118
jcallahan@189 3119
jcallahan@90 3120 function WDP:TAXIMAP_OPENED(event_name)
jcallahan@90 3121 SetUnitField("flight_master", private.UNIT_TYPES.NPC)
jcallahan@90 3122 end
jcallahan@90 3123
jcallahan@90 3124
jcallahan@90 3125 function WDP:TRANSMOGRIFY_OPEN(event_name)
jcallahan@90 3126 SetUnitField("transmogrifier", private.UNIT_TYPES.NPC)
jcallahan@90 3127 end
jcallahan@90 3128
jcallahan@90 3129
jcallahan@90 3130 function WDP:VOID_STORAGE_OPEN(event_name)
jcallahan@90 3131 SetUnitField("void_storage", private.UNIT_TYPES.NPC)
jcallahan@90 3132 end
jcallahan@90 3133 end -- do-block