Release 3.8.2: healing predictions and native API improvements

This commit is contained in:
2026-11-04 21:39:57 -04:00
parent 48ee0747c3
commit d51d2556ef
50 changed files with 2680 additions and 1174 deletions
+117 -15
View File
@@ -131,12 +131,7 @@ local function Supported()
return true
end
local function ModifiersDown()
local shift = API.IsShiftKeyDown or IsShiftKeyDown
local ctrl = API.IsControlKeyDown or IsControlKeyDown
local alt = API.IsAltKeyDown or IsAltKeyDown
return shift() and ctrl() and alt()
end
local ModifiersDown = ShaguTweaks.API.AreMoveModifiersDown
local function HasOffhandWeapon()
local items = _G.C_Item
@@ -207,14 +202,47 @@ local function SetProgress(bar, progress, r, g, b)
end
local function StopBar(bar)
if bar.wake then bar.wake:Cancel(); bar.wake = nil end
if bar.windupWake then bar.windupWake:Cancel(); bar.windupWake = nil end
if bar.binding then bar.binding:Disable() end
bar.active = false
bar.inWindup = false
bar.timer = 0
bar.graceAt = nil
if not S.preview then bar:Hide() end
end
local function Running(bar)
return bar.key == "ranged" and S.autorepeat
or (bar.key ~= "ranged" and S.autoattack and S.combat)
end
local function UpdateColors()
if S.preview then return end
ReconcileQueue()
for i = 1, 3 do
local bar = S.bars[ORDER[i]]
local color = COLORS[bar.key]
if bar.key == "mainhand" and S.queued then color = COLORS[S.queued] end
if bar.key == "ranged" and S.hunter and bar.active and bar.inWindup then color = COLORS.windup end
if S.far[bar.swingType] then color = COLORS.far end
bar:SetStatusBarColor(unpack(color))
end
end
local function StopWaitingBars()
if not S.native then return end
local changed = false
for i = 1, 3 do
local bar = S.bars[ORDER[i]]
if bar.active and bar.graced and not Running(bar) then StopBar(bar); changed = true end
end
if changed then Layout() end
end
local function StartBar(bar, remaining)
if not Wanted(bar) or S.preview then return end
if type(remaining) ~= "number" or remaining ~= remaining or remaining < 0 then return end
UpdateSpeeds()
if bar.speed <= 0 then return end
@@ -224,13 +252,29 @@ local function StartBar(bar, remaining)
bar.timer = remaining
bar.graceAt = nil
bar.active = true
bar.graced = nil
bar.inWindup = bar.key == "ranged" and S.hunter and remaining > 0 and remaining <= AUTOSHOT_WINDUP
UpdateIcon(bar)
if S.native then
if bar.wake then bar.wake:Cancel() end
if bar.windupWake then bar.windupWake:Cancel(); bar.windupWake = nil end
bar.duration:SetTimeFromEnd(GetTime() + remaining, bar.max)
bar:SetTimerDuration(bar.duration, _G.Enum.StatusBarInterpolation.Immediate,
_G.Enum.StatusBarTimerDirection.ElapsedTime)
bar.binding:Enable()
bar.wake = _G.C_Timer.NewTimer(remaining, bar.due)
if bar.key == "ranged" and S.hunter and remaining > AUTOSHOT_WINDUP then
bar.windupWake = _G.C_Timer.NewTimer(remaining - AUTOSHOT_WINDUP, bar.windup)
end
UpdateColors()
end
if not bar:IsShown() then
bar:Show()
Layout()
end
S.driver:Show()
if not S.native then S.driver:Show() end
end
local function ResetAll()
@@ -270,10 +314,10 @@ local function Tick(bar, delta, now)
SetProgress(bar, 1 - bar.timer / bar.max, r, g, b)
local text = string.format("%.1f", math.floor(bar.timer * 10) / 10)
if bar.lastText ~= text then
bar.lastText = text
bar.text:SetText(text)
local tenth = math.floor(bar.timer * 10)
if bar.lastTenth ~= tenth then
bar.lastTenth = tenth
bar.text:SetText(string.format("%.1f", tenth / 10))
end
end
@@ -325,6 +369,39 @@ local function CreateBar(key)
bar.text = bar:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
bar.text:SetPoint("CENTER", bar, "CENTER", 0, 3)
-- As in pfUI, native duration objects own the fill and countdown. The
-- spark follows the native fill texture without a Lua position updater.
if S.native and type(bar.SetTimerDuration) ~= "function" then S.native = false end
if S.native then
bar.duration = _G.C_DurationUtil.CreateDuration()
bar.binding = _G.C_DurationUtil.CreateDurationTextBinding()
bar.binding:SetFontString(bar.text)
bar.binding:SetDuration(bar.duration)
bar.binding:SetFormatter(S.formatter)
bar.binding:SetExpiredText("0.0")
bar.binding:Disable()
bar.spark:ClearAllPoints()
bar.spark:SetPoint("CENTER", bar:GetStatusBarTexture(), "RIGHT", 0, 2)
-- Allocate callbacks once per bar, not on every swing reset.
bar.grace = function()
bar.wake = nil
bar.graced = true
if not Running(bar) then StopBar(bar); Layout() end
end
bar.due = function()
bar.inWindup = false
bar.binding:Disable()
if not S.preview then bar.text:SetText("0.0") end
bar.wake = _G.C_Timer.NewTimer(GRACE, bar.grace)
UpdateColors()
end
bar.windup = function()
bar.windupWake = nil
bar.inWindup = true
UpdateColors()
end
end
bar.icon = CreateFrame("Frame", nil, bar)
bar.icon:SetPoint("RIGHT", bar, "LEFT", -10, 2)
bar.icon:SetWidth(28)
@@ -381,18 +458,27 @@ local function SetPreview(on)
bar:EnableMouse(on)
if not on then bar:StopMovingOrSizing() end
if on and Wanted(bar) then
if bar.binding then bar.binding:Disable(); bar:SetMinMaxValues(0, 1) end
UpdateIcon(bar)
local r, g, b = unpack(COLORS[bar.key])
SetProgress(bar, 1, r, g, b)
bar.text:SetText(PREVIEW[bar.key])
bar.lastText = nil
bar.lastTenth = nil
bar:Show()
elseif S.native and bar.active then
bar.spark:ClearAllPoints()
bar.spark:SetPoint("CENTER", bar:GetStatusBarTexture(), "RIGHT", 0, 2)
bar:SetTimerDuration(bar.duration, _G.Enum.StatusBarInterpolation.Immediate,
_G.Enum.StatusBarTimerDirection.ElapsedTime)
if bar.duration:GetRemainingDuration() > 0 then bar.binding:Enable()
else bar.text:SetText("0.0") end
elseif not bar.active then
bar:Hide()
end
end
Layout()
if S.native then UpdateColors() end
end
-- Keep the bars where they are when something they were stacked on (the cast
@@ -431,11 +517,20 @@ local function CreateCore()
S.container:SetHeight(HEIGHT)
S.container:EnableMouse(false)
S.native = API.durationtimers and true or false
if S.native then
S.formatter = _G.C_StringUtil.CreateNumericRuleFormatter()
S.formatter:AddBreakpoint({ threshold = 0, step = .1,
rounding = _G.Enum.NumericRuleFormatRounding.Down, format = "%.1f" })
end
for i = 1, 3 do S.bars[ORDER[i]] = CreateBar(ORDER[i]) end
S.driver = CreateFrame("Frame", nil, UIParent)
S.driver:Hide()
S.driver:SetScript("OnUpdate", OnUpdate)
if not S.native then
S.driver = CreateFrame("Frame", nil, UIParent)
S.driver:Hide()
S.driver:SetScript("OnUpdate", OnUpdate)
end
S.events = CreateFrame("Frame")
S.events:SetScript("OnEvent", function()
@@ -492,6 +587,11 @@ local function CreateCore()
elseif event == "ACTIONBAR_SLOT_CHANGED" then
if S.queue then ScanQueueSlots() end
elseif event == "ACTIONBAR_UPDATE_STATE" then
-- Native timers do not poll action buttons. This event covers cancelling
-- a queued attack by pressing it again or pressing Escape.
ReconcileQueue()
elseif event == "PLAYER_EQUIPMENT_CHANGED" or event == "UNIT_INVENTORY_CHANGED" then
if event == "PLAYER_EQUIPMENT_CHANGED" and arg1 ~= 16 and arg1 ~= 17 and arg1 ~= 18 then return end
UpdateSpeeds()
@@ -515,6 +615,7 @@ local function CreateCore()
UpdateSpeeds()
RestorePosition()
end
if S.native then StopWaitingBars(); UpdateColors() end
end)
local events = {
@@ -581,6 +682,7 @@ local function EnableQueueColors()
end
end
events:RegisterEvent("ACTIONBAR_SLOT_CHANGED")
events:RegisterEvent("ACTIONBAR_UPDATE_STATE")
end
swing.enable = function(self)