Mercurial > wow > wowdb-profiler
diff Main.lua @ 358:703714202ffe 6.0.2-1
Merge with WoD
author | MMOSimca <MMOSimca@gmail.com> |
---|---|
date | Tue, 14 Oct 2014 00:36:05 -0400 |
parents | f8190e012c92 |
children | 7e7d57948295 |
line wrap: on
line diff
--- a/Main.lua Wed Aug 06 04:31:49 2014 -0400 +++ b/Main.lua Tue Oct 14 00:36:05 2014 -0400 @@ -10,6 +10,7 @@ local math = _G.math local table = _G.table +local next = _G.next local select = _G.select local unpack = _G.unpack @@ -47,12 +48,27 @@ local PLAYER_NAME = _G.UnitName("player") local PLAYER_RACE = _G.select(2, _G.UnitRace("player")) -local SPELL_ID_CHI_WAVE = 132464 -local SPELL_ID_DISGUISE = 121308 +local TIMBER_ITEM_ID = 114781 + +-- Ignoring NPC casts of the following spells +local CHI_WAVE_SPELL_ID = 132464 +local DISGUISE_SPELL_ID = 121308 + +-- For timer-based loot gathering of abnormal containers (that don't use SHOW_LOOT_TOAST, sadly) +local BAG_OF_SALVAGE_ITEM_ID = private.SALVAGE_SPELL_ID_TO_ITEM_ID_MAP[168178] +local CRATE_OF_SALVAGE_ITEM_ID = private.SALVAGE_SPELL_ID_TO_ITEM_ID_MAP[168179] +local BIG_CRATE_OF_SALVAGE_ITEM_ID = private.SALVAGE_SPELL_ID_TO_ITEM_ID_MAP[168180] + +-- Constant for duplicate boss data; a dirty hack to get around world bosses that cannot be identified individually and cannot be linked on wowdb because they are not in a raid +local DUPLICATE_WORLD_BOSS_IDS = { + [71952] = { 71953, 71954, 71955, }, +} local ALLOWED_LOCALES = { enUS = true, enGB = true, + enTW = true, + enCN = true, } local DATABASE_DEFAULTS = { @@ -149,6 +165,9 @@ local killed_boss_id_timer_handle local killed_npc_id local target_location_timer_handle +local last_timber_spell_id +local last_garrison_cache_object_id +local chat_loot_timer_handle local current_target_id local current_area_id local current_loot @@ -172,17 +191,22 @@ -- HELPERS ------------------------------------------------------------ local function Debug(message, ...) - if not DEBUGGING or not message or not ... then + if not DEBUGGING or not message then return end - local args = { ... } - - for index = 1, #args do - if args[index] == nil then - args[index] = "nil" + + if ... then + local args = { ... } + + for index = 1, #args do + if args[index] == nil then + args[index] = "nil" + end end + _G.print(message:format(unpack(args))) + else + _G.print(message) end - _G.print(message:format(unpack(args))) end @@ -217,7 +241,7 @@ _G.TradeSkillFrame.filterTbl.hasSkillUp = false _G.TradeSkillOnlyShowSkillUps(false) end - _G.SetTradeSkillInvSlotFilter(0, 1, 1) + _G.SetTradeSkillInvSlotFilter(0, true, true) _G.TradeSkillUpdateFilterBar() _G.TradeSkillFrame_Update() @@ -292,54 +316,6 @@ end -- do-block --- constant for duplicate boss data; a dirty hack to get around world bosses that cannot be identified individually and cannot be linked on wowdb because they are not in a raid -local DUPLICATE_WORLD_BOSS_IDS = { - [71952] = { 71953, 71954, 71955, }, -} - - --- Called on a timer -local function ClearKilledNPC() - killed_npc_id = nil -end - - -local function ClearKilledBossID() - if killed_boss_id_timer_handle then - WDP:CancelTimer(killed_boss_id_timer_handle) - killed_boss_id_timer_handle = nil - end - - table.wipe(boss_loot_toasting) - private.raid_finder_boss_id = nil - private.world_boss_id = nil -end - - -local function ClearLootToastContainerID() - if loot_toast_container_timer_handle then - WDP:CancelTimer(loot_toast_container_timer_handle) - loot_toast_container_timer_handle = nil - end - - private.container_loot_toasting = false - private.loot_toast_container_id = nil -end - - -local function ClearLootToastData() - -- cancel existing timer if found - if loot_toast_data_timer_handle then - WDP:CancelTimer(loot_toast_data_timer_handle) - loot_toast_data_timer_handle = nil - end - - if loot_toast_data then - table.wipe(loot_toast_data) - end -end - - local function InstanceDifficultyToken() local _, instance_type, instance_difficulty, _, _, _, is_dynamic = _G.GetInstanceInfo() @@ -380,11 +356,7 @@ function NPCEntry(identifier) local npc = DBEntry("npcs", identifier) - - if not npc then - return - end - return _G.setmetatable(npc, npc_meta) + return npc and _G.setmetatable(npc, npc_meta) or nil end function npc_prototype:EncounterData(difficulty_token) @@ -463,26 +435,37 @@ local ParseGUID do local UNIT_TYPES = private.UNIT_TYPES - local UNIT_TYPE_BITMASK = 0x007 local NPC_ID_MAPPING = { [62164] = 63191, -- Garalon } + local function MatchUnitTypes(unit_type_name) + if not unit_type_name then + return UNIT_TYPES.UNKNOWN + end + + for def, text in next, UNIT_TYPES do + if unit_type_name == text then + return UNIT_TYPES[def] + end + end + return UNIT_TYPES.UNKNOWN + end + + function ParseGUID(guid) if not guid then return end - local bitfield = tonumber(guid:sub(1, 5)) - - if not bitfield then - return UNIT_TYPES.UNKNOWN - end - local unit_type = _G.bit.band(bitfield, UNIT_TYPE_BITMASK) - - if unit_type ~= UNIT_TYPES.PLAYER and unit_type ~= UNIT_TYPES.PET then - local unit_idnum = tonumber(guid:sub(6, 10), 16) + + -- We might want to use some of this new information later, but leaving the returns alone for now + local unit_type_name, unk_id1, server_id, instance_id, unk_id2, unit_idnum, spawn_id = ("-"):split(guid) + + local unit_type = MatchUnitTypes(unit_type_name) + if unit_type ~= UNIT_TYPES.PLAYER and unit_type ~= UNIT_TYPES.PET and unit_type ~= UNIT_TYPES.ITEM then + local id_mapping = NPC_ID_MAPPING[unit_idnum] if id_mapping and UnitTypeIsNPC(unit_type) then @@ -583,7 +566,7 @@ local current_line = _G["WDPDatamineTTTextLeft" .. line_index] if not current_line then - Debug("HandleItemUse: Item with ID %d and link %s had an invalid tooltip.", item_id, item_link, _G.ITEM_OPENABLE) + Debug("HandleItemUse: Item with ID %d and link %s had an invalid tooltip.", item_id, item_link) return end @@ -597,7 +580,6 @@ return end end - Debug("HandleItemUse: Item with ID %d and link %s did not have a tooltip that contained the string %s.", item_id, item_link, _G.ITEM_OPENABLE) end @@ -674,7 +656,7 @@ -- Items return the player as the source, so we need to use the item's ID (disenchant, milling, etc) source_id = current_loot.identifier else - local unit_ID = select(2, ParseGUID(source_guid)) + local _, unit_ID = ParseGUID(source_guid) if unit_ID then if current_loot.target_type == AF.OBJECT then source_id = ("%s:%s"):format(current_loot.spell_label, unit_ID) @@ -805,7 +787,8 @@ _G.SetCVar("Sound_EnableSFX", 0) world_map:Show() end - local micro_dungeon_id = MICRO_DUNGEON_IDS[select(5, _G.GetMapInfo())] + local _, _, _, _, micro_dungeon_map_name = _G.GetMapInfo() + local micro_dungeon_id = MICRO_DUNGEON_IDS[micro_dungeon_map_name] _G.SetMapToCurrentZone() @@ -847,6 +830,63 @@ table.wipe(current_action) end + +-- TIMERS ------------------------------------------------------------- + +local function ClearKilledNPC() + killed_npc_id = nil +end + + +local function ClearKilledBossID() + if killed_boss_id_timer_handle then + WDP:CancelTimer(killed_boss_id_timer_handle) + killed_boss_id_timer_handle = nil + end + + table.wipe(boss_loot_toasting) + private.raid_boss_id = nil +end + + +local function ClearLootToastContainerID() + if loot_toast_container_timer_handle then + WDP:CancelTimer(loot_toast_container_timer_handle) + loot_toast_container_timer_handle = nil + end + + private.container_loot_toasting = false + private.loot_toast_container_id = nil +end + + +local function ClearLootToastData() + -- cancel existing timer if found + if loot_toast_data_timer_handle then + WDP:CancelTimer(loot_toast_data_timer_handle) + loot_toast_data_timer_handle = nil + end + + if loot_toast_data then + table.wipe(loot_toast_data) + end +end + + +local function ClearTimeBasedLootData() + Debug("ClearTimeBasedLootData: Ending salvage loot timer.") + if chat_loot_timer_handle then + WDP:CancelTimer(chat_loot_timer_handle) + chat_loot_timer_handle = nil + end + + if current_loot and current_loot.identifier and (current_loot.identifier == BAG_OF_SALVAGE_ITEM_ID or current_loot.identifier == CRATE_OF_SALVAGE_ITEM_ID or current_loot.identifier == BIG_CRATE_OF_SALVAGE_ITEM_ID) then + GenericLootUpdate("items") + end + current_loot = nil +end + + -- METHODS ------------------------------------------------------------ function WDP:OnInitialize() @@ -858,12 +898,19 @@ local raw_db = _G.WoWDBProfilerData local build_num = tonumber(private.build_num) + -- Disable if using a MoP build + if build_num < 19000 then + WDP:Disable() + return + end + if (raw_db.version and raw_db.version < DB_VERSION) or (raw_db.build_num and raw_db.build_num < build_num) then for entry in pairs(DATABASE_DEFAULTS.global) do global_db[entry] = {} end end raw_db.build_num = build_num + raw_db.region = private.region raw_db.version = DB_VERSION private.InitializeCommentSystem() @@ -949,14 +996,6 @@ local amount, stat = left_text:match("+(.-) (.*)") if amount and stat then - if reforge_id and reforge_id ~= 0 then - local reforge_string = stat:find("Reforged") - - if reforge_string then - stat = stat:sub(0, reforge_string - 3) - intermediary.reforge_id = reforge_id - end - end create_entry = true intermediary[stat:lower():gsub(" ", "_"):gsub("|r", "")] = tonumber((amount:gsub(",", ""))) end @@ -974,29 +1013,54 @@ item.upgrades[upgrade_id][stat] = amount end end -end - --- do-block - - -local function RecordItemData(item_id, item_link, durability) - local item_string = select(3, item_link:find("^|%x+|H(.+)|h%[.+%]")) +end -- do-block + + +local function RecordItemData(item_id, item_link, process_bonus_ids, durability) + local _, _, item_string = item_link:find("^|%x+|H(.+)|h%[.+%]") local item if item_string then - local _, _, _, _, _, _, _, suffix_id, unique_id, _, reforge_id, upgrade_id = (":"):split(item_string) - suffix_id = tonumber(suffix_id) - upgrade_id = tonumber(upgrade_id) - - if suffix_id and suffix_id ~= 0 then + local item_results = { (":"):split(item_string) } + + local suffix_id = tonumber(item_results[8]) + local unique_id = item_results[9] + local upgrade_id = tonumber(item_results[11]) + local instance_difficulty_id = tonumber(item_results[12]) + local num_bonus_ids = tonumber(item_results[13]) + + if not num_bonus_ids or num_bonus_ids == 0 or not process_bonus_ids then + if (suffix_id and suffix_id ~= 0) or (instance_difficulty_id and instance_difficulty_id ~= 0) then + item = DBEntry("items", item_id) + item.unique_id = bit.band(unique_id, 0xFFFF) + + if suffix_id and suffix_id ~= 0 then + item.suffix_id = suffix_id + end + + if instance_difficulty_id and instance_difficulty_id ~= 0 then + item.instance_difficulty_id = instance_difficulty_id + end + end + elseif num_bonus_ids > 0 then item = DBEntry("items", item_id) - item.suffix_id = suffix_id + item.unique_id = bit.band(unique_id, 0xFFFF) + item.instance_difficulty_id = instance_difficulty_id + + if not item.bonus_ids then + item.bonus_ids = {} + end + + for bonus_index = 1, num_bonus_ids do + item.bonus_ids[tonumber(item_results[13 + bonus_index])] = true + end + else + Debug("RecordItemData: Item_system is supposed to be 0 or positive, instead it was %s.", item_system) end - if upgrade_id and upgrade_id ~= 0 then DatamineTT:SetHyperlink(item_link) - ScrapeItemUpgradeStats(item_id, upgrade_id, reforge_id) + ScrapeItemUpgradeStats(item_id, upgrade_id) end end @@ -1013,7 +1077,7 @@ if item_id and item_id > 0 then local _, max_durability = _G.GetInventoryItemDurability(slot_index) - RecordItemData(item_id, _G.GetInventoryItemLink("player", slot_index), max_durability) + RecordItemData(item_id, _G.GetInventoryItemLink("player", slot_index), false, max_durability) end end @@ -1023,7 +1087,7 @@ if item_id and item_id > 0 then local _, max_durability = _G.GetContainerItemDurability(bag_index, slot_index) - RecordItemData(item_id, _G.GetContainerItemLink(bag_index, slot_index), max_durability) + RecordItemData(item_id, _G.GetContainerItemLink(bag_index, slot_index), false, max_durability) end end end @@ -1092,7 +1156,7 @@ level_data.max_health = level_data.max_health or _G.UnitHealthMax("target") if not level_data.power then - local max_power = _G.UnitManaMax("target") + local max_power = _G.UnitPowerMax("target") if max_power > 0 then local power_type = _G.UnitPowerType("target") @@ -1209,16 +1273,40 @@ end -function WDP:SHOW_LOOT_TOAST(event_name, loot_type, item_link, quantity) +function WDP:SHOW_LOOT_TOAST(event_name, loot_type, item_link, quantity, specID, sex, isPersonal, lootSource) if not loot_type or (loot_type ~= "item" and loot_type ~= "money" and loot_type ~= "currency") then Debug("%s: loot_type is %s. Item link is %s, and quantity is %d.", event_name, loot_type, item_link, quantity) return end local container_id = private.loot_toast_container_id - local npc_id = private.raid_finder_boss_id or private.world_boss_id - - if npc_id then - -- slightly messy hack to workaround duplicate world bosses + local npc_id = private.raid_boss_id + + -- Handle Garrison cache specially + if lootSource and last_garrison_cache_object_id and (lootSource == private.GARRISON_CACHE_LOOT_SOURCE_ID) then + -- Record location data for cache + UpdateDBEntryLocation("objects", ("OPENING:%d"):format(last_garrison_cache_object_id)) + + -- Add drop data + local currency_texture = CurrencyLinkToTexture(item_link) + if currency_texture and currency_texture ~= "" then + -- Check for top level object data + local object_entry = DBEntry("objects", ("OPENING:%d"):format(last_garrison_cache_object_id)) + local difficulty_token = InstanceDifficultyToken() + if object_entry[difficulty_token] then + -- Increment loot count + object_entry[difficulty_token]["opening_count"] = (object_entry[difficulty_token]["opening_count"] or 0) + 1 + + Debug("%s: %s X %d", event_name, currency_texture, quantity) + object_entry[difficulty_token]["opening"] = object_entry[difficulty_token]["opening"] or {} + table.insert(object_entry[difficulty_token]["opening"], ("currency:%d:%s"):format(quantity, currency_texture)) + else + Debug("%s: When handling the Garrison cache, the top level loot data was missing for objectID %d.", event_name, last_garrison_cache_object_id) + end + else + Debug("%s: Currency texture is nil, from currency link %s", event_name, item_link) + end + elseif npc_id then + -- Slightly messy hack to workaround duplicate world bosses local upper_limit = 0 if DUPLICATE_WORLD_BOSS_IDS[npc_id] then upper_limit = #DUPLICATE_WORLD_BOSS_IDS[npc_id] @@ -1241,6 +1329,7 @@ local item_id = ItemLinkToID(item_link) if item_id then Debug("%s: %s X %d (%d)", event_name, item_link, quantity, item_id) + RecordItemData(item_id, item_link, true) table.insert(encounter_data[loot_label], ("%d:%d"):format(item_id, quantity)) else Debug("%s: ItemID is nil, from item link %s", event_name, item_link) @@ -1253,10 +1342,6 @@ local currency_texture = CurrencyLinkToTexture(item_link) if currency_texture and currency_texture ~= "" then Debug("%s: %s X %d", event_name, currency_texture, quantity) - -- workaround for Patch 5.4.0 bug with Flexible raid Siege of Orgrimmar bosses and Valor Points - if quantity > 1000 and currency_texture == "pvecurrency-valor" then - quantity = math.floor(quantity / 100) - end table.insert(encounter_data[loot_label], ("currency:%d:%s"):format(quantity, currency_texture)) else Debug("%s: Currency texture is nil, from currency link %s", event_name, item_link) @@ -1284,6 +1369,7 @@ local item_id = ItemLinkToID(item_link) if item_id then Debug("%s: %s X %d (%d)", event_name, item_link, quantity, item_id) + RecordItemData(item_id, item_link, true) current_loot.sources[container_id][item_id] = current_loot.sources[container_id][item_id] or 0 + quantity else Debug("%s: ItemID is nil, from item link %s", event_name, item_link) @@ -1315,6 +1401,11 @@ loot_toast_data = loot_toast_data or {} loot_toast_data[#loot_toast_data + 1] = { loot_type, item_link, quantity } + local item_id = ItemLinkToID(item_link) + if item_id then + RecordItemData(item_id, item_link, true) + end + loot_toast_data_timer_handle = WDP:ScheduleTimer(ClearLootToastData, 5) end end @@ -1322,10 +1413,41 @@ do local CHAT_MSG_LOOT_UPDATE_FUNCS = { + [AF.ITEM] = function(item_id, quantity) + local container_id = current_loot.identifier -- For faster access, since this is going to be called 9 times in the next 3 lines + -- Verify that we're still assigning data to the right items + if container_id and (container_id == BAG_OF_SALVAGE_ITEM_ID or container_id == CRATE_OF_SALVAGE_ITEM_ID or container_id == BIG_CRATE_OF_SALVAGE_ITEM_ID) then + Debug("CHAT_MSG_LOOT: AF.ITEM %d (%d)", item_id, quantity) + current_loot.sources[container_id] = current_loot.sources[container_id] or {} + current_loot.sources[container_id][item_id] = (current_loot.sources[container_id][item_id] or 0) + quantity + else -- If not, cancel the timer and wipe the loot table early + Debug("CHAT_MSG_LOOT: We would have assigned the wrong loot to salvage crates!") + ClearTimeBasedLootData() + end + end, [AF.NPC] = function(item_id, quantity) - Debug("CHAT_MSG_LOOT: %d (%d)", item_id, quantity) + Debug("CHAT_MSG_LOOT: AF.NPC %d (%d)", item_id, quantity) + end, + [AF.OBJECT] = function(item_id, quantity) + Debug("CHAT_MSG_LOOT: AF.OBJECT %d (%d)", item_id, quantity) + --for timber_variant = 1, #private.LOGGING_SPELL_ID_TO_OBJECT_ID_MAP[last_timber_spell_id] do + -- Check for top level object data + local object_entry = DBEntry("objects", ("OPENING:%s"):format(private.LOGGING_SPELL_ID_TO_OBJECT_ID_MAP[last_timber_spell_id])) + local difficulty_token = InstanceDifficultyToken() + if object_entry[difficulty_token] then + -- Increment loot count + object_entry[difficulty_token]["opening_count"] = (object_entry[difficulty_token]["opening_count"] or 0) + 1 + + -- Add drop data + object_entry[difficulty_token]["opening"] = object_entry[difficulty_token]["opening"] or {} + table.insert(object_entry[difficulty_token]["opening"], ("%d:%d"):format(item_id, quantity)) + else + Debug("CHAT_MSG_LOOT: When handling timber, the top level loot data was missing for objectID %s.", private.LOGGING_SPELL_ID_TO_OBJECT_ID_MAP[last_timber_spell_id]) + end + --end end, [AF.ZONE] = function(item_id, quantity) + Debug("CHAT_MSG_LOOT: AF.ZONE %d (%d)", item_id, quantity) InitializeCurrentLoot() current_loot.list[1] = ("%d:%d"):format(item_id, quantity) GenericLootUpdate("zones") @@ -1337,18 +1459,7 @@ function WDP:CHAT_MSG_LOOT(event_name, message) local category - if current_action.spell_label ~= "EXTRACT_GAS" then - category = AF.ZONE - elseif private.raid_finder_boss_id then - category = AF.NPC - end - local update_func = CHAT_MSG_LOOT_UPDATE_FUNCS[category] - - if not category or not update_func then - return - end local item_link, quantity = deformat(message, _G.LOOT_ITEM_PUSHED_SELF_MULTIPLE) - if not item_link then quantity, item_link = 1, deformat(message, _G.LOOT_ITEM_PUSHED_SELF) end @@ -1357,6 +1468,26 @@ if not item_id then return end + + -- Set update category + if last_timber_spell_id and item_id == TIMBER_ITEM_ID then + category = AF.OBJECT + -- Recently changed from ~= "EXTRACT_GAS" because of some occassional bad data, and, as far as I know, no benefit. + elseif current_action.spell_label == "FISHING" then + category = AF.ZONE + elseif private.raid_boss_id then + category = AF.NPC + elseif chat_loot_timer_handle then + category = AF.ITEM + end + + -- Take action based on update category + local update_func = CHAT_MSG_LOOT_UPDATE_FUNCS[category] + if not category or not update_func then + -- We still want to record the item's data, even if it doesn't need its drop location recorded + RecordItemData(item_id, item_link, true) + return + end update_func(item_id, quantity) end end @@ -1443,12 +1574,12 @@ end -do -- do-block +do local FLAGS_NPC = bit.bor(_G.COMBATLOG_OBJECT_TYPE_GUARDIAN, _G.COMBATLOG_OBJECT_CONTROL_NPC) local FLAGS_NPC_CONTROL = bit.bor(_G.COMBATLOG_OBJECT_AFFILIATION_OUTSIDER, _G.COMBATLOG_OBJECT_CONTROL_NPC) local function RecordNPCSpell(sub_event, source_guid, source_name, source_flags, dest_guid, dest_name, dest_flags, spell_id, spell_name) - if not spell_id or spell_id == SPELL_ID_CHI_WAVE or spell_id == SPELL_ID_DISGUISE then + if not spell_id or spell_id == CHI_WAVE_SPELL_ID or spell_id == DISGUISE_SPELL_ID then return end local source_type, source_id = ParseGUID(source_guid) @@ -1548,70 +1679,12 @@ end end + local DIPLOMACY_SPELL_ID = 20599 local MR_POP_RANK1_SPELL_ID = 78634 local MR_POP_RANK2_SPELL_ID = 78635 - - local REP_BUFFS = { - [_G.GetSpellInfo(30754)] = "CENARION_FAVOR", - [_G.GetSpellInfo(24705)] = "GRIM_VISAGE", - [_G.GetSpellInfo(32098)] = "HONOR_HOLD_FAVOR", - [_G.GetSpellInfo(39913)] = "NAZGRELS_FERVOR", - [_G.GetSpellInfo(39953)] = "SONG_OF_BATTLE", - [_G.GetSpellInfo(61849)] = "SPIRIT_OF_SHARING", - [_G.GetSpellInfo(32096)] = "THRALLMARS_FAVOR", - [_G.GetSpellInfo(39911)] = "TROLLBANES_COMMAND", - [_G.GetSpellInfo(95987)] = "UNBURDENED", - [_G.GetSpellInfo(100951)] = "WOW_ANNIVERSARY", - } - - - local FACTION_NAMES = { - CENARION_CIRCLE = _G.GetFactionInfoByID(609), - HONOR_HOLD = _G.GetFactionInfoByID(946), - THE_SHATAR = _G.GetFactionInfoByID(935), - THRALLMAR = _G.GetFactionInfoByID(947), - } - - - local MODIFIERS = { - CENARION_FAVOR = { - faction = FACTION_NAMES.CENARION_CIRCLE, - modifier = 0.25, - }, - GRIM_VISAGE = { - modifier = 0.1, - }, - HONOR_HOLD_FAVOR = { - faction = FACTION_NAMES.HONOR_HOLD, - modifier = 0.25, - }, - NAZGRELS_FERVOR = { - faction = FACTION_NAMES.THRALLMAR, - modifier = 0.1, - }, - SONG_OF_BATTLE = { - faction = FACTION_NAMES.THE_SHATAR, - modifier = 0.1, - }, - SPIRIT_OF_SHARING = { - modifier = 0.1, - }, - THRALLMARS_FAVOR = { - faction = FACTION_NAMES.THRALLMAR, - modifier = 0.25, - }, - TROLLBANES_COMMAND = { - faction = FACTION_NAMES.HONOR_HOLD, - modifier = 0.1, - }, - UNBURDENED = { - modifier = 0.1, - }, - WOW_ANNIVERSARY = { - modifier = 0.08, - } - } + local FACTION_DATA = private.FACTION_DATA + local REP_BUFFS = private.REP_BUFFS function WDP:COMBAT_TEXT_UPDATE(event_name, message_type, faction_name, amount) @@ -1635,25 +1708,48 @@ local modifier = 1 + -- Check for modifiers from known spells if _G.IsSpellKnown(DIPLOMACY_SPELL_ID) then modifier = modifier + 0.1 end - if _G.IsSpellKnown(MR_POP_RANK2_SPELL_ID) then modifier = modifier + 0.1 elseif _G.IsSpellKnown(MR_POP_RANK1_SPELL_ID) then modifier = modifier + 0.05 end - for buff_name, buff_label in pairs(REP_BUFFS) do + -- Determine faction ID + local faction_ID + for pseudo_faction_name, faction_data_table in pairs(FACTION_DATA) do + if faction_name == faction_data_table[2] then + faction_ID = faction_data_table[1] + end + end + if faction_ID and faction_ID > 0 then + -- Check for modifiers from Commendations (applied directly to the faction, account-wide) + local _, _, _, _, _, _, _, _, _, _, _, _, _, _, has_bonus_rep_gain = GetFactionInfoByID(faction_ID) + if has_bonus_rep_gain then + modifier = modifier + 1 + end + end + + -- Check for modifiers from buffs + for buff_name, buff_data_table in pairs(REP_BUFFS) do if _G.UnitBuff("player", buff_name) then - local modded_faction = MODIFIERS[buff_label].faction + local modded_faction = buff_data_table.faction if not modded_faction or faction_name == modded_faction then - modifier = modifier + MODIFIERS[buff_label].modifier + if buff_data_table.ignore then + -- Some buffs from tabards convert all rep of other factions into rep for a specific faction. + -- We can't know what faction the rep was orginally from, so we must ignore the data entirely in these cases. + return + else + modifier = modifier + buff_data_table.modifier + end end end end + npc.reputations = npc.reputations or {} npc.reputations[("%s:%s"):format(faction_name, faction_standings[faction_name])] = math.floor(amount / modifier) end @@ -1661,17 +1757,23 @@ function WDP:CURSOR_UPDATE(event_name) - if current_action.fishing_target or _G.Minimap:IsMouseOver() or current_action.spell_label ~= "FISHING" then + if current_action.fishing_target or _G.Minimap:IsMouseOver() then return end local text = _G["GameTooltipTextLeft1"]:GetText() - if not text or text == "Fishing Bobber" then - text = "NONE" - else - current_action.fishing_target = true + -- Handle Fishing + if (current_action.spell_label == "FISHING") then + if not text or text == "Fishing Bobber" then + text = "NONE" + else + current_action.fishing_target = true + end + current_action.identifier = ("%s:%s"):format(current_action.spell_label, text) + -- Handle Garrison Cache + elseif private.GARRISON_CACHE_OBJECT_NAME_TO_OBJECT_ID_MAP[text] then + last_garrison_cache_object_id = private.GARRISON_CACHE_OBJECT_NAME_TO_OBJECT_ID_MAP[text] end - current_action.identifier = ("%s:%s"):format(current_action.spell_label, text) end @@ -1731,7 +1833,7 @@ local source_list = {} for source_guid, loot_data in pairs(current_loot.sources) do - local source_id = select(2, ParseGUID(source_guid)) + local _, source_id = ParseGUID(source_guid) local npc = NPCEntry(source_id) if npc then @@ -1808,6 +1910,13 @@ local extrapolated_guid_registry = {} local num_guids = 0 + -- Loot extrapolation cannot handle objects that need special spell labels (like HERBALISM or MINING) (MIND_CONTROL is okay) + if private.SPELL_FLAGS_BY_LABEL[current_action.spell_label] and not private.NON_LOOT_SPELL_LABELS[current_action.spell_label] then + 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) + table.wipe(current_action) + return false + end + table.wipe(current_action) for loot_slot = 1, _G.GetNumLootItems() do @@ -1839,11 +1948,6 @@ return false end - if private.previous_spell_id and private.EXTRAPOLATION_BANNED_SPELL_IDS[private.previous_spell_id] then - Debug("%s: Problematic spell id %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) - return false - end - local num_npcs = 0 local num_objects = 0 local num_itemcontainers = 0 @@ -1868,8 +1972,8 @@ current_action.target_type = AF.NPC current_action.identifier = unit_idnum num_npcs = num_npcs + 1 - -- Item container GUIDs are currently of the 'PLAYER' type; this may be unintended and could change in the future. elseif unit_type == private.UNIT_TYPES.PLAYER then + -- Item container GUIDs are currently of the 'PLAYER' type; this may be unintended and could change in the future. current_action.loot_label = loot_label current_action.target_type = AF.ITEM -- current_action.identifier assigned during loot verification. @@ -1947,16 +2051,14 @@ if not loot_guid_registry[current_loot.label][source_guid] then local loot_quantity = loot_info[loot_index + 1] - -- There is a new bug in 5.4.0 that causes GetLootSlotInfo() to (rarely) return nil values for slot_quantity. if slot_quantity then -- We need slot_quantity to account for an old bug where loot_quantity is sometimes '1' for stacks of items, such as cloth. if slot_quantity > loot_quantity then loot_quantity = slot_quantity end - local source_type, source_id = ParseGUID(source_guid) - local source_key = ("%s:%d"):format(private.UNIT_TYPE_NAMES[source_type + 1], source_id) + local source_key = ("%s:%d"):format(source_type, source_id) if slot_type == _G.LOOT_SLOT_ITEM then local item_id = ItemLinkToID(_G.GetLootSlotLink(loot_slot)) @@ -1992,7 +2094,7 @@ end else -- 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. - 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) + Debug("%s: Slot quantity is nil for loot slot %d.", event_name, loot_slot) end end end @@ -2045,7 +2147,8 @@ if not unit_idnum or not UnitTypeIsNPC(unit_type) then return end - merchant_standing = select(2, UnitFactionStanding("npc")) + local _, faction_standing = UnitFactionStanding("npc") + merchant_standing = faction_standing current_merchant = NPCEntry(unit_idnum) current_merchant.sells = current_merchant.sells or {} end @@ -2065,11 +2168,7 @@ if not item_id then local item_name, item_link = DatamineTT:GetItem() item_id = ItemLinkToID(item_link) - if item_id then - Debug("%s: GetMerchantItemLink() still ocassionally fails, apparently. Failed item's ID - %s", event_name, item_id) - else - Debug("%s: GetMerchantItemLink() still ocassionally fails, apparently. Failed item's ID - nil", event_name) - end + -- GetMerchantItemLink() still ocassionally fails as of Patch 6.0.2. It fails so badly that debug functions cause considerable slowdown. end if item_id and item_id > 0 then @@ -2168,7 +2267,7 @@ function WDP:PET_BAR_UPDATE(event_name) - if current_action.spell_label ~= "MIND_CONTROL" then + if not private.NON_LOOT_SPELL_LABELS[current_action.spell_label] then return end local unit_type, unit_idnum = ParseGUID(_G.UnitGUID("pet")) @@ -2245,13 +2344,13 @@ return end local unit_type, unit_id = ParseGUID(_G.UnitGUID("questnpc")) - + Debug("UpdateQuestJuncture: Updating quest juncture for %s.", ("%s:%d"):format(private.UNIT_TYPE_NAMES[unit_type], unit_id)) if unit_type == private.UNIT_TYPES.OBJECT then UpdateDBEntryLocation("objects", unit_id) end local quest = DBEntry("quests", _G.GetQuestID()) quest[point] = quest[point] or {} - quest[point][("%s:%d"):format(private.UNIT_TYPE_NAMES[unit_type + 1], unit_id)] = true + quest[point][("%s:%d"):format(private.UNIT_TYPE_NAMES[unit_type], unit_id)] = true return quest end @@ -2289,7 +2388,7 @@ local _, num_quests = _G.GetNumQuestLogEntries() while processed_quests <= num_quests do - local _, _, _, _, is_header, _, _, _, quest_id = _G.GetQuestLogTitle(entry_index) + local _, _, _, is_header, _, _, _, quest_id = _G.GetQuestLogTitle(entry_index) if quest_id == 0 then processed_quests = processed_quests + 1 @@ -2332,13 +2431,16 @@ local function RegisterTools(tradeskill_name, tradeskill_index) - local spell_id = tonumber(_G.GetTradeSkillRecipeLink(tradeskill_index):match("^|c%x%x%x%x%x%x%x%x|H%w+:(%d+)")) - local required_tool = _G.GetTradeSkillTools(tradeskill_index) - - if required_tool then - for tool_name, registry in pairs(TRADESKILL_TOOLS) do - if required_tool:find(tool_name) then - registry[spell_id] = true + local link = _G.GetTradeSkillRecipeLink(tradeskill_index) + if link then + local spell_id = tonumber(link:match("^|c%x%x%x%x%x%x%x%x|H%w+:(%d+)")) + local required_tool = _G.GetTradeSkillTools(tradeskill_index) + + if required_tool then + for tool_name, registry in pairs(TRADESKILL_TOOLS) do + if required_tool:find(tool_name) then + registry[spell_id] = true + end end end end @@ -2368,15 +2470,15 @@ if not trainer then return end - local trainer_standing = select(2, UnitFactionStanding("npc")) + local _, trainer_standing = UnitFactionStanding("npc") trainer.teaches = trainer.teaches or {} private.trainer_shown = true -- Get the initial trainer filters - local available = _G.GetTrainerServiceTypeFilter("available") - local unavailable = _G.GetTrainerServiceTypeFilter("unavailable") - local used = _G.GetTrainerServiceTypeFilter("used") + local available = _G.GetTrainerServiceTypeFilter("available") and 1 or 0 + local unavailable = _G.GetTrainerServiceTypeFilter("unavailable") and 1 or 0 + local used = _G.GetTrainerServiceTypeFilter("used") and 1 or 0 -- Clear the trainer filters _G.SetTrainerServiceTypeFilter("available", 1) @@ -2429,6 +2531,7 @@ return end + Debug("UNIT_SPELLCAST_SENT: %s was cast.", spell_name) local item_name, item_link = _G.GameTooltip:GetItem() local unit_name, unit_id = _G.GameTooltip:GetUnit() @@ -2468,7 +2571,8 @@ if item_name and item_name == target_name then current_action.identifier = ItemLinkToID(item_link) elseif target_name and target_name ~= "" then - current_action.identifier = ItemLinkToID(select(2, _G.GetItemInfo(target_name))) + local _, item_link = _G.GetItemInfo(target_name) + current_action.identifier = ItemLinkToID(item_link) end elseif not item_name and not unit_name then if bit.band(spell_flags, AF.OBJECT) == AF.OBJECT then @@ -2489,19 +2593,15 @@ if private.RAID_BOSS_BONUS_SPELL_ID_TO_NPC_ID_MAP[spell_id] then ClearKilledBossID() ClearLootToastContainerID() - private.raid_finder_boss_id = private.RAID_BOSS_BONUS_SPELL_ID_TO_NPC_ID_MAP[spell_id] - elseif private.WORLD_BOSS_BONUS_SPELL_ID_TO_NPC_ID_MAP[spell_id] then - ClearKilledBossID() - ClearLootToastContainerID() - private.world_boss_id = private.WORLD_BOSS_BONUS_SPELL_ID_TO_NPC_ID_MAP[spell_id] + private.raid_boss_id = private.RAID_BOSS_BONUS_SPELL_ID_TO_NPC_ID_MAP[spell_id] else - Debug("%s: Spell ID %d is not a known raid or world boss 'Bonus' spell.", event_name, spell_id) + Debug("%s: Spell ID %d is not a known raid boss 'Bonus' spell.", event_name, spell_id) return end -- Assign existing loot data to boss if it exists if loot_toast_data then - local npc_id = private.raid_finder_boss_id or private.world_boss_id + local npc_id = private.raid_boss_id -- Slightly messy hack to workaround duplicate world bosses local upper_limit = 0 @@ -2571,6 +2671,16 @@ private.tracked_line = nil private.previous_spell_id = spell_id + -- Handle Logging spell casts + if private.LOGGING_SPELL_ID_TO_OBJECT_ID_MAP[spell_id] then + last_timber_spell_id = spell_id + --for timber_variant = 1, #private.LOGGING_SPELL_ID_TO_OBJECT_ID_MAP[spell_id] do + UpdateDBEntryLocation("objects", ("OPENING:%s"):format(private.LOGGING_SPELL_ID_TO_OBJECT_ID_MAP[spell_id])) + --end + return + end + + -- Handle Loot Toast spell casts if private.LOOT_SPELL_ID_TO_ITEM_ID_MAP[spell_id] then ClearKilledBossID() ClearLootToastContainerID() @@ -2578,6 +2688,23 @@ private.loot_toast_container_id = private.LOOT_SPELL_ID_TO_ITEM_ID_MAP[spell_id] loot_toast_container_timer_handle = WDP:ScheduleTimer(ClearLootToastContainerID, 1) -- we need to assign a handle here to cancel it later + return + end + + -- For Crates of Salvage (and potentially other items based on spell casts in the future which need manual handling) + if private.SALVAGE_SPELL_ID_TO_ITEM_ID_MAP[spell_id] then + -- Set up timer + Debug("%s: Beginning Salvage loot timer for spellID %d", event_name, spell_id) + chat_loot_timer_handle = WDP:ScheduleTimer(ClearTimeBasedLootData, 1) + + -- Standard item handling setup + table.wipe(current_action) + current_loot = nil + current_action.target_type = AF.ITEM + current_action.identifier = private.SALVAGE_SPELL_ID_TO_ITEM_ID_MAP[spell_id] + current_action.loot_label = "contains" + InitializeCurrentLoot() + return end if anvil_spell_ids[spell_id] then @@ -2586,7 +2713,7 @@ UpdateDBEntryLocation("objects", OBJECT_ID_FORGE) elseif spell_name:match("^Harvest.+") then killed_npc_id = current_target_id - private.harvesting = true + private.harvesting = true -- Used to track which NPCs can be harvested (can we get this from CreatureCache instead?) end end