AnySCT 0.3.1

This commit is contained in:
2026-10-15 14:55:12 +02:00
parent 4600cc0719
commit 4a0c325fd7
8 changed files with 93 additions and 14 deletions
+1 -1
View File
@@ -2,7 +2,7 @@
## Title: AnySCT ## Title: AnySCT
## Notes: Blizzard-pet-style floating combat text for any units you pick: tanks, their pets and totems, mobs, enemy players. ## Notes: Blizzard-pet-style floating combat text for any units you pick: tanks, their pets and totems, mobs, enemy players.
## Author: ShempError ## Author: ShempError
## Version: 0.3.0 ## Version: 0.3.1
## X-Website: https://octowow.st/git/ShempError/AnySCT ## X-Website: https://octowow.st/git/ShempError/AnySCT
## OptionalDeps: pfUI, CT_RaidAssist, oRA, TWThreat ## OptionalDeps: pfUI, CT_RaidAssist, oRA, TWThreat
## SavedVariables: AnySCTDB ## SavedVariables: AnySCTDB
+6
View File
@@ -2,6 +2,12 @@
All notable changes to AnySCT are documented here. All notable changes to AnySCT are documented here.
## 0.3.1 — 2026-10-15
### Fixed
- Lines in a tower no longer overlap: each line reserves its real height (crits at the peak of their size pop) plus the suffix line hanging below the line above it, and crits now rise with the rest of the stack instead of standing still, so newer lines can no longer rise into them.
- Crits of tiny procs (relative to the source's average hit) pop at up to half size.
## 0.3.0 — 2026-10-15 ## 0.3.0 — 2026-10-15
### Added ### Added
+24 -7
View File
@@ -16,6 +16,14 @@ are on the list, your own SCT addon stays the only one that shows your damage).
*A listed tank hitting the training dummies in Orgrimmar, seen from another character: an orange melee *A listed tank hitting the training dummies in Orgrimmar, seen from another character: an orange melee
"Miss", a gold spell "Resist", and the experimental WAIT label under the dummy being attacked.* "Miss", a gold spell "Resist", and the experimental WAIT label under the dummy being attacked.*
## What's new — 0.3.1 · 2026-10-15
- **The line tower no longer overlaps.** Every line reserves its real height, including the crit size pop
and the spell / name line under it, and crits now rise with the rest of the stack, so newer lines can
not run into them.
- **Crits of tiny procs pop smaller** (a crit "3" among Fire Strike ticks is half size), measured against
the source's average hit.
## What's new — 0.3.0 · 2026-10-15 ## What's new — 0.3.0 · 2026-10-15
- **Identical hits are merged.** Fire Strike style tick storms become one line with the running sum - **Identical hits are merged.** Fire Strike style tick storms become one line with the running sum
@@ -45,6 +53,8 @@ are on the list, your own SCT addon stays the only one that shows your damage).
**Version history** — details in [CHANGELOG.md](CHANGELOG.md): **Version history** — details in [CHANGELOG.md](CHANGELOG.md):
- **0.3.1** (2026-10-15) — fix: the line tower no longer overlaps (real line heights, crits rise with the
stack), crits of tiny procs pop smaller.
- **0.3.0** (2026-10-15) — merged identical hits, "Nameplate + anchor" placement, a demo that plays your own - **0.3.0** (2026-10-15) — merged identical hits, "Nameplate + anchor" placement, a demo that plays your own
targetSCT's class burst and previews GO / WAIT, anchor drop fix. targetSCT's class burst and previews GO / WAIT, anchor drop fix.
- **0.2.0** (2026-10-15) — targetSCT rename, name option, per-targetSCT colours, regrouped options with - **0.2.0** (2026-10-15) — targetSCT rename, name option, per-targetSCT colours, regrouped options with
@@ -57,7 +67,7 @@ are on the list, your own SCT addon stays the only one that shows your damage).
Everything floats over the **victim** of the event, never over the source: Everything floats over the **victim** of the event, never over the source:
- **Melee in orange, special attacks in gold, heals in green.** Numbers use the client's own damage font, - **Melee in orange, special attacks in gold, heals in green.** Numbers use the client's own damage font,
no outline, a black shadow. Ordinary hits rise and fade; **crits pop** at a larger size and stay in place. no outline, a black shadow. Ordinary hits rise and fade; **crits pop** at a larger size and rise with the stack.
- **Words for what did not land**: Miss, Dodge, Parry, Block, Resist, Immune, ... in the same style. - **Words for what did not land**: Miss, Dodge, Parry, Block, Resist, Immune, ... in the same style.
- **Every hit is shown**: up to 16 lines per mob and 60 on screen by default, so a fast burst is not - **Every hit is shown**: up to 16 lines per mob and 60 on screen by default, so a fast burst is not
recycled early (both are sliders on the Text tab). recycled early (both are sliders on the Text tab).
@@ -71,11 +81,18 @@ Everything floats over the **victim** of the event, never over the source:
*A crit "366" popping over the Heroic Training Dummy, a "Block" in grey, and a GO label partly covered by *A crit "366" popping over the Heroic Training Dummy, a "Block" in grey, and a GO label partly covered by
the crit.* the crit.*
<img src="screenshots/demo-anchor-tower.png" alt="Demo at the fixed anchor: a shaman burst with spell names, merged Fire Strike ticks, words and the WAIT label inside the anchor box" width="300"> <table>
<tr>
*The Demo button at the fixed anchor (no target): a shaman burst with spell names on - a 783 Stormstrike <td align="center" width="50%">
crit, a white hit, merged Fire Strike ticks (7), Miss / Dodge / Parry / Block, and the GO / WAIT preview <a href="screenshots/anchor-wait.png"><img src="screenshots/anchor-wait.png" width="300" alt="Demo at the fixed anchor early in the burst: a tower of numbers and words and WAIT 1350 / 1500 inside the anchor box"></a><br>
"WAIT 1200 / 1500" inside the anchor box.* <sub><b>WAIT at the anchor</b> — the Demo's shaman burst, no overlaps: a faded 783 Stormstrike on top, Dodge, 97, a merged 7 Fire Strike, Parry, Block, 100 Ancient Accord, a half-size crit 3 Fire Strike, and "WAIT 1350 / 1500" inside the anchor box</sub>
</td>
<td align="center" width="50%">
<a href="screenshots/anchor-go.png"><img src="screenshots/anchor-go.png" width="300" alt="Demo at the fixed anchor later in the burst: crits 216 and 760 in the tower and GO +1875 inside the anchor box"></a><br>
<sub><b>GO at the anchor</b> — later in the burst: a fading Ancient Accord, 3 Fire Strike, Resist, 7 Fire Strike, a big crit 216, 194, a crit 760 Earth Shock, and "GO +1875" inside the box</sub>
</td>
</tr>
</table>
## GO / WAIT threat label (experimental) ## GO / WAIT threat label (experimental)
@@ -236,7 +253,7 @@ six sliders including the line limits) below.*
| Direction | text rises (Up) or sinks (Down) | Up | | Direction | text rises (Up) or sinks (Down) | Up |
| Number size | factor on the engine size, 0.5-3 | 1.00 | | Number size | factor on the engine size, 0.5-3 | 1.00 |
| Word size | same for Miss / Dodge / Parry ... | 1.00 | | Word size | same for Miss / Dodge / Parry ... | 1.00 |
| Rise (px) | how far a normal text rises, 0-300; crits stay in place | 120 | | Rise (px) | how far a normal text rises, 0-300; crits rise with the stack | 120 |
| Jitter (px) | random horizontal offset per line, 0-40 | 0 | | Jitter (px) | random horizontal offset per line, 0-40 | 0 |
| Max lines | lines on screen over all mobs, 3-100; the oldest is recycled at the limit | 60 | | Max lines | lines on screen over all mobs, 3-100; the oldest is recycled at the limit | 60 |
| Max lines per mob | lines over one mob, 1-30; lines live 1.5 s, so 16 shows every hit of a burst of about 10 per second | 16 | | Max lines per mob | lines over one mob, 1-30; lines live 1.5 s, so 16 shows every hit of a burst of about 10 per second | 16 |
+1 -1
View File
@@ -97,7 +97,7 @@ if CreateFrame then
"Size factor of damage numbers. Size = 0.018333 x screen diagonal x factor (crits 0.0275): 23 px at 1024x768, 54 px at 2560x1440." }, "Size factor of damage numbers. Size = 0.018333 x screen diagonal x factor (crits 0.0275): 23 px at 1024x768, 54 px at 2560x1440." },
{ "wordFactor", "Word size", 0.5, 3, 0.05, "%.2f", "Size factor of Miss / Dodge / Parry / ..." }, { "wordFactor", "Word size", 0.5, 3, 0.05, "%.2f", "Size factor of Miss / Dodge / Parry / ..." },
{ "riseHeight", "Rise (px)", 0, 300, 5, nil, { "riseHeight", "Rise (px)", 0, 300, 5, nil,
"How far a normal text rises (crits stay in place). The engine rises 2 yards; the screen distance depends on the camera." }, "How far a normal text rises (crits rise with the stack). The engine rises 2 yards; the screen distance depends on the camera." },
{ "mergeWindow", "Merge window (s)", 0.1, 1.5, 0.05, "%.2f", { "mergeWindow", "Merge window (s)", 0.1, 1.5, 0.05, "%.2f",
"How long a line keeps absorbing identical hits, counted from its first hit. Short: more, smaller lines. Long: fewer lines that update longer. Only used with Merge identical hits." }, "How long a line keeps absorbing identical hits, counted from its first hit. Short: more, smaller lines. Long: fewer lines that update longer. Only used with Merge identical hits." },
{ "jitter", "Jitter (px)", 0, 40, 1, nil, "Random horizontal offset per line. 0 like the engine; lines stack instead." }, { "jitter", "Jitter (px)", 0, 40, 1, nil, "Random horizontal offset per line. 0 like the engine; lines stack instead." },
Binary file not shown.

