annotate Main.lua @ 573:444d15b2091e 8.0.1-8

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