annotate Main.lua @ 550:c6fafd40a7b7 7.3.5-1

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