annotate Main.lua @ 548:66240e36fb16 7.3.2-4

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