annotate Main.lua @ 582:b15d966bf6ae tip

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