annotate Main.lua @ 566:244aec3f15b0 8.0.1-5

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