You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
373 lines
14 KiB
373 lines
14 KiB
-- Redefine often used functions locally.
|
|
local GetServerTime = GetServerTime
|
|
local InterfaceOptionsFrame_Show = InterfaceOptionsFrame_Show
|
|
local InterfaceOptionsFrame_OpenToCategory = InterfaceOptionsFrame_OpenToCategory
|
|
local tinsert = tinsert
|
|
local UnitHealthMax = UnitHealthMax
|
|
local UnitHealth = UnitHealth
|
|
local CreateFrame = CreateFrame
|
|
local GetTime = GetTime
|
|
|
|
-- Redefine global variables locally.
|
|
local C_Map = C_Map
|
|
local UIParent = UIParent
|
|
|
|
-- ####################################################################
|
|
-- ## Core ##
|
|
-- ####################################################################
|
|
|
|
-- Create the primary addon object.
|
|
RareTracker = LibStub("AceAddon-3.0"):NewAddon("RareTracker", "AceConsole-3.0", "AceEvent-3.0", "AceComm-3.0", "AceSerializer-3.0")
|
|
|
|
-- Create the frame, such that the position will be saved correctly.
|
|
RareTracker.gui = CreateFrame("Frame", "RT", UIParent)
|
|
|
|
-- ####################################################################
|
|
-- ## Localization Support ##
|
|
-- ####################################################################
|
|
|
|
-- Get an object we can use for the localization of the addon.
|
|
local L = LibStub("AceLocale-3.0"):GetLocale("RareTracker", true)
|
|
|
|
-- ####################################################################
|
|
-- ## Variables ##
|
|
-- ####################################################################
|
|
|
|
-- Create a mapping from the zone id to the primary zone id.
|
|
RareTracker.zone_id_to_primary_id = {}
|
|
|
|
-- Create a mapping from primary id to zone data.
|
|
RareTracker.primary_id_to_data = {}
|
|
|
|
-- A master list of all tracked rares.
|
|
RareTracker.tracked_npc_ids = {}
|
|
|
|
-- A master list of all tracked rares.
|
|
RareTracker.completion_quest_to_npc_ids = {}
|
|
|
|
-- The short-hand code of the addon.
|
|
RareTracker.addon_code = "RT"
|
|
|
|
-- Define the default settings.
|
|
local defaults = {
|
|
global = {
|
|
communication = {
|
|
raid_communication = true,
|
|
},
|
|
debug = {
|
|
enable = false,
|
|
},
|
|
favorite_alert = {
|
|
favorite_alert_sound_channel = "SFX",
|
|
favorite_sound_alert = 552503,
|
|
},
|
|
window = {
|
|
hide = false,
|
|
scale = 1.0,
|
|
hide_killed_entities = false,
|
|
force_display_in_english = false,
|
|
},
|
|
previous_records = {},
|
|
favorite_rares = {},
|
|
ignored_rares = {},
|
|
banned_NPC_ids = {},
|
|
version = 0,
|
|
},
|
|
profile = {
|
|
minimap = {
|
|
hide = false,
|
|
},
|
|
},
|
|
}
|
|
|
|
-- ####################################################################
|
|
-- ## Module Registration Variables ##
|
|
-- ####################################################################
|
|
|
|
-- Keep a list of modules that have been registered, such that we can add them when loaded.
|
|
local plugin_data = {}
|
|
|
|
-- A metatable that simplifies accessing rare data.
|
|
local rare_data_metatable = {
|
|
__index = function(t, k)
|
|
if k == "name" then
|
|
return t[1]
|
|
elseif k == "quest_id" then
|
|
return t[2]
|
|
elseif k == "coordinates" then
|
|
return t[3]
|
|
else
|
|
return nil
|
|
end
|
|
end
|
|
}
|
|
|
|
-- ####################################################################
|
|
-- ## Module Registration Messages ##
|
|
-- ####################################################################
|
|
|
|
-- NPCs that are banned during shard detection.
|
|
-- Player followers sometimes spawn with the wrong zone id.
|
|
local banned_NPC_ids = {
|
|
154297, 150202, 154304, 152108, 151300, 151310, 142666, 142668, 69792, 62821, 62822, 32639, 32638, 89715, 89713, 180182, 180181, 180483, 180208, 183143
|
|
}
|
|
|
|
-- Fired when new all rare tracker modules have registered their data to the core.
|
|
function RareTracker:PLAYER_LOGIN()
|
|
-- We no longer need the player login event. Unsubscribe.
|
|
self:UnregisterEvent("PLAYER_LOGIN")
|
|
|
|
-- Add all the requested zones and rares.
|
|
for primary_id, rare_data in pairs(plugin_data) do
|
|
self:AddRaresForModule(rare_data)
|
|
plugin_data[primary_id] = nil
|
|
end
|
|
|
|
-- As a precaution, we remove all actively tracked rares from the blacklist.
|
|
for npc_id, _ in pairs(self.tracked_npc_ids) do
|
|
self.db.global.banned_NPC_ids[npc_id] = nil
|
|
end
|
|
|
|
-- There are several npcs that always have to be banned.
|
|
for _, npc_id in pairs(banned_NPC_ids) do
|
|
self.db.global.banned_NPC_ids[npc_id] = true
|
|
end
|
|
|
|
-- Import previous settings when applicable.
|
|
self:ImportOldSettings()
|
|
|
|
self:InitializeOptionsMenu()
|
|
self:InitializeRareTrackerLDB()
|
|
|
|
-- Register the resired chat commands.
|
|
self:RegisterChatCommand("rtc", "OnChatCommand")
|
|
self:RegisterChatCommand("raretracker", "OnChatCommand")
|
|
|
|
-- Initialize the interface.
|
|
self:InitializeInterface()
|
|
self:CorrectFavoriteMarks()
|
|
self:AddDailyResetHandler()
|
|
|
|
-- Register all the events that have to be tracked continuously.
|
|
self:RegisterEvent("ZONE_CHANGED_NEW_AREA", "OnZoneTransition")
|
|
self:RegisterEvent("PLAYER_ENTERING_WORLD", "OnZoneTransition")
|
|
self:RegisterEvent("ZONE_CHANGED", "OnZoneTransition")
|
|
end
|
|
|
|
-- ####################################################################
|
|
-- ## Module Registration Functions ##
|
|
-- ####################################################################
|
|
|
|
-- Register a list of rare data that will be processed upon successful load.
|
|
function RareTracker.RegisterRaresForModule(rare_data)
|
|
tinsert(plugin_data, rare_data)
|
|
end
|
|
|
|
-- Register a list of rare entities for a given zone id/zone ids.
|
|
function RareTracker:AddRaresForModule(rare_data)
|
|
local primary_id = rare_data.target_zones[1]
|
|
|
|
-- Only define the data for the zone once by making a pointer to the primary id.
|
|
for _, value in pairs(rare_data.target_zones) do
|
|
self.zone_id_to_primary_id[value] = primary_id
|
|
end
|
|
|
|
-- Store the data.
|
|
self.primary_id_to_data[primary_id] = rare_data
|
|
for key, _ in pairs(rare_data.entities) do
|
|
setmetatable(rare_data.entities[key], rare_data_metatable)
|
|
end
|
|
|
|
-- Construct the inverse quest id list.
|
|
for key, value in pairs(rare_data.entities) do
|
|
if value.quest_id then
|
|
if not self.completion_quest_to_npc_ids[value.quest_id] then
|
|
self.completion_quest_to_npc_ids[value.quest_id] = {}
|
|
end
|
|
tinsert(self.completion_quest_to_npc_ids[value.quest_id], key)
|
|
end
|
|
end
|
|
|
|
-- Populate the master list of tracked npcs.
|
|
for npc_id, _ in pairs(rare_data.entities) do
|
|
self.tracked_npc_ids[npc_id] = true
|
|
end
|
|
|
|
-- Create an ordering (alphabetical is the default).
|
|
-- If an order is defined by the module, use that order instead!
|
|
if rare_data.rare_order then
|
|
self.primary_id_to_data[primary_id].ordering = rare_data.rare_order
|
|
else
|
|
local ordering = {}
|
|
for npc_id, _ in pairs(rare_data.entities) do
|
|
table.insert(ordering, npc_id)
|
|
end
|
|
table.sort(ordering, function(a, b)
|
|
return rare_data.entities[a].name < rare_data.entities[b].name
|
|
end)
|
|
self.primary_id_to_data[primary_id].ordering = ordering
|
|
end
|
|
end
|
|
|
|
-- Extract all the re-usable data from the old databases and put them in the new one.
|
|
function RareTracker:ImportOldSettings()
|
|
self:ImportOldSettingFromDB(RareTrackerNazjatarDB)
|
|
self:ImportOldSettingFromDB(RareTrackerMechagonDB)
|
|
self:ImportOldSettingFromDB(RareTrackerUldumDB)
|
|
self:ImportOldSettingFromDB(RareTrackerValeDB)
|
|
end
|
|
|
|
-- Extract all the re-usable data from the old database and put them in the new one.
|
|
function RareTracker:ImportOldSettingFromDB(db)
|
|
if db and not db.has_been_imported then
|
|
if db.global.favorite_rares then
|
|
for npc_id, _ in pairs(db.global.favorite_rares) do
|
|
self.db.global.favorite_rares[npc_id] = true
|
|
end
|
|
end
|
|
if db.global.ignore_rares then
|
|
for npc_id, _ in pairs(db.global.ignore_rares) do
|
|
self.db.global.ignored_rares[npc_id] = true
|
|
end
|
|
end
|
|
db.has_been_imported = true
|
|
end
|
|
end
|
|
|
|
-- ####################################################################
|
|
-- ## Standard Ace3 Methods ##
|
|
-- ####################################################################
|
|
|
|
-- A function that is called when the addon is first loaded.
|
|
-- Note: we have to delay the initialization until all the rare data has been gathered.
|
|
function RareTracker:OnInitialize()
|
|
-- Register the addon's prefix and the associated communication function.
|
|
self:RegisterComm(self.addon_code)
|
|
|
|
-- Load the database.
|
|
self.db = LibStub("AceDB-3.0"):New("RareTrackerDB", defaults, true)
|
|
|
|
-- Register the callback to the logout function.
|
|
self.db.RegisterCallback(self, "OnDatabaseShutdown", "OnDatabaseShutdown")
|
|
|
|
-- Remove any data in the previous records that have expired.
|
|
for shard_id, _ in pairs(self.db.global.previous_records) do
|
|
if GetServerTime() - self.db.global.previous_records[shard_id].time_stamp > 900 then
|
|
self:Debug("Removing cached data for shard "..shard_id)
|
|
self.db.global.previous_records[shard_id] = nil
|
|
end
|
|
end
|
|
|
|
-- Remove any data in the previous records that are outdated because of an addon update.
|
|
for shard_id, _ in pairs(self.db.global.previous_records) do
|
|
local version = self.db.global.previous_records[shard_id].version
|
|
if not version or version < 1 then
|
|
self:Debug("Removing cached data for shard "..shard_id)
|
|
self.db.global.previous_records[shard_id] = nil
|
|
end
|
|
end
|
|
|
|
-- Wait for the player login event before initializing the rest of the data.
|
|
self:RegisterEvent("PLAYER_LOGIN")
|
|
end
|
|
|
|
-- Called when the player logs out, such that we can save the current time table for later use.
|
|
function RareTracker:OnDatabaseShutdown()
|
|
self:SaveRecordedData()
|
|
end
|
|
|
|
-- ####################################################################
|
|
-- ## Commands ##
|
|
-- ####################################################################
|
|
|
|
-- Remember when the last refresh was issued by the user, to block refresh spamming.
|
|
RareTracker.last_data_refresh = 0
|
|
|
|
-- A function that is called when calling a chat command.
|
|
function RareTracker:OnChatCommand(input)
|
|
input = input:trim()
|
|
if not input or input == "" then
|
|
InterfaceOptionsFrame_Show()
|
|
InterfaceOptionsFrame_OpenToCategory(self.options_frame)
|
|
else
|
|
local _, _, cmd, _ = string.find(input, "%s?(%w+)%s?(.*)")
|
|
local zone_id = C_Map.GetBestMapForUnit("player")
|
|
if cmd == "show" then
|
|
if zone_id and self.zone_id_to_primary_id[zone_id] then
|
|
self.gui:Show()
|
|
self.db.global.window.hide = false
|
|
else
|
|
print(L["<RT> The rare window cannot be shown, since the current zone is not covered by any of the zone modules."])
|
|
end
|
|
elseif cmd == "hide" then
|
|
if zone_id and self.zone_id_to_primary_id[zone_id] then
|
|
self.gui:Hide()
|
|
end
|
|
self.db.global.window.hide = true
|
|
elseif cmd == "refresh" then
|
|
if self.shard_id ~= nil and GetServerTime() - self.last_data_refresh > 600 then
|
|
-- Reset all tracked data.
|
|
self:ResetTrackedData()
|
|
self.db.global.previous_records[self.shard_id] = nil
|
|
|
|
-- Re-register the arrival.
|
|
self.last_data_refresh = GetServerTime()
|
|
self:AnnounceArrival()
|
|
|
|
print(L["<RT> Resetting current rare timers and requesting up-to-date data."])
|
|
elseif self.shard_id == nil then
|
|
print(L["<RT> Please target a non-player entity prior to resetting, "..
|
|
"such that the addon can determine the current shard id."])
|
|
else
|
|
print(L["<RT> The reset button is on cooldown. Please note that a reset is not needed "..
|
|
"to receive new timers. If it is your intention to reset the data, "..
|
|
"please do a /reload and click the reset button again."])
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- ####################################################################
|
|
-- ## Helper functions ##
|
|
-- ####################################################################
|
|
|
|
-- Get the current health of the entity, rounded down to an integer.
|
|
function RareTracker.GetTargetHealthPercentage(target)
|
|
-- Find the current and maximum health of the current target.
|
|
local max_hp = UnitHealthMax(target)
|
|
|
|
-- Check for division by zero.
|
|
if max_hp == 0 then
|
|
return -1
|
|
end
|
|
|
|
-- Return the amount of health as a percentage.
|
|
return math.floor((100 * UnitHealth(target)) / UnitHealthMax(target))
|
|
end
|
|
|
|
-- A function that enables the delayed execution of a function.
|
|
function RareTracker:DelayedExecution(delay, _function)
|
|
local frame = CreateFrame("Frame", nil, UIParent)
|
|
frame.start_time = GetTime()
|
|
frame:SetScript("OnUpdate",
|
|
function(f)
|
|
if GetTime() - f.start_time > delay then
|
|
if not pcall(_function) then
|
|
self:Debug("Delayed execution failed.")
|
|
end
|
|
|
|
f:SetScript("OnUpdate", nil)
|
|
f:Hide()
|
|
f:SetParent(nil)
|
|
end
|
|
end
|
|
)
|
|
frame:Show()
|
|
end
|
|
|
|
-- A print function used for debug purposes.
|
|
function RareTracker:Debug(...)
|
|
if self.db and self.db.global.debug.enable then
|
|
print("[Debug.RT]", ...)
|
|
end
|
|
end
|