annotate Main.lua @ 559:eb77bf9c8e2f 8.0.1-3

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