AnySCT 0.3.0
This commit is contained in:
+1
-1
@@ -2,7 +2,7 @@
|
||||
## Title: AnySCT
|
||||
## Notes: Blizzard-pet-style floating combat text for any units you pick: tanks, their pets and totems, mobs, enemy players.
|
||||
## Author: ShempError
|
||||
## Version: 0.2.0
|
||||
## Version: 0.3.0
|
||||
## X-Website: https://octowow.st/git/ShempError/AnySCT
|
||||
## OptionalDeps: pfUI, CT_RaidAssist, oRA, TWThreat
|
||||
## SavedVariables: AnySCTDB
|
||||
|
||||
@@ -2,6 +2,21 @@
|
||||
|
||||
All notable changes to AnySCT are documented here.
|
||||
|
||||
## 0.3.0 — 2026-10-15
|
||||
|
||||
### Added
|
||||
- **Merge identical hits** (Text tab, on by default): identical attacks (same source, victim and ability; normal hit or crit kept apart) within the **merge window** (0.1-1.5 s, default 0.4) become ONE line whose number is the running sum, updated in place. Misses and other words are not summed; identical words collapse. Optional **Show hit count** appends e.g. ` x5`.
|
||||
- Placement **Nameplate + anchor** (`/asct place both`): every line and the GO / WAIT label appear over the victim's nameplate and at the fixed anchor, with separate line limits per place. Without a plate it shows once at the anchor.
|
||||
- The **Demo** button and `/asct demo` now play a 2.5 s burst of the first listed targetSCT's class skills (a shaman demo tank without a list; amounts for the shaman set from recorded tanking fights, estimates for other classes) through the normal display path, so spell names, name prefixes, colours, limits and placement apply. Every result type (crit, Miss, Dodge, Parry, Block) appears, and a simulated GO / WAIT label previews the threat display (also with GO / WAIT switched off).
|
||||
|
||||
### Changed
|
||||
- The GO / WAIT label at the fixed anchor sits inside the anchor box (now 200 x 30); damage lines in anchor mode start above its top edge.
|
||||
- Anchor X / Y sliders span the real screen instead of fixed +-800 / +-500 and never clamp a stored position.
|
||||
- The options window is 540 px high to fit the new Text-tab controls.
|
||||
|
||||
### Fixed
|
||||
- The fixed anchor jumped to another place when dropped after a drag: the drop position was computed from `UIParent:GetWidth()` / `GetHeight()`, which are not in the coordinate system of frame rects on a non-default UI scale. It now uses the screen rect's centre and rounds to whole units; slider refreshes no longer write back while dragging.
|
||||
|
||||
## 0.2.0 — 2026-10-15
|
||||
|
||||
### Changed
|
||||
|
||||
@@ -16,6 +16,19 @@ 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
|
||||
"Miss", a gold spell "Resist", and the experimental WAIT label under the dummy being attacked.*
|
||||
|
||||
## 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
|
||||
(same source, victim, ability and hit/crit within 0.4 s; the number updates in place, words are not
|
||||
summed). Switch off or tune under Text: *Merge identical hits*, *Merge window*, *Show hit count*.
|
||||
- **Placement "Nameplate + anchor"** shows every line, and the GO / WAIT label, over the plate and at the
|
||||
fixed anchor at once, with separate line limits.
|
||||
- **The Demo previews your own setup**: a 2.5 s burst of your first targetSCT's class skills (Stormstrike,
|
||||
Earth Shock and friends for a shaman) with all your display settings, plus the GO / WAIT label inside
|
||||
the anchor box, to judge the line tower without a second character.
|
||||
- **Fixed: the anchor no longer jumps when you drop it.** The dropped position is now exact, and the
|
||||
Anchor X / Y sliders cover the whole screen.
|
||||
|
||||
## What's new — 0.2.0 · 2026-10-15
|
||||
|
||||
- **targetSCT.** What used to be called "tank" is now *targetSCT* everywhere (menu, options, commands
|
||||
@@ -32,6 +45,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):
|
||||
|
||||
- **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.
|
||||
- **0.2.0** (2026-10-15) — targetSCT rename, name option, per-targetSCT colours, regrouped options with
|
||||
label tooltips, higher line limits, experimental threat-based GO / WAIT.
|
||||
- **0.1.0** (2026-10-14) — first release: list, victim-routed text in the client's own style, nameplate
|
||||
@@ -46,6 +61,8 @@ Everything floats over the **victim** of the event, never over the source:
|
||||
- **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
|
||||
recycled early (both are sliders on the Text tab).
|
||||
- **Identical hits are merged**: the same attack from the same source on the same mob within 0.4 s shows
|
||||
as one line with the summed value (tiny procs like Fire Strike no longer flood the tower; Text tab).
|
||||
- Pets and totems of a listed unit are shown under their owner; Fire Nova, traps and other area damage
|
||||
land on the mob they hit.
|
||||
|
||||
@@ -54,6 +71,12 @@ 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
|
||||
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">
|
||||
|
||||
*The Demo button at the fixed anchor (no target): a shaman burst with spell names on - a 783 Stormstrike
|
||||
crit, a white hit, merged Fire Strike ticks (7), Miss / Dodge / Parry / Block, and the GO / WAIT preview
|
||||
"WAIT 1200 / 1500" inside the anchor box.*
|
||||
|
||||
## GO / WAIT threat label (experimental)
|
||||
|
||||
Off by default. Shows under the nameplate of **your own target** how much threat lead the targetSCT has
|
||||
@@ -151,9 +174,9 @@ character.
|
||||
| `list` | print the list |
|
||||
| `clear` | empty the list |
|
||||
| `colours` | give every listed unit a new automatic colour |
|
||||
| `place clamp/anchoroff/anchor` | where the text appears |
|
||||
| `place clamp/anchoroff/anchor/both` | where the text appears |
|
||||
| `unlock` / `lock` | move the fixed anchor (drag the box; a demo stream plays) |
|
||||
| `demo` | sample text at your target's nameplate, to compare with your own numbers |
|
||||
| `demo` | sample text and a 2.5 s damage burst at your target's nameplate, to compare with your own numbers and judge the line tower |
|
||||
| `threat` | one line with the state of the GO / WAIT label |
|
||||
| `debug` | toggle the event / drop trace in the chat frame |
|
||||
| `mem` | memory self-test (idle and a demo burst) |
|
||||
@@ -206,6 +229,9 @@ six sliders including the line limits) below.*
|
||||
| Show spell name | ability name under the number for special attacks | off |
|
||||
| Show healer name | healer's name under heal numbers | on |
|
||||
| Mark glancing / crushing | add the word after the number | off |
|
||||
| Merge identical hits | identical attacks (same source, victim, ability; normal hit or crit) within the merge window become one line with the running sum, updated in place; words are not summed | on |
|
||||
| Show hit count | append the number of merged hits, e.g. `12 x5` | off |
|
||||
| Merge window | seconds a line keeps absorbing identical hits, counted from its first hit (0.1-1.5) | 0.4 |
|
||||
| Font | Client (the damage font, honours pfUI) / FrizQT / Skurri / Arial Narrow | Client |
|
||||
| Direction | text rises (Up) or sinks (Down) | Up |
|
||||
| Number size | factor on the engine size, 0.5-3 | 1.00 |
|
||||
@@ -243,7 +269,7 @@ list label, and the Debug section.*
|
||||
|
||||
| control | what it does | default |
|
||||
|---|---|---|
|
||||
| Placement | Nameplate, clamp (over the victim's plate, held at the screen edge) / Nameplate, else anchor / Always anchor | Nameplate, clamp |
|
||||
| Placement | Nameplate, clamp (over the victim's plate, held at the screen edge) / Nameplate, else anchor / Always anchor / Nameplate + anchor (both at once, separate line limits) | Nameplate, clamp |
|
||||
| No nameplate | when the victim has no visible plate: Anchor / Hide | Anchor |
|
||||
| Plate X offset | horizontal shift from the plate centre, -150..150 | 40 |
|
||||
| Plate Y offset | vertical shift from the plate's top edge, -100..100 | 0 |
|
||||
@@ -252,7 +278,7 @@ list label, and the Debug section.*
|
||||
| Anchor X / Anchor Y | position of the fixed anchor from the screen centre, -800..800 / -500..500 | 0 / 120 |
|
||||
| Unlock anchor / Lock anchor | drag the anchor box; a demo stream plays while unlocked | |
|
||||
| Reset anchor | back to screen centre, 120 px up | |
|
||||
| Demo | hit, hit, crit, miss, dodge, parry, block at your target's plate (anchor without a target) | |
|
||||
| Demo | starts at once with a 2.5 s burst of your first targetSCT's class skills (a shaman demo tank without one) on one victim, using all your display settings (Stormstrike, Earth Shock, tiny Fire Strike ticks, melee, crits, extra attacks, and every result type: crit, miss, dodge, parry, block, resist) that fills the per-mob line tower, plus a simulated GO / WAIT threat label (works with Threat GO off); at your target's plate (anchor without a target); click again to restart | |
|
||||
| Show targetSCT list label | a `targetSCTs: ...` line above the anchor | off |
|
||||
| Record threat trace | writes the received threat data to `<WoW>\imports\anysct_threat.txt` (needs SuperWoW), for bug reports | off |
|
||||
|
||||
@@ -262,7 +288,7 @@ Enemy nameplates must be on (V key) for text over the mob.
|
||||
|
||||
The text always floats over the **victim** of the event, not over the source. Placement modes: over the
|
||||
nameplate clamped to the screen edge (default), over the nameplate with the fixed anchor as fallback,
|
||||
or always the fixed anchor. At the fixed anchor the victim's name is always added because the position no
|
||||
always the fixed anchor, or both the plate and the fixed anchor at once (the GO / WAIT label too; with no plate it shows once at the anchor). At the fixed anchor the victim's name is always added because the position no
|
||||
longer identifies the mob. If no plate addon or plate is found the text simply uses the fixed anchor.
|
||||
|
||||
## Colours and names
|
||||
|
||||
@@ -28,6 +28,9 @@ AnySCT_DEFAULTS = {
|
||||
maxLinesPerTarget = 16,
|
||||
onlyWhileSourceFights = false, -- true: hide text while the listed source itself is not in combat (renamed from showOnlyInCombat, which gated on the PLAYER)
|
||||
colourize = false, -- false: everything orange like the pet text; true: crit yellow, miss red, block blue, ...
|
||||
mergeHits = true, -- identical attacks (same source, victim, attack, hit/crit) within a short window become ONE line with the running sum
|
||||
mergeWindow = 0.4, -- seconds a line keeps absorbing identical hits
|
||||
mergeCount = false, -- append " x5" to a merged line
|
||||
markGlancing = false, -- true: add "glancing" / "crushing" after the number
|
||||
guessTotemOwner = false, -- unknown totem owner = the only listed shaman (can misattribute enemy / own totems)
|
||||
classColours = false, -- numbers / words take the source's class colour (pets: the owner's)
|
||||
@@ -49,7 +52,7 @@ AnySCT_DEFAULTS = {
|
||||
minimap = true,
|
||||
mmAngle = 200,
|
||||
importMain = true,
|
||||
placement = "clamp", -- "clamp" | "anchorOff" | "anchor"
|
||||
placement = "clamp", -- "clamp" | "anchorOff" | "anchor" | "both"
|
||||
noPlate = "anchor", -- "anchor" | "hide"
|
||||
edgeMargin = 40,
|
||||
xOffset = 40, -- plate modes: shift right so targetSCT numbers sit beside the player's own
|
||||
@@ -1242,6 +1245,23 @@ function C.LeadFromPos(p)
|
||||
return p * 100
|
||||
end
|
||||
|
||||
-- Fixed-anchor geometry. Frame rects (GetCenter/GetLeft/...) and SetPoint offsets live in one
|
||||
-- coordinate system; UIParent:GetWidth()/GetHeight() do NOT (1834x768 against a 2580x1080 rect on a
|
||||
-- UI scale of 0.711), so the screen centre must come from UIParent's own rect, never from its size.
|
||||
function C.AnchorPosFromCenter(cx, cy, ux, uy)
|
||||
return { point = "CENTER", x = math.floor(cx - ux + 0.5), y = math.floor(cy - uy + 0.5) }
|
||||
end
|
||||
|
||||
-- Half extents of the screen as slider limits: rounded up to a multiple of 5, at least 100.
|
||||
function C.ScreenHalfRange(left, right, bottom, top)
|
||||
local function half(a, b)
|
||||
local h = math.ceil(((b or 0) - (a or 0)) / 2 / 5) * 5
|
||||
if h < 100 then h = 100 end
|
||||
return h
|
||||
end
|
||||
return half(left, right), half(bottom, top)
|
||||
end
|
||||
|
||||
function C.PosFromLead(v)
|
||||
v = v or 0
|
||||
if v < 0 then v = 0 end
|
||||
@@ -1346,6 +1366,8 @@ local function threatSrcName(name)
|
||||
return nil
|
||||
end
|
||||
|
||||
C.ThreatSrcName = threatSrcName
|
||||
|
||||
local function threatHide()
|
||||
if TH.shown then
|
||||
TH.shown = false
|
||||
@@ -1427,7 +1449,7 @@ local HELP = {
|
||||
" clear remove every targetSCT",
|
||||
" colours re-assign the targetSCT colours automatically",
|
||||
" threat state of the experimental threat GO / WAIT label",
|
||||
" place clamp|anchoroff|anchor where the text appears",
|
||||
" place clamp|anchoroff|anchor|both where the text appears",
|
||||
" unlock | lock move the fixed anchor",
|
||||
" demo show sample text",
|
||||
" mem measure memory use (10 s idle, then a demo burst)",
|
||||
@@ -1449,7 +1471,7 @@ function C.Canon(n)
|
||||
return nil
|
||||
end
|
||||
|
||||
local PLACE = { clamp = "clamp", anchoroff = "anchorOff", anchor = "anchor" }
|
||||
local PLACE = { clamp = "clamp", anchoroff = "anchorOff", anchor = "anchor", both = "both" }
|
||||
|
||||
function C.Slash(msg)
|
||||
msg = msg or ""
|
||||
@@ -1482,7 +1504,7 @@ function C.Slash(msg)
|
||||
C.Print("targetSCT colours re-assigned")
|
||||
elseif cmd == "place" then
|
||||
local p = PLACE[string.lower(rest)]
|
||||
if p then C.SetOpt("placement", p) C.Print("placement: " .. p) else C.Print("usage: /anysct place clamp|anchoroff|anchor") end
|
||||
if p then C.SetOpt("placement", p) C.Print("placement: " .. p) else C.Print("usage: /anysct place clamp|anchoroff|anchor|both") end
|
||||
elseif cmd == "unlock" or cmd == "lock" then
|
||||
if AnySCT_SCT and AnySCT_SCT.SetMoveMode then AnySCT_SCT.SetMoveMode(cmd == "unlock") end
|
||||
elseif cmd == "demo" then
|
||||
|
||||
+24
-8
@@ -36,6 +36,10 @@ if CreateFrame then
|
||||
"Print the healer's name under the heal number." },
|
||||
{ "colourize", "Colour coding",
|
||||
"Off: everything is orange like Blizzard's pet text. On: crit yellow, miss / dodge / parry red, block blue, absorb / resist / immune purple. A per-targetSCT colour (Colours tab) overrides this." },
|
||||
{ "mergeHits", "Merge identical hits",
|
||||
"Identical attacks (same source, victim and ability, normal hit or crit) that arrive within the merge window become ONE line whose number is the running sum, updated in place. Tames Fire Strike style tick storms. Misses, dodges and the like are not summed; identical words collapse to one line. On by default." },
|
||||
{ "mergeCount", "Show hit count",
|
||||
"Append the number of merged hits to a merged line, e.g. 12 x5. Off by default." },
|
||||
{ "markGlancing", "Mark glancing / crushing",
|
||||
"Add the word glancing or crushing after the number (Blizzard style). Off: shown as a plain number." },
|
||||
{ "showSpells", "Show special attacks",
|
||||
@@ -68,8 +72,8 @@ if CreateFrame then
|
||||
local CYCLES = {
|
||||
{ "placement", "Placement",
|
||||
{ { "clamp", "Nameplate, clamp" }, { "anchorOff", "Nameplate, else anchor" },
|
||||
{ "anchor", "Always anchor" } },
|
||||
"Where the text appears. Over the nameplate needs enemy nameplates on (V). Clamp keeps text along the screen edge when the plate is off screen." },
|
||||
{ "anchor", "Always anchor" }, { "both", "Nameplate + anchor" } },
|
||||
"Where the text appears. Over the nameplate needs enemy nameplates on (V). Clamp keeps text along the screen edge when the plate is off screen. 'Nameplate + anchor' shows every line and the GO / WAIT label over the plate AND at the fixed anchor (separate line limits); without a plate it shows once at the anchor." },
|
||||
{ "noPlate", "No nameplate",
|
||||
{ { "anchor", "Anchor" }, { "hide", "Hide" } },
|
||||
"What to do when the targetSCT's target has no visible nameplate." },
|
||||
@@ -94,6 +98,8 @@ if CreateFrame then
|
||||
{ "wordFactor", "Word size", 0.5, 3, 0.05, "%.2f", "Size factor of Miss / Dodge / Parry / ..." },
|
||||
{ "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." },
|
||||
{ "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." },
|
||||
{ "jitter", "Jitter (px)", 0, 40, 1, nil, "Random horizontal offset per line. 0 like the engine; lines stack instead." },
|
||||
{ "maxLines", "Max lines", 3, 100, 1, nil, "Maximum number of lines on screen over all mobs; when it is reached the oldest line is recycled." },
|
||||
{ "maxLinesPerTarget", "Max lines per mob", 1, 30, 1, nil, "Maximum number of lines over one mob. Lines live 1.5 s, so 16 shows every hit of a burst of about 10 per second; lower values recycle the oldest line early." },
|
||||
@@ -114,8 +120,8 @@ if CreateFrame then
|
||||
|
||||
local function getVal(key)
|
||||
if key == "threatGoLead" then return C.PosFromLead(C.Opt(key) or 1500) end
|
||||
if key == "posX" then return (C.Opt("pos") or {}).x or 0 end
|
||||
if key == "posY" then return (C.Opt("pos") or {}).y or 0 end
|
||||
if key == "posX" then return math.floor(((C.Opt("pos") or {}).x or 0) + 0.5) end
|
||||
if key == "posY" then return math.floor(((C.Opt("pos") or {}).y or 0) + 0.5) end
|
||||
return C.Opt(key) or 0
|
||||
end
|
||||
|
||||
@@ -202,6 +208,15 @@ if CreateFrame then
|
||||
local sl = sliders[i]
|
||||
local v = getVal(sl.asctKey)
|
||||
sl.asctBusy = true
|
||||
if (sl.asctKey == "posX" or sl.asctKey == "posY") and AnySCT_SCT and AnySCT_SCT.ScreenRect then
|
||||
local hw, hh = C.ScreenHalfRange(AnySCT_SCT.ScreenRect())
|
||||
local lim = sl.asctKey == "posX" and hw or hh
|
||||
-- never clamp a stored position: widen the range to hold it
|
||||
if math.abs(v) > lim then lim = math.ceil(math.abs(v) / 5) * 5 end
|
||||
sl:SetMinMaxValues(-lim, lim)
|
||||
getglobal(sl:GetName() .. "Low"):SetText(tostring(-lim))
|
||||
getglobal(sl:GetName() .. "High"):SetText(tostring(lim))
|
||||
end
|
||||
sl:SetValue(v)
|
||||
sl.asctBusy = false
|
||||
local txt = sl.asctFmt and string.format(sl.asctFmt, v) or tostring(v)
|
||||
@@ -237,7 +252,7 @@ if CreateFrame then
|
||||
O.refresh = refresh
|
||||
|
||||
-- Fixed panel geometry (UI scale 1): 520 x 460, four tabs hanging below the bottom edge, 16 px margins.
|
||||
local PANEL_W, PANEL_H = 520, 460
|
||||
local PANEL_W, PANEL_H = 520, 540
|
||||
local COLA, COLB = 20, 270 -- two content columns, 230-240 wide each
|
||||
local TOP = -48 -- first content row, below the title
|
||||
|
||||
@@ -424,6 +439,7 @@ if CreateFrame then
|
||||
if d[8] then getglobal(name .. "High"):SetText("7500") end
|
||||
sl:SetScript("OnValueChanged", function()
|
||||
if this.asctBusy then return end
|
||||
if AnySCT_SCT and AnySCT_SCT.IsDragging and AnySCT_SCT.IsDragging() then return end
|
||||
local v = math.floor(this:GetValue() / this.asctStep + 0.5) * this.asctStep
|
||||
local txt = this.asctFmt and string.format(this.asctFmt, v) or tostring(v)
|
||||
if this.asctFmt then v = tonumber(txt) end
|
||||
@@ -656,7 +672,7 @@ if CreateFrame then
|
||||
y2 = y2 - 30
|
||||
makeCycle(p2, "mobNameMode", COLA, y2)
|
||||
y2 = y2 - 30
|
||||
local CONTENT = { "showSpellName", "healShowHealer", "markGlancing" }
|
||||
local CONTENT = { "showSpellName", "healShowHealer", "markGlancing", "mergeHits", "mergeCount" }
|
||||
for i = 1, table.getn(CONTENT) do
|
||||
makeCheck(p2, CONTENT[i], COLA, y2)
|
||||
y2 = y2 - 21
|
||||
@@ -668,7 +684,7 @@ if CreateFrame then
|
||||
y2 = y2 - 30
|
||||
makeCycle(p2, "direction", COLA, y2)
|
||||
y2 = y2 - 48
|
||||
local LOOK = { "sizeFactor", "wordFactor", "riseHeight", "jitter", "maxLines", "maxLinesPerTarget" }
|
||||
local LOOK = { "sizeFactor", "wordFactor", "riseHeight", "jitter", "maxLines", "maxLinesPerTarget", "mergeWindow" }
|
||||
for i = 1, table.getn(LOOK) do
|
||||
local col = COLA + 10
|
||||
if math.mod(i, 2) == 0 then col = COLB + 10 end
|
||||
@@ -738,7 +754,7 @@ if CreateFrame then
|
||||
end)
|
||||
ax = ax + bPos:GetWidth() + 6
|
||||
makeButton(p4, "AnySCTOptionsDemo", "Demo", nil, ax, y4, "Demo",
|
||||
"Play hit, hit, crit, miss, dodge, parry, block at your target's nameplate (fixed anchor without a target) to compare with your own numbers.",
|
||||
"Plays a 2.5 s burst of your first targetSCT's class skills (a shaman demo tank without one) on your target's nameplate (fixed anchor without a target), using all your display settings. Fills the per-mob tower and previews the GO / WAIT threat label; click again to restart.",
|
||||
function() if AnySCT_SCT then AnySCT_SCT.Demo() end end)
|
||||
y4 = y4 - 28
|
||||
makeCheck(p4, "showLabel", COLA, y4)
|
||||
|
||||
+13
-1
@@ -221,7 +221,7 @@ function P.PlatePosition(plate)
|
||||
return cx, cy, UIParent:GetWidth(), UIParent:GetHeight()
|
||||
end
|
||||
|
||||
-- Decide the display mode. opt needs: placement ("clamp" | "anchorOff" | "anchor"), noPlate
|
||||
-- Decide the display mode. opt needs: placement ("clamp" | "anchorOff" | "anchor" | "both"), noPlate
|
||||
-- ("anchor" | "hide"), edgeMargin. Returns mode, x, y, screenW, screenH (x, y only for mode 1/2).
|
||||
function P.Select(plate, opt)
|
||||
if opt.placement == "anchor" then return 3 end
|
||||
@@ -237,6 +237,18 @@ function P.Select(plate, opt)
|
||||
end
|
||||
AnySCT_SelectMode = P.Select
|
||||
|
||||
-- Placement "both": the plate rules of "clamp" (modes 1/2) plus a copy at the fixed anchor. Without a
|
||||
-- usable plate (none found, off screen with hide, or hidden) the anchor shows it once, whatever the
|
||||
-- no-plate setting says. Returns mode, x, y, screenW, screenH, alsoAnchor.
|
||||
function P.Places(plate, opt)
|
||||
local mode, x, y, w, h = P.Select(plate, opt)
|
||||
if opt.placement == "both" then
|
||||
if mode == 1 or mode == 2 then return mode, x, y, w, h, true end
|
||||
return 3
|
||||
end
|
||||
return mode, x, y, w, h, false
|
||||
end
|
||||
|
||||
-- Does the plate still show the mob? Uses the last scan only (no per-frame GetName).
|
||||
function P.PlateMatches(plate, guidNorm)
|
||||
if not plate:IsVisible() then return false end
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 301 KiB |
@@ -216,6 +216,152 @@ function S.Format(res, opt, mode)
|
||||
return text, colourKey, isWord, mul, suffix
|
||||
end
|
||||
|
||||
-- Demo threat preview: the buffer climbs from ~300 to 1.3x the required lead over the burst (f 0..1).
|
||||
function S.DemoThreat(f, need)
|
||||
need = need or 1500
|
||||
local start = (need > 300) and 300 or need * 0.2
|
||||
if f < 0 then f = 0 elseif f > 1 then f = 1 end
|
||||
local b = start + (need * 1.3 - start) * f
|
||||
return (b >= need) and "GO" or "WAIT", b, need
|
||||
end
|
||||
-- Demo timeline: see S.BuildDemo. Event tables are reused on every run (no allocation while it plays).
|
||||
-- Fixed-anchor box and the GO / WAIT label sitting inside it (rough glyph width: 0.62 x size per char).
|
||||
S.ANCHOR_W, S.ANCHOR_H, S.ANCHOR_LABEL_SIZE, S.PLATE_LABEL_SIZE = 200, 30, 12, 14
|
||||
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).
|
||||
function S.AnchorLineOffset(showLabel) return showLabel and 16 or 2 end
|
||||
-- ---- merging identical attacks ---------------------------------------------------------------
|
||||
-- Is the result a word (Miss / Dodge / ...)? Same rule as S.Format.
|
||||
function S.IsWordResult(res, opt)
|
||||
return (S.WORDS[res.kind] ~= nil) and (((res.amount or 0) <= 0) or (opt.colourize and true or false))
|
||||
end
|
||||
|
||||
-- Result class that must match to merge: normal hits (glancing / crushing too) and crits stay apart.
|
||||
function S.MergeClass(res)
|
||||
local k = res.kind
|
||||
if k == "crit" or res.crit then return "crit" end
|
||||
if k == "hit" or k == "glancing" or k == "crushing" then return "hit" end
|
||||
return k
|
||||
end
|
||||
|
||||
-- Does line slot s (fields mKey, mWord, mCls, mSrc, mSpell, mHealer, mPet, t) absorb this result?
|
||||
-- key = the place/victim key of the line (guid, name or "anchor"); words ignore the spell.
|
||||
function S.MergeMatches(s, res, key, isWord, window)
|
||||
if not s.active or s.mKey ~= key or s.mWord ~= isWord or s.t > window then return false end
|
||||
if s.mCls ~= S.MergeClass(res) or s.mSrc ~= res.source or s.mPet ~= res.srcPet then return false end
|
||||
if isWord then return true end
|
||||
return s.mSpell == (res.spellId or res.spell) and s.mHealer == res.healer
|
||||
end
|
||||
|
||||
function S.MergeLabel(text, count, showCount)
|
||||
if showCount and count > 1 then return text .. " x" .. count end
|
||||
return text
|
||||
end
|
||||
|
||||
S.DEMO_BURST_LEN = 2.5
|
||||
|
||||
-- Demo source: the first listed targetSCT (name, class, own colour); without one a generic shaman tank.
|
||||
S.DEMO_FALLBACK_NAME = "Shemp" -- leak-gate: allow
|
||||
S.DEMO_VICTIM = "Training Dummy"
|
||||
function S.DemoSource(st)
|
||||
local e = st and st.order and st.byName and st.byName[st.order[1]]
|
||||
if e then
|
||||
return e.name, e.class, e.rgb, table.getn(st.order) > 1
|
||||
end
|
||||
return S.DEMO_FALLBACK_NAME, "SHAMAN", { 0, 0.44, 0.87 }, false
|
||||
end
|
||||
|
||||
-- Burst skill sets per class. Entry: { spell (nil = white hit, "@kind" = miss word), weight, typical
|
||||
-- amount, upper typical amount, typical crit amount, crit % , windfury-style extra hits }.
|
||||
-- SHAMAN: shares and amounts from recorded tanking fights in dungeons and raids, last 30 days
|
||||
-- (Stormstrike and Earth Shock are boosted so the preview shows them; crit rates are raised to
|
||||
-- make a few crits visible). Every other class: plausible level-60 tank numbers, ESTIMATES.
|
||||
S.DEMO_SETS = {
|
||||
SHAMAN = {
|
||||
{ "Fire Strike", 51, 2, 2, 3, 10 }, { "Lightning Strike", 15, 3, 3, 5, 12 },
|
||||
{ "Tidal Wave", 10, 29, 31, 43, 10 }, { nil, 9, 97, 118, 216, 22, 2 },
|
||||
{ "Engulfing Shadows", 6, 100, 100, 100, 0 }, { "Ancient Accord", 3, 100, 110, 150, 6 },
|
||||
{ "Thundercall", 2, 39, 56, 56, 0 }, { "Earth Shock", 8, 666, 760, 992, 15 },
|
||||
{ "Stormstrike", 10, 117, 140, 241, 30 }, { "Flame Shock", 2, 85, 100, 458, 5 },
|
||||
{ "@miss", 1 }, { "@dodge", 1 }, { "@parry", 1 }, { "@block", 1 }, { "@resist", 1 },
|
||||
},
|
||||
WARRIOR = {
|
||||
{ nil, 28, 150, 190, 320, 25 }, { "Heroic Strike", 20, 280, 340, 560, 25 },
|
||||
{ "Revenge", 14, 350, 420, 700, 25 }, { "Shield Slam", 14, 480, 560, 900, 25 },
|
||||
{ "Sunder Armor", 10, 60, 60, 60, 0 }, { "@miss", 1 }, { "@dodge", 1 }, { "@parry", 1 }, { "@block", 1 },
|
||||
},
|
||||
DRUID = {
|
||||
{ nil, 24, 120, 150, 260, 25 }, { "Maul", 22, 260, 320, 520, 25 }, { "Swipe", 30, 90, 110, 190, 25 },
|
||||
{ "Lacerate", 12, 70, 70, 70, 0 }, { "@miss", 1 }, { "@dodge", 1 }, { "@parry", 1 }, { "@block", 1 },
|
||||
},
|
||||
PALADIN = {
|
||||
{ nil, 28, 140, 175, 300, 25 }, { "Holy Shield", 18, 100, 120, 200, 0 },
|
||||
{ "Consecration", 16, 70, 70, 70, 0 }, { "Seal of Righteousness", 22, 90, 110, 190, 25 },
|
||||
{ "Hammer of the Righteous", 8, 320, 380, 640, 25 }, { "@miss", 1 }, { "@dodge", 1 }, { "@parry", 1 }, { "@block", 1 },
|
||||
},
|
||||
}
|
||||
function S.DemoSet(class)
|
||||
return S.DEMO_SETS[class or ""] or S.DEMO_SETS.WARRIOR
|
||||
end
|
||||
|
||||
-- Timeline: a 2.5 s burst starting at once, of ~9 slots/s picked from the class set
|
||||
-- by weight (deterministic credit round robin, so rare skills still show up). Each entry: t, f (0..1
|
||||
-- through the burst, nil before it), sn (spell name or nil), res (event table, reused every run).
|
||||
function S.BuildDemo(class)
|
||||
local set = S.DemoSet(class)
|
||||
local list, n = {}, 0
|
||||
local t0
|
||||
local function add(t, kind, amount, sn)
|
||||
n = n + 1
|
||||
list[n] = { t = t, sn = sn, res = { kind = kind, amount = amount } }
|
||||
if t0 then list[n].f = (t - t0) / S.DEMO_BURST_LEN end
|
||||
end
|
||||
t0 = 0
|
||||
local total = 0
|
||||
local credit, uses, critAcc = {}, {}, {}
|
||||
for i = 1, table.getn(set) do
|
||||
total = total + set[i][2]
|
||||
credit[i], uses[i], critAcc[i] = 0, 0, 60
|
||||
end
|
||||
local count = 22
|
||||
-- every word of the set is guaranteed to appear once, spread over the burst
|
||||
local forced, words = {}, {}
|
||||
for i = 1, table.getn(set) do
|
||||
if set[i][1] and string.sub(set[i][1], 1, 1) == "@" then table.insert(words, i) end
|
||||
end
|
||||
local nw = table.getn(words)
|
||||
for j = 1, nw do forced[3 + math.floor((j - 1) * (count - 4) / nw)] = words[j] end
|
||||
for slot = 1, count do
|
||||
local t = t0 + (slot - 1) * S.DEMO_BURST_LEN / count
|
||||
local best = 1
|
||||
for i = 1, table.getn(set) do
|
||||
credit[i] = credit[i] + set[i][2]
|
||||
if credit[i] > credit[best] then best = i end
|
||||
end
|
||||
credit[best] = credit[best] - total
|
||||
if forced[slot] then best = forced[slot] end
|
||||
local d = set[best]
|
||||
uses[best] = uses[best] + 1
|
||||
local word = d[1] and string.sub(d[1], 1, 1) == "@" and string.sub(d[1], 2)
|
||||
if word then
|
||||
add(t, word, 0)
|
||||
else
|
||||
local crit = false
|
||||
local rate = d[6] or 0
|
||||
if rate > 0 then
|
||||
critAcc[best] = critAcc[best] + rate
|
||||
if critAcc[best] >= 100 then critAcc[best] = critAcc[best] - 100; crit = true end
|
||||
end
|
||||
local amount = crit and d[5] or ((math.mod(uses[best], 2) == 1) and d[3] or d[4])
|
||||
add(t, crit and "crit" or "hit", amount, d[1])
|
||||
if not d[1] and d[7] and uses[best] == 2 then -- an extra attack in the same frame
|
||||
for _ = 1, d[7] do add(t, "hit", d[3], nil) end
|
||||
end
|
||||
end
|
||||
end
|
||||
return list, n, list[n].t + 1.5
|
||||
end
|
||||
|
||||
if CreateFrame then
|
||||
local W = AnySCT_W
|
||||
local C = AnySCT_Core
|
||||
@@ -258,6 +404,19 @@ if CreateFrame then
|
||||
end
|
||||
end
|
||||
|
||||
local dragging = false
|
||||
|
||||
-- UIParent's rect in the same coordinate system as the anchor's (see C.AnchorPosFromCenter).
|
||||
function S.ScreenRect()
|
||||
local l, b, r, t = UIParent:GetLeft(), UIParent:GetBottom(), UIParent:GetRight(), UIParent:GetTop()
|
||||
if not (l and b and r and t) then
|
||||
l, b, r, t = 0, 0, UIParent:GetWidth(), UIParent:GetHeight()
|
||||
end
|
||||
return l, b, r, t
|
||||
end
|
||||
|
||||
function S.IsDragging() return dragging end
|
||||
|
||||
local function placeAnchor()
|
||||
local pos = opt("pos") or AnySCT_DEFAULTS.pos
|
||||
anchor:ClearAllPoints()
|
||||
@@ -276,8 +435,8 @@ if CreateFrame then
|
||||
float:SetClampedToScreen(true)
|
||||
|
||||
local a = CreateFrame("Frame", "AnySCTAnchor", UIParent)
|
||||
a:SetWidth(160)
|
||||
a:SetHeight(30)
|
||||
a:SetWidth(S.ANCHOR_W)
|
||||
a:SetHeight(S.ANCHOR_H)
|
||||
a:SetFrameStrata("HIGH")
|
||||
a:SetFrameLevel(10)
|
||||
a:SetMovable(true)
|
||||
@@ -286,12 +445,18 @@ if CreateFrame then
|
||||
a:RegisterForDrag("LeftButton")
|
||||
a:SetBackdrop(W.SOLID_BACKDROP)
|
||||
a:SetScript("OnDragStart", function()
|
||||
if moveMode then this:StartMoving() end
|
||||
if moveMode then
|
||||
dragging = true
|
||||
this:StartMoving()
|
||||
end
|
||||
end)
|
||||
a:SetScript("OnDragStop", function()
|
||||
this:StopMovingOrSizing()
|
||||
dragging = false
|
||||
local cx, cy = this:GetCenter()
|
||||
C.SetOpt("pos", { point = "CENTER", x = cx - UIParent:GetWidth() / 2, y = cy - UIParent:GetHeight() / 2 })
|
||||
if not cx then return end
|
||||
local l, b, r, t = S.ScreenRect()
|
||||
C.SetOpt("pos", C.AnchorPosFromCenter(cx, cy, (l + r) / 2, (b + t) / 2))
|
||||
end)
|
||||
anchor = a
|
||||
|
||||
@@ -403,7 +568,11 @@ if CreateFrame then
|
||||
if y > s.lastH - margin then y = s.lastH - margin end
|
||||
s.fs:SetPoint("BOTTOM", UIParent, "BOTTOMLEFT", x, y)
|
||||
else
|
||||
s.fs:SetPoint("BOTTOM", anchor, "CENTER", s.dx, off)
|
||||
if s.dir < 0 then
|
||||
s.fs:SetPoint("TOP", anchor, "BOTTOM", s.dx, off - 2)
|
||||
else
|
||||
s.fs:SetPoint("BOTTOM", anchor, "TOP", s.dx, off + S.AnchorLineOffset(opt("showLabel")))
|
||||
end
|
||||
end
|
||||
s.fs:SetAlpha(a)
|
||||
s.fs:SetShadowColor(0, 0, 0, a)
|
||||
@@ -420,8 +589,36 @@ if CreateFrame then
|
||||
local o = C.Opts()
|
||||
local key = res.mobGuid or res.mobName or "anchor"
|
||||
if mode == 3 then key = "anchor" end
|
||||
local isWord0 = S.IsWordResult(res, o)
|
||||
if o.mergeHits ~= false then
|
||||
local window = o.mergeWindow or 0.4
|
||||
for j = 1, POOL_MAX do
|
||||
local m = slots[j]
|
||||
if m.active and S.MergeMatches(m, res, key, isWord0, window) then
|
||||
-- fold into the live line: same position and fade, only the text changes
|
||||
if not isWord0 then
|
||||
m.mSum = m.mSum + (res.amount or 0)
|
||||
m.mCount = m.mCount + 1
|
||||
local a0 = res.amount
|
||||
res.amount = m.mSum
|
||||
local text = S.Format(res, o, mode)
|
||||
res.amount = a0
|
||||
text = S.MergeLabel(text, m.mCount, o.mergeCount)
|
||||
if text ~= m.mText then
|
||||
m.mText = text
|
||||
m.fs:SetText(text)
|
||||
end
|
||||
end
|
||||
C.Ledger(res, "merged")
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
local i = acquire(key)
|
||||
local s = slots[i]
|
||||
s.mKey, s.mWord, s.mCls, s.mSrc, s.mPet = key, isWord0, S.MergeClass(res), res.source, res.srcPet
|
||||
s.mSpell, s.mHealer = res.spellId or res.spell, res.healer
|
||||
s.mSum, s.mCount = res.amount or 0, 1
|
||||
C.Ledger(res, s.active and "shown+recycled" or "shown")
|
||||
s.key = key
|
||||
ensureFs(i)
|
||||
@@ -475,6 +672,7 @@ if CreateFrame then
|
||||
local cr, cg, cb = S.PickColour(res, o, ckey)
|
||||
fs:SetTextColor(cr, cg, cb)
|
||||
fs:SetText(text)
|
||||
s.mText = text
|
||||
fs:SetAlpha(0)
|
||||
fs:SetShadowColor(0, 0, 0, 0)
|
||||
fs:Show()
|
||||
@@ -503,7 +701,7 @@ if CreateFrame then
|
||||
if o.placement ~= "anchor" and (res.mobGuid or res.mobName) then
|
||||
plate = P.Find(res.mobGuid, res.mobName, res.mobUnit)
|
||||
end
|
||||
local mode, px, py, pw, ph = P.Select(plate, o)
|
||||
local mode, px, py, pw, ph, also = P.Places(plate, o)
|
||||
if C.debug then
|
||||
C.Dbg("place " .. tostring(res.mobName) .. ": " .. (plate and "plate found" or "no plate")
|
||||
.. ", mode " .. tostring(mode) .. (mode == 0 and " (hidden by option)" or ""))
|
||||
@@ -513,11 +711,12 @@ if CreateFrame then
|
||||
return
|
||||
end
|
||||
spawn(res, mode, plate, px, py, pw, ph)
|
||||
if also then spawn(res, 3) end -- "Nameplate + anchor": a second, separately capped stack
|
||||
end
|
||||
|
||||
function S.OptionChanged(key)
|
||||
if not anchor then return end
|
||||
if key == "all" or key == "pos" then placeAnchor() end
|
||||
if (key == "all" or key == "pos") and not dragging then placeAnchor() end
|
||||
S.UpdateLabel()
|
||||
end
|
||||
|
||||
@@ -526,6 +725,7 @@ if CreateFrame then
|
||||
-- (C.ThreatDecide); this only draws. The follow OnUpdate is attached only while the label is shown.
|
||||
local TL = { fs = nil, shown = false, plate = nil, status = nil, text = nil, lastText = nil,
|
||||
lastMode = 0, lastName = nil, mobName = nil, guid = nil }
|
||||
local TL2 = { fs = nil, lastText = nil, lastMode = 0, lastName = nil } -- anchor copy ("both")
|
||||
local followFrame
|
||||
local findAcc = 0
|
||||
|
||||
@@ -536,6 +736,7 @@ if CreateFrame then
|
||||
if TL.shown then
|
||||
TL.shown = false
|
||||
if TL.fs then TL.fs:Hide() end
|
||||
if TL2.fs then TL2.fs:Hide() end
|
||||
end
|
||||
if followFrame then followFrame:SetScript("OnUpdate", nil) end
|
||||
return
|
||||
@@ -552,6 +753,31 @@ if CreateFrame then
|
||||
followFrame:SetScript("OnUpdate", followUpdate)
|
||||
end
|
||||
|
||||
-- Draw the label fs (state record L) for a placement mode; text and font change only on change.
|
||||
local function drawLabel(L, fs, mode, px, py, pw, ph, margin, lift)
|
||||
local size = (mode == 3) and S.ANCHOR_LABEL_SIZE or S.PLATE_LABEL_SIZE
|
||||
if size ~= L.size then
|
||||
L.size = size
|
||||
fs:SetFont(S.FontPath(opt("font")), size, "OUTLINE")
|
||||
L.lastText = nil
|
||||
end
|
||||
local name = (mode == 3) and TL.mobName or nil
|
||||
if TL.text ~= L.lastText or mode ~= L.lastMode or name ~= L.lastName then
|
||||
L.lastText, L.lastMode, L.lastName = TL.text, mode, name
|
||||
fs:SetText(name and (name .. ": " .. TL.text) or TL.text)
|
||||
if TL.status == "GO" then fs:SetTextColor(0.2, 1, 0.2) else fs:SetTextColor(1, 0.6, 0.1) end
|
||||
end
|
||||
fs:ClearAllPoints()
|
||||
if mode == 3 then
|
||||
fs:SetPoint("CENTER", anchor, "CENTER", 0, 0)
|
||||
else
|
||||
local x, y = px, py + lift
|
||||
if mode == 2 then x, y = AnySCT_ClampToScreen(x, y, pw, ph, margin) end
|
||||
fs:SetPoint("BOTTOM", UIParent, "BOTTOMLEFT", x, y)
|
||||
end
|
||||
fs:Show()
|
||||
end
|
||||
|
||||
followUpdate = function()
|
||||
if not TL.shown then return end
|
||||
findAcc = findAcc + arg1
|
||||
@@ -561,56 +787,47 @@ if CreateFrame then
|
||||
TL.plate = nil
|
||||
TL.mobName = UnitName("target")
|
||||
TL.guid = C.ResolveGuid("target")
|
||||
if o.placement ~= "anchor" then TL.plate = P.Find(TL.guid, TL.mobName, "target") end
|
||||
if o.placement ~= "anchor" and not TL.forceAnchor then TL.plate = P.Find(TL.guid, TL.mobName, "target") end
|
||||
end
|
||||
local margin = o.edgeMargin or 40
|
||||
local lift = S.TextSize(STYLE.sizeScale, o.sizeFactor or 1, GetScreenWidth(), GetScreenHeight()) + 6
|
||||
local mode, px, py, pw, ph = P.Select(TL.plate, o)
|
||||
if mode == 0 then TL.fs:Hide() return end
|
||||
local name = (mode == 3) and TL.mobName or nil
|
||||
if TL.text ~= TL.lastText or mode ~= TL.lastMode or name ~= TL.lastName then
|
||||
TL.lastText, TL.lastMode, TL.lastName = TL.text, mode, name
|
||||
TL.fs:SetText(name and (name .. ": " .. TL.text) or TL.text)
|
||||
if TL.status == "GO" then TL.fs:SetTextColor(0.2, 1, 0.2) else TL.fs:SetTextColor(1, 0.6, 0.1) end
|
||||
local mode, px, py, pw, ph, also = P.Places(TL.plate, o)
|
||||
if mode == 0 then
|
||||
TL.fs:Hide()
|
||||
if TL2.fs then TL2.fs:Hide() end
|
||||
return
|
||||
end
|
||||
TL.fs:ClearAllPoints()
|
||||
if mode == 3 then
|
||||
TL.fs:SetPoint("BOTTOM", anchor, "TOP", 0, 18)
|
||||
else
|
||||
local x, y = px, py + lift
|
||||
if mode == 2 then x, y = AnySCT_ClampToScreen(x, y, pw, ph, margin) end
|
||||
TL.fs:SetPoint("BOTTOM", UIParent, "BOTTOMLEFT", x, y)
|
||||
drawLabel(TL, TL.fs, mode, px, py, pw, ph, margin, lift)
|
||||
if also then
|
||||
if not TL2.fs then
|
||||
TL2.fs = float:CreateFontString("AnySCTThreatLabel2", "OVERLAY")
|
||||
TL2.fs:SetFont(S.FontPath(opt("font")), S.ANCHOR_LABEL_SIZE, "OUTLINE")
|
||||
end
|
||||
drawLabel(TL2, TL2.fs, 3, nil, nil, nil, nil, margin, lift)
|
||||
elseif TL2.fs then
|
||||
TL2.fs:Hide()
|
||||
end
|
||||
TL.fs:Show()
|
||||
end
|
||||
|
||||
-- ---- demo / move / memory ------------------------------------------------------------------
|
||||
-- Fixed sequence for side-by-side comparison with the player's own real numbers.
|
||||
local DEMO = {
|
||||
{ kind = "hit", amount = 72 }, { kind = "hit", amount = 66 }, { kind = "crit", amount = 141 },
|
||||
{ kind = "miss", amount = 0 }, { kind = "dodge", amount = 0 }, { kind = "parry", amount = 0 },
|
||||
{ kind = "block", amount = 0 },
|
||||
}
|
||||
local demoIdx, demoLeft, demoAcc, demoTicker, demoAtAnchor = 0, 0, 0, nil, false
|
||||
-- Fixed sequence for side-by-side comparison with the player's own real numbers, then the burst.
|
||||
local demoCache = {}
|
||||
local DEMO, DEMO_N, DEMO_END
|
||||
local demoIdx, demoT, demoTicker, demoAtAnchor = 1, 0, nil, false
|
||||
local dSrc, dClass, dRgb, dMulti, dVictim = nil, nil, nil, false, nil
|
||||
|
||||
-- /asct demo plays at the nameplate of the current target (the same placement rules as real
|
||||
-- events); without a target, or with atAnchor, at the fixed anchor.
|
||||
function S.Demo(atAnchor)
|
||||
build()
|
||||
demoLeft = table.getn(DEMO)
|
||||
demoIdx = 0
|
||||
demoAcc = 0
|
||||
demoAtAnchor = atAnchor and true or false
|
||||
if not demoTicker then demoTicker = CreateFrame("Frame", "AnySCTDemoFrame", UIParent) end
|
||||
demoTicker:SetScript("OnUpdate", function()
|
||||
demoAcc = demoAcc + arg1
|
||||
if demoAcc < 0.45 then return end
|
||||
demoAcc = 0
|
||||
if demoLeft <= 0 then this:SetScript("OnUpdate", nil) return end
|
||||
demoLeft = demoLeft - 1
|
||||
local function demoOnUpdate()
|
||||
demoT = demoT + arg1
|
||||
local showName = opt("showSpellName")
|
||||
while demoIdx <= DEMO_N and DEMO[demoIdx].t <= demoT do
|
||||
local e = DEMO[demoIdx]
|
||||
local res = e.res
|
||||
demoIdx = demoIdx + 1
|
||||
local res = DEMO[demoIdx]
|
||||
res.mobGuid, res.mobName, res.mobUnit = nil, nil, nil
|
||||
-- same fields a real event carries (C.Emit), so every display setting applies
|
||||
res.source, res.srcRgb, res.srcMulti, res.class = dSrc, dRgb, dMulti, dClass
|
||||
res.spell = (e.sn and showName) and e.sn or nil
|
||||
res.spellId = e.sn and 1 or nil
|
||||
res.mobGuid, res.mobName, res.mobUnit = nil, dVictim, nil
|
||||
if not demoAtAnchor and UnitExists("target") then
|
||||
res.mobGuid = C.ResolveGuid("target")
|
||||
res.mobName = UnitName("target")
|
||||
@@ -619,7 +836,37 @@ if CreateFrame then
|
||||
else
|
||||
spawn(res, 3)
|
||||
end
|
||||
end)
|
||||
if e.f then
|
||||
-- the GO / WAIT preview rides the burst (real label code path, even with threatGo off)
|
||||
local status, buf, need = S.DemoThreat(e.f, opt("threatGoLead"))
|
||||
S.SetThreat(status, buf, need, C.ThreatSrcName and C.ThreatSrcName(dSrc))
|
||||
end
|
||||
end
|
||||
if demoT >= DEMO_END then
|
||||
TL.forceAnchor = false
|
||||
S.SetThreat(nil)
|
||||
this:SetScript("OnUpdate", nil)
|
||||
end
|
||||
end
|
||||
|
||||
-- /asct demo plays at the nameplate of the current target (the same placement rules as real
|
||||
-- events); without a target, or with atAnchor, at the fixed anchor. A second call restarts it.
|
||||
function S.Demo(atAnchor)
|
||||
build()
|
||||
demoIdx, demoT = 1, 0
|
||||
demoAtAnchor = atAnchor and true or false
|
||||
dSrc, dClass, dRgb, dMulti = S.DemoSource(C.state)
|
||||
dVictim = UnitExists("target") and UnitName("target") or S.DEMO_VICTIM
|
||||
local key = S.DEMO_SETS[dClass or ""] and dClass or "WARRIOR"
|
||||
if not demoCache[key] then
|
||||
local l, n, e = S.BuildDemo(key)
|
||||
demoCache[key] = { l, n, e }
|
||||
end
|
||||
DEMO, DEMO_N, DEMO_END = demoCache[key][1], demoCache[key][2], demoCache[key][3]
|
||||
TL.forceAnchor = demoAtAnchor
|
||||
S.SetThreat(nil)
|
||||
if not demoTicker then demoTicker = CreateFrame("Frame", "AnySCTDemoFrame", UIParent) end
|
||||
demoTicker:SetScript("OnUpdate", demoOnUpdate)
|
||||
end
|
||||
|
||||
function S.FontStringCount()
|
||||
|
||||
Reference in New Issue
Block a user