Add files via upload
This commit is contained in:
+7
-3
@@ -1658,10 +1658,14 @@ function BNP:CreateOptions()
|
||||
|
||||
local questPlateIndicators = CreateCheck(totemsPage, "Enable Quest Icons", -368, function()
|
||||
BNP_DB.questPlateIndicators = this:GetChecked() and true or false
|
||||
if BNP.RequestQuestPlateObjectiveUpdate and BNP_DB.questPlateIndicators then
|
||||
BNP:RequestQuestPlateObjectiveUpdate()
|
||||
if BNP.SetQuestPlateRuntimeEnabled then
|
||||
BNP:SetQuestPlateRuntimeEnabled(BNP_DB.questPlateIndicators, true)
|
||||
else
|
||||
if BNP_DB.questPlateIndicators and BNP.RequestQuestPlateObjectiveUpdate then
|
||||
BNP:RequestQuestPlateObjectiveUpdate()
|
||||
end
|
||||
if BNP.RefreshQuestPlateIndicators then BNP:RefreshQuestPlateIndicators() end
|
||||
end
|
||||
if BNP.RefreshQuestPlateIndicators then BNP:RefreshQuestPlateIndicators() end
|
||||
if frame.UpdateDependentControls then frame:UpdateDependentControls() end
|
||||
end)
|
||||
frame.questPlateIndicatorsCheck = questPlateIndicators
|
||||
|
||||
+43
-22
@@ -1003,33 +1003,54 @@ end
|
||||
local targetEvents = CreateFrame("Frame")
|
||||
targetEvents:RegisterEvent("PLAYER_TARGET_CHANGED")
|
||||
targetEvents:SetScript("OnEvent", function()
|
||||
-- Target switches used to synchronously walk every known nameplate here.
|
||||
-- Every visible plate already runs ApplyTargetAlpha/target visuals from its
|
||||
-- normal OnUpdate, so that extra full pass duplicated the same work in one
|
||||
-- event frame and could show up as a short hitch in crowded scenes.
|
||||
--
|
||||
-- Keep the switch responsive by refreshing only the old/new target frames
|
||||
-- immediately. All other plates converge on the very next normal OnUpdate
|
||||
-- without one large target-change CPU spike. Alpha-zero Target Only plates
|
||||
-- remain :IsShown(), so their OnUpdate continues to run as well.
|
||||
local previousTargetPlate = strictTargetCachePlate or classicTargetCachePlate
|
||||
|
||||
InvalidateTargetResolution()
|
||||
|
||||
local targetGUID = GetTargetGUID()
|
||||
local _, classicTargetAvailable = GetClassicAPITargetPlate(targetGUID)
|
||||
local holdUntil = GetTime() + ALPHA_IDENTITY_HOLD_TIME
|
||||
local plate
|
||||
local targetOnly = BNP.IsTargetOnlyNameplatesEnabled and
|
||||
BNP:IsTargetOnlyNameplatesEnabled()
|
||||
for plate in pairs(BNP.plates or {}) do
|
||||
if classicTargetAvailable or targetOnly then
|
||||
-- Target Only is intentionally fail-closed and therefore does not use
|
||||
-- the legacy full-alpha identity hold during target switches.
|
||||
plate.BNPAlphaIdentityHoldUntil = nil
|
||||
ApplyTargetAlpha(plate)
|
||||
if BNP.MaintainTargetScale then BNP:MaintainTargetScale(plate) end
|
||||
else
|
||||
plate.BNPAlphaIdentityHoldUntil = holdUntil
|
||||
if plate.GetAlpha and plate.SetAlpha and plate:GetAlpha() ~= 1 then
|
||||
plate:SetAlpha(1)
|
||||
end
|
||||
end
|
||||
ApplyTargetFrameLevel(plate)
|
||||
ApplyTargetArrows(plate)
|
||||
ApplyTargetBorderColor(plate)
|
||||
local currentTargetPlate, classicTargetAvailable = GetClassicAPITargetPlate(targetGUID)
|
||||
if not currentTargetPlate then
|
||||
currentTargetPlate = GetStrictTargetPlate(targetGUID)
|
||||
end
|
||||
|
||||
if BNP.RefreshAuraPriorityAlpha then BNP:RefreshAuraPriorityAlpha() end
|
||||
local function RefreshImmediateTargetPlate(plate)
|
||||
if not plate or not BNP.plates or not BNP.plates[plate] then return end
|
||||
if not plate.IsShown or not plate:IsShown() then return end
|
||||
|
||||
-- Exact ClassicAPI target identity means no transition hold is needed.
|
||||
-- On the legacy path preserve the old neutral hold for the former target
|
||||
-- while projected SuperWoW identity settles.
|
||||
if classicTargetAvailable or
|
||||
(BNP.IsTargetOnlyNameplatesEnabled and BNP:IsTargetOnlyNameplatesEnabled()) then
|
||||
plate.BNPAlphaIdentityHoldUntil = nil
|
||||
else
|
||||
plate.BNPAlphaIdentityHoldUntil = GetTime() + ALPHA_IDENTITY_HOLD_TIME
|
||||
end
|
||||
|
||||
ApplyTargetAlpha(plate)
|
||||
if BNP.MaintainTargetScale then BNP:MaintainTargetScale(plate) end
|
||||
ApplyTargetGlow(plate)
|
||||
ApplyTargetArrows(plate)
|
||||
ApplyTargetBorderColor(plate)
|
||||
ApplyTargetFrameLevel(plate)
|
||||
end
|
||||
|
||||
RefreshImmediateTargetPlate(previousTargetPlate)
|
||||
if currentTargetPlate ~= previousTargetPlate then
|
||||
RefreshImmediateTargetPlate(currentTargetPlate)
|
||||
end
|
||||
|
||||
-- Aura/CC containers already mirror plate alpha from ApplyTargetAlpha and
|
||||
-- their own renderer refresh. Avoid another synchronous all-plate pass here.
|
||||
end)
|
||||
|
||||
function BNP:RefreshTargetFocus()
|
||||
|
||||
@@ -252,11 +252,11 @@ if BNP.libnameplate then
|
||||
end)
|
||||
end
|
||||
|
||||
-- Target switches can change nameplate frame levels in the same frame. Because
|
||||
-- the text now belongs to the healthbar this is only a value/visibility refresh,
|
||||
-- not a layering workaround.
|
||||
-- Health text is parented directly to the healthbar and therefore follows
|
||||
-- target frame-level changes automatically. PLAYER_TARGET_CHANGED used to
|
||||
-- force a synchronous refresh of every plate even though no health value had
|
||||
-- changed; the regular 0.10s bucketed updater already keeps values current.
|
||||
local healthEvents = CreateFrame("Frame")
|
||||
healthEvents:RegisterEvent("PLAYER_TARGET_CHANGED")
|
||||
healthEvents:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||||
healthEvents:SetScript("OnEvent", function()
|
||||
if BNP.RefreshHealthPercent then
|
||||
|
||||
+221
-39
@@ -12,11 +12,25 @@ local ICON_SPACING = 5
|
||||
local MAX_ICONS_PER_PLATE = 8
|
||||
|
||||
local questObjectives = {}
|
||||
local questObjectiveKeys = {}
|
||||
local questIdCache = {}
|
||||
local questMonsterSourceCache = {}
|
||||
local questItemSourceCache = {}
|
||||
local cachedDependencyName = nil
|
||||
local questRevision = 0
|
||||
local updatePending = true
|
||||
local updatePending = false
|
||||
local updateDelay = 0.50
|
||||
local updateElapsed = 0
|
||||
local questRuntimeEnabled = false
|
||||
local questCallbacksRegistered = false
|
||||
local eventFrame
|
||||
local QuestRuntimeOnUpdate
|
||||
|
||||
local QUEST_EVENTS = {
|
||||
"QUEST_LOG_UPDATE",
|
||||
"QUEST_WATCH_UPDATE",
|
||||
"UNIT_QUEST_LOG_CHANGED",
|
||||
}
|
||||
|
||||
local function ClearTable(tbl)
|
||||
local key
|
||||
@@ -44,6 +58,21 @@ function BNP:GetQuestPlateDependencyName()
|
||||
return nil
|
||||
end
|
||||
|
||||
local function ResetQuestDataCaches()
|
||||
ClearTable(questIdCache)
|
||||
ClearTable(questMonsterSourceCache)
|
||||
ClearTable(questItemSourceCache)
|
||||
end
|
||||
|
||||
local function SyncQuestDataDependency()
|
||||
local dependencyName = BNP:GetQuestPlateDependencyName()
|
||||
if dependencyName ~= cachedDependencyName then
|
||||
cachedDependencyName = dependencyName
|
||||
ResetQuestDataCaches()
|
||||
end
|
||||
return dependencyName
|
||||
end
|
||||
|
||||
local function GetQuestIdByTitle(title, level)
|
||||
if not title then return nil end
|
||||
local wantedTitle = Lower(title)
|
||||
@@ -198,13 +227,21 @@ end
|
||||
|
||||
local function GetItemDropSource(questId, itemName)
|
||||
if not questId or not itemName then return nil end
|
||||
|
||||
local wanted = Lower(itemName) or ""
|
||||
local cacheKey = tostring(questId) .. "|" .. wanted
|
||||
if questItemSourceCache[cacheKey] ~= nil then
|
||||
return questItemSourceCache[cacheKey] or nil
|
||||
end
|
||||
|
||||
local mobs
|
||||
if QuestieReady() then mobs = GetDataFromQuestie(questId, itemName) end
|
||||
if (not mobs or table.getn(mobs) == 0) and pfDB then
|
||||
if (not mobs or table.getn(mobs) == 0) and PfQuestReady() then
|
||||
mobs = GetDataFromPfDb(questId, itemName)
|
||||
end
|
||||
if mobs and table.getn(mobs) > 0 then return mobs end
|
||||
return nil
|
||||
|
||||
questItemSourceCache[cacheKey] = (mobs and table.getn(mobs) > 0) and mobs or false
|
||||
return questItemSourceCache[cacheKey] or nil
|
||||
end
|
||||
|
||||
local function GetMonsterDataFromQuestie(questId)
|
||||
@@ -248,6 +285,22 @@ local function GetMonsterDataFromPfDb(questId)
|
||||
return nil
|
||||
end
|
||||
|
||||
local function GetMonsterSource(questId)
|
||||
if not questId then return nil end
|
||||
if questMonsterSourceCache[questId] ~= nil then
|
||||
return questMonsterSourceCache[questId] or nil
|
||||
end
|
||||
|
||||
local mobs
|
||||
if QuestieReady() then mobs = GetMonsterDataFromQuestie(questId) end
|
||||
if (not mobs or table.getn(mobs) == 0) and PfQuestReady() then
|
||||
mobs = GetMonsterDataFromPfDb(questId)
|
||||
end
|
||||
|
||||
questMonsterSourceCache[questId] = (mobs and table.getn(mobs) > 0) and mobs or false
|
||||
return questMonsterSourceCache[questId] or nil
|
||||
end
|
||||
|
||||
local function IsMobMatchObjective(mobName, objectiveName)
|
||||
local lm = Lower(mobName) or ""
|
||||
local lo = Lower(objectiveName) or ""
|
||||
@@ -260,20 +313,55 @@ local function IsMobMatchObjective(mobName, objectiveName)
|
||||
return false
|
||||
end
|
||||
|
||||
local function AddObjective(mobName, objectiveName, texture, remaining)
|
||||
local function AddObjective(target, mobName, objectiveName, texture, remaining)
|
||||
local key = Lower(mobName)
|
||||
if not key or key == "" then return end
|
||||
if not questObjectives[key] then questObjectives[key] = {} end
|
||||
questObjectives[key][objectiveName] = {
|
||||
if not target[key] then target[key] = {} end
|
||||
target[key][objectiveName] = {
|
||||
icon = texture,
|
||||
text = tostring(remaining),
|
||||
}
|
||||
end
|
||||
|
||||
local function BuildObjectiveKey(objectives)
|
||||
if not objectives then return "" end
|
||||
local parts = {}
|
||||
local objectiveName, data
|
||||
for objectiveName, data in pairs(objectives) do
|
||||
table.insert(parts, tostring(objectiveName) .. "=" .. tostring(data.icon or "") .. ":" .. tostring(data.text or ""))
|
||||
end
|
||||
table.sort(parts)
|
||||
return table.concat(parts, "|")
|
||||
end
|
||||
|
||||
local function BuildObjectiveKeys(objectives)
|
||||
local keys = {}
|
||||
local mobKey, mobObjectives
|
||||
for mobKey, mobObjectives in pairs(objectives) do
|
||||
keys[mobKey] = BuildObjectiveKey(mobObjectives)
|
||||
end
|
||||
return keys
|
||||
end
|
||||
|
||||
local function FindChangedObjectiveKeys(oldKeys, newKeys)
|
||||
local changed = {}
|
||||
local key, value
|
||||
for key, value in pairs(oldKeys) do
|
||||
if newKeys[key] ~= value then changed[key] = true end
|
||||
end
|
||||
for key, value in pairs(newKeys) do
|
||||
if oldKeys[key] ~= value then changed[key] = true end
|
||||
end
|
||||
return changed
|
||||
end
|
||||
|
||||
function BNP:UpdateQuestPlateObjectives()
|
||||
ClearTable(questObjectives)
|
||||
-- A dependency may have become ready after an earlier negative lookup.
|
||||
ClearTable(questIdCache)
|
||||
if not questRuntimeEnabled or not self:AreQuestPlateIndicatorsEnabled() then return end
|
||||
|
||||
-- Quest IDs and source mappings are static. Keep them cached between
|
||||
-- QUEST_LOG_UPDATE events; only invalidate if the backing dependency changes.
|
||||
SyncQuestDataDependency()
|
||||
local nextObjectives = {}
|
||||
|
||||
local oldSelection
|
||||
if GetQuestLogSelection then oldSelection = GetQuestLogSelection() end
|
||||
@@ -307,10 +395,7 @@ function BNP:UpdateQuestPlateObjectives()
|
||||
local texture
|
||||
|
||||
if objectiveType == "monster" then
|
||||
if questId and QuestieReady() then mobs = GetMonsterDataFromQuestie(questId) end
|
||||
if (not mobs or table.getn(mobs) == 0) and questId and pfDB then
|
||||
mobs = GetMonsterDataFromPfDb(questId)
|
||||
end
|
||||
if questId then mobs = GetMonsterSource(questId) end
|
||||
|
||||
if mobs and monsterCount > 1 then
|
||||
local filtered = {}
|
||||
@@ -336,7 +421,7 @@ function BNP:UpdateQuestPlateObjectives()
|
||||
if mobs and texture then
|
||||
local _, mobName
|
||||
for _, mobName in pairs(mobs) do
|
||||
AddObjective(mobName, objectiveName, texture, total - current)
|
||||
AddObjective(nextObjectives, mobName, objectiveName, texture, total - current)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -349,8 +434,16 @@ function BNP:UpdateQuestPlateObjectives()
|
||||
SelectQuestLogEntry(oldSelection)
|
||||
end
|
||||
|
||||
local nextObjectiveKeys = BuildObjectiveKeys(nextObjectives)
|
||||
local changedKeys = FindChangedObjectiveKeys(questObjectiveKeys, nextObjectiveKeys)
|
||||
|
||||
questObjectives = nextObjectives
|
||||
questObjectiveKeys = nextObjectiveKeys
|
||||
questRevision = questRevision + 1
|
||||
if self.RefreshQuestPlateIndicators then self:RefreshQuestPlateIndicators() end
|
||||
|
||||
-- Only touch nameplates whose objective display actually changed. A kill or
|
||||
-- loot event normally affects one mob type, not every visible nameplate.
|
||||
if self.RefreshQuestPlateIndicators then self:RefreshQuestPlateIndicators(changedKeys) end
|
||||
end
|
||||
|
||||
local function HideQuestIcons(plate)
|
||||
@@ -404,7 +497,8 @@ function BNP:UpdateQuestPlateIndicator(plate, force)
|
||||
local size = self:GetQuestPlateIconSize()
|
||||
local x = self:GetQuestPlateXOffset()
|
||||
local y = self:GetQuestPlateYOffset()
|
||||
local renderKey = tostring(questRevision) .. "|" .. tostring(unitName) .. "|" ..
|
||||
local objectiveKey = questObjectiveKeys[Lower(unitName)] or ""
|
||||
local renderKey = tostring(objectiveKey) .. "|" .. tostring(unitName) .. "|" ..
|
||||
tostring(size) .. "|" .. tostring(x) .. "|" .. tostring(y)
|
||||
if not force and plate.BNPQuestRenderKey == renderKey then return end
|
||||
plate.BNPQuestRenderKey = renderKey
|
||||
@@ -448,50 +542,138 @@ function BNP:UpdateQuestPlateIndicator(plate, force)
|
||||
end
|
||||
end
|
||||
|
||||
function BNP:RefreshQuestPlateIndicators()
|
||||
function BNP:RefreshQuestPlateIndicators(changedKeys)
|
||||
local plate
|
||||
for plate in pairs(self.plates or {}) do
|
||||
if plate and plate.IsShown and plate:IsShown() then
|
||||
self:UpdateQuestPlateIndicator(plate, true)
|
||||
else
|
||||
HideQuestIcons(plate)
|
||||
local shouldUpdate = true
|
||||
if changedKeys then
|
||||
local nameRegion = plate and plate.name
|
||||
local unitName = nameRegion and nameRegion.GetText and nameRegion:GetText()
|
||||
local mobKey = unitName and Lower(unitName) or nil
|
||||
shouldUpdate = mobKey and changedKeys[mobKey]
|
||||
end
|
||||
|
||||
if shouldUpdate then
|
||||
if plate and plate.IsShown and plate:IsShown() then
|
||||
self:UpdateQuestPlateIndicator(plate, true)
|
||||
else
|
||||
HideQuestIcons(plate)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function BNP:RequestQuestPlateObjectiveUpdate()
|
||||
if not questRuntimeEnabled or not self:AreQuestPlateIndicatorsEnabled() then return end
|
||||
updatePending = true
|
||||
updateElapsed = 0
|
||||
end
|
||||
|
||||
-- Nameplate integration: reuse BNP's existing 10 Hz plate update rather than
|
||||
-- scanning WorldFrame a second time just for quest icons.
|
||||
table.insert(BNP.libnameplate.OnShow, function(plate)
|
||||
local function SetQuestEventsRegistered(enabled)
|
||||
if not eventFrame then return end
|
||||
local i
|
||||
for i = 1, table.getn(QUEST_EVENTS) do
|
||||
if enabled then
|
||||
eventFrame:RegisterEvent(QUEST_EVENTS[i])
|
||||
else
|
||||
eventFrame:UnregisterEvent(QUEST_EVENTS[i])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function RemoveCallback(list, callback)
|
||||
local i
|
||||
for i = table.getn(list), 1, -1 do
|
||||
if list[i] == callback then
|
||||
table.remove(list, i)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local QuestPlateOnShow = function(plate)
|
||||
plate.BNPQuestRenderKey = nil
|
||||
BNP:UpdateQuestPlateIndicator(plate, true)
|
||||
end)
|
||||
end
|
||||
|
||||
table.insert(BNP.libnameplate.OnUpdate, function(plate)
|
||||
local QuestPlateOnUpdate = function(plate)
|
||||
BNP:UpdateQuestPlateIndicator(plate, false)
|
||||
end)
|
||||
end
|
||||
|
||||
local eventFrame = CreateFrame("Frame")
|
||||
eventFrame:RegisterEvent("VARIABLES_LOADED")
|
||||
eventFrame:RegisterEvent("PLAYER_LOGIN")
|
||||
eventFrame:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||||
eventFrame:RegisterEvent("QUEST_LOG_UPDATE")
|
||||
eventFrame:RegisterEvent("QUEST_WATCH_UPDATE")
|
||||
eventFrame:RegisterEvent("UNIT_QUEST_LOG_CHANGED")
|
||||
local function SetQuestNameplateCallbacksRegistered(enabled)
|
||||
if enabled and not questCallbacksRegistered then
|
||||
table.insert(BNP.libnameplate.OnShow, QuestPlateOnShow)
|
||||
table.insert(BNP.libnameplate.OnUpdate, QuestPlateOnUpdate)
|
||||
questCallbacksRegistered = true
|
||||
elseif not enabled and questCallbacksRegistered then
|
||||
RemoveCallback(BNP.libnameplate.OnShow, QuestPlateOnShow)
|
||||
RemoveCallback(BNP.libnameplate.OnUpdate, QuestPlateOnUpdate)
|
||||
questCallbacksRegistered = false
|
||||
end
|
||||
end
|
||||
|
||||
eventFrame:SetScript("OnEvent", function()
|
||||
BNP:RequestQuestPlateObjectiveUpdate()
|
||||
end)
|
||||
function BNP:SetQuestPlateRuntimeEnabled(enabled, rebuild)
|
||||
enabled = enabled and true or false
|
||||
|
||||
eventFrame:SetScript("OnUpdate", function()
|
||||
if questRuntimeEnabled ~= enabled then
|
||||
questRuntimeEnabled = enabled
|
||||
SetQuestEventsRegistered(enabled)
|
||||
SetQuestNameplateCallbacksRegistered(enabled)
|
||||
if eventFrame then
|
||||
eventFrame:SetScript("OnUpdate", enabled and QuestRuntimeOnUpdate or nil)
|
||||
end
|
||||
end
|
||||
|
||||
if enabled then
|
||||
if rebuild then
|
||||
ClearTable(questObjectives)
|
||||
ClearTable(questObjectiveKeys)
|
||||
cachedDependencyName = nil
|
||||
ResetQuestDataCaches()
|
||||
questRevision = questRevision + 1
|
||||
end
|
||||
self:RequestQuestPlateObjectiveUpdate()
|
||||
if self.RefreshQuestPlateIndicators then self:RefreshQuestPlateIndicators() end
|
||||
else
|
||||
updatePending = false
|
||||
updateElapsed = 0
|
||||
ClearTable(questObjectives)
|
||||
ClearTable(questObjectiveKeys)
|
||||
cachedDependencyName = nil
|
||||
ResetQuestDataCaches()
|
||||
questRevision = questRevision + 1
|
||||
if self.RefreshQuestPlateIndicators then self:RefreshQuestPlateIndicators() end
|
||||
end
|
||||
end
|
||||
|
||||
function BNP:SyncQuestPlateRuntime(rebuild)
|
||||
self:SetQuestPlateRuntimeEnabled(self:AreQuestPlateIndicatorsEnabled(), rebuild)
|
||||
end
|
||||
|
||||
QuestRuntimeOnUpdate = function()
|
||||
if not updatePending then return end
|
||||
updateElapsed = updateElapsed + arg1
|
||||
if updateElapsed < updateDelay then return end
|
||||
updatePending = false
|
||||
updateElapsed = 0
|
||||
BNP:UpdateQuestPlateObjectives()
|
||||
end
|
||||
|
||||
eventFrame = CreateFrame("Frame")
|
||||
eventFrame:RegisterEvent("VARIABLES_LOADED")
|
||||
eventFrame:RegisterEvent("PLAYER_LOGIN")
|
||||
eventFrame:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||||
|
||||
eventFrame:SetScript("OnEvent", function()
|
||||
if event == "VARIABLES_LOADED" or event == "PLAYER_LOGIN" or event == "PLAYER_ENTERING_WORLD" then
|
||||
if BNP:AreQuestPlateIndicatorsEnabled() then
|
||||
BNP:SetQuestPlateRuntimeEnabled(true, true)
|
||||
elseif questRuntimeEnabled then
|
||||
BNP:SetQuestPlateRuntimeEnabled(false, false)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
if questRuntimeEnabled then
|
||||
BNP:RequestQuestPlateObjectiveUpdate()
|
||||
end
|
||||
end)
|
||||
|
||||
Reference in New Issue
Block a user