annotate Main.lua @ 539:1bcaf08ae424

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