annotate Main.lua @ 537:ec2ee7b48c21 7.2.5-1

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