After

Width:  |  Height:  |  Size: 403 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 371 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 301 KiB

+61 -5
View File
@@ -24,7 +24,6 @@ AnySCT_STYLE = {
fadeOutWord = 1.0, -- Miss / Dodge / Parry / Block ... fadeOutWord = 1.0, -- Miss / Dodge / Parry / Block ...
fadeOutAbsorb = 0.09, -- "Absorb" is a quick flicker fadeOutAbsorb = 0.09, -- "Absorb" is a quick flicker
riseHeight = 120, -- px a normal text rises (engine: 2 world units; screen distance varies) riseHeight = 120, -- px a normal text rises (engine: 2 world units; screen distance varies)
critRiseFactor = 0, -- crits do not rise, they pop in place
sizeScale = 0.018333, -- text pixel size = scale x screen diagonal (1024x768 -> 23 px) sizeScale = 0.018333, -- text pixel size = scale x screen diagonal (1024x768 -> 23 px)
critSizeScale = 0.0275, -- crits (1024x768 -> 35 px) critSizeScale = 0.0275, -- crits (1024x768 -> 35 px)
shadow = 0.002, -- shadow offset, fraction of the viewport per axis (down-right) shadow = 0.002, -- shadow offset, fraction of the viewport per axis (down-right)
@@ -230,6 +229,43 @@ S.ANCHOR_W, S.ANCHOR_H, S.ANCHOR_LABEL_SIZE, S.PLATE_LABEL_SIZE = 200, 30, 12, 1
function S.EstTextWidth(text, size) return string.len(text) * size * 0.62 end function S.EstTextWidth(text, size) return string.len(text) * size * 0.62 end
-- Where anchor-mode damage lines start so they clear the box (and the list label above it). -- Where anchor-mode damage lines start so they clear the box (and the list label above it).
function S.AnchorLineOffset(showLabel) return showLabel and 16 or 2 end function S.AnchorLineOffset(showLabel) return showLabel and 16 or 2 end
-- ---- stack geometry -------------------------------------------------------------------------
-- A line occupies [bottom - suffixH, bottom + numberH]: the number sits on its anchor point, the
-- suffix (spell / name) hangs below it. Crits are reserved at the peak of their size pop. A new line
-- is inserted at the bottom; every older line of the same mob moves up by the new line's height plus
-- the suffix height of the line directly above it, which is what makes the new line fit under it.
function S.CritPeak(kf)
local m = 1
for i = 1, table.getn(kf) do if kf[i][2] > m then m = kf[i][2] end end
return m
end
-- Distance of a line's anchor point from its start: the same rise rate for every line of a tower, so
-- older lines (larger t) only ever move away from newer ones.
function S.LineOffset(t, dur, rise, push) return t / dur * rise + push end
function S.NumberHeight(base, isCrit, peak) return isCrit and base * peak or base end
function S.SuffixHeight(base, hasSuffix)
if not hasSuffix then return 0 end
local sz = math.floor(base * 0.3)
if sz < 10 then sz = 10 end
return sz + 2
end
function S.StackAdvance(base, isCrit, peak, prevSuffixH)
return S.NumberHeight(base, isCrit, peak) + (prevSuffixH or 0)
end
-- A crit of a tiny proc must not pop at full crit size: scale by the amount against the source's
-- running average hit (0.5 .. 1).
function S.CritFactor(amount, ref)
if not ref or ref <= 0 then return 1 end
local f = (amount or 0) / ref
if f > 1 then return 1 end
if f < 0.5 then return 0.5 end
return f
end
function S.UpdateRef(ref, amount)
if not ref then return amount end
return ref + (amount - ref) * 0.1
end
-- ---- merging identical attacks --------------------------------------------------------------- -- ---- merging identical attacks ---------------------------------------------------------------
-- Is the result a word (Miss / Dodge / ...)? Same rule as S.Format. -- Is the result a word (Miss / Dodge / ...)? Same rule as S.Format.
function S.IsWordResult(res, opt) function S.IsWordResult(res, opt)
@@ -370,6 +406,8 @@ if CreateFrame then
local POOL_MAX = 100 -- slot tables only; FontStrings are created on first use (ensureFs) local POOL_MAX = 100 -- slot tables only; FontStrings are created on first use (ensureFs)
local critAnimated = S.CurveAnimated(STYLE.critKeyframes) local critAnimated = S.CurveAnimated(STYLE.critKeyframes)
local critActive = S.CurveActive(STYLE.critKeyframes) local critActive = S.CurveActive(STYLE.critKeyframes)
local avgHit = {} -- per source: running average hit, for the crit pop scale
local critPeak = S.CritPeak(STYLE.critKeyframes)
local GAP = 3 -- px between the plate top and the text local GAP = 3 -- px between the plate top and the text
local anchor, float, listLabel local anchor, float, listLabel
@@ -548,13 +586,14 @@ if CreateFrame then
if t < STYLE.fadeIn then a = t / STYLE.fadeIn end if t < STYLE.fadeIn then a = t / STYLE.fadeIn end
if t > s.fadeOut then a = a * (s.dur - t) / (s.dur - s.fadeOut) end if t > s.fadeOut then a = a * (s.dur - t) / (s.dur - s.fadeOut) end
local rise = s.rise local rise = s.rise
-- crits rise with the tower like every other line: a line that stood still would
-- let the newer, rising lines below it run into it
if s.crit then if s.crit then
rise = rise * STYLE.critRiseFactor
if critAnimated then if critAnimated then
s.fs:SetTextHeight(s.base * S.KeyScale(STYLE.critKeyframes, t / s.dur)) s.fs:SetTextHeight(s.base * S.KeyScale(STYLE.critKeyframes, t / s.dur))
end end
end end
local off = (t / s.dur * rise + s.push) * s.dir local off = S.LineOffset(t, s.dur, rise, s.push) * s.dir
s.fs:ClearAllPoints() s.fs:ClearAllPoints()
if s.plate then if s.plate then
needScan = true needScan = true
@@ -632,9 +671,26 @@ if CreateFrame then
local base = S.TextSize(scale, factor, sw, sh) local base = S.TextSize(scale, factor, sw, sh)
s.base = base * mul s.base = base * mul
-- older lines of the same mob move one text height away -- tiny crits (procs) pop smaller: scale by the amount against the source's running average hit
local refKey = res.source or "?"
if not isWord and res.kind ~= "heal" then
if s.crit then s.base = s.base * S.CritFactor(res.amount, avgHit[refKey]) end
avgHit[refKey] = S.UpdateRef(avgHit[refKey], res.amount or 0)
end
s.sfxH = S.SuffixHeight(s.base, suffix ~= nil)
-- older lines of the same mob make room for this line's real height (crit peak included) plus
-- the suffix hanging below the line right above it
local prevSfx, prevT = 0, nil
for j = 1, POOL_MAX do for j = 1, POOL_MAX do
if slots[j].active and slots[j].key == key then slots[j].push = slots[j].push + s.base end local m = slots[j]
if m.active and m.key == key and j ~= i and (not prevT or m.t < prevT) then
prevT, prevSfx = m.t, m.sfxH or 0
end
end
local adv = S.StackAdvance(s.base, s.crit, critPeak, prevSfx)
for j = 1, POOL_MAX do
if slots[j].active and slots[j].key == key and j ~= i then slots[j].push = slots[j].push + adv end
end end
s.active = true s.active = true