Add /tfb test mode for previewing target buffs and debuffs

- /tfb test [buffs debuffs] and an options-panel button fill the target frame
  with fake auras (default 32 buffs, 16 debuffs) to check layout, stacks,
  borders, timers and sweeps. Targets the player if nothing is targeted.
- Fake auras restart when they expire so timers keep running.
- Works on the native path too (icons, stacks, borders only).
- Bump to 0.9.4; tests now 21 scenarios.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019W3rwutyoLNHxXPycYWSMc
This commit is contained in:
2026-11-01 00:22:03 +00:00
co-authored by Claude Sonnet 5.5
parent 09784ec5b2
commit cbf459104b
7 changed files with 153 additions and 11 deletions
+91 -6
View File
@@ -26,6 +26,42 @@ local lHelpfulSlots = {};
local lHarmfulSlots = {};
local lRemovableColor = {r = 1, g = 0.82, b = 0};
-- Test mode (/tfb test): fake auras on whatever you are targeting, for checking
-- layout, timers, stacks and borders. Lasts until turned off or /reload.
local lTestMode = false;
local lTestBuffs = 32;
local lTestDebuffs = 16;
local lTestStart = 0;
local lTestElapsed = 0;
local TEST_ICONS = {
"Interface\\Icons\\Spell_Holy_WordFortitude", "Interface\\Icons\\Spell_Nature_Regeneration",
"Interface\\Icons\\Spell_Shadow_ShadowWordPain", "Interface\\Icons\\Spell_Fire_FlameBolt",
"Interface\\Icons\\Spell_Frost_FrostArmor02", "Interface\\Icons\\Spell_Nature_Slow",
"Interface\\Icons\\Ability_Rogue_Rupture", "Interface\\Icons\\Spell_Holy_PowerWordShield"
};
local TEST_TYPES = {"Magic", "Curse", "Disease", "Poison"};
-- Mixed lengths so seconds, minutes, hours and the red warning all appear.
local TEST_DURATIONS = {10, 18, 45, 90, 300, 900, 1800, 3600, 7200};
-- Lua 5.0 has no % operator.
local function TargetFrameBuff_Mod(a, b)
return a - b * math.floor(a / b);
end
-- Same values as UnitAura: name, icon, count, dispelType, duration, expiration,
-- source, removable. Each aura restarts when its time runs out.
local function TargetFrameBuff_TestAura(pass, index)
local duration = TEST_DURATIONS[TargetFrameBuff_Mod(index * 2, table.getn(TEST_DURATIONS)) + 1];
local expiration = lTestStart + duration * (math.floor((GetTime() - lTestStart) / duration) + 1);
local count = 1;
if (TargetFrameBuff_Mod(index, 4) == 0) then count = TargetFrameBuff_Mod(index, 7) + 2; end
local dispelType = nil;
if (pass == 2) then dispelType = TEST_TYPES[TargetFrameBuff_Mod(index, 5) + 1]; end
local icon = TEST_ICONS[TargetFrameBuff_Mod(index, table.getn(TEST_ICONS)) + 1];
return "Test aura "..index, icon, count, dispelType, duration, expiration, "player",
(pass == 1 and TargetFrameBuff_Mod(index, 5) == 0);
end
local function TargetFrameBuff_Setting(key)
if (TargetFrameBuffDB and TargetFrameBuffDB[key] ~= nil) then
return TargetFrameBuffDB[key];
@@ -49,7 +85,11 @@ local function TargetFrameBuff_AuraOnEnter()
TargetFrameBuff_UpdateClassicAPI();
if (not this.auraIndex) then GameTooltip:Hide(); return; end
GameTooltip:SetOwner(this, "ANCHOR_BOTTOMRIGHT", 15, -25);
GameTooltip:SetUnitAura("target", this.auraIndex, this.auraFilter);
if (lTestMode) then
GameTooltip:SetText("Test "..(this.auraFilter == "HARMFUL" and "debuff " or "buff ")..this.auraIndex);
else
GameTooltip:SetUnitAura("target", this.auraIndex, this.auraFilter);
end
GameTooltip:Show();
end
@@ -205,7 +245,12 @@ end
function TargetFrameBuff_UpdateNative()
local button, buff, i;
for i=1, TARGETFRAMEBUFF_MAX_TARGET_BUFFS do
buff = UnitBuff("target", i);
if (lTestMode) then
buff = nil;
if (i <= lTestBuffs) then buff = TEST_ICONS[TargetFrameBuff_Mod(i, table.getn(TEST_ICONS)) + 1]; end
else
buff = UnitBuff("target", i);
end
button = lBuffButtons[i];
if (buff) then
getglobal("TargetFrameBuff"..i.."Icon"):SetTexture(buff);
@@ -218,7 +263,16 @@ function TargetFrameBuff_UpdateNative()
local debuff, debuffApplications, debuffType;
for i=1, TARGETFRAMEBUFF_MAX_TARGET_DEBUFFS do
debuff, debuffApplications, debuffType = UnitDebuff("target", i);
if (lTestMode) then
debuff, debuffApplications, debuffType = nil, nil, nil;
if (i <= lTestDebuffs) then
debuff = TEST_ICONS[TargetFrameBuff_Mod(i, table.getn(TEST_ICONS)) + 1];
debuffApplications = TargetFrameBuff_Mod(i, 4) == 0 and 3 or 1;
debuffType = TEST_TYPES[TargetFrameBuff_Mod(i, 5) + 1];
end
else
debuff, debuffApplications, debuffType = UnitDebuff("target", i);
end
button = lDebuffButtons[i];
if (debuff) then
TargetFrameBuff_ColorBorder(i, debuffType);
@@ -320,7 +374,7 @@ local function TargetFrameBuff_ShowAura(button, filter, index, icon, count, disp
button.timerText = nil;
button.timer:Hide();
end
if (GameTooltip:IsOwned(button)) then
if (GameTooltip:IsOwned(button) and not lTestMode) then
GameTooltip:SetUnitAura("target", index, filter);
end
end
@@ -342,13 +396,17 @@ function TargetFrameBuff_UpdateClassicAPI()
else
filter, slots = "HARMFUL", lHarmfulSlots;
end
if (batched) then
if (lTestMode) then
n = (pass == 1) and lTestBuffs or lTestDebuffs;
elseif (batched) then
token, n = C_UnitAuras.GetAuraSlots("target", filter, nil, nil, slots);
else
n = TARGETFRAMEBUFF_MAX_TARGET_BUFFS + TARGETFRAMEBUFF_MAX_TARGET_DEBUFFS;
end
for index = 1, n do
if (batched) then
if (lTestMode) then
name, icon, count, dispelType, duration, expiration, source, removable = TargetFrameBuff_TestAura(pass, index);
elseif (batched) then
name, icon, count, dispelType, duration, expiration, source, removable = C_UnitAuras.UnitAuraBySlot("target", slots[index]);
else
name, icon, count, dispelType, duration, expiration, source, removable = C_UnitAuras.UnitAura("target", index, filter);
@@ -455,6 +513,13 @@ end
-- Driven by TargetFrameBuffFrame, which is parented to TargetFrame, so this
-- only runs while you have a target.
function TargetFrameBuff_OnUpdate(elapsed)
if (lTestMode) then
lTestElapsed = lTestElapsed + (elapsed or 0);
if (lTestElapsed >= 1) then
lTestElapsed = 0;
TargetFrameBuff_Update();
end
end
if (not lBuffButtons[1] or not lBuffButtons[1].timer or not TargetFrameBuff_HasClassicAPI()) then
return;
end
@@ -500,3 +565,23 @@ function TargetFrameBuff_Restore()
end
end
end
-- Turn test mode on or off. buffs (0-32) and debuffs (0-32) are optional; debuffs
-- past 16 spill into the buff row, as real ones do with ClassicAPI.
function TargetFrameBuff_SetTest(enabled, buffs, debuffs)
lTestMode = enabled and true or false;
if (buffs) then lTestBuffs = math.max(0, math.min(TARGETFRAMEBUFF_MAX_TARGET_BUFFS, math.floor(buffs))); end
if (debuffs) then lTestDebuffs = math.max(0, math.min(TARGETFRAMEBUFF_MAX_TARGET_BUFFS, math.floor(debuffs))); end
lTestStart = GetTime();
lTestElapsed = 0;
if (lTestMode and UnitExists and TargetUnit and not UnitExists("target")) then
TargetUnit("player");
end
TargetFrameBuff_Update();
return lTestMode, lTestBuffs, lTestDebuffs;
end
function TargetFrameBuff_IsTest()
return lTestMode;
end