annotate Main.lua @ 564:cff3dab8ea61

Removed reference to HereBeDragons 1.0, which is no longer needed.
author Caleb Atherton <atcaleb@twitch.tv>
date Mon, 30 Jul 2018 23:37:15 -0400
parents 2be7eedb205b
children 19c59ee05ab1
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
atcaleb@561 444 local _, q_vector = _G.C_Map.GetWorldPosFromMapPos(area_id, _G.CreateVector2D(0,0))
atcaleb@561 445 local _, w_vector = _G.C_Map.GetWorldPosFromMapPos(area_id, _G.CreateVector2D(1,1))
atcaleb@561 446 local yard_width, yard_height = q_vector.y - w_vector.y, q_vector.x - w_vector.x
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
atcaleb@562 929 item_process_timer_handle = C_Timer.NewTicker(DELAY_PROCESS_ITEMS, WDP.ProcessItems, nil)
atcaleb@562 930 target_location_timer_handle = C_Timer.NewTicker(DELAY_UPDATE_TARGET_LOCATION, WDP.UpdateTargetLocation, nil)
atcaleb@562 931 world_quest_timer_handle = C_Timer.NewTicker(DELAY_PROCESS_WORLD_QUESTS, WDP.ProcessWorldQuests, nil)
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
atcaleb@562 954 local function RecordWorldQuestData(quest_id, api_data_table)
mmosimca@485 955 -- Ensure we have location data and rewards (barely readable so putting it on multiple lines)
mmosimca@487 956 -- (Honor is built in to the quest; it is not a sign rewards have been loaded)
atcaleb@562 957 if not api_data_table or not api_data_table.x or not api_data_table.y or not api_data_table.mapID or not
mmosimca@485 958 (_G.GetQuestLogRewardXP(quest_id) > 0 or _G.GetNumQuestLogRewardCurrencies(quest_id) > 0
atcaleb@562 959 or _G.GetNumQuestLogRewards(quest_id) > 0 or _G.GetQuestLogRewardMoney(quest_id) > 0) then
atcaleb@562 960 --or _G.GetQuestLogRewardArtifactXP(quest_id) > 0)
mmosimca@485 961 return
mmosimca@485 962 end
mmosimca@485 963
mmosimca@485 964 local entry = DBEntry("world_quests", quest_id)
mmosimca@485 965 if entry then
mmosimca@485 966
mmosimca@485 967 -- Record location
mmosimca@485 968 entry["location"] = {}
atcaleb@562 969 entry["location"]["world_map_id"] = api_data_table.mapID
atcaleb@562 970 entry["location"]["x"] = api_data_table.x * 100
atcaleb@562 971 entry["location"]["y"] = api_data_table.y * 100
mmosimca@485 972
mmosimca@485 973 -- Record simple rewards (XP, money, artifact XP, honor)
mmosimca@485 974 entry["rewards"] = {}
mmosimca@485 975 entry["rewards"]["xp"] = tonumber(_G.GetQuestLogRewardXP(quest_id)) or 0
mmosimca@485 976 entry["rewards"]["money"] = tonumber(_G.GetQuestLogRewardMoney(quest_id)) or 0
atcaleb@562 977 --local actualXP, scaling = _G.GetQuestLogRewardArtifactXP(quest_id)
atcaleb@562 978 --entry["rewards"]["artifact_xp"] = ("%d:%d"):format(tonumber(actualXP) or 0, tonumber(scaling) or 0)
mmosimca@485 979 entry["rewards"]["honor"] = tonumber(_G.GetQuestLogRewardHonor(quest_id)) or 0
mmosimca@485 980
mmosimca@485 981 -- Record currencies
mmosimca@485 982 entry["rewards"]["currency_count"] = tonumber(_G.GetNumQuestLogRewardCurrencies(quest_id)) or 0
mmosimca@485 983
mmosimca@496 984 -- Create currency rewards sub-table and fill
mmosimca@485 985 if entry["rewards"]["currency_count"] > 0 then
mmosimca@485 986 entry["rewards"]["currencies"] = {}
mmosimca@485 987 for i = 1, entry["rewards"]["currency_count"] do
mmosimca@503 988 local name, texture_path, quantity, currency_id = _G.GetQuestLogRewardCurrencyInfo(i, quest_id)
mmosimca@503 989 table.insert(entry["rewards"]["currencies"], ("%d:%d"):format(quantity, currency_id))
mmosimca@485 990 end
mmosimca@485 991 end
mmosimca@485 992
mmosimca@485 993 -- Record items
mmosimca@485 994 entry["rewards"]["item_count"] = tonumber(_G.GetNumQuestLogRewards(quest_id)) or 0
mmosimca@485 995
mmosimca@496 996 -- Create item rewards sub-table and fill
mmosimca@485 997 if entry["rewards"]["item_count"] > 0 then
mmosimca@485 998 entry["rewards"]["items"] = {}
mmosimca@485 999 for i = 1, entry["rewards"]["item_count"] do
mmosimca@485 1000 local item_name, item_texture, quantity, quality, is_usable, item_id = _G.GetQuestLogRewardInfo(i, quest_id)
mmosimca@485 1001 table.insert(entry["rewards"]["items"], ("%d:%d"):format(item_id, quantity))
mmosimca@485 1002 end
mmosimca@485 1003 end
mmosimca@485 1004
mmosimca@485 1005 -- Record time remaining
mmosimca@485 1006 entry["estimated_end_time"] = _G.GetServerTime() + ((_G.C_TaskQuest.GetQuestTimeLeftMinutes(quest_id) or 0) * 60)
mmosimca@485 1007 end
mmosimca@485 1008 end
mmosimca@485 1009
mmosimca@485 1010
mmosimca@485 1011 function WDP:ProcessWorldQuests()
MMOSimca@532 1012 -- Ignore if player is low level (there are some world quests before max level now, but we can collect enough data from 110s alone still)
atcaleb@562 1013 if _G.UnitLevel("player") < 110 then return end
atcaleb@559 1014
atcaleb@559 1015 local current_ui_map_id = C_Map.GetBestMapForUnit("player")
atcaleb@559 1016 local bounty_maps = MapUtil.GetRelatedBountyZoneMaps(current_ui_map_id)
MMOSimca@535 1017
MMOSimca@535 1018 -- Iterate over zones in continent
atcaleb@559 1019 for i = 1, #bounty_maps do
MMOSimca@535 1020
MMOSimca@535 1021 -- Get data for World Quests
atcaleb@559 1022 local api_data = C_TaskQuest.GetQuestsForPlayerByMapID(bounty_maps[i]);
atcaleb@559 1023 local continent_ui_map_id = MapUtil.GetMapParentInfo(bounty_maps[i], _G.Enum.UIMapType.Continent)
MMOSimca@535 1024
MMOSimca@535 1025 -- Iterate over the questIDs for each map, doing preload reward requests and creating SavedVariables entries
MMOSimca@535 1026 if api_data and type(api_data) == "table" and #api_data > 0 then
atcaleb@562 1027 for j = 1, #api_data do
MMOSimca@535 1028
MMOSimca@535 1029 -- Check if we had a valid API table returned to us
atcaleb@562 1030 if api_data[j] and type(api_data[j]) == "table" and api_data[j].questId then
atcaleb@562 1031 local quest_id = tonumber(api_data[j].questId) or 0
MMOSimca@535 1032
MMOSimca@535 1033 -- Check if we have quest data
atcaleb@562 1034 if quest_id > 0 and _G.HaveQuestData(quest_id) and QuestUtils_IsQuestWorldQuest(quest_id) and api_data[j].mapID == bounty_maps[i] then
atcaleb@559 1035 _G.C_TaskQuest.RequestPreloadRewardData(quest_id)
atcaleb@562 1036 RecordWorldQuestData(quest_id, api_data[j])
mmosimca@485 1037 end
mmosimca@485 1038 end
mmosimca@485 1039 end
mmosimca@485 1040 end
mmosimca@485 1041 end
mmosimca@485 1042 end
mmosimca@485 1043
mmosimca@485 1044
MMOSimca@340 1045 local function RecordItemData(item_id, item_link, process_bonus_ids, durability)
jcallahan@331 1046 local _, _, item_string = item_link:find("^|%x+|H(.+)|h%[.+%]")
jcallahan@219 1047 local item
jcallahan@0 1048
jcallahan@191 1049 if item_string then
MMOSimca@338 1050 local item_results = { (":"):split(item_string) }
MMOSimca@338 1051
MMOSimca@460 1052 local suffix_id = tonumber(item_results[8]) or 0
MMOSimca@462 1053 local unique_id = tonumber(item_results[9]) or 0
MMOSimca@447 1054 --local level = tonumber(item_results[10])
MMOSimca@460 1055 --local specialization_id = tonumber(item_results[11])
catherton@469 1056 --local upgrade_type_id = tonumber(item_results[12])
MMOSimca@460 1057 local instance_difficulty_id = tonumber(item_results[13]) or 0
MMOSimca@460 1058 local num_bonus_ids = tonumber(item_results[14]) or 0
catherton@471 1059 -- 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 1060 local upgrade_value = tonumber(item_results[15 + num_bonus_ids]) or 0
catherton@465 1061
mmosimca@484 1062 local unk_item_field_1 = tonumber(item_results[16 + num_bonus_ids]) or 0
mmosimca@484 1063 local unk_item_field_2 = tonumber(item_results[17 + num_bonus_ids]) or 0
mmosimca@484 1064 --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 1065 --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 1066
MMOSimca@460 1067 -- If there is anything special (non-zero) for this item then we need to make note of everything
catherton@471 1068 if math.max(suffix_id, instance_difficulty_id, num_bonus_ids, upgrade_value) ~= 0 then
MMOSimca@460 1069 item = DBEntry("items", item_id)
MMOSimca@460 1070 item.suffix_id = suffix_id
MMOSimca@460 1071 item.unique_id = bit.band(unique_id, 0xFFFF)
MMOSimca@460 1072 item.instance_difficulty_id = instance_difficulty_id
catherton@471 1073 item.upgrade_value = upgrade_value
MMOSimca@460 1074
MMOSimca@460 1075 if process_bonus_ids then
MMOSimca@460 1076
MMOSimca@460 1077 -- Get ready for bonus IDs
MMOSimca@384 1078 if not item.seen_bonuses then
MMOSimca@384 1079 item.seen_bonuses = {}
MMOSimca@372 1080 end
catherton@465 1081
MMOSimca@460 1082 if num_bonus_ids > 0 then
MMOSimca@460 1083 -- We want the bonus ID combo output to be in the form ["bonusID1:bonusID2:bonusID3"] = true
MMOSimca@460 1084 -- And sorted numerically with the smallest bonusID first
MMOSimca@460 1085 local sorted_bonus_string = ""
MMOSimca@460 1086 local min_bonus_id_array = {}
MMOSimca@460 1087 for iterations = 1, num_bonus_ids do
MMOSimca@460 1088 -- Find minimum of this iteration
MMOSimca@460 1089 local min_bonus_id = 100000
MMOSimca@460 1090 for bonus_index = 1, num_bonus_ids do
MMOSimca@460 1091 local temp_bonus_id = tonumber(item_results[14 + bonus_index])
MMOSimca@460 1092 if temp_bonus_id and (not min_bonus_id_array[temp_bonus_id]) and (temp_bonus_id < min_bonus_id) then
MMOSimca@460 1093 min_bonus_id = temp_bonus_id
MMOSimca@460 1094 end
MMOSimca@460 1095 end
MMOSimca@460 1096
MMOSimca@460 1097 -- Keep track of already processed IDs
MMOSimca@460 1098 min_bonus_id_array[min_bonus_id] = true
MMOSimca@460 1099
MMOSimca@460 1100 -- Build string
MMOSimca@460 1101 if iterations == 1 then
MMOSimca@460 1102 sorted_bonus_string = sorted_bonus_string .. tostring(min_bonus_id)
MMOSimca@460 1103 else
MMOSimca@460 1104 sorted_bonus_string = sorted_bonus_string .. ":" .. tostring(min_bonus_id)
MMOSimca@460 1105 end
MMOSimca@384 1106 end
MMOSimca@460 1107
MMOSimca@460 1108 item.seen_bonuses[sorted_bonus_string] = true
MMOSimca@460 1109 Debug("RecordItemData: Recorded bonus IDs %s for item %d.", sorted_bonus_string, item_id)
MMOSimca@384 1110 else
MMOSimca@460 1111 item.seen_bonuses["0"] = true
MMOSimca@384 1112 end
MMOSimca@329 1113 end
jcallahan@191 1114 end
jcallahan@0 1115 end
jcallahan@212 1116
jcallahan@212 1117 if durability and durability > 0 then
jcallahan@219 1118 item = item or DBEntry("items", item_id)
jcallahan@212 1119 item.durability = durability
jcallahan@212 1120 end
jcallahan@0 1121 end
jcallahan@0 1122
jcallahan@0 1123
jcallahan@187 1124 function WDP:ProcessItems()
jcallahan@187 1125 for slot_index = _G.INVSLOT_FIRST_EQUIPPED, _G.INVSLOT_LAST_EQUIPPED do
jcallahan@1 1126 local item_id = _G.GetInventoryItemID("player", slot_index)
jcallahan@0 1127
jcallahan@0 1128 if item_id and item_id > 0 then
jcallahan@1 1129 local _, max_durability = _G.GetInventoryItemDurability(slot_index)
MMOSimca@340 1130 RecordItemData(item_id, _G.GetInventoryItemLink("player", slot_index), false, max_durability)
jcallahan@0 1131 end
jcallahan@0 1132 end
jcallahan@0 1133
jcallahan@0 1134 for bag_index = 0, _G.NUM_BAG_SLOTS do
jcallahan@0 1135 for slot_index = 1, _G.GetContainerNumSlots(bag_index) do
jcallahan@1 1136 local item_id = _G.GetContainerItemID(bag_index, slot_index)
jcallahan@0 1137
jcallahan@0 1138 if item_id and item_id > 0 then
jcallahan@1 1139 local _, max_durability = _G.GetContainerItemDurability(bag_index, slot_index)
MMOSimca@340 1140 RecordItemData(item_id, _G.GetContainerItemLink(bag_index, slot_index), false, max_durability)
jcallahan@0 1141 end
jcallahan@0 1142 end
jcallahan@0 1143 end
jcallahan@0 1144 end
jcallahan@0 1145
jcallahan@118 1146
atcaleb@558 1147 local function TargetedNPC()
atcaleb@558 1148 if not _G.UnitExists("target") or _G.UnitPlayerControlled("target") or currently_drunk then
atcaleb@558 1149 current_target_id = nil
atcaleb@558 1150 return
atcaleb@558 1151 end
atcaleb@558 1152 local unit_type, unit_idnum = ParseGUID(_G.UnitGUID("target"))
atcaleb@558 1153
atcaleb@558 1154 if not unit_idnum or not UnitTypeIsNPC(unit_type) then
atcaleb@558 1155 return
atcaleb@558 1156 end
atcaleb@558 1157 current_target_id = unit_idnum
atcaleb@558 1158
atcaleb@558 1159 local npc = NPCEntry(unit_idnum)
atcaleb@558 1160 local _, class_token = _G.UnitClass("target")
atcaleb@558 1161 npc.class = class_token
atcaleb@558 1162 npc.faction = UnitFactionStanding("target")
atcaleb@558 1163 npc.genders = npc.genders or {}
atcaleb@558 1164 npc.genders[private.GENDER_NAMES[_G.UnitSex("target")] or "UNDEFINED"] = true
atcaleb@558 1165 npc.is_pvp = _G.UnitIsPVP("target") and true or nil
atcaleb@558 1166 npc.reaction = ("%s:%s:%s"):format(_G.UnitLevel("player"), _G.UnitFactionGroup("player"), private.REACTION_NAMES[_G.UnitReaction("player", "target")])
atcaleb@558 1167
atcaleb@558 1168 local encounter_data = npc:EncounterData(InstanceDifficultyToken()).stats
atcaleb@558 1169 local npc_level = ("level_%d"):format(_G.UnitLevel("target"))
atcaleb@558 1170 local level_data = encounter_data[npc_level]
atcaleb@558 1171
atcaleb@558 1172 if not level_data then
atcaleb@558 1173 level_data = {}
atcaleb@558 1174 encounter_data[npc_level] = level_data
atcaleb@558 1175 end
atcaleb@558 1176 level_data.max_health = level_data.max_health or _G.UnitHealthMax("target")
atcaleb@558 1177
atcaleb@558 1178 -- May not capture as much data as it could, since the API changed in Legion to report multiple types of power
atcaleb@558 1179 if not level_data.power then
atcaleb@558 1180 local max_power = _G.UnitPowerMax("target")
atcaleb@558 1181
atcaleb@558 1182 if max_power > 0 then
atcaleb@558 1183 local power_type = _G.UnitPowerType("target")
atcaleb@558 1184 level_data.power = ("%s:%d"):format(private.POWER_TYPE_NAMES[tostring(power_type)] or power_type, max_power)
jcallahan@118 1185 end
jcallahan@118 1186 end
atcaleb@558 1187 name_to_id_map[_G.UnitName("target")] = unit_idnum
atcaleb@558 1188 return npc, unit_idnum
atcaleb@558 1189 end
jcallahan@118 1190
jcallahan@118 1191
jcallahan@113 1192 do
jcallahan@113 1193 local COORD_MAX = 5
jcallahan@0 1194
jcallahan@113 1195 function WDP:UpdateTargetLocation()
catherton@480 1196 if currently_drunk or not _G.UnitExists("target") or _G.UnitPlayerControlled("target") or (_G.UnitIsTapDenied("target") and not _G.UnitIsDead("target")) then
jcallahan@2 1197 return
jcallahan@2 1198 end
jcallahan@113 1199
jcallahan@113 1200 for index = 1, 4 do
jcallahan@113 1201 if not _G.CheckInteractDistance("target", index) then
jcallahan@113 1202 return
jcallahan@113 1203 end
jcallahan@113 1204 end
jcallahan@215 1205 local npc = TargetedNPC()
jcallahan@113 1206
jcallahan@113 1207 if not npc then
jcallahan@113 1208 return
jcallahan@113 1209 end
jcallahan@113 1210 local zone_name, area_id, x, y, map_level, difficulty_token = CurrentLocationData()
MMOSimca@328 1211 if not (zone_name and area_id and x and y and map_level) then
mmosimca@508 1212 if not (_G.IsInInstance()) then
mmosimca@508 1213 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 1214 end
MMOSimca@328 1215 return
MMOSimca@328 1216 end
jcallahan@248 1217 local npc_data = npc:EncounterData(difficulty_token).stats[("level_%d"):format(_G.UnitLevel("target"))]
jcallahan@113 1218 local zone_token = ("%s:%d"):format(zone_name, area_id)
jcallahan@118 1219 npc_data.locations = npc_data.locations or {} -- TODO: Fix this. It is broken. Possibly something to do with the timed updates.
jcallahan@113 1220
jcallahan@113 1221 local zone_data = npc_data.locations[zone_token]
jcallahan@113 1222
jcallahan@113 1223 if not zone_data then
jcallahan@113 1224 zone_data = {}
jcallahan@113 1225 npc_data.locations[zone_token] = zone_data
jcallahan@113 1226 end
jcallahan@113 1227
jcallahan@113 1228 for location_token in pairs(zone_data) do
jcallahan@113 1229 local loc_level, loc_x, loc_y = (":"):split(location_token)
jcallahan@113 1230 loc_level = tonumber(loc_level)
jcallahan@113 1231
jcallahan@113 1232 if map_level == loc_level and math.abs(x - loc_x) <= COORD_MAX and math.abs(y - loc_y) <= COORD_MAX then
jcallahan@113 1233 return
jcallahan@113 1234 end
jcallahan@113 1235 end
jcallahan@141 1236 zone_data[("%d:%d:%d"):format(map_level, x, y)] = true
jcallahan@2 1237 end
jcallahan@113 1238 end -- do-block
jcallahan@2 1239
jcallahan@118 1240
MMOSimca@412 1241 function WDP:HandleBadChatLootData(...)
MMOSimca@398 1242 ClearChatLootData()
MMOSimca@398 1243 end
MMOSimca@398 1244
MMOSimca@398 1245
MMOSimca@420 1246 -- EVENT HANDLERS -----------------------------------------------------
MMOSimca@420 1247
MMOSimca@436 1248 -- 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 1249 function WDP:StopChatLootRecording(event_name)
MMOSimca@436 1250 if not block_chat_loot_data then
MMOSimca@439 1251 Debug("%s: Pausing chat-based loot recording.", event_name)
MMOSimca@436 1252 ClearChatLootData()
MMOSimca@436 1253 block_chat_loot_data = true
MMOSimca@436 1254 end
MMOSimca@436 1255 end
MMOSimca@436 1256
MMOSimca@436 1257
MMOSimca@436 1258 function WDP:ResumeChatLootRecording(event_name)
MMOSimca@436 1259 if block_chat_loot_data then
MMOSimca@439 1260 Debug("%s: Resuming chat-based loot recording.", event_name)
MMOSimca@436 1261 block_chat_loot_data = false
MMOSimca@436 1262 end
MMOSimca@436 1263 end
MMOSimca@436 1264
MMOSimca@436 1265
MMOSimca@408 1266 -- 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 1267 function WDP:BONUS_ROLL_RESULT(event_name)
MMOSimca@408 1268 Debug("%s: Bonus roll detected; stopping loot recording for this boss to avoid recording bonus loot.", event_name)
MMOSimca@408 1269 ClearKilledBossID()
MMOSimca@408 1270 ClearLootToastContainerID()
MMOSimca@408 1271 end
MMOSimca@408 1272
MMOSimca@408 1273
jcallahan@90 1274 function WDP:BLACK_MARKET_ITEM_UPDATE(event_name)
jcallahan@243 1275 if not ALLOWED_LOCALES[CLIENT_LOCALE] then
jcallahan@243 1276 return
jcallahan@243 1277 end
jcallahan@282 1278 local num_items = _G.C_BlackMarket.GetNumItems() or 0
jcallahan@56 1279
jcallahan@56 1280 for index = 1, num_items do
jcallahan@56 1281 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 1282
jcallahan@56 1283 if item_link then
jcallahan@56 1284 DBEntry("items", ItemLinkToID(item_link)).black_market = seller_name or "UNKNOWN"
jcallahan@56 1285 end
jcallahan@56 1286 end
jcallahan@56 1287 end
jcallahan@56 1288
jcallahan@56 1289
jcallahan@298 1290 local function UpdateUnitPet(unit_guid, unit_id)
jcallahan@246 1291 local current_pet_guid = group_owner_guids_to_pet_guids[unit_guid]
jcallahan@246 1292
jcallahan@246 1293 if current_pet_guid then
jcallahan@246 1294 group_owner_guids_to_pet_guids[unit_guid] = nil
jcallahan@246 1295 group_pet_guids[current_pet_guid] = nil
jcallahan@246 1296 end
jcallahan@246 1297 local pet_guid = _G.UnitGUID(unit_id .. "pet")
jcallahan@246 1298
jcallahan@246 1299 if pet_guid then
jcallahan@296 1300 group_owner_guids_to_pet_guids[unit_guid] = pet_guid
jcallahan@246 1301 group_pet_guids[pet_guid] = true
jcallahan@246 1302 end
jcallahan@246 1303 end
jcallahan@246 1304
jcallahan@246 1305
jcallahan@298 1306 function WDP:GROUP_ROSTER_UPDATE(event_name)
jcallahan@298 1307 local is_raid = _G.IsInRaid()
jcallahan@298 1308 local unit_type = is_raid and "raid" or "party"
jcallahan@298 1309 local group_size = is_raid and _G.GetNumGroupMembers() or _G.GetNumSubgroupMembers()
jcallahan@298 1310
jcallahan@299 1311 table.wipe(group_member_guids)
jcallahan@298 1312
jcallahan@298 1313 for index = 1, group_size do
jcallahan@298 1314 local unit_id = unit_type .. index
jcallahan@298 1315 local unit_guid = _G.UnitGUID(unit_id)
jcallahan@298 1316
jcallahan@299 1317 group_member_guids[unit_guid] = true
jcallahan@298 1318 UpdateUnitPet(unit_guid, unit_id)
jcallahan@298 1319 end
jcallahan@299 1320 group_member_guids[PLAYER_GUID] = true
jcallahan@298 1321 end
jcallahan@298 1322
jcallahan@298 1323
jcallahan@298 1324 function WDP:UNIT_PET(event_name, unit_id)
jcallahan@298 1325 UpdateUnitPet(_G.UnitGUID(unit_id), unit_id)
jcallahan@298 1326 end
jcallahan@298 1327
jcallahan@298 1328
MMOSimca@375 1329 function WDP:SHOW_LOOT_TOAST(event_name, loot_type, item_link, quantity, spec_ID, sex_ID, is_personal, loot_source)
jcallahan@312 1330 if not loot_type or (loot_type ~= "item" and loot_type ~= "money" and loot_type ~= "currency") then
jcallahan@306 1331 Debug("%s: loot_type is %s. Item link is %s, and quantity is %d.", event_name, loot_type, item_link, quantity)
jcallahan@306 1332 return
jcallahan@306 1333 end
MMOSimca@372 1334
MMOSimca@372 1335 -- Need information on the most recent args, so using this complete debug statement for now
MMOSimca@375 1336 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 1337
MMOSimca@355 1338 -- Handle Garrison cache specially
MMOSimca@422 1339 if loot_source and (loot_source == LOOT_SOURCE_ID_GARRISON_CACHE) and last_garrison_cache_object_id then
MMOSimca@355 1340 -- Record location data for cache
MMOSimca@355 1341 UpdateDBEntryLocation("objects", ("OPENING:%d"):format(last_garrison_cache_object_id))
MMOSimca@355 1342
MMOSimca@355 1343 -- Add drop data
mmosimca@496 1344 local currency_id = CurrencyLinkToID(item_link)
mmosimca@496 1345 if currency_id and currency_id ~= 0 then
MMOSimca@355 1346 -- Check for top level object data
MMOSimca@355 1347 local object_entry = DBEntry("objects", ("OPENING:%d"):format(last_garrison_cache_object_id))
MMOSimca@355 1348 local difficulty_token = InstanceDifficultyToken()
MMOSimca@355 1349 if object_entry[difficulty_token] then
MMOSimca@355 1350 -- Increment loot count
MMOSimca@355 1351 object_entry[difficulty_token]["opening_count"] = (object_entry[difficulty_token]["opening_count"] or 0) + 1
MMOSimca@355 1352
mmosimca@496 1353 Debug("%s: %d X %d", event_name, currency_id, quantity)
MMOSimca@355 1354 object_entry[difficulty_token]["opening"] = object_entry[difficulty_token]["opening"] or {}
mmosimca@496 1355 table.insert(object_entry[difficulty_token]["opening"], ("currency:%d:%d"):format(quantity, currency_id))
MMOSimca@355 1356 else
MMOSimca@355 1357 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 1358 end
MMOSimca@355 1359 else
mmosimca@496 1360 Debug("%s: Currency ID is nil or 0, from currency link %s", event_name, item_link)
MMOSimca@355 1361 end
catherton@465 1362
MMOSimca@431 1363 -- Wipe object ID until future mouseover
MMOSimca@431 1364 last_garrison_cache_object_id = nil
MMOSimca@387 1365 elseif raid_boss_id then
MMOSimca@427 1366 local npc = NPCEntry(raid_boss_id)
MMOSimca@427 1367 if npc then
MMOSimca@427 1368 local loot_label = "drops"
MMOSimca@427 1369 local encounter_data = npc:EncounterData(InstanceDifficultyToken())
MMOSimca@427 1370 encounter_data[loot_label] = encounter_data[loot_label] or {}
MMOSimca@427 1371 encounter_data.loot_counts = encounter_data.loot_counts or {}
MMOSimca@427 1372
MMOSimca@427 1373 if loot_type == "item" then
MMOSimca@427 1374 local item_id = ItemLinkToID(item_link)
MMOSimca@427 1375 if item_id then
MMOSimca@427 1376 Debug("%s: %s X %d (%d)", event_name, item_link, quantity, item_id)
MMOSimca@427 1377 RecordItemData(item_id, item_link, true)
MMOSimca@427 1378 table.insert(encounter_data[loot_label], ("%d:%d"):format(item_id, quantity))
MMOSimca@427 1379 else
MMOSimca@427 1380 Debug("%s: ItemID is nil, from item link %s", event_name, item_link)
MMOSimca@427 1381 return
MMOSimca@427 1382 end
MMOSimca@427 1383 elseif loot_type == "money" then
MMOSimca@427 1384 Debug("%s: money X %d", event_name, quantity)
MMOSimca@427 1385 table.insert(encounter_data[loot_label], ("money:%d"):format(quantity))
MMOSimca@427 1386 elseif loot_type == "currency" then
mmosimca@496 1387 local currency_id = CurrencyLinkToID(item_link)
mmosimca@496 1388 if currency_id and currency_id ~= 0 then
mmosimca@496 1389 Debug("%s: %d X %d", event_name, currency_id, quantity)
mmosimca@496 1390 table.insert(encounter_data[loot_label], ("currency:%d:%d"):format(quantity, currency_id))
MMOSimca@427 1391 else
mmosimca@496 1392 Debug("%s: Currency ID is nil or 0, from currency link %s", event_name, item_link)
MMOSimca@427 1393 return
MMOSimca@427 1394 end
jcallahan@312 1395 end
jcallahan@317 1396
MMOSimca@427 1397 if not boss_loot_toasting[raid_boss_id] then
MMOSimca@427 1398 encounter_data.loot_counts[loot_label] = (encounter_data.loot_counts[loot_label] or 0) + 1
MMOSimca@427 1399 boss_loot_toasting[raid_boss_id] = true -- Do not count further loots until timer expires or another boss is killed
jcallahan@312 1400 end
jcallahan@312 1401 end
MMOSimca@387 1402 elseif loot_toast_container_id then
jcallahan@305 1403 InitializeCurrentLoot()
jcallahan@305 1404
jcallahan@306 1405 -- Fake the loot characteristics to match that of an actual container item
MMOSimca@387 1406 current_loot.identifier = loot_toast_container_id
jcallahan@306 1407 current_loot.label = "contains"
jcallahan@306 1408 current_loot.target_type = AF.ITEM
jcallahan@306 1409
MMOSimca@387 1410 current_loot.sources[loot_toast_container_id] = current_loot.sources[loot_toast_container_id] or {}
jcallahan@312 1411
jcallahan@301 1412 if loot_type == "item" then
jcallahan@312 1413 local item_id = ItemLinkToID(item_link)
jcallahan@312 1414 if item_id then
jcallahan@312 1415 Debug("%s: %s X %d (%d)", event_name, item_link, quantity, item_id)
MMOSimca@340 1416 RecordItemData(item_id, item_link, true)
MMOSimca@387 1417 current_loot.sources[loot_toast_container_id][item_id] = (current_loot.sources[loot_toast_container_id][item_id] or 0) + quantity
jcallahan@312 1418 else
jcallahan@301 1419 Debug("%s: ItemID is nil, from item link %s", event_name, item_link)
jcallahan@312 1420 current_loot = nil
jcallahan@301 1421 return
jcallahan@301 1422 end
jcallahan@301 1423 elseif loot_type == "money" then
jcallahan@312 1424 Debug("%s: money X %d", event_name, quantity)
MMOSimca@387 1425 current_loot.sources[loot_toast_container_id]["money"] = (current_loot.sources[loot_toast_container_id]["money"] or 0) + quantity
jcallahan@312 1426 elseif loot_type == "currency" then
mmosimca@496 1427 local currency_id = CurrencyLinkToID(item_link)
mmosimca@496 1428 if currency_id and currency_id ~= 0 then
mmosimca@496 1429 Debug("%s: %d X %d", event_name, currency_id, quantity)
mmosimca@496 1430 local currency_token = ("currency:%d"):format(currency_id)
MMOSimca@387 1431 current_loot.sources[loot_toast_container_id][currency_token] = (current_loot.sources[loot_toast_container_id][currency_token] or 0) + quantity
jcallahan@312 1432 else
mmosimca@496 1433 Debug("%s: Currency ID is nil or 0, from currency link %s", event_name, item_link)
jcallahan@312 1434 current_loot = nil
jcallahan@312 1435 return
jcallahan@312 1436 end
jcallahan@301 1437 end
jcallahan@312 1438
jcallahan@301 1439 GenericLootUpdate("items")
jcallahan@301 1440 current_loot = nil
MMOSimca@387 1441 container_loot_toasting = true -- Do not count further loots until timer expires or another container is opened
MMOSimca@539 1442 elseif loot_source and chat_loot_timer_handle then
MMOSimca@444 1443 -- 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 1444 if loot_type == "currency" then
mmosimca@496 1445 local currency_id = CurrencyLinkToID(item_link)
mmosimca@496 1446 if currency_id and currency_id ~= 0 then
MMOSimca@444 1447 -- Verify that we're still assigning data to the right items
MMOSimca@444 1448 if chat_loot_data.identifier and (private.CONTAINER_ITEM_ID_LIST[chat_loot_data.identifier] ~= nil) then
mmosimca@496 1449 Debug("%s: Captured currency for chat-based loot recording. %d X %d", event_name, currency_id, quantity)
mmosimca@496 1450 local currency_token = ("currency:%d"):format(currency_id)
MMOSimca@444 1451 chat_loot_data.loot = chat_loot_data.loot or {}
MMOSimca@444 1452 chat_loot_data.loot[currency_token] = (chat_loot_data.loot[currency_token] or 0) + quantity
MMOSimca@444 1453 else -- If not, cancel the timer and wipe the loot table early
MMOSimca@444 1454 Debug("%s: Canceled chat-based loot recording because we would have assigned the wrong loot!", event_name)
MMOSimca@444 1455 ClearChatLootData()
MMOSimca@444 1456 end
MMOSimca@444 1457 else
mmosimca@496 1458 Debug("%s: Currency ID is nil or 0, from currency link %s", event_name, item_link)
MMOSimca@444 1459 end
MMOSimca@444 1460 -- 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 1461 elseif loot_type == "money" then
MMOSimca@424 1462 -- Verify that we're still assigning data to the right items
MMOSimca@435 1463 if chat_loot_data.identifier and (private.CONTAINER_ITEM_ID_LIST[chat_loot_data.identifier] ~= nil) then
MMOSimca@444 1464 Debug("%s: Captured money for chat-based loot recording. money X %d", event_name, quantity)
MMOSimca@435 1465 chat_loot_data.loot = chat_loot_data.loot or {}
MMOSimca@444 1466 chat_loot_data.loot["money"] = (chat_loot_data.loot["money"] or 0) + quantity
MMOSimca@424 1467 else -- If not, cancel the timer and wipe the loot table early
MMOSimca@424 1468 Debug("%s: Canceled chat-based loot recording because we would have assigned the wrong loot!", event_name)
MMOSimca@424 1469 ClearChatLootData()
MMOSimca@424 1470 end
MMOSimca@424 1471 end
jcallahan@301 1472 else
jcallahan@307 1473 Debug("%s: NPC and Container are nil, storing loot toast data for 5 seconds.", event_name)
jcallahan@307 1474
jcallahan@307 1475 loot_toast_data = loot_toast_data or {}
jcallahan@312 1476 loot_toast_data[#loot_toast_data + 1] = { loot_type, item_link, quantity }
jcallahan@307 1477
MMOSimca@340 1478 local item_id = ItemLinkToID(item_link)
MMOSimca@340 1479 if item_id then
MMOSimca@340 1480 RecordItemData(item_id, item_link, true)
MMOSimca@340 1481 end
MMOSimca@340 1482
MMOSimca@383 1483 loot_toast_data_timer_handle = C_Timer.NewTimer(5, ClearLootToastData)
jcallahan@178 1484 end
jcallahan@178 1485 end
jcallahan@178 1486
jcallahan@178 1487
jcallahan@179 1488 do
MMOSimca@388 1489 local CHAT_MSG_CURRENCY_UPDATE_FUNCS = {
mmosimca@496 1490 [AF.NPC] = function(currency_id, quantity)
mmosimca@496 1491 Debug("CHAT_MSG_CURRENCY: AF.NPC currency:%d (%d)", currency_id, quantity)
MMOSimca@388 1492 end,
mmosimca@496 1493 [AF.ZONE] = function(currency_id, quantity)
mmosimca@496 1494 Debug("CHAT_MSG_CURRENCY: AF.ZONE currency:%d (%d)", currency_id, quantity)
MMOSimca@388 1495 InitializeCurrentLoot()
mmosimca@496 1496 current_loot.list[1] = ("currency:%d:%d"):format(quantity, currency_id)
MMOSimca@388 1497 GenericLootUpdate("zones")
MMOSimca@388 1498 current_loot = nil
MMOSimca@388 1499 end,
MMOSimca@388 1500 }
MMOSimca@388 1501
MMOSimca@388 1502
MMOSimca@388 1503 function WDP:CHAT_MSG_CURRENCY(event_name, message)
MMOSimca@388 1504 local category
MMOSimca@388 1505
MMOSimca@388 1506 local currency_link, quantity = deformat(message, _G.CURRENCY_GAINED_MULTIPLE)
MMOSimca@388 1507 if not currency_link then
MMOSimca@388 1508 quantity, currency_link = 1, deformat(message, _G.CURRENCY_GAINED)
MMOSimca@388 1509 end
mmosimca@496 1510 local currency_id = CurrencyLinkToID(currency_link)
mmosimca@496 1511
mmosimca@496 1512 if not currency_id or currency_id == 0 then
MMOSimca@388 1513 return
MMOSimca@388 1514 end
MMOSimca@388 1515
MMOSimca@388 1516 -- Set update category
MMOSimca@388 1517 if current_action.spell_label == "FISHING" then
MMOSimca@388 1518 category = AF.ZONE
MMOSimca@388 1519 elseif raid_boss_id then
MMOSimca@388 1520 category = AF.NPC
MMOSimca@388 1521 end
MMOSimca@388 1522
MMOSimca@388 1523 -- Take action based on update category
MMOSimca@388 1524 local update_func = CHAT_MSG_CURRENCY_UPDATE_FUNCS[category]
MMOSimca@388 1525 if not category or not update_func then
MMOSimca@388 1526 return
MMOSimca@388 1527 end
mmosimca@496 1528 update_func(currency_id, quantity)
MMOSimca@388 1529 end
MMOSimca@388 1530
MMOSimca@388 1531
jcallahan@179 1532 local CHAT_MSG_LOOT_UPDATE_FUNCS = {
MMOSimca@347 1533 [AF.ITEM] = function(item_id, quantity)
MMOSimca@347 1534 -- Verify that we're still assigning data to the right items
MMOSimca@435 1535 if chat_loot_data.identifier and (private.CONTAINER_ITEM_ID_LIST[chat_loot_data.identifier] ~= nil) then
MMOSimca@347 1536 Debug("CHAT_MSG_LOOT: AF.ITEM %d (%d)", item_id, quantity)
MMOSimca@435 1537 chat_loot_data.loot = chat_loot_data.loot or {}
MMOSimca@435 1538 chat_loot_data.loot[item_id] = (chat_loot_data.loot[item_id] or 0) + quantity
MMOSimca@347 1539 else -- If not, cancel the timer and wipe the loot table early
MMOSimca@387 1540 Debug("CHAT_MSG_LOOT: We would have assigned the wrong loot!")
MMOSimca@387 1541 ClearChatLootData()
MMOSimca@347 1542 end
MMOSimca@347 1543 end,
jcallahan@179 1544 [AF.NPC] = function(item_id, quantity)
MMOSimca@345 1545 Debug("CHAT_MSG_LOOT: AF.NPC %d (%d)", item_id, quantity)
MMOSimca@345 1546 end,
MMOSimca@345 1547 [AF.OBJECT] = function(item_id, quantity)
MMOSimca@345 1548 Debug("CHAT_MSG_LOOT: AF.OBJECT %d (%d)", item_id, quantity)
MMOSimca@381 1549 -- Check for top level object data
MMOSimca@381 1550 local object_entry = DBEntry("objects", ("OPENING:%s"):format(private.LOGGING_SPELL_ID_TO_OBJECT_ID_MAP[last_timber_spell_id]))
MMOSimca@381 1551 local difficulty_token = InstanceDifficultyToken()
MMOSimca@381 1552 if object_entry[difficulty_token] then
MMOSimca@381 1553 -- Increment loot count
MMOSimca@381 1554 object_entry[difficulty_token]["opening_count"] = (object_entry[difficulty_token]["opening_count"] or 0) + 1
MMOSimca@381 1555
MMOSimca@381 1556 -- Add drop data
MMOSimca@381 1557 object_entry[difficulty_token]["opening"] = object_entry[difficulty_token]["opening"] or {}
MMOSimca@381 1558 table.insert(object_entry[difficulty_token]["opening"], ("%d:%d"):format(item_id, quantity))
MMOSimca@381 1559 else
MMOSimca@381 1560 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 1561 end
jcallahan@179 1562 end,
jcallahan@179 1563 [AF.ZONE] = function(item_id, quantity)
MMOSimca@345 1564 Debug("CHAT_MSG_LOOT: AF.ZONE %d (%d)", item_id, quantity)
jcallahan@312 1565 InitializeCurrentLoot()
jcallahan@312 1566 current_loot.list[1] = ("%d:%d"):format(item_id, quantity)
jcallahan@179 1567 GenericLootUpdate("zones")
jcallahan@312 1568 current_loot = nil
jcallahan@179 1569 end,
jcallahan@179 1570 }
jcallahan@177 1571
jcallahan@177 1572
MMOSimca@388 1573 function WDP:CHAT_MSG_LOOT(event_name, message)
jcallahan@179 1574 local category
jcallahan@177 1575
MMOSimca@345 1576 local item_link, quantity = deformat(message, _G.LOOT_ITEM_PUSHED_SELF_MULTIPLE)
MMOSimca@345 1577 if not item_link then
MMOSimca@345 1578 quantity, item_link = 1, deformat(message, _G.LOOT_ITEM_PUSHED_SELF)
MMOSimca@345 1579 end
MMOSimca@345 1580 local item_id = ItemLinkToID(item_link)
MMOSimca@345 1581
MMOSimca@345 1582 if not item_id then
MMOSimca@345 1583 return
MMOSimca@345 1584 end
MMOSimca@345 1585
MMOSimca@345 1586 -- Set update category
MMOSimca@405 1587 if last_timber_spell_id and item_id == ITEM_ID_TIMBER then
MMOSimca@345 1588 category = AF.OBJECT
MMOSimca@345 1589 -- Recently changed from ~= "EXTRACT_GAS" because of some occassional bad data, and, as far as I know, no benefit.
MMOSimca@345 1590 elseif current_action.spell_label == "FISHING" then
jcallahan@179 1591 category = AF.ZONE
MMOSimca@388 1592 elseif raid_boss_id then
jcallahan@179 1593 category = AF.NPC
MMOSimca@347 1594 elseif chat_loot_timer_handle then
MMOSimca@347 1595 category = AF.ITEM
jcallahan@179 1596 end
MMOSimca@345 1597
MMOSimca@395 1598 -- We still want to record the item's data, even if it doesn't need its drop location recorded
MMOSimca@395 1599 RecordItemData(item_id, item_link, true)
MMOSimca@395 1600
MMOSimca@345 1601 -- Take action based on update category
jcallahan@179 1602 local update_func = CHAT_MSG_LOOT_UPDATE_FUNCS[category]
atcaleb@558 1603 if not category or not update_func or private.BLACKLISTED_ITEMS[item_id] then
MMOSimca@340 1604 return
MMOSimca@340 1605 end
jcallahan@179 1606 update_func(item_id, quantity)
jcallahan@177 1607 end
MMOSimca@388 1608 end
MMOSimca@388 1609
MMOSimca@388 1610
jcallahan@97 1611 function WDP:RecordQuote(event_name, message, source_name, language_name)
jcallahan@112 1612 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 1613 return
jcallahan@95 1614 end
jcallahan@97 1615 local npc = NPCEntry(name_to_id_map[source_name])
jcallahan@97 1616 npc.quotes = npc.quotes or {}
jcallahan@97 1617 npc.quotes[event_name] = npc.quotes[event_name] or {}
jcallahan@97 1618 npc.quotes[event_name][ReplaceKeywords(message)] = true
jcallahan@97 1619 end
jcallahan@95 1620
jcallahan@95 1621
jcallahan@95 1622 do
jcallahan@40 1623 local SOBER_MATCH = _G.DRUNK_MESSAGE_ITEM_SELF1:gsub("%%s", ".+")
jcallahan@40 1624
jcallahan@40 1625 local DRUNK_COMPARES = {
jcallahan@40 1626 _G.DRUNK_MESSAGE_SELF2,
jcallahan@40 1627 _G.DRUNK_MESSAGE_SELF3,
jcallahan@40 1628 _G.DRUNK_MESSAGE_SELF4,
jcallahan@40 1629 }
jcallahan@40 1630
jcallahan@40 1631 local DRUNK_MATCHES = {
jcallahan@254 1632 (_G.DRUNK_MESSAGE_SELF2:gsub("%%s", ".+")),
jcallahan@254 1633 (_G.DRUNK_MESSAGE_SELF3:gsub("%%s", ".+")),
jcallahan@254 1634 (_G.DRUNK_MESSAGE_SELF4:gsub("%%s", ".+")),
jcallahan@40 1635 }
jcallahan@40 1636
jcallahan@167 1637 local RECIPE_MATCH = _G.ERR_LEARN_RECIPE_S:gsub("%%s", "(.*)")
jcallahan@167 1638
jcallahan@167 1639
jcallahan@167 1640 local function RecordDiscovery(tradeskill_name, tradeskill_index)
jcallahan@167 1641 if tradeskill_name == private.discovered_recipe_name then
catherton@479 1642 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 1643
jcallahan@167 1644 private.discovered_recipe_name = nil
jcallahan@167 1645 private.profession_level = nil
jcallahan@167 1646 private.previous_spell_id = nil
jcallahan@169 1647
jcallahan@169 1648 return true
jcallahan@167 1649 end
jcallahan@167 1650 end
jcallahan@167 1651
jcallahan@167 1652
jcallahan@167 1653 local function IterativeRecordDiscovery()
jcallahan@167 1654 TradeSkillExecutePer(RecordDiscovery)
jcallahan@167 1655 end
jcallahan@167 1656
jcallahan@167 1657
jcallahan@92 1658 function WDP:CHAT_MSG_SYSTEM(event_name, message)
mmosimca@514 1659 -- 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 1660 --[[
MMOSimca@532 1661 -- Removed in Patch 7.0.3; previously used to determine if a system message was a quest reward or not
MMOSimca@532 1662 local ERR_QUEST_REWARD_ITEM_MULT_IS = _G.ERR_QUEST_REWARD_ITEM_MULT_IS or "Received %d of item: %s."
MMOSimca@532 1663 local ERR_QUEST_REWARD_ITEM_S = _G.ERR_QUEST_REWARD_ITEM_S or "Received item: %s."
MMOSimca@532 1664
catherton@472 1665 local item_link, quantity = deformat(message, ERR_QUEST_REWARD_ITEM_MULT_IS)
MMOSimca@400 1666 if not item_link then
catherton@472 1667 quantity, item_link = 1, deformat(message, ERR_QUEST_REWARD_ITEM_S)
MMOSimca@400 1668 end
MMOSimca@400 1669 local item_id = ItemLinkToID(item_link)
MMOSimca@400 1670
mmosimca@514 1671 if item_id then
mmosimca@514 1672 -- If it was a quest message (that we can decode), parse its link
mmosimca@514 1673 RecordItemData(item_id, item_link, true)
mmosimca@514 1674 else
mmosimca@514 1675 -- If it isn't a quest message, check the other uses of system messages
MMOSimca@532 1676 end
MMOSimca@532 1677 ]]--
MMOSimca@532 1678
MMOSimca@532 1679 if not private.trainer_shown then
MMOSimca@532 1680 local recipe_name = message:match(RECIPE_MATCH)
MMOSimca@532 1681
MMOSimca@532 1682 if recipe_name and private.previous_spell_id then
MMOSimca@532 1683 local profession_name, prof_level = _G.C_TradeSkillUI.GetTradeSkillLine()
MMOSimca@532 1684
MMOSimca@532 1685 if profession_name == _G.UNKNOWN then
MMOSimca@532 1686 return
jcallahan@167 1687 end
MMOSimca@532 1688 private.discovered_recipe_name = recipe_name
MMOSimca@532 1689 private.profession_level = prof_level
MMOSimca@532 1690
MMOSimca@532 1691 C_Timer.After(0.2, IterativeRecordDiscovery)
jcallahan@167 1692 end
MMOSimca@532 1693 end
MMOSimca@532 1694
MMOSimca@532 1695 if currently_drunk then
MMOSimca@532 1696 if message == _G.DRUNK_MESSAGE_SELF1 or message:match(SOBER_MATCH) then
MMOSimca@532 1697 currently_drunk = nil
MMOSimca@400 1698 end
MMOSimca@532 1699 return
MMOSimca@532 1700 end
MMOSimca@532 1701
MMOSimca@532 1702 for index = 1, #DRUNK_MATCHES do
MMOSimca@532 1703 if message == DRUNK_COMPARES[index] or message:match(DRUNK_MATCHES[index]) then
MMOSimca@532 1704 currently_drunk = true
MMOSimca@532 1705 break
jcallahan@40 1706 end
jcallahan@40 1707 end
jcallahan@40 1708 end
jcallahan@40 1709 end
jcallahan@40 1710
jcallahan@307 1711
jcallahan@331 1712 do
jcallahan@23 1713 local FLAGS_NPC = bit.bor(_G.COMBATLOG_OBJECT_TYPE_GUARDIAN, _G.COMBATLOG_OBJECT_CONTROL_NPC)
jcallahan@23 1714 local FLAGS_NPC_CONTROL = bit.bor(_G.COMBATLOG_OBJECT_AFFILIATION_OUTSIDER, _G.COMBATLOG_OBJECT_CONTROL_NPC)
jcallahan@23 1715
atcaleb@552 1716 local function RecordNPCSpell(sub_event, source_guid, source_name, source_flags, dest_guid, dest_name, dest_flags, spell_id)
atcaleb@558 1717 if not spell_id or private.BLACKLISTED_SPELLS[spell_id] then
jcallahan@23 1718 return
jcallahan@23 1719 end
jcallahan@34 1720 local source_type, source_id = ParseGUID(source_guid)
jcallahan@23 1721
jcallahan@171 1722 if not source_id or not UnitTypeIsNPC(source_type) then
jcallahan@23 1723 return
jcallahan@23 1724 end
jcallahan@23 1725
jcallahan@23 1726 if bit.band(FLAGS_NPC_CONTROL, source_flags) == FLAGS_NPC_CONTROL and bit.band(FLAGS_NPC, source_flags) ~= 0 then
jcallahan@248 1727 local encounter_data = NPCEntry(source_id):EncounterData(InstanceDifficultyToken())
jcallahan@28 1728 encounter_data.spells = encounter_data.spells or {}
jcallahan@28 1729 encounter_data.spells[spell_id] = (encounter_data.spells[spell_id] or 0) + 1
jcallahan@23 1730 end
jcallahan@23 1731 end
jcallahan@23 1732
jcallahan@115 1733 local HEAL_BATTLE_PETS_SPELL_ID = 125801
jcallahan@115 1734
jcallahan@246 1735 local previous_combat_event = {}
jcallahan@246 1736
jcallahan@23 1737 local COMBAT_LOG_FUNCS = {
jcallahan@23 1738 SPELL_AURA_APPLIED = RecordNPCSpell,
jcallahan@23 1739 SPELL_CAST_START = RecordNPCSpell,
atcaleb@552 1740 SPELL_CAST_SUCCESS = function(sub_event, source_guid, source_name, source_flags, dest_guid, dest_name, dest_flags, spell_id)
jcallahan@115 1741 if spell_id == HEAL_BATTLE_PETS_SPELL_ID then
jcallahan@115 1742 local unit_type, unit_idnum = ParseGUID(source_guid)
jcallahan@115 1743
jcallahan@171 1744 if unit_idnum and UnitTypeIsNPC(unit_type) then
jcallahan@115 1745 NPCEntry(unit_idnum).stable_master = true
jcallahan@115 1746 end
jcallahan@115 1747 end
atcaleb@552 1748 RecordNPCSpell(sub_event, source_guid, source_name, source_flags, dest_guid, dest_name, dest_flags, spell_id)
jcallahan@115 1749 end,
atcaleb@552 1750 UNIT_DIED = function(sub_event, source_guid, source_name, source_flags, dest_guid, dest_name, dest_flags, spell_id)
jcallahan@65 1751 local unit_type, unit_idnum = ParseGUID(dest_guid)
jcallahan@65 1752
jcallahan@171 1753 if not unit_idnum or not UnitTypeIsNPC(unit_type) then
mmosimca@515 1754 --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 1755 ClearKilledNPC()
jcallahan@98 1756 private.harvesting = nil
jcallahan@65 1757 return
jcallahan@65 1758 end
jcallahan@177 1759
jcallahan@246 1760 if source_guid == "" then
jcallahan@246 1761 source_guid = nil
jcallahan@246 1762 end
jcallahan@246 1763 local killer_guid = source_guid or previous_combat_event.source_guid
jcallahan@246 1764 local killer_name = source_name or previous_combat_event.source_name
jcallahan@246 1765
jcallahan@299 1766 if not previous_combat_event.party_damage then
jcallahan@312 1767 --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 1768 table.wipe(previous_combat_event)
jcallahan@217 1769 ClearKilledNPC()
jcallahan@306 1770 else
jcallahan@317 1771 --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 1772 end
jcallahan@177 1773 killed_npc_id = unit_idnum
MMOSimca@383 1774 C_Timer.After(0.1, ClearKilledNPC)
jcallahan@65 1775 end,
jcallahan@23 1776 }
jcallahan@23 1777
jcallahan@23 1778
jcallahan@246 1779 local NON_DAMAGE_EVENTS = {
jcallahan@246 1780 SPELL_AURA_APPLIED = true,
jcallahan@246 1781 SPELL_AURA_REMOVED = true,
jcallahan@246 1782 SPELL_AURA_REMOVED_DOSE = true,
jcallahan@246 1783 SPELL_CAST_FAILED = true,
jcallahan@246 1784 SWING_MISSED = true,
jcallahan@246 1785 }
jcallahan@246 1786
jcallahan@299 1787 local DAMAGE_EVENTS = {
jcallahan@299 1788 RANGE_DAMAGE = true,
jcallahan@299 1789 SPELL_BUILDING_DAMAGE = true,
jcallahan@299 1790 SPELL_DAMAGE = true,
jcallahan@299 1791 SPELL_PERIODIC_DAMAGE = true,
jcallahan@299 1792 SWING_DAMAGE = true,
jcallahan@299 1793 }
jcallahan@299 1794
jcallahan@246 1795
atcaleb@552 1796 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 1797 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 1798
jcallahan@23 1799 local combat_log_func = COMBAT_LOG_FUNCS[sub_event]
jcallahan@23 1800
jcallahan@23 1801 if not combat_log_func then
jcallahan@299 1802 if DAMAGE_EVENTS[sub_event] then
jcallahan@299 1803 table.wipe(previous_combat_event)
jcallahan@246 1804 previous_combat_event.source_name = source_name
jcallahan@299 1805
jcallahan@299 1806 if source_guid ~= dest_guid and (in_instance or group_member_guids[source_guid] or group_pet_guids[source_guid]) then
jcallahan@299 1807 previous_combat_event.party_damage = true
jcallahan@299 1808 end
jcallahan@246 1809 end
jcallahan@23 1810 return
jcallahan@23 1811 end
atcaleb@552 1812 combat_log_func(sub_event, source_guid, source_name, source_flags, dest_guid, dest_name, dest_flags, spell_id)
jcallahan@297 1813
jcallahan@297 1814 if NON_DAMAGE_EVENTS[sub_event] then
jcallahan@297 1815 table.wipe(previous_combat_event)
jcallahan@297 1816 end
jcallahan@23 1817 end
jcallahan@23 1818
catherton@465 1819
jcallahan@44 1820 local DIPLOMACY_SPELL_ID = 20599
jcallahan@44 1821 local MR_POP_RANK1_SPELL_ID = 78634
jcallahan@44 1822 local MR_POP_RANK2_SPELL_ID = 78635
MMOSimca@418 1823 local TRADING_PACT_SPELL_ID = 170200
jcallahan@44 1824
jcallahan@44 1825
jcallahan@92 1826 function WDP:COMBAT_TEXT_UPDATE(event_name, message_type, faction_name, amount)
jcallahan@177 1827 if message_type ~= "FACTION" or not killed_npc_id then
jcallahan@44 1828 return
jcallahan@44 1829 end
jcallahan@44 1830 UpdateFactionData()
jcallahan@44 1831
jcallahan@46 1832 if not faction_name or not faction_standings[faction_name] then
jcallahan@46 1833 return
jcallahan@46 1834 end
jcallahan@177 1835 local npc = NPCEntry(killed_npc_id)
jcallahan@177 1836 ClearKilledNPC()
jcallahan@46 1837
jcallahan@44 1838 if not npc then
jcallahan@98 1839 private.harvesting = nil
jcallahan@44 1840 return
jcallahan@44 1841 end
jcallahan@98 1842 npc.harvested = private.harvesting
jcallahan@98 1843 private.harvesting = nil
jcallahan@98 1844
jcallahan@44 1845 local modifier = 1
jcallahan@44 1846
MMOSimca@340 1847 -- Check for modifiers from known spells
jcallahan@44 1848 if _G.IsSpellKnown(DIPLOMACY_SPELL_ID) then
jcallahan@44 1849 modifier = modifier + 0.1
jcallahan@44 1850 end
jcallahan@44 1851 if _G.IsSpellKnown(MR_POP_RANK2_SPELL_ID) then
jcallahan@44 1852 modifier = modifier + 0.1
jcallahan@44 1853 elseif _G.IsSpellKnown(MR_POP_RANK1_SPELL_ID) then
jcallahan@44 1854 modifier = modifier + 0.05
jcallahan@44 1855 end
MMOSimca@418 1856 if _G.IsSpellKnown(TRADING_PACT_SPELL_ID) then
MMOSimca@418 1857 modifier = modifier + 0.2
MMOSimca@418 1858 end
jcallahan@44 1859
MMOSimca@340 1860 -- Determine faction ID
MMOSimca@340 1861 local faction_ID
MMOSimca@418 1862 for pseudo_faction_name, faction_data_table in pairs(private.FACTION_DATA) do
MMOSimca@418 1863 if faction_name == faction_data_table[3] then
MMOSimca@418 1864 -- Check ignore flag
MMOSimca@418 1865 if faction_data_table[2] then
MMOSimca@418 1866 return
MMOSimca@418 1867 end
MMOSimca@340 1868 faction_ID = faction_data_table[1]
MMOSimca@418 1869 break
MMOSimca@340 1870 end
MMOSimca@340 1871 end
MMOSimca@340 1872 if faction_ID and faction_ID > 0 then
MMOSimca@340 1873 -- Check for modifiers from Commendations (applied directly to the faction, account-wide)
MMOSimca@340 1874 local _, _, _, _, _, _, _, _, _, _, _, _, _, _, has_bonus_rep_gain = GetFactionInfoByID(faction_ID)
MMOSimca@340 1875 if has_bonus_rep_gain then
MMOSimca@340 1876 modifier = modifier + 1
MMOSimca@340 1877 end
MMOSimca@340 1878 end
MMOSimca@340 1879
MMOSimca@340 1880 -- Check for modifiers from buffs
MMOSimca@418 1881 for buff_name, buff_data_table in pairs(private.REP_BUFFS) do
jcallahan@44 1882 if _G.UnitBuff("player", buff_name) then
MMOSimca@340 1883 local modded_faction = buff_data_table.faction
jcallahan@44 1884
jcallahan@44 1885 if not modded_faction or faction_name == modded_faction then
MMOSimca@340 1886 if buff_data_table.ignore then
MMOSimca@340 1887 -- Some buffs from tabards convert all rep of other factions into rep for a specific faction.
MMOSimca@340 1888 -- We can't know what faction the rep was orginally from, so we must ignore the data entirely in these cases.
MMOSimca@340 1889 return
MMOSimca@340 1890 else
MMOSimca@340 1891 modifier = modifier + buff_data_table.modifier
MMOSimca@340 1892 end
jcallahan@44 1893 end
jcallahan@44 1894 end
jcallahan@44 1895 end
catherton@465 1896
jcallahan@65 1897 npc.reputations = npc.reputations or {}
jcallahan@65 1898 npc.reputations[("%s:%s"):format(faction_name, faction_standings[faction_name])] = math.floor(amount / modifier)
jcallahan@32 1899 end
jcallahan@44 1900 end -- do-block
jcallahan@18 1901
jcallahan@18 1902
jcallahan@140 1903 function WDP:CURSOR_UPDATE(event_name)
MMOSimca@355 1904 if current_action.fishing_target or _G.Minimap:IsMouseOver() then
jcallahan@140 1905 return
jcallahan@140 1906 end
jcallahan@140 1907 local text = _G["GameTooltipTextLeft1"]:GetText()
jcallahan@140 1908
MMOSimca@355 1909 -- Handle Fishing
MMOSimca@355 1910 if (current_action.spell_label == "FISHING") then
MMOSimca@355 1911 if not text or text == "Fishing Bobber" then
MMOSimca@355 1912 text = "NONE"
MMOSimca@355 1913 else
MMOSimca@355 1914 current_action.fishing_target = true
MMOSimca@355 1915 end
MMOSimca@355 1916 current_action.identifier = ("%s:%s"):format(current_action.spell_label, text)
MMOSimca@355 1917 -- Handle Garrison Cache
MMOSimca@355 1918 elseif private.GARRISON_CACHE_OBJECT_NAME_TO_OBJECT_ID_MAP[text] then
MMOSimca@355 1919 last_garrison_cache_object_id = private.GARRISON_CACHE_OBJECT_NAME_TO_OBJECT_ID_MAP[text]
jcallahan@140 1920 end
jcallahan@140 1921 end
jcallahan@140 1922
jcallahan@140 1923
jcallahan@92 1924 function WDP:ITEM_TEXT_BEGIN(event_name)
jcallahan@42 1925 local unit_type, unit_idnum = ParseGUID(_G.UnitGUID("npc"))
jcallahan@42 1926
jcallahan@42 1927 if not unit_idnum or unit_type ~= private.UNIT_TYPES.OBJECT or _G.UnitName("npc") ~= _G.ItemTextGetItem() then
jcallahan@42 1928 return
jcallahan@42 1929 end
jcallahan@42 1930 UpdateDBEntryLocation("objects", unit_idnum)
jcallahan@42 1931 end
jcallahan@42 1932
jcallahan@42 1933
jcallahan@13 1934 do
MMOSimca@343 1935 local LOOT_OPENED_VERIFY_FUNCS = {
jcallahan@324 1936 -- 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 1937 [AF.ITEM] = function()
jcallahan@16 1938 local locked_item_id
jcallahan@16 1939
jcallahan@16 1940 for bag_index = 0, _G.NUM_BAG_FRAMES do
jcallahan@16 1941 for slot_index = 1, _G.GetContainerNumSlots(bag_index) do
jcallahan@324 1942 local _, _, is_locked, _, _, is_lootable = _G.GetContainerItemInfo(bag_index, slot_index)
jcallahan@324 1943
jcallahan@324 1944 if is_locked and is_lootable then
jcallahan@16 1945 locked_item_id = ItemLinkToID(_G.GetContainerItemLink(bag_index, slot_index))
jcallahan@165 1946 break
jcallahan@16 1947 end
jcallahan@16 1948 end
jcallahan@165 1949
jcallahan@165 1950 if locked_item_id then
jcallahan@165 1951 break
jcallahan@165 1952 end
jcallahan@16 1953 end
jcallahan@16 1954
MMOSimca@367 1955 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 1956 return false
jcallahan@16 1957 end
jcallahan@122 1958 current_action.identifier = locked_item_id
jcallahan@16 1959 return true
jcallahan@16 1960 end,
MMOSimca@401 1961 [AF.NPC] = function()
MMOSimca@401 1962 return not _G.IsFishingLoot()
MMOSimca@401 1963 end,
MMOSimca@401 1964 [AF.OBJECT] = function()
MMOSimca@401 1965 return not _G.IsFishingLoot()
MMOSimca@401 1966 end,
jcallahan@17 1967 [AF.ZONE] = function()
jcallahan@140 1968 current_action.zone_data = UpdateDBEntryLocation("zones", current_action.identifier)
jcallahan@210 1969 return _G.IsFishingLoot()
jcallahan@17 1970 end,
jcallahan@13 1971 }
jcallahan@13 1972
jcallahan@13 1973
MMOSimca@343 1974 local LOOT_OPENED_UPDATE_FUNCS = {
jcallahan@16 1975 [AF.ITEM] = function()
jcallahan@28 1976 GenericLootUpdate("items")
jcallahan@28 1977 end,
jcallahan@28 1978 [AF.NPC] = function()
jcallahan@75 1979 local difficulty_token = InstanceDifficultyToken()
jcallahan@312 1980 local loot_label = current_loot.label
jcallahan@77 1981 local source_list = {}
jcallahan@75 1982
jcallahan@131 1983 for source_guid, loot_data in pairs(current_loot.sources) do
jcallahan@331 1984 local _, source_id = ParseGUID(source_guid)
jcallahan@75 1985 local npc = NPCEntry(source_id)
jcallahan@75 1986
jcallahan@75 1987 if npc then
jcallahan@248 1988 local encounter_data = npc:EncounterData(difficulty_token)
jcallahan@312 1989 encounter_data[loot_label] = encounter_data[loot_label] or {}
jcallahan@75 1990
jcallahan@78 1991 if not source_list[source_guid] then
jcallahan@77 1992 encounter_data.loot_counts = encounter_data.loot_counts or {}
MMOSimca@426 1993 encounter_data.loot_counts[loot_label] = (encounter_data.loot_counts[loot_label] or 0) + 1
jcallahan@312 1994 source_list[source_guid] = true
jcallahan@77 1995 end
jcallahan@77 1996
jcallahan@309 1997 for loot_token, quantity in pairs(loot_data) do
mmosimca@496 1998 local loot_type, currency_id = (":"):split(loot_token)
mmosimca@496 1999
mmosimca@496 2000 if loot_type == "currency" and currency_id then
mmosimca@496 2001 -- Convert currency_id back into number from string
mmosimca@496 2002 currency_id = tonumber(currency_id) or 0
mmosimca@496 2003 if currency_id ~= 0 then
mmosimca@496 2004 table.insert(encounter_data[loot_label], ("currency:%d:%d"):format(quantity, currency_id))
mmosimca@496 2005 end
jcallahan@309 2006 elseif loot_token == "money" then
jcallahan@312 2007 table.insert(encounter_data[loot_label], ("money:%d"):format(quantity))
jcallahan@309 2008 else
jcallahan@312 2009 table.insert(encounter_data[loot_label], ("%d:%d"):format(loot_token, quantity))
jcallahan@309 2010 end
jcallahan@75 2011 end
jcallahan@75 2012 end
jcallahan@75 2013 end
jcallahan@16 2014 end,
jcallahan@13 2015 [AF.OBJECT] = function()
jcallahan@28 2016 GenericLootUpdate("objects", InstanceDifficultyToken())
jcallahan@17 2017 end,
jcallahan@17 2018 [AF.ZONE] = function()
MMOSimca@328 2019 if not (current_loot.map_level and current_loot.x and current_loot.y and current_loot.zone_data) then
MMOSimca@328 2020 return
MMOSimca@328 2021 end
jcallahan@141 2022 local location_token = ("%d:%d:%d"):format(current_loot.map_level, current_loot.x, current_loot.y)
jcallahan@41 2023
jcallahan@41 2024 -- This will start life as a boolean true.
mmosimca@522 2025 if type(current_loot.zone_data[location_token]) ~= "table" then
jcallahan@131 2026 current_loot.zone_data[location_token] = {
jcallahan@41 2027 drops = {}
jcallahan@41 2028 }
jcallahan@41 2029 end
jcallahan@132 2030 local loot_count = ("%s_count"):format(current_loot.label)
jcallahan@131 2031 current_loot.zone_data[location_token][loot_count] = (current_loot.zone_data[location_token][loot_count] or 0) + 1
jcallahan@41 2032
jcallahan@131 2033 if current_loot.sources then
jcallahan@131 2034 for source_guid, loot_data in pairs(current_loot.sources) do
jcallahan@131 2035 for item_id, quantity in pairs(loot_data) do
jcallahan@131 2036 table.insert(current_loot.zone_data[location_token].drops, ("%d:%d"):format(item_id, quantity))
jcallahan@131 2037 end
jcallahan@131 2038 end
jcallahan@131 2039 end
jcallahan@131 2040
jcallahan@131 2041 if #current_loot.list <= 0 then
jcallahan@131 2042 return
jcallahan@131 2043 end
jcallahan@131 2044
jcallahan@131 2045 for index = 1, #current_loot.list do
MMOSimca@443 2046 table.insert(current_loot.zone_data[location_token].drops, current_loot.list[index])
jcallahan@41 2047 end
jcallahan@13 2048 end,
jcallahan@13 2049 }
jcallahan@13 2050
jcallahan@79 2051 -- Prevent opening the same loot window multiple times from recording data multiple times.
jcallahan@79 2052 local loot_guid_registry = {}
jcallahan@124 2053
jcallahan@124 2054
jcallahan@124 2055 function WDP:LOOT_CLOSED(event_name)
MMOSimca@398 2056 ClearChatLootData()
jcallahan@131 2057 current_loot = nil
jcallahan@131 2058 table.wipe(current_action)
jcallahan@124 2059 end
jcallahan@124 2060
jcallahan@13 2061
jcallahan@322 2062 local function ExtrapolatedCurrentActionFromLootData(event_name)
MMOSimca@402 2063 local log_source = event_name .. "- ExtrapolatedCurrentActionFromLootData"
MMOSimca@402 2064 local previous_spell_label = current_action.spell_label
jcallahan@322 2065 local extrapolated_guid_registry = {}
jcallahan@322 2066 local num_guids = 0
MMOSimca@402 2067 table.wipe(current_action)
MMOSimca@402 2068
MMOSimca@402 2069 if _G.IsFishingLoot() then
MMOSimca@402 2070 -- Set up a proper 'fishing' current_action table
MMOSimca@402 2071 local zone_name, area_id, x, y, map_level, instance_token = CurrentLocationData()
MMOSimca@402 2072 if not (zone_name and area_id and x and y and map_level) then
mmosimca@508 2073 if not (_G.IsInInstance()) then
mmosimca@508 2074 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 2075 end
MMOSimca@402 2076 return
MMOSimca@402 2077 end
MMOSimca@402 2078 current_action.instance_token = instance_token
MMOSimca@402 2079 current_action.map_level = map_level
MMOSimca@402 2080 current_action.x = x
MMOSimca@402 2081 current_action.y = y
MMOSimca@402 2082 current_action.zone_data = ("%s:%d"):format(zone_name, area_id)
MMOSimca@402 2083 current_action.spell_label = "FISHING"
MMOSimca@402 2084 current_action.loot_label = "fishing"
MMOSimca@402 2085 current_action.identifier = "FISHING:NONE"
MMOSimca@402 2086 current_action.target_type = AF.ZONE
MMOSimca@402 2087
MMOSimca@402 2088 Debug("%s: Fishing loot detected.", log_source)
MMOSimca@402 2089 return true
MMOSimca@402 2090 end
jcallahan@322 2091
MMOSimca@344 2092 -- Loot extrapolation cannot handle objects that need special spell labels (like HERBALISM or MINING) (MIND_CONTROL is okay)
MMOSimca@402 2093 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 2094 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 2095 return false
MMOSimca@344 2096 end
MMOSimca@344 2097
jcallahan@322 2098 for loot_slot = 1, _G.GetNumLootItems() do
jcallahan@322 2099 local loot_info = {
jcallahan@322 2100 _G.GetLootSourceInfo(loot_slot)
jcallahan@322 2101 }
jcallahan@322 2102
jcallahan@322 2103 for loot_index = 1, #loot_info, 2 do
jcallahan@322 2104 local source_guid = loot_info[loot_index]
jcallahan@322 2105
jcallahan@322 2106 if not extrapolated_guid_registry[source_guid] then
jcallahan@322 2107 local unit_type, unit_idnum = ParseGUID(source_guid)
jcallahan@322 2108
jcallahan@322 2109 if unit_type and unit_idnum then
jcallahan@322 2110 extrapolated_guid_registry[source_guid] = {
jcallahan@322 2111 unit_type,
jcallahan@322 2112 unit_idnum
jcallahan@322 2113 }
jcallahan@322 2114
jcallahan@322 2115 num_guids = num_guids + 1
jcallahan@322 2116 end
jcallahan@322 2117 end
jcallahan@322 2118 end
jcallahan@322 2119 end
jcallahan@322 2120
jcallahan@322 2121 if num_guids == 0 then
jcallahan@322 2122 Debug("%s: No GUIDs found in loot. Blank loot window?", log_source)
jcallahan@322 2123 return false
jcallahan@322 2124 end
jcallahan@326 2125
jcallahan@322 2126 local num_npcs = 0
jcallahan@322 2127 local num_objects = 0
jcallahan@324 2128 local num_itemcontainers = 0
jcallahan@322 2129
jcallahan@322 2130 for source_guid, guid_data in pairs(extrapolated_guid_registry) do
jcallahan@322 2131 local unit_type = guid_data[1]
mmosimca@516 2132 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 2133
jcallahan@322 2134 if loot_label then
jcallahan@322 2135 local unit_idnum = guid_data[2]
jcallahan@322 2136
jcallahan@322 2137 if loot_guid_registry[loot_label] and loot_guid_registry[loot_label][source_guid] then
jcallahan@322 2138 Debug("%s: Previously scanned loot for unit with GUID %s and identifier %s.", log_source, source_guid, unit_idnum)
jcallahan@322 2139 elseif unit_type == private.UNIT_TYPES.OBJECT and unit_idnum ~= OBJECT_ID_FISHING_BOBBER then
jcallahan@322 2140 current_action.loot_label = loot_label
jcallahan@322 2141 current_action.spell_label = "OPENING"
jcallahan@322 2142 current_action.target_type = AF.OBJECT
jcallahan@322 2143 current_action.identifier = unit_idnum
jcallahan@322 2144 num_objects = num_objects + 1
jcallahan@322 2145 elseif UnitTypeIsNPC(unit_type) then
jcallahan@322 2146 current_action.loot_label = loot_label
jcallahan@322 2147 current_action.target_type = AF.NPC
jcallahan@322 2148 current_action.identifier = unit_idnum
jcallahan@322 2149 num_npcs = num_npcs + 1
mmosimca@516 2150 elseif unit_type == private.UNIT_TYPES.ITEM then
jcallahan@324 2151 current_action.loot_label = loot_label
jcallahan@324 2152 current_action.target_type = AF.ITEM
jcallahan@324 2153 -- current_action.identifier assigned during loot verification.
jcallahan@324 2154 num_itemcontainers = num_itemcontainers + 1
jcallahan@322 2155 end
jcallahan@322 2156 else
jcallahan@322 2157 -- 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 2158 Debug("%s: Unit with GUID %s has unsupported type for looting.", log_source, source_guid)
jcallahan@322 2159 return false
jcallahan@322 2160 end
jcallahan@322 2161 end
jcallahan@322 2162
jcallahan@322 2163 if not current_action.target_type then
jcallahan@322 2164 Debug("%s: Failure to obtain target_type.", log_source)
jcallahan@322 2165 return false
jcallahan@322 2166 end
jcallahan@322 2167
jcallahan@322 2168 -- 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 2169 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 2170 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 2171 return false
jcallahan@322 2172 end
jcallahan@322 2173
jcallahan@322 2174 return true
jcallahan@322 2175 end
jcallahan@322 2176
jcallahan@322 2177
MMOSimca@343 2178 function WDP:LOOT_OPENED(event_name)
MMOSimca@398 2179 ClearChatLootData()
MMOSimca@387 2180
jcallahan@132 2181 if current_loot then
jcallahan@322 2182 current_loot = nil
jcallahan@322 2183 Debug("%s: Previous loot did not process in time for this event. Attempting to extrapolate current_action from loot data.", event_name)
jcallahan@322 2184
jcallahan@322 2185 if not ExtrapolatedCurrentActionFromLootData(event_name) then
jcallahan@322 2186 Debug("%s: Unable to extrapolate current_action. Aborting attempts to handle loot for now.", event_name)
jcallahan@322 2187 return
jcallahan@322 2188 end
jcallahan@18 2189 end
jcallahan@151 2190
jcallahan@151 2191 if not current_action.target_type then
jcallahan@322 2192 if not ExtrapolatedCurrentActionFromLootData(event_name) then
jcallahan@322 2193 Debug("%s: Unable to extrapolate current_action. Aborting attempts to handle loot for now.", event_name)
jcallahan@322 2194 return
jcallahan@322 2195 end
jcallahan@151 2196 end
MMOSimca@343 2197 local verify_func = LOOT_OPENED_VERIFY_FUNCS[current_action.target_type]
MMOSimca@343 2198 local update_func = LOOT_OPENED_UPDATE_FUNCS[current_action.target_type]
jcallahan@13 2199
jcallahan@14 2200 if not verify_func or not update_func then
jcallahan@322 2201 Debug("%s: The current action's target type is unsupported or nil.", event_name)
jcallahan@13 2202 return
jcallahan@13 2203 end
jcallahan@13 2204
mmosimca@522 2205 if type(verify_func) == "function" and not verify_func() then
jcallahan@324 2206 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 2207 return
jcallahan@14 2208 end
jcallahan@80 2209 local guids_used = {}
jcallahan@132 2210
jcallahan@301 2211 InitializeCurrentLoot()
jcallahan@217 2212 loot_guid_registry[current_loot.label] = loot_guid_registry[current_loot.label] or {}
jcallahan@217 2213
jcallahan@55 2214 for loot_slot = 1, _G.GetNumLootItems() do
mmosimca@496 2215 local texture_filedata_id, item_text, slot_quantity, quality, locked = _G.GetLootSlotInfo(loot_slot)
jcallahan@55 2216 local slot_type = _G.GetLootSlotType(loot_slot)
catherton@463 2217 local loot_info = { _G.GetLootSourceInfo(loot_slot) }
catherton@464 2218 local loot_link = _G.GetLootSlotLink(loot_slot)
jcallahan@13 2219
jcallahan@237 2220 -- Odd index is GUID, even is count.
jcallahan@237 2221 for loot_index = 1, #loot_info, 2 do
jcallahan@237 2222 local source_guid = loot_info[loot_index]
jcallahan@75 2223
jcallahan@237 2224 if not loot_guid_registry[current_loot.label][source_guid] then
jcallahan@237 2225 local loot_quantity = loot_info[loot_index + 1]
jcallahan@324 2226 -- There is a new bug in 5.4.0 that causes GetLootSlotInfo() to (rarely) return nil values for slot_quantity.
jcallahan@324 2227 if slot_quantity then
jcallahan@324 2228 -- 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 2229 if slot_quantity > loot_quantity then
jcallahan@324 2230 loot_quantity = slot_quantity
jcallahan@324 2231 end
jcallahan@324 2232 local source_type, source_id = ParseGUID(source_guid)
MMOSimca@329 2233 local source_key = ("%s:%d"):format(source_type, source_id)
jcallahan@324 2234
MMOSimca@366 2235 if slot_type == LOOT_SLOT_ITEM then
catherton@464 2236 if loot_link then
catherton@464 2237 local item_id = ItemLinkToID(loot_link)
catherton@464 2238 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 2239 current_loot.sources[source_guid] = current_loot.sources[source_guid] or {}
catherton@464 2240 current_loot.sources[source_guid][item_id] = (current_loot.sources[source_guid][item_id] or 0) + loot_quantity
catherton@464 2241 guids_used[source_guid] = true
catherton@463 2242 else
catherton@463 2243 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 2244 end
MMOSimca@366 2245 elseif slot_type == LOOT_SLOT_MONEY then
jcallahan@324 2246 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 2247 if current_loot.target_type == AF.ZONE then
jcallahan@324 2248 table.insert(current_loot.list, ("money:%d"):format(loot_quantity))
jcallahan@324 2249 else
jcallahan@324 2250 current_loot.sources[source_guid] = current_loot.sources[source_guid] or {}
MMOSimca@367 2251 current_loot.sources[source_guid]["money"] = (current_loot.sources[source_guid]["money"] or 0) + loot_quantity
jcallahan@324 2252 guids_used[source_guid] = true
jcallahan@324 2253 end
MMOSimca@366 2254 elseif slot_type == LOOT_SLOT_CURRENCY then
jcallahan@324 2255 -- Same bug with GetLootSlotInfo() will screw up currency when it happens, so we won't process this slot's loot.
catherton@463 2256 if loot_link then
mmosimca@496 2257 local currency_id = CurrencyLinkToID(loot_link)
mmosimca@496 2258 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 2259 if current_loot.target_type == AF.ZONE then
mmosimca@496 2260 table.insert(current_loot.list, ("currency:%d:%d"):format(loot_quantity, currency_id))
jcallahan@324 2261 else
mmosimca@496 2262 local currency_token = ("currency:%d"):format(currency_id)
jcallahan@324 2263
jcallahan@324 2264 current_loot.sources[source_guid] = current_loot.sources[source_guid] or {}
MMOSimca@367 2265 current_loot.sources[source_guid][currency_token] = (current_loot.sources[source_guid][currency_token] or 0) + loot_quantity
jcallahan@324 2266 guids_used[source_guid] = true
jcallahan@324 2267 end
jcallahan@324 2268 else
catherton@463 2269 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 2270 end
jcallahan@308 2271 end
jcallahan@324 2272 else
jcallahan@324 2273 -- 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 2274 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 2275 end
jcallahan@75 2276 end
jcallahan@13 2277 end
jcallahan@13 2278 end
jcallahan@80 2279
jcallahan@81 2280 for guid in pairs(guids_used) do
jcallahan@217 2281 loot_guid_registry[current_loot.label][guid] = true
jcallahan@80 2282 end
jcallahan@13 2283 update_func()
jcallahan@1 2284 end
jcallahan@13 2285 end -- do-block
jcallahan@0 2286
jcallahan@0 2287
jcallahan@89 2288 function WDP:MAIL_SHOW(event_name)
MMOSimca@436 2289 WDP:StopChatLootRecording(event_name)
jcallahan@89 2290 local unit_type, unit_idnum = ParseGUID(_G.UnitGUID("npc"))
jcallahan@89 2291
jcallahan@89 2292 if not unit_idnum or unit_type ~= private.UNIT_TYPES.OBJECT then
jcallahan@89 2293 return
jcallahan@89 2294 end
jcallahan@89 2295 UpdateDBEntryLocation("objects", unit_idnum)
jcallahan@89 2296 end
jcallahan@89 2297
jcallahan@89 2298
jcallahan@44 2299 do
jcallahan@44 2300 local POINT_MATCH_PATTERNS = {
jcallahan@44 2301 ("^%s$"):format(_G.ITEM_REQ_ARENA_RATING:gsub("%%d", "(%%d+)")), -- May no longer be necessary
jcallahan@44 2302 ("^%s$"):format(_G.ITEM_REQ_ARENA_RATING_3V3:gsub("%%d", "(%%d+)")), -- May no longer be necessary
jcallahan@44 2303 ("^%s$"):format(_G.ITEM_REQ_ARENA_RATING_5V5:gsub("%%d", "(%%d+)")), -- May no longer be necessary
jcallahan@44 2304 ("^%s$"):format(_G.ITEM_REQ_ARENA_RATING_BG:gsub("%%d", "(%%d+)")),
jcallahan@44 2305 ("^%s$"):format(_G.ITEM_REQ_ARENA_RATING_3V3_BG:gsub("%%d", "(%%d+)")),
jcallahan@44 2306 }
jcallahan@5 2307
jcallahan@68 2308 local ITEM_REQ_REPUTATION_MATCH = "Requires (.-) %- (.*)"
jcallahan@87 2309 local ITEM_REQ_QUEST_MATCH1 = "Requires: .*"
jcallahan@87 2310 local ITEM_REQ_QUEST_MATCH2 = "Must have completed: .*"
jcallahan@68 2311
jcallahan@133 2312 local current_merchant
jcallahan@133 2313 local merchant_standing
jcallahan@133 2314
jcallahan@133 2315 function WDP:MERCHANT_CLOSED(event_name)
MMOSimca@436 2316 WDP:ResumeChatLootRecording(event_name)
jcallahan@133 2317 current_merchant = nil
jcallahan@133 2318 merchant_standing = nil
jcallahan@133 2319 end
jcallahan@133 2320
jcallahan@133 2321
jcallahan@89 2322 function WDP:UpdateMerchantItems(event_name)
jcallahan@144 2323 if not current_merchant or event_name == "MERCHANT_SHOW" then
MMOSimca@436 2324 WDP:StopChatLootRecording(event_name)
jcallahan@195 2325 local unit_type, unit_idnum = ParseGUID(_G.UnitGUID("npc"))
jcallahan@4 2326
jcallahan@171 2327 if not unit_idnum or not UnitTypeIsNPC(unit_type) then
jcallahan@133 2328 return
jcallahan@133 2329 end
jcallahan@331 2330 local _, faction_standing = UnitFactionStanding("npc")
jcallahan@331 2331 merchant_standing = faction_standing
jcallahan@133 2332 current_merchant = NPCEntry(unit_idnum)
jcallahan@133 2333 current_merchant.sells = current_merchant.sells or {}
jcallahan@44 2334 end
jcallahan@55 2335 local current_filters = _G.GetMerchantFilter()
jcallahan@57 2336 _G.SetMerchantFilter(_G.LE_LOOT_FILTER_ALL)
jcallahan@57 2337 _G.MerchantFrame_Update()
jcallahan@57 2338
jcallahan@150 2339 local num_items = _G.GetMerchantNumItems()
jcallahan@150 2340
jcallahan@44 2341 for item_index = 1, num_items do
jcallahan@44 2342 local _, _, copper_price, stack_size, num_available, _, extended_cost = _G.GetMerchantItemInfo(item_index)
jcallahan@44 2343 local item_id = ItemLinkToID(_G.GetMerchantItemLink(item_index))
jcallahan@5 2344
jcallahan@324 2345 DatamineTT:ClearLines()
jcallahan@324 2346 DatamineTT:SetMerchantItem(item_index)
jcallahan@324 2347
jcallahan@324 2348 if not item_id then
jcallahan@324 2349 local item_name, item_link = DatamineTT:GetItem()
jcallahan@324 2350 item_id = ItemLinkToID(item_link)
MMOSimca@354 2351 -- GetMerchantItemLink() still ocassionally fails as of Patch 6.0.2. It fails so badly that debug functions cause considerable slowdown.
jcallahan@324 2352 end
jcallahan@324 2353
jcallahan@44 2354 if item_id and item_id > 0 then
jcallahan@44 2355 local price_string = ActualCopperCost(copper_price, merchant_standing)
jcallahan@5 2356
jcallahan@68 2357 local num_lines = DatamineTT:NumLines()
jcallahan@68 2358
jcallahan@68 2359 for line_index = 1, num_lines do
jcallahan@68 2360 local current_line = _G["WDPDatamineTTTextLeft" .. line_index]
jcallahan@68 2361
jcallahan@68 2362 if not current_line then
jcallahan@68 2363 break
jcallahan@68 2364 end
jcallahan@68 2365 local faction, reputation = current_line:GetText():match(ITEM_REQ_REPUTATION_MATCH)
jcallahan@68 2366
jcallahan@68 2367 if faction or reputation then
jcallahan@68 2368 DBEntry("items", item_id).req_reputation = ("%s:%s"):format(faction:gsub("-", ""), reputation:upper())
jcallahan@68 2369 break
jcallahan@68 2370 end
jcallahan@68 2371 end
jcallahan@68 2372
jcallahan@87 2373 for line_index = 1, num_lines do
jcallahan@87 2374 local current_line = _G["WDPDatamineTTTextLeft" .. line_index]
jcallahan@87 2375
jcallahan@87 2376 if not current_line then
jcallahan@87 2377 break
jcallahan@87 2378 end
jcallahan@87 2379 local line_text = current_line:GetText()
jcallahan@87 2380 local quest_name = line_text:match(ITEM_REQ_QUEST_MATCH1) or line_text:match(ITEM_REQ_QUEST_MATCH2)
jcallahan@87 2381
jcallahan@87 2382 if quest_name then
jcallahan@87 2383 DBEntry("items", item_id).req_quest = ("%s"):format(quest_name:gsub("(.+): ", ""), quest_name)
jcallahan@87 2384 break
jcallahan@87 2385 end
jcallahan@87 2386 end
jcallahan@87 2387
jcallahan@44 2388 if extended_cost then
jcallahan@53 2389 local battleground_rating = 0
jcallahan@53 2390 local personal_rating = 0
jcallahan@53 2391 local required_season_amount
jcallahan@5 2392
jcallahan@68 2393 for line_index = 1, num_lines do
jcallahan@44 2394 local current_line = _G["WDPDatamineTTTextLeft" .. line_index]
jcallahan@5 2395
jcallahan@44 2396 if not current_line then
jcallahan@44 2397 break
jcallahan@44 2398 end
jcallahan@53 2399 required_season_amount = current_line:GetText():match("Requires earning a total of (%d+)\n(.-) for the season.")
jcallahan@5 2400
jcallahan@44 2401 for match_index = 1, #POINT_MATCH_PATTERNS do
jcallahan@44 2402 local match1, match2 = current_line:GetText():match(POINT_MATCH_PATTERNS[match_index])
jcallahan@53 2403 personal_rating = personal_rating + (match1 or 0)
jcallahan@53 2404 battleground_rating = battleground_rating + (match2 or 0)
jcallahan@5 2405
jcallahan@44 2406 if match1 or match2 then
jcallahan@44 2407 break
jcallahan@44 2408 end
jcallahan@44 2409 end
jcallahan@5 2410 end
jcallahan@44 2411 local currency_list = {}
jcallahan@44 2412 local item_count = _G.GetMerchantItemCostInfo(item_index)
jcallahan@50 2413
mmosimca@518 2414 -- Keeping this around in case Blizzard makes the two ratings (personal and battleground) diverge at some point
mmosimca@518 2415 --price_string = ("%s:%s:%s:%s"):format(price_string, battleground_rating, personal_rating, required_season_amount or 0)
jcallahan@53 2416 price_string = ("%s:%s:%s"):format(price_string, personal_rating, required_season_amount or 0)
jcallahan@5 2417
jcallahan@44 2418 for cost_index = 1, item_count do
jcallahan@324 2419 -- The third return (Blizz calls "currency_link") of GetMerchantItemCostItem only returns item links as of Patch 5.3.0.
MMOSimca@540 2420 local texture_id, amount_required, hyperlink, name = _G.GetMerchantItemCostItem(item_index, cost_index)
mmosimca@496 2421
mmosimca@519 2422 -- Try to get item ID
MMOSimca@540 2423 local item_id = ItemLinkToID(hyperlink)
mmosimca@519 2424
mmosimca@519 2425 -- 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 2426 -- Handle cases when the additional cost is another item
mmosimca@519 2427 if item_id and item_id > 0 then
mmosimca@519 2428 currency_list[#currency_list + 1] = ("(%s:%d)"):format(amount_required, item_id)
mmosimca@519 2429 else
MMOSimca@540 2430 -- Try to get currency ID
MMOSimca@540 2431 local currency_id = CurrencyLinkToID(hyperlink)
mmosimca@519 2432 if currency_id and currency_id > 0 then
mmosimca@501 2433 currency_list[#currency_list + 1] = ("(%s:%d)"):format(amount_required, currency_id)
MMOSimca@540 2434 else
MMOSimca@540 2435 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 2436 end
jcallahan@44 2437 end
jcallahan@44 2438 end
jcallahan@44 2439
jcallahan@44 2440 for currency_index = 1, #currency_list do
jcallahan@44 2441 price_string = ("%s:%s"):format(price_string, currency_list[currency_index])
jcallahan@5 2442 end
jcallahan@5 2443 end
jcallahan@133 2444 current_merchant.sells[item_id] = ("%s:%s:[%s]"):format(num_available, stack_size, price_string)
jcallahan@44 2445 end
jcallahan@44 2446 end
jcallahan@5 2447
jcallahan@44 2448 if _G.CanMerchantRepair() then
jcallahan@133 2449 current_merchant.can_repair = true
jcallahan@5 2450 end
jcallahan@57 2451 _G.SetMerchantFilter(current_filters)
jcallahan@57 2452 _G.MerchantFrame_Update()
jcallahan@4 2453 end
jcallahan@44 2454 end -- do-block
jcallahan@4 2455
jcallahan@89 2456
jcallahan@92 2457 function WDP:PET_BAR_UPDATE(event_name)
MMOSimca@336 2458 if not private.NON_LOOT_SPELL_LABELS[current_action.spell_label] then
jcallahan@25 2459 return
jcallahan@25 2460 end
jcallahan@34 2461 local unit_type, unit_idnum = ParseGUID(_G.UnitGUID("pet"))
jcallahan@25 2462
jcallahan@171 2463 if not unit_idnum or not UnitTypeIsNPC(unit_type) then
jcallahan@25 2464 return
jcallahan@25 2465 end
jcallahan@29 2466 NPCEntry(unit_idnum).mind_control = true
jcallahan@122 2467 table.wipe(current_action)
jcallahan@25 2468 end
jcallahan@25 2469
jcallahan@25 2470
MMOSimca@368 2471 -- This function produces data currently unused by wowdb.com, and it causes unneeded bloat in the raw lua DB.
MMOSimca@442 2472 --[[local LPJ = LibStub("LibPetJournal-2.0")
MMOSimca@442 2473 function WDP:PET_JOURNAL_LIST_UPDATE(event_name)
MMOSimca@346 2474 if DEBUGGING then
jcallahan@309 2475 return
jcallahan@309 2476 end
jcallahan@309 2477
jcallahan@115 2478 local num_pets = LPJ:NumPets()
jcallahan@115 2479
jcallahan@115 2480 for index, pet_id in LPJ:IteratePetIDs() do
jcallahan@115 2481 local _, _, is_owned, _, level, _, _, name, icon, pet_type, npc_id, _, _, is_wild = _G.C_PetJournal.GetPetInfoByIndex(index)
jcallahan@115 2482
jcallahan@115 2483 if is_owned then
jcallahan@115 2484 local health, max_health, attack, speed, rarity = _G.C_PetJournal.GetPetStats(pet_id)
jcallahan@115 2485
jcallahan@139 2486 if rarity then
jcallahan@139 2487 local rarity_name = _G["BATTLE_PET_BREED_QUALITY" .. rarity]
jcallahan@139 2488 local npc = NPCEntry(npc_id)
jcallahan@139 2489 npc.wild_pet = is_wild or nil
jcallahan@139 2490 npc.battle_pet_data = npc.battle_pet_data or {}
jcallahan@139 2491 npc.battle_pet_data[rarity_name] = npc.battle_pet_data[rarity_name] or {}
jcallahan@139 2492 npc.battle_pet_data[rarity_name]["level_" .. level] = npc.battle_pet_data[rarity_name]["level_" .. level] or {}
jcallahan@139 2493
jcallahan@139 2494 local data = npc.battle_pet_data[rarity_name]["level_" .. level]
jcallahan@139 2495 data.max_health = max_health
jcallahan@139 2496 data.attack = attack
jcallahan@139 2497 data.speed = speed
jcallahan@139 2498 end
jcallahan@115 2499 end
jcallahan@115 2500 end
MMOSimca@368 2501 end]]--
jcallahan@115 2502
jcallahan@115 2503
jcallahan@156 2504 function WDP:PLAYER_REGEN_DISABLED(event_name)
jcallahan@156 2505 private.in_combat = true
jcallahan@156 2506 end
jcallahan@156 2507
jcallahan@156 2508
jcallahan@156 2509 function WDP:PLAYER_REGEN_ENABLED(event_name)
jcallahan@156 2510 private.in_combat = nil
jcallahan@156 2511 end
jcallahan@156 2512
jcallahan@156 2513
jcallahan@118 2514 function WDP:PLAYER_TARGET_CHANGED(event_name)
jcallahan@215 2515 if not TargetedNPC() then
jcallahan@118 2516 return
jcallahan@2 2517 end
jcallahan@151 2518 current_action.target_type = AF.NPC
jcallahan@118 2519 self:UpdateTargetLocation()
jcallahan@118 2520 end
jcallahan@2 2521
jcallahan@89 2522
jcallahan@12 2523 do
jcallahan@12 2524 local function UpdateQuestJuncture(point)
jcallahan@12 2525 local unit_name = _G.UnitName("questnpc")
jcallahan@9 2526
jcallahan@12 2527 if not unit_name then
jcallahan@12 2528 return
jcallahan@12 2529 end
jcallahan@34 2530 local unit_type, unit_id = ParseGUID(_G.UnitGUID("questnpc"))
MMOSimca@351 2531 Debug("UpdateQuestJuncture: Updating quest juncture for %s.", ("%s:%d"):format(private.UNIT_TYPE_NAMES[unit_type], unit_id))
jcallahan@12 2532 if unit_type == private.UNIT_TYPES.OBJECT then
jcallahan@38 2533 UpdateDBEntryLocation("objects", unit_id)
jcallahan@12 2534 end
jcallahan@19 2535 local quest = DBEntry("quests", _G.GetQuestID())
jcallahan@12 2536 quest[point] = quest[point] or {}
MMOSimca@329 2537 quest[point][("%s:%d"):format(private.UNIT_TYPE_NAMES[unit_type], unit_id)] = true
jcallahan@24 2538
jcallahan@24 2539 return quest
jcallahan@12 2540 end
jcallahan@10 2541
jcallahan@12 2542
jcallahan@92 2543 function WDP:QUEST_COMPLETE(event_name)
jcallahan@97 2544 local quest = UpdateQuestJuncture("end")
catherton@465 2545
MMOSimca@446 2546 if not quest then
MMOSimca@446 2547 return
MMOSimca@446 2548 end
jcallahan@97 2549
jcallahan@112 2550 if ALLOWED_LOCALES[CLIENT_LOCALE] then
jcallahan@112 2551 quest.reward_text = ReplaceKeywords(_G.GetRewardText())
jcallahan@112 2552 end
jcallahan@67 2553 -- Make sure the quest NPC isn't erroneously recorded as giving reputation for quests which award it.
jcallahan@177 2554 ClearKilledNPC()
jcallahan@10 2555 end
jcallahan@10 2556
jcallahan@12 2557
jcallahan@92 2558 function WDP:QUEST_DETAIL(event_name)
jcallahan@24 2559 local quest = UpdateQuestJuncture("begin")
jcallahan@24 2560
jcallahan@46 2561 if not quest then
jcallahan@46 2562 return
jcallahan@46 2563 end
jcallahan@24 2564 quest.classes = quest.classes or {}
jcallahan@27 2565 quest.classes[PLAYER_CLASS] = true
jcallahan@24 2566
jcallahan@24 2567 quest.races = quest.races or {}
jcallahan@100 2568 quest.races[(PLAYER_RACE == "Pandaren") and ("%s_%s"):format(PLAYER_RACE, PLAYER_FACTION or "Neutral") or PLAYER_RACE] = true
jcallahan@10 2569 end
jcallahan@12 2570 end -- do-block
jcallahan@9 2571
jcallahan@9 2572
jcallahan@92 2573 function WDP:QUEST_LOG_UPDATE(event_name)
jcallahan@38 2574 local selected_quest = _G.GetQuestLogSelection() -- Save current selection to be restored when we're done.
jcallahan@36 2575 local entry_index, processed_quests = 1, 0
jcallahan@36 2576 local _, num_quests = _G.GetNumQuestLogEntries()
jcallahan@36 2577
jcallahan@36 2578 while processed_quests <= num_quests do
MMOSimca@329 2579 local _, _, _, is_header, _, _, _, quest_id = _G.GetQuestLogTitle(entry_index)
jcallahan@36 2580
jcallahan@84 2581 if quest_id == 0 then
jcallahan@84 2582 processed_quests = processed_quests + 1
jcallahan@84 2583 elseif not is_header then
jcallahan@36 2584 _G.SelectQuestLogEntry(entry_index);
jcallahan@36 2585
jcallahan@36 2586 local quest = DBEntry("quests", quest_id)
jcallahan@36 2587 quest.timer = _G.GetQuestLogTimeLeft()
jcallahan@37 2588 quest.can_share = _G.GetQuestLogPushable() and true or nil
jcallahan@36 2589 processed_quests = processed_quests + 1
jcallahan@36 2590 end
jcallahan@36 2591 entry_index = entry_index + 1
jcallahan@36 2592 end
jcallahan@36 2593 _G.SelectQuestLogEntry(selected_quest)
jcallahan@4 2594 self:UnregisterEvent("QUEST_LOG_UPDATE")
jcallahan@4 2595 end
jcallahan@4 2596
jcallahan@4 2597
jcallahan@97 2598 function WDP:QUEST_PROGRESS(event_name)
jcallahan@112 2599 if not ALLOWED_LOCALES[CLIENT_LOCALE] then
jcallahan@112 2600 return
jcallahan@112 2601 end
jcallahan@97 2602 DBEntry("quests", _G.GetQuestID()).progress_text = ReplaceKeywords(_G.GetProgressText())
jcallahan@97 2603 end
jcallahan@97 2604
jcallahan@97 2605
jcallahan@92 2606 function WDP:UNIT_QUEST_LOG_CHANGED(event_name, unit_id)
jcallahan@4 2607 if unit_id ~= "player" then
jcallahan@4 2608 return
jcallahan@4 2609 end
jcallahan@4 2610 self:RegisterEvent("QUEST_LOG_UPDATE")
jcallahan@4 2611 end
jcallahan@4 2612
jcallahan@4 2613
jcallahan@92 2614 do
jcallahan@92 2615 local TRADESKILL_TOOLS = {
jcallahan@92 2616 Anvil = anvil_spell_ids,
jcallahan@92 2617 Forge = forge_spell_ids,
jcallahan@92 2618 }
jcallahan@92 2619
jcallahan@92 2620
jcallahan@167 2621 local function RegisterTools(tradeskill_name, tradeskill_index)
catherton@479 2622 local link = _G.C_TradeSkillUI.GetRecipeLink(tradeskill_index)
catherton@465 2623
MMOSimca@352 2624 if link then
MMOSimca@352 2625 local spell_id = tonumber(link:match("^|c%x%x%x%x%x%x%x%x|H%w+:(%d+)"))
catherton@479 2626 local required_tool = _G.C_TradeSkillUI.GetRecipeTools(tradeskill_index)
MMOSimca@352 2627
MMOSimca@352 2628 if required_tool then
MMOSimca@352 2629 for tool_name, registry in pairs(TRADESKILL_TOOLS) do
MMOSimca@352 2630 if required_tool:find(tool_name) then
MMOSimca@352 2631 registry[spell_id] = true
MMOSimca@352 2632 end
jcallahan@167 2633 end
jcallahan@167 2634 end
jcallahan@167 2635 end
jcallahan@167 2636 end
jcallahan@167 2637
jcallahan@167 2638
jcallahan@92 2639 function WDP:TRADE_SKILL_SHOW(event_name)
catherton@479 2640 local profession_name, prof_level = _G.C_TradeSkillUI.GetTradeSkillLine()
jcallahan@92 2641
jcallahan@92 2642 if profession_name == _G.UNKNOWN then
jcallahan@92 2643 return
jcallahan@92 2644 end
jcallahan@167 2645 TradeSkillExecutePer(RegisterTools)
jcallahan@92 2646 end
jcallahan@92 2647 end -- do-block
jcallahan@92 2648
jcallahan@92 2649
jcallahan@167 2650 function WDP:TRAINER_CLOSED(event_name)
jcallahan@167 2651 private.trainer_shown = nil
jcallahan@167 2652 end
jcallahan@167 2653
jcallahan@167 2654
jcallahan@92 2655 function WDP:TRAINER_SHOW(event_name)
jcallahan@232 2656 local unit_type, unit_idnum = ParseGUID(_G.UnitGUID("npc"))
jcallahan@164 2657 local trainer = NPCEntry(unit_idnum)
jcallahan@58 2658
jcallahan@164 2659 if not trainer then
jcallahan@58 2660 return
jcallahan@58 2661 end
jcallahan@331 2662 local _, trainer_standing = UnitFactionStanding("npc")
jcallahan@164 2663 trainer.teaches = trainer.teaches or {}
jcallahan@27 2664
jcallahan@167 2665 private.trainer_shown = true
jcallahan@167 2666
jcallahan@27 2667 -- Get the initial trainer filters
MMOSimca@344 2668 local available = _G.GetTrainerServiceTypeFilter("available") and 1 or 0
MMOSimca@344 2669 local unavailable = _G.GetTrainerServiceTypeFilter("unavailable") and 1 or 0
MMOSimca@344 2670 local used = _G.GetTrainerServiceTypeFilter("used") and 1 or 0
jcallahan@27 2671
jcallahan@27 2672 -- Clear the trainer filters
MMOSimca@344 2673 _G.SetTrainerServiceTypeFilter("available", 1)
MMOSimca@344 2674 _G.SetTrainerServiceTypeFilter("unavailable", 1)
MMOSimca@344 2675 _G.SetTrainerServiceTypeFilter("used", 1)
jcallahan@27 2676
jcallahan@27 2677 for index = 1, _G.GetNumTrainerServices(), 1 do
jcallahan@27 2678 local spell_name, rank_name, _, _, required_level = _G.GetTrainerServiceInfo(index)
jcallahan@27 2679
jcallahan@27 2680 if spell_name then
jcallahan@27 2681 DatamineTT:ClearLines()
jcallahan@27 2682 DatamineTT:SetTrainerService(index)
jcallahan@27 2683
jcallahan@27 2684 local _, _, spell_id = DatamineTT:GetSpell()
jcallahan@27 2685
jcallahan@43 2686 if spell_id then
jcallahan@164 2687 local class_professions = trainer.teaches[PLAYER_CLASS]
jcallahan@164 2688
jcallahan@164 2689 if not class_professions then
jcallahan@164 2690 trainer.teaches[PLAYER_CLASS] = {}
jcallahan@164 2691 class_professions = trainer.teaches[PLAYER_CLASS]
jcallahan@164 2692 end
jcallahan@43 2693 local profession, min_skill = _G.GetTrainerServiceSkillReq(index)
jcallahan@43 2694 profession = profession or "General"
jcallahan@43 2695
jcallahan@164 2696 local profession_skills = class_professions[profession]
jcallahan@43 2697
jcallahan@43 2698 if not profession_skills then
jcallahan@43 2699 class_professions[profession] = {}
jcallahan@43 2700 profession_skills = class_professions[profession]
jcallahan@43 2701 end
jcallahan@173 2702 profession_skills[spell_id] = ("%d:%d:%d"):format(required_level, min_skill, _G.GetTrainerServiceCost(index))
jcallahan@27 2703 end
jcallahan@27 2704 end
jcallahan@27 2705 end
jcallahan@27 2706 -- Reset the filters to what they were before
MMOSimca@344 2707 _G.SetTrainerServiceTypeFilter("available", available or 0)
MMOSimca@344 2708 _G.SetTrainerServiceTypeFilter("unavailable", unavailable or 0)
MMOSimca@344 2709 _G.SetTrainerServiceTypeFilter("used", used or 0)
jcallahan@27 2710 end
jcallahan@27 2711
jcallahan@27 2712
jcallahan@1 2713 function WDP:UNIT_SPELLCAST_SENT(event_name, unit_id, spell_name, spell_rank, target_name, spell_line)
jcallahan@1 2714 if private.tracked_line or unit_id ~= "player" then
jcallahan@1 2715 return
jcallahan@1 2716 end
jcallahan@1 2717 local spell_label = private.SPELL_LABELS_BY_NAME[spell_name]
jcallahan@1 2718
jcallahan@1 2719 if not spell_label then
jcallahan@1 2720 return
jcallahan@1 2721 end
jcallahan@306 2722
MMOSimca@343 2723 Debug("UNIT_SPELLCAST_SENT: %s was cast.", spell_name)
jcallahan@150 2724 local item_name, item_link = _G.GameTooltip:GetItem()
jcallahan@150 2725 local unit_name, unit_id = _G.GameTooltip:GetUnit()
jcallahan@1 2726
jcallahan@150 2727 if not unit_name and _G.UnitName("target") == target_name then
jcallahan@150 2728 unit_name = target_name
jcallahan@150 2729 unit_id = "target"
jcallahan@1 2730 end
jcallahan@1 2731 local spell_flags = private.SPELL_FLAGS_BY_LABEL[spell_label]
jcallahan@28 2732 local zone_name, area_id, x, y, map_level, instance_token = CurrentLocationData()
MMOSimca@328 2733 if not (zone_name and area_id and x and y and map_level) then
mmosimca@508 2734 if not (_G.IsInInstance()) then
mmosimca@508 2735 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 2736 end
MMOSimca@328 2737 return
MMOSimca@328 2738 end
jcallahan@28 2739
jcallahan@205 2740 table.wipe(current_action)
jcallahan@122 2741 current_action.instance_token = instance_token
jcallahan@122 2742 current_action.map_level = map_level
jcallahan@122 2743 current_action.x = x
jcallahan@122 2744 current_action.y = y
jcallahan@122 2745 current_action.zone_data = ("%s:%d"):format(zone_name, area_id)
jcallahan@122 2746 current_action.spell_label = spell_label
jcallahan@105 2747
jcallahan@105 2748 if not private.NON_LOOT_SPELL_LABELS[spell_label] then
jcallahan@122 2749 current_action.loot_label = spell_label:lower()
jcallahan@105 2750 end
jcallahan@1 2751
jcallahan@151 2752 if unit_name and unit_name == target_name and not item_name then
jcallahan@16 2753 if bit.band(spell_flags, AF.NPC) == AF.NPC then
jcallahan@150 2754 if not unit_id or unit_name ~= target_name then
jcallahan@16 2755 return
jcallahan@16 2756 end
jcallahan@123 2757 current_action.target_type = AF.NPC
jcallahan@16 2758 end
jcallahan@16 2759 elseif bit.band(spell_flags, AF.ITEM) == AF.ITEM then
jcallahan@123 2760 current_action.target_type = AF.ITEM
jcallahan@16 2761
jcallahan@150 2762 if item_name and item_name == target_name then
jcallahan@150 2763 current_action.identifier = ItemLinkToID(item_link)
jcallahan@16 2764 elseif target_name and target_name ~= "" then
jcallahan@331 2765 local _, item_link = _G.GetItemInfo(target_name)
jcallahan@331 2766 current_action.identifier = ItemLinkToID(item_link)
jcallahan@16 2767 end
jcallahan@150 2768 elseif not item_name and not unit_name then
jcallahan@1 2769 if bit.band(spell_flags, AF.OBJECT) == AF.OBJECT then
jcallahan@17 2770 if target_name == "" then
jcallahan@17 2771 return
jcallahan@17 2772 end
jcallahan@122 2773 current_action.object_name = target_name
jcallahan@123 2774 current_action.target_type = AF.OBJECT
jcallahan@1 2775 elseif bit.band(spell_flags, AF.ZONE) == AF.ZONE then
jcallahan@123 2776 current_action.target_type = AF.ZONE
jcallahan@1 2777 end
jcallahan@1 2778 end
jcallahan@1 2779 private.tracked_line = spell_line
jcallahan@0 2780 end
jcallahan@0 2781
jcallahan@94 2782
MMOSimca@393 2783 -- Triggered by bonus roll prompts, disenchant prompts, and in a few other rare circumstances
jcallahan@312 2784 function WDP:SPELL_CONFIRMATION_PROMPT(event_name, spell_id, confirm_type, text, duration, currency_id_cost)
jcallahan@306 2785 if private.RAID_BOSS_BONUS_SPELL_ID_TO_NPC_ID_MAP[spell_id] then
jcallahan@306 2786 ClearKilledBossID()
jcallahan@306 2787 ClearLootToastContainerID()
MMOSimca@387 2788 raid_boss_id = private.RAID_BOSS_BONUS_SPELL_ID_TO_NPC_ID_MAP[spell_id]
jcallahan@306 2789 else
MMOSimca@336 2790 Debug("%s: Spell ID %d is not a known raid boss 'Bonus' spell.", event_name, spell_id)
jcallahan@306 2791 return
jcallahan@1 2792 end
jcallahan@306 2793
jcallahan@324 2794 -- Assign existing loot data to boss if it exists
jcallahan@307 2795 if loot_toast_data then
MMOSimca@427 2796 local npc = NPCEntry(raid_boss_id)
MMOSimca@427 2797 if npc then
MMOSimca@427 2798 -- Create needed npc fields if required
MMOSimca@427 2799 local loot_label = "drops"
MMOSimca@427 2800 local encounter_data = npc:EncounterData(InstanceDifficultyToken())
MMOSimca@427 2801 encounter_data[loot_label] = encounter_data[loot_label] or {}
MMOSimca@427 2802 encounter_data.loot_counts = encounter_data.loot_counts or {}
MMOSimca@427 2803
MMOSimca@427 2804 for index = 1, #loot_toast_data do
MMOSimca@427 2805 local data = loot_toast_data[index]
MMOSimca@427 2806 local loot_type = data[1]
MMOSimca@427 2807 local hyperlink = data[2]
MMOSimca@427 2808 local quantity = data[3]
MMOSimca@427 2809
MMOSimca@427 2810 if loot_type == "item" then
MMOSimca@427 2811 local item_id = ItemLinkToID(hyperlink)
MMOSimca@427 2812 Debug("%s: Assigned stored item loot data - %s - %d:%d", event_name, hyperlink, item_id, quantity)
MMOSimca@427 2813 table.insert(encounter_data[loot_label], ("%d:%d"):format(item_id, quantity))
MMOSimca@427 2814 elseif loot_type == "money" then
MMOSimca@427 2815 Debug("%s: Assigned stored money loot data - money:%d", event_name, quantity)
MMOSimca@427 2816 table.insert(encounter_data[loot_label], ("money:%d"):format(quantity))
MMOSimca@427 2817 elseif loot_type == "currency" then
mmosimca@496 2818 local currency_id = CurrencyLinkToID(hyperlink)
mmosimca@496 2819 Debug("%s: Assigned stored currency loot data - %s - currency:%d (%d)", event_name, hyperlink, currency_id, quantity)
mmosimca@496 2820 table.insert(encounter_data[loot_label], ("currency:%d:%d"):format(quantity, currency_id))
MMOSimca@427 2821 end
jcallahan@317 2822 end
jcallahan@317 2823
MMOSimca@427 2824 if not boss_loot_toasting[raid_boss_id] then
MMOSimca@427 2825 encounter_data.loot_counts[loot_label] = (encounter_data.loot_counts[loot_label] or 0) + 1
MMOSimca@427 2826 boss_loot_toasting[raid_boss_id] = true -- Do not count further loots until timer expires or another boss is killed
jcallahan@307 2827 end
MMOSimca@427 2828 else
MMOSimca@427 2829 Debug("%s: NPC is nil, but we have stored loot data...", event_name)
jcallahan@307 2830 end
jcallahan@307 2831 end
jcallahan@307 2832
jcallahan@307 2833 ClearLootToastData()
MMOSimca@427 2834 killed_boss_id_timer_handle = C_Timer.NewTimer(5, ClearKilledBossID)
jcallahan@306 2835 end
jcallahan@306 2836
jcallahan@306 2837
jcallahan@306 2838 function WDP:UNIT_SPELLCAST_SUCCEEDED(event_name, unit_id, spell_name, spell_rank, spell_line, spell_id)
jcallahan@306 2839 if unit_id ~= "player" then
jcallahan@306 2840 return
jcallahan@306 2841 end
jcallahan@306 2842 private.tracked_line = nil
jcallahan@306 2843 private.previous_spell_id = spell_id
jcallahan@306 2844
MMOSimca@393 2845 -- For spells cast when Logging
MMOSimca@345 2846 if private.LOGGING_SPELL_ID_TO_OBJECT_ID_MAP[spell_id] then
MMOSimca@345 2847 last_timber_spell_id = spell_id
MMOSimca@351 2848 UpdateDBEntryLocation("objects", ("OPENING:%s"):format(private.LOGGING_SPELL_ID_TO_OBJECT_ID_MAP[spell_id]))
MMOSimca@345 2849 return
MMOSimca@345 2850 end
MMOSimca@345 2851
MMOSimca@393 2852 -- For spells cast by items that always trigger loot toasts
MMOSimca@381 2853 if private.LOOT_TOAST_CONTAINER_SPELL_ID_TO_ITEM_ID_MAP[spell_id] then
jcallahan@306 2854 ClearKilledBossID()
jcallahan@306 2855 ClearLootToastContainerID()
jcallahan@307 2856 ClearLootToastData()
jcallahan@306 2857
MMOSimca@387 2858 loot_toast_container_id = private.LOOT_TOAST_CONTAINER_SPELL_ID_TO_ITEM_ID_MAP[spell_id]
MMOSimca@383 2859 loot_toast_container_timer_handle = C_Timer.NewTimer(1, ClearLootToastContainerID) -- we need to assign a handle here to cancel it later
MMOSimca@345 2860 return
jcallahan@306 2861 end
jcallahan@306 2862
MMOSimca@393 2863 -- For spells cast by items that don't usually trigger loot toasts
catherton@473 2864 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 2865 -- Set up timer
MMOSimca@393 2866 ClearChatLootData()
MMOSimca@393 2867 Debug("%s: Beginning chat-based loot timer for spellID %d", event_name, spell_id)
MMOSimca@411 2868 chat_loot_timer_handle = C_Timer.NewTimer(1.5, ClearChatLootData)
catherton@473 2869 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 2870 chat_loot_data.identifier = private.DELAYED_CONTAINER_SPELL_ID_TO_ITEM_ID_BY_CLASS_ID_MAP[spell_id][PLAYER_CLASS_ID]
MMOSimca@454 2871 else
MMOSimca@454 2872 chat_loot_data.identifier = private.DELAYED_CONTAINER_SPELL_ID_TO_ITEM_ID_MAP[spell_id]
MMOSimca@454 2873 end
MMOSimca@347 2874 return
MMOSimca@347 2875 end
MMOSimca@347 2876
mmosimca@520 2877 -- 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 2878 local text = _G["GameTooltipTextLeft1"] and _G["GameTooltipTextLeft1"]:GetText() or nil
mmosimca@520 2879 if spell_id == SPELL_ID_UPDATE_INTERACTIONS and text and text == "Ephemeral Crystal" then
mmosimca@522 2880 Debug("%s: Detected location for an Ephemeral Crystal.", event_name)
mmosimca@520 2881 for index = 1, #private.EPHEMERAL_CRYSTAL_OBJECT_IDS do
mmosimca@520 2882 UpdateDBEntryLocation("objects", private.EPHEMERAL_CRYSTAL_OBJECT_IDS[index])
mmosimca@520 2883 end
mmosimca@520 2884 end
mmosimca@520 2885
jcallahan@306 2886 if anvil_spell_ids[spell_id] then
jcallahan@306 2887 UpdateDBEntryLocation("objects", OBJECT_ID_ANVIL)
jcallahan@306 2888 elseif forge_spell_ids[spell_id] then
jcallahan@306 2889 UpdateDBEntryLocation("objects", OBJECT_ID_FORGE)
jcallahan@306 2890 elseif spell_name:match("^Harvest.+") then
jcallahan@306 2891 killed_npc_id = current_target_id
MMOSimca@343 2892 private.harvesting = true -- Used to track which NPCs can be harvested (can we get this from CreatureCache instead?)
jcallahan@306 2893 end
jcallahan@306 2894 end
jcallahan@0 2895
jcallahan@90 2896
jcallahan@1 2897 function WDP:HandleSpellFailure(event_name, unit_id, spell_name, spell_rank, spell_line, spell_id)
jcallahan@1 2898 if unit_id ~= "player" then
jcallahan@1 2899 return
jcallahan@1 2900 end
jcallahan@0 2901
jcallahan@1 2902 if private.tracked_line == spell_line then
jcallahan@1 2903 private.tracked_line = nil
jcallahan@1 2904 end
jcallahan@147 2905 table.wipe(current_action)
jcallahan@0 2906 end
jcallahan@90 2907
jcallahan@90 2908
jcallahan@90 2909 do
jcallahan@90 2910 local function SetUnitField(field, required_type)
jcallahan@90 2911 local unit_type, unit_idnum = ParseGUID(_G.UnitGUID("npc"))
jcallahan@90 2912
jcallahan@90 2913 if not unit_idnum or (required_type and unit_type ~= required_type) then
jcallahan@90 2914 return
jcallahan@90 2915 end
jcallahan@90 2916
jcallahan@171 2917 if UnitTypeIsNPC(unit_type) then
jcallahan@90 2918 NPCEntry(unit_idnum)[field] = true
jcallahan@90 2919 elseif unit_type == private.UNIT_TYPES.OBJECT then
jcallahan@90 2920 DBEntry("objects", unit_idnum)[field] = true
jcallahan@93 2921 UpdateDBEntryLocation("objects", unit_idnum)
jcallahan@90 2922 end
jcallahan@90 2923 end
jcallahan@90 2924
jcallahan@90 2925
jcallahan@90 2926 function WDP:AUCTION_HOUSE_SHOW(event_name)
MMOSimca@436 2927 WDP:StopChatLootRecording(event_name)
jcallahan@90 2928 SetUnitField("auctioneer", private.UNIT_TYPES.NPC)
jcallahan@90 2929 end
jcallahan@90 2930
jcallahan@90 2931
jcallahan@90 2932 function WDP:BANKFRAME_OPENED(event_name)
MMOSimca@436 2933 WDP:StopChatLootRecording(event_name)
jcallahan@90 2934 SetUnitField("banker", private.UNIT_TYPES.NPC)
jcallahan@90 2935 end
jcallahan@90 2936
jcallahan@90 2937
jcallahan@90 2938 function WDP:BATTLEFIELDS_SHOW(event_name)
jcallahan@90 2939 SetUnitField("battlemaster", private.UNIT_TYPES.NPC)
jcallahan@90 2940 end
jcallahan@90 2941
jcallahan@90 2942
jcallahan@323 2943 local GOSSIP_SHOW_FUNCS = {
jcallahan@323 2944 [private.UNIT_TYPES.NPC] = function(unit_idnum)
jcallahan@323 2945 local gossip_options = { _G.GetGossipOptions() }
jcallahan@323 2946
jcallahan@323 2947 for index = 2, #gossip_options, 2 do
jcallahan@323 2948 if gossip_options[index] == "binder" then
jcallahan@323 2949 SetUnitField("innkeeper", private.UNIT_TYPES.NPC)
jcallahan@323 2950 return
jcallahan@323 2951 end
jcallahan@323 2952 end
jcallahan@323 2953 end,
jcallahan@323 2954 [private.UNIT_TYPES.OBJECT] = function(unit_idnum)
jcallahan@323 2955 UpdateDBEntryLocation("objects", unit_idnum)
jcallahan@323 2956 end,
jcallahan@323 2957 }
jcallahan@323 2958
jcallahan@323 2959
jcallahan@92 2960 function WDP:GOSSIP_SHOW(event_name)
MMOSimca@436 2961 WDP:StopChatLootRecording(event_name)
jcallahan@323 2962 local unit_type, unit_idnum = ParseGUID(_G.UnitGUID("npc"))
jcallahan@323 2963 if not unit_idnum then
jcallahan@323 2964 return
jcallahan@323 2965 end
jcallahan@323 2966
jcallahan@323 2967 if GOSSIP_SHOW_FUNCS[unit_type] then
jcallahan@323 2968 GOSSIP_SHOW_FUNCS[unit_type](unit_idnum)
jcallahan@90 2969 end
jcallahan@90 2970 end
jcallahan@90 2971
jcallahan@90 2972
jcallahan@93 2973 function WDP:GUILDBANKFRAME_OPENED(event_name)
MMOSimca@436 2974 WDP:StopChatLootRecording(event_name)
jcallahan@93 2975 SetUnitField("guild_bank", private.UNIT_TYPES.OBJECT)
jcallahan@93 2976 end
jcallahan@93 2977
jcallahan@93 2978
jcallahan@189 2979 function WDP:ITEM_UPGRADE_MASTER_OPENED(event_name)
jcallahan@189 2980 SetUnitField("item_upgrade_master", private.UNIT_TYPES.NPC)
jcallahan@189 2981 end
jcallahan@189 2982
jcallahan@189 2983
jcallahan@90 2984 function WDP:TAXIMAP_OPENED(event_name)
jcallahan@90 2985 SetUnitField("flight_master", private.UNIT_TYPES.NPC)
jcallahan@90 2986 end
jcallahan@90 2987
jcallahan@90 2988
jcallahan@90 2989 function WDP:TRANSMOGRIFY_OPEN(event_name)
jcallahan@90 2990 SetUnitField("transmogrifier", private.UNIT_TYPES.NPC)
jcallahan@90 2991 end
jcallahan@90 2992
jcallahan@90 2993
jcallahan@90 2994 function WDP:VOID_STORAGE_OPEN(event_name)
jcallahan@90 2995 SetUnitField("void_storage", private.UNIT_TYPES.NPC)
jcallahan@90 2996 end
jcallahan@90 2997 end -- do-block