Release 3.8.4: pale-blue proc and reactive spell highlights

This commit is contained in:
2026-11-06 00:34:09 -04:00
parent 8b62382660
commit 224f6db154
7 changed files with 300 additions and 3 deletions
+4
View File
@@ -4,6 +4,10 @@ Changes in OEVG/ShaguTweaks-ClassicAPI, newest first. Entries summarize released
Based on [Dusk/ShaguTweaks-ClassicAPI](https://octowow.st/git/Dusk/ShaguTweaks-ClassicAPI) at `4391564` (2026-09-08). Earlier history is available upstream.
## 3.8.4 — 2026-11-06 — Action-bar proc highlights
- Add **Proc & Reactive Spell Highlights** (Action Bar, off): event-driven pale light-blue glows for supported spells and macros, with native pulsing animation where available. Central proc alerts remain with dedicated addons such as Proc Doc.
## 3.8.3 — 2026-11-05 — Personal Resource Display
- Replace **Standalone Player Frame** with **Personal Resource Display**: independent, movable and resizable copies of player health and power, leaving the original player frame intact. Existing health/power positions migrate.
+119
View File
@@ -0,0 +1,119 @@
# 3.8.3 profiling: current bottlenecks and theme cost
Only the player can run these checks in the game. No performance gain has been
measured by the automated tests. The live game folder was not modified.
## Focused theme comparison
First measure the current configuration, then repeat with both recoloring options
off, Golden UI only, and Darkened UI only. Reload after each setting change.
Use the same addons, character, location, FPS limit and GentleGC configuration.
For every mode, warm up for two minutes and open the same windows once before
sampling. Then take three 60-second idle samples and three 60-second samples
opening/closing the same map, bags, mail and auction windows the same number of
times. Record CPU ms/s, memory before/after, and any hitches. Measure initial
window creation separately from repeated openings if only the first opening hitches.
| Mode | Scenario | CPU ms/s, three runs | Memory before/after | Hitches |
| --- | --- | --- | --- | --- |
| Both themes off | Idle / windows | | | |
| Golden UI only | Idle / windows | | | |
| Darkened UI only | Idle / windows | | | |
The reported 6.2 MB versus 6.9 MB is approximately 0.7 MB extra retained Lua
memory. These two snapshots do not measure allocation rate, CPU cost or a leak.
After several identical window-opening cycles and natural GC cycles, check
whether memory settles or keeps rising. Do not manually collect during CPU runs.
Also profile the current build in combat/forms/healing as below. Theme comparisons
isolate one option's cost; they do not establish gains over an older release.
## First verify behavior
- Real Health Numbers: take damage, heal, spend/regenerate power, switch targets
and summon/dismiss a pet. Druid forms should show the appropriate resource and
update the separate mana display. Check Big Health and Personal Resource Display layouts.
- Swing/Wand Timer: attack, stop, move out of range, change targets, and change
weapons. Dual-wield bars and wand Shoot should reset and stop correctly.
- Druid Maul or Warrior Heroic Strike/Cleave: queue and cancel, checking colors.
Hunter Auto Shot: check the last-half-second red warning if a Hunter is available.
- Hold Ctrl+Shift+Alt during a swing: preview labels remain stable, countdowns
resume on release, and drag/reset/saved positions still work after reload.
- Energy & Mana Tick: test a fresh login, Cat/Bear/caster transitions, spending
mana, channel completion/cancellation, a potion and a drain if practical.
Verify the spark after the first observed gain, its five-second cast-cost
sweep, and resizing with Big Health and Personal Resource Display bars.
- Macros: test bare/explicit/conditional #showtooltip, item macros, castsequence,
chosen icons and any legacy --showtooltip or /pfcast display hints you use.
Edit/delete a custom hint and reload; check for stale overrides. Check missing
/equip, /use, focus and attack aliases without replacing another addon's aliases.
- Shift-compare rings, trinkets and weapons from bags/vendors/chat links; check
one empty paired slot, enchanted/random-suffix items, Shift release, hover
changes and tooltip closure. Compare with other tooltip addons enabled.
## Find the remaining bottlenecks
Profile the current build first. The purpose is to identify expensive workloads
and then narrow them down to modules/functions, not to prove the recent changes
made the addon faster. Older builds are not required for this investigation.
Keep the same character, location, target, addon list, UI settings and frame-rate
limit. Enable Real Health Numbers and the applicable Swing/Wand Timer in both.
Warm up for two minutes after loading; do not include loading or option changes
in a measured run. Keep GentleGC and other client mods configured identically.
Enable profiling, then reload:
```
/console scriptProfile 1
/reload
```
Start each sample with:
```
/script ResetCPUUsage(); STPerfStart=GetTime()
```
Perform the chosen scenario for about 60 seconds, then print average addon Lua
CPU milliseconds per elapsed second:
```
/script UpdateAddOnCPUUsage(); DEFAULT_CHAT_FRAME:AddMessage(string.format("ShaguTweaks: %.3f ms/s over %.1f s",GetAddOnCPUUsage("ShaguTweaks")/(GetTime()-STPerfStart),GetTime()-STPerfStart))
```
Scenarios: (1) idle with no windows open; (2) steady melee/wand attacks;
(3) repeated damage/healing and power changes, including druid form changes;
(4) repeated map, inventory, mail and auction-window interactions. Record which
windows were used and how many times. Repeat each three times on the current
build and compare medians. Repeating comparable activity matters more than
identical duration alone.
These addon totals are the first triage step, not module/function attribution.
For workloads with meaningful cost, follow with focused function CPU profiling
and, where practical, repeat the same scenario with one relevant option disabled
(reload after changing options). That separates module cost from unrelated
activity and tells us where a code change might be worthwhile. Respect the
decision to preserve the working theme recoloring behavior.
At the same point after warm-up and after a fixed test session, sample Lua memory:
```
/script UpdateAddOnMemoryUsage(); DEFAULT_CHAT_FRAME:AddMessage(string.format("ShaguTweaks: %.1f KiB",GetAddOnMemoryUsage("ShaguTweaks")))
```
Memory includes uncollected objects; compare at equivalent GentleGC collection
points. Do not force collections during CPU samples. Native textures/DLL memory
and total garbage production are not represented by this single memory number.
Send the build, scenario, enabled options, three CPU results and memory readings.
Those results guide the next targeted investigation. Lower addon Lua
CPU does not by itself prove higher FPS: native timer work moves out of Lua,
profiling adds overhead, and other addons/client rendering may dominate. Do not
use GetFrameCPUUsage for this comparison. Afterward, turn profiling off:
```
/console scriptProfile 0
/reload
```
+3 -1
View File
@@ -9,12 +9,14 @@ This is an English-only customization of [Dusk/ShaguTweaks-ClassicAPI](https://o
> This fork requires ClassicAPI and targets **Turtle WoW-like server/client environments**.
**Version 3.8.3.** What changed in each update: see the [changelog](CHANGELOG.md).
**Version 3.8.4.** What changed in each update: see the [changelog](CHANGELOG.md).
> **Upgrading to 3.7:** the modules of the separate *ShaguTweaks Extras ClassicAPI* addon are now built into ShaguTweaks. **Disable or delete `ShaguTweaks-extras`** after updating, otherwise its older copies replace the built-in ones. Your on/off settings carry over. Raid Frames, Bag Item Click, Bag Search Bar and Auction Alt-Buy were not merged. See [Docs/EXTRAS_MERGE.md](Docs/EXTRAS_MERGE.md) for where each module went and why some were left out.
For druids, Personal Resource Display also shows a small mana bar beneath the power bar while shapeshifted. It stays centered at 60% of the power bar width and mirrors native alternate mana, including when the original resources are hidden. Energy & Mana Tick also adds its mana-regeneration spark.
**Proc & Reactive Spell Highlights** (Action Bar, off): pale light-blue action-button glows for supported procs and reactive spells, including macros. Uses ClassicAPI glow events and native animation where available, with a static fallback. Central artwork is left to Proc Doc or other alert addons. Requires ClassicAPI spell-overlay support; older supported DLLs leave this feature inactive.
## Screenshots
Personal Resource Display, independently positioned player name and movable action bars with Golden UI.
+2 -1
View File
@@ -2,7 +2,7 @@
## Title: |cffffcc00Shagu|cffffffffTweaks |cffcccccc[ClassicAPI]
## Author: Shagu
## Notes: A collection of tweaks and enhancements to the Default UI
## Version: 3.8.3
## Version: 3.8.4
## Dependencies: !!!ClassicAPI
## SavedVariables: ShaguTweaks_player_cache, ShaguTweaks_social_cache
## SavedVariablesPerCharacter: ShaguTweaks_config, ShaguTweaks_cache
@@ -42,6 +42,7 @@ mods\minimap-clean.lua
mods\minimap-zoom.lua
mods\minimap-tracking.lua
mods\actionbar-range-color.lua
mods\actionbar-proc-highlights.lua
mods\free-slots.lua
mods\xpbar-text.lua
mods\sell-junk.lua
+104
View File
@@ -0,0 +1,104 @@
local _G = ShaguTweaks.GetGlobalEnv()
local T = ShaguTweaks.T
local API = ShaguTweaks.API
local module = ShaguTweaks:register({
title = T["Proc & Reactive Spell Highlights"],
description = T["Highlights action buttons for active procs and reactive spells using ClassicAPI. Supports spells and macros; central proc artwork is left to other addons."],
expansions = { ["vanilla"] = true }, category = T["Action Bar"], enabled = nil,
})
module.enable = function(self)
if self.states then return end
local query = _G.C_SpellActivationOverlay and _G.C_SpellActivationOverlay.IsSpellOverlayed or _G.IsSpellOverlayed
local valid = _G.C_EventUtils and _G.C_EventUtils.IsEventValid
if type(query) ~= "function" or not API.GetActionInfo or not valid
or not valid("SPELL_ACTIVATION_OVERLAY_GLOW_SHOW")
or not valid("SPELL_ACTIVATION_OVERLAY_GLOW_HIDE") then return end
local states, byButton = {}, {}
self.states = states
local function Paint(state, active)
if active then
if not state.glow:IsShown() then
state.glow:Show()
if state.pulse then state.pulse:Play() end
end
else
if state.pulse then state.pulse:Stop() end
state.glow:Hide()
end
end
local function Sync(state)
local spell
if state.button:IsVisible() then
local slot = ActionButton_GetPagedID(state.button)
if slot then
local kind, id = API.GetActionInfo(slot)
if kind == "spell" then spell = id
elseif kind == "macro" and _G.GetMacroSpell then
local name, rank, spellID = _G.GetMacroSpell(id)
spell = spellID
end
end
end
state.spell = spell
Paint(state, spell and query(spell))
end
for _, prefix in ipairs({"ActionButton", "BonusActionButton", "MultiBarBottomLeftButton",
"MultiBarBottomRightButton", "MultiBarRightButton", "MultiBarLeftButton"}) do
for i = 1, 12 do
local button = _G[prefix .. i]
if button then
local glow = CreateFrame("Frame", nil, button)
glow:SetAllPoints(button)
glow:SetFrameLevel(button:GetFrameLevel() + 5)
glow:SetBackdrop({ edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border", edgeSize = 12 })
glow:SetBackdropBorderColor(.7, .9, 1, 1)
-- Additive light stands out against both dark and gold button rims.
-- Use the client's existing action-button glow artwork.
local halo = glow:CreateTexture(nil, "OVERLAY")
halo:SetTexture("Interface\\Buttons\\UI-ActionButton-Border")
halo:SetBlendMode("ADD")
halo:SetVertexColor(.55, .85, 1, 1)
halo:SetPoint("TOPLEFT", button, "TOPLEFT", -8, 8)
halo:SetPoint("BOTTOMRIGHT", button, "BOTTOMRIGHT", 8, -8)
glow:EnableMouse(false)
glow:Hide()
local state = { button = button, glow = glow, halo = halo }
if type(glow.CreateAnimationGroup) == "function" then
local pulse = glow:CreateAnimationGroup()
local fade = pulse:CreateAnimation("Alpha")
if type(fade.SetFromAlpha) == "function" and type(fade.SetToAlpha) == "function" then
pulse:SetLooping("BOUNCE")
fade:SetFromAlpha(1); fade:SetToAlpha(.7)
fade:SetDuration(.4); fade:SetSmoothing("IN_OUT")
state.pulse = pulse
end
end
table.insert(states, state); byButton[button] = state
local function Changed() Sync(state) end
ShaguTweaks.HookScript(button, "OnShow", Changed)
ShaguTweaks.HookScript(button, "OnHide", Changed)
Sync(state)
end
end
end
ShaguTweaks.hooksecurefunc("ActionButton_Update", function()
local state = byButton[this]
if state then Sync(state) end
end)
local events = CreateFrame("Frame")
self.events = events
for _, name in ipairs({"SPELL_ACTIVATION_OVERLAY_GLOW_SHOW", "SPELL_ACTIVATION_OVERLAY_GLOW_HIDE",
"PLAYER_ENTERING_WORLD", "ACTIONBAR_SLOT_CHANGED", "ACTIONBAR_PAGE_CHANGED",
"UPDATE_BONUS_ACTIONBAR", "UPDATE_MACROS", "ACTIONBAR_UPDATE_STATE",
"PLAYER_TARGET_CHANGED", "MODIFIER_STATE_CHANGED", "SPELLS_CHANGED"}) do
if valid(name) then events:RegisterEvent(name) end
end
events:SetScript("OnEvent", function()
local proc = event == "SPELL_ACTIVATION_OVERLAY_GLOW_SHOW" or event == "SPELL_ACTIVATION_OVERLAY_GLOW_HIDE"
for _, state in ipairs(states) do
if proc then
if state.spell == arg1 then Paint(state, state.button:IsVisible() and query(arg1)) end
else Sync(state) end
end
end)
end
+3 -1
View File
@@ -9,7 +9,7 @@ sh tests/run_all.sh ../tools/lua5.0/lua5.0
The interpreter defaults to `lua5.0` if omitted. The runner discovers all
`tests/test_*.lua` files, reports every failure, prints a final count, and exits
unsuccessfully if any suite fails. It is developer tooling, is not loaded by the
addon, and has no in-game overhead. The current suite contains 26 test files.
addon, and has no in-game overhead. The current suite contains 27 test files.
Individual checks can also be run from the addon root:
@@ -107,3 +107,5 @@ An independently hidden comparison recovers without a new source event. The
unchanged visible case still avoids native rebuilds. Changed links/equipment
rebuild; a non-item or persistently hidden source removes comparisons after the
deferred lifecycle check.
Proc-highlight checks cover capability detection, pre-existing procs, direct spells/macros, paging, hidden buttons, event-only updates and native-animation/static paths.
+65
View File
@@ -0,0 +1,65 @@
local root=(arg[1] or '.')..'/'
local function Check(supported,animated)
local module,frames,updates=nil,{},0
local methods={}
local function noop() end
local function new()
return setmetatable({shown=true,scripts={},events={}},{__index=function(t,k)
if k=='CreateAnimationGroup' and not animated then return nil end
return methods[k] or (string.find(k,'^[A-Z]') and noop)
end})
end
function methods:Show() self.shown=true end
function methods:Hide() self.shown=false end
function methods:IsShown() return self.shown end
function methods:IsVisible() return self.shown end
function methods:GetFrameLevel() return 1 end
function methods:SetScript(k,v) self.scripts[k]=v end
function methods:RegisterEvent(k) self.events[k]=true end
function methods:CreateAnimationGroup() return new() end
function methods:CreateAnimation() return new() end
function methods:CreateTexture() return new() end
function methods:SetTexture(path) self.path=path end
function methods:SetBlendMode(mode) self.blend=mode end
function methods:SetToAlpha(value) self.minimum=value end
function methods:Play() self.playing=true end
function methods:Stop() self.playing=false end
CreateFrame=function() local f=new();table.insert(frames,f);return f end
ActionButton1=new();BonusActionButton1=new();MultiBarBottomLeftButton1=new()
local slots={[ActionButton1]=1,[BonusActionButton1]=73,[MultiBarBottomLeftButton1]=61}
local actions={[1]={'spell',10},[73]={'macro',2},[61]={'item',30}}
local active={[10]=true,[20]=true}
ActionButton_GetPagedID=function(b) return slots[b] end
GetMacroSpell=function(id) return 'Spell','Rank',20 end
C_SpellActivationOverlay={IsSpellOverlayed=function(id) return active[id] end}
C_EventUtils={IsEventValid=function() return supported end}
ShaguTweaks={T=setmetatable({},{__index=function(t,k) return k end}),
GetGlobalEnv=function() return _G end,API={GetActionInfo=function(slot)
updates=updates+1;local a=actions[slot];if a then return unpack(a) end
end}}
function ShaguTweaks:register(m) module=m;return m end
function ShaguTweaks.HookScript(f,k,fn) f.scripts[k]=fn end
local refresh
function ShaguTweaks.hooksecurefunc(name,fn) assert(name=='ActionButton_Update');refresh=fn end
dofile(root..'mods/actionbar-proc-highlights.lua');module:enable()
if not supported then assert(not module.states and table.getn(frames)==0);return end
assert(table.getn(module.states)==3)
local a,b,c=module.states[1],module.states[2],module.states[3]
assert(a.glow.shown and b.glow.shown and not c.glow.shown)
assert(a.halo.path=='Interface\\Buttons\\UI-ActionButton-Border' and a.halo.blend=='ADD')
assert(not module.events.scripts.OnUpdate and not a.glow.scripts.OnUpdate)
if animated then assert(a.pulse.playing) else assert(not a.pulse) end
local count=table.getn(frames);module:enable();assert(table.getn(frames)==count)
local function emit(e,id) event,arg1=e,id;module.events.scripts.OnEvent() end
local before=updates;active[20]=false;emit('SPELL_ACTIVATION_OVERLAY_GLOW_HIDE',20)
assert(not b.glow.shown and a.glow.shown and updates==before)
actions[13]={'spell',20};slots[ActionButton1]=13;emit('ACTIONBAR_PAGE_CHANGED')
assert(not a.glow.shown and a.spell==20)
active[20]=true;emit('SPELL_ACTIVATION_OVERLAY_GLOW_SHOW',20);assert(a.glow.shown and b.glow.shown)
ActionButton1:Hide();a.button.scripts.OnHide();assert(not a.glow.shown and not a.spell)
ActionButton1:Show();a.button.scripts.OnShow();assert(a.glow.shown)
actions[13]={'item',1};this=ActionButton1;refresh();this=nil;assert(not a.glow.shown)
GetMacroSpell=function() return nil end;emit('UPDATE_MACROS');assert(not b.glow.shown)
end
Check(false,true);Check(true,true);Check(true,false)
print('Proc highlights: capability guard, existing procs, ranks/macros, event-only changes, paging, visibility, native animation/static fallback and idempotence passed')