forked from Dusk/ShaguTweaks-ClassicAPI
Release 3.8.4: pale-blue proc and reactive spell highlights
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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
|
||||
```
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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.
|
||||
|
||||
@@ -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')
|
||||
Reference in New Issue
Block a user