v1.0.0-beta6-forever
What's new
Changelog
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
[Unreleased]
[1.0.0-beta6] - 2026-10-15
Added
- EditMode.lua (placement: EventRegistry callbacks, Blizzard selection art via NineSliceUtil with a plain-band fallback, drag, snap and preview lines, per-layout positions, reset) and EditModeDialog.lua (the panel).
- Each hand's bar is its own frame, as the native swing timers are: it has its own Edit Mode selection, drag/snap (also to the other bars), position per Edit Mode layout, and its own panel with scale, opacity, width, height, visibility, range feedback and (main hand) the queued-attack highlight. Bar style, flat bar colors and direction stay global, in the addon options (Style section), not in Edit Mode. The saved variables move to schema 3 (db.hands[hand], db.positions[layout][hand]); the older global settings and single-stack positions migrate at load. In Edit Mode every bar shows, even a Hidden one.
- Per-hand visibility (Edit Mode panel): each bar is Always, In combat, While swinging or Hidden, like the native bars; the old global mode and per-hand toggles migrate at load.
/4everswingtimer reset settings(every setting back to its default) andreset all(settings and positions); saved values are validated at load (out-of-range numbers clamped, unknown choices reset); positions of deleted Edit Mode layouts are dropped; the panel's test previews act on the selected hand.- Range feedback is on by default (per hand).
- Opacity setting (Edit Mode panel); while Edit Mode is open the bars show, parked, and range feedback is suppressed, as on Blizzard's own bars.
Changed
- Configuration moved to Blizzard's own patterns, after 4everActionTimeline's ADR-0008 (same client, its M10 probes): the bars are placed in Edit Mode and everything about the frame itself lives in a click-opened panel beside them.
- The bars are no longer a registered drag target with a Locked setting: outside Edit Mode they are click-through, and while Edit Mode is open they wear Blizzard's own selection art (blue idle, yellow selected, a label), drag, and snap to nearby Edit Mode frames and the grid, drawing Blizzard's red snap preview lines. Clicking them opens their settings panel. The bars are not a registered Edit Mode system and nothing writes into Blizzard's layout tables: every code path into the Edit Mode manager taints the raid frames' updates on this client (timeline probes E3/E6, in game), so the addon only listens for the EventRegistry's Edit Mode enter/exit and reads Blizzard's frames.
- The frame's own settings - scale, opacity (new), width, height and visibility - moved from the Settings category into the Edit Mode panel, together with a Reset position button and the swing/effect test previews that used to sit under the unlocked bars (EditModeDialog.lua, built from generic templates only, pcall-guarded; no Options button - stepping Edit Mode aside is the taint path - a note points at /4everswingtimer options).
- The position is now stored per Edit Mode layout (db.positions, UIParent top-center units so a bar's own scale never moves it, with a default entry; saved-variable schema 2 - the old single point/x/y anchor migrates at load). Switching Edit Mode layouts moves the bars to that layout's own position; /4everswingtimer reset puts the active layout back at the built-in default spot.
- The Settings category (Options.lua) keeps the per-hand bar toggles, the feedback features (queued highlight, range feedback) and the text readouts, under section headers (build 70338+ CreateSettingsListSectionHeaderInitializer through the layout's second return, nil-guarded). Settings.OpenToCategory now passes the numeric category id first - the category-object path failed on 4everActionTimeline with addon taint on Blizzard's own Settings pages.
Removed
- The Spacing setting and the single stacked layout (each bar is placed alone; the default places still stack them).
- The global "Show bars" mode and the per-hand toggles in Settings (replaced
by per-hand visibility); - The Locked setting, the unlock overlay and its test buttons (now in the
Edit Mode panel); the
lock/unlockcommands now only point at Edit Mode.
[1.0.0-beta5] - 2026-10-14
Added
Range feedback (new "Range feedback" checkbox, default off): the three- state target-distance indicator from SPEC 10.1, probe-verified on both probe characters (2026-10-14, build 70374). The probe runs split the native C_SwingTimer subsystem by class: dead on the paladin (query nil in and out of melee after EnableRangeCheck, event only its initial pushes), alive on the hunter (query booleans for both hands, event firing on every transition) - likely the ranged-weapon context arming it. The bars read the native query first (
EnableRangeCheckis armed when the feature is on); a nil answer falls through to the fallback probes -C_Spell.IsSpellInRange/C_Item.IsItemInRangeat 0.2 s - rather than reading as out, so a dead-native character's bars never stick dimmed. Melee hands fall back to a class melee-band ability (Addon.RANGE_MELEE_SPELLS, shared with the range probe command), then CheckInteractDistance's duel distance; the hunter has NO working spell probe - both candidates probe-verified useless on build 70374 (Wing Clip nil even at melee, Raptor Strike true even at 30+ yd) - so the native query is its melee answer and duel distance the fallback. Dead targets gate to neutral: the second probe run showed the native query and every range probe reading nil on a corpse while CheckInteractDistance reads in-range, which would otherwise paint a dead target as melee-in. The ranged hand falls back to the class's own ranged attack spell - Auto Shot for hunters, Shoot for the wand classes - resolved once at load, with the 30 yd band item only the no-spell last resort: the level-5 hunter run showed the band item carries no dead zone (it reads in-range inside the min range) and, with the native query nil on that character, masked the close state entirely - out-of-range and ranged-range worked while the dead zone stayed silent until the spell probes took over. Presentation: while a state is active the fill takes the DELAY amber tint, applied through the base fill funnel (GetBaseFillColor/ ApplyFillStyle) so effect tints override the state and fade back into it; the state word - "Too far" or "Too close", WHITE with a black drop shadow - reads right AFTER the hand name ("Main Hand Too far"), readable on the dark parked-bar background and on the amber fill alike (red proved unreadable on the dark background), and the delta readout yields its slot until the state clears. Entering a state (or switching far/close) plays the interrupt treatment's SHAKE once (the PlayShake helper, extracted so the interrupt and the range entry share the exact animation and ShakeStrengthUI gate): motion is the peripheral catch, and the decaying sway reads without resizing the bar - the delay anti-pop scaled it, which read unwanted in game. Entry only; clearing the state is its own confirmation (the in-game verdict on the iterations: the red interrupted ART too flashy, the muted grey art not catchable peripherally, the red word alone not working). With "Show label" off only the hand name hides - the word is its own text and shows. Friendly or absent targets and classes without a melee probe keep the bars neutral./4everswingtimer proberange: an on-demand range-signal probe for the swing-timer range question (SPEC 10.1's blocked out-of-range candidate). While on, it polls once per second against the current target and prints one sample line with every candidate signal: the nativeC_SwingTimer.IsTargetWithinSwingRangequery for the main-hand and ranged hands (withEnableRangeCheckarmed for both at start andPLAYER_SWING_RANGE_UPDATEregistered, each fire printed with its payload), and the fallback signals the ref addons use -C_Spell.IsSpellInRangeon a melee-band class ability and Auto Shot,C_Item.IsItemInRangeon vanilla 10/15/20/30 yd band items, andCheckInteractDistance's duel distance. Every API is nil-guarded, so missing ones read "gone"/"none" and the probe doubles as a census of what exists on the current build. Signal changes record RANGEPROBE lines into the trace SavedVariables (no recording gate), so the walk-in/walk-out session persists for the range-indicator decision: native path if Blizzard fixed the wiring, ref-addons' probe path otherwise.GLOSSARY.mdat the repo root: the canonical ubiquitous language for both repos (swing model, secrecy, event and signal taxonomy, capture rig, project terms). One glossary exists today; aGLOSSARY-MAP.mdis added only if a second glossary ever appears.
Changed
Internal cleanup, no behavior change: the range feedback's rationale had accumulated across five overlapping comment blocks through the design iterations; each topic now has one canonical comment (the presentation with ApplyRangeState, the signal evidence in SPEC 10.1 and here) and the pointers replace the duplicates. The probe sample's target-status expression became an explicit branch chain. Verified: syntax and a bytecode audit for nil-global reads both clean, no code paths changed.
In-game finding (beta client 70374): a cast completing right after the swing landed - the bar sitting at full - played no reset effect, because the reason dispatch guarded the named-reset interrupt on the swing being in flight and the reset START arrives with nothing in flight. A named "reset" now plays the interrupt treatment regardless of bar state: the swing is reset either way, and the bar restarting from empty is the reset. "clip" keeps the guard (its STOP precedes the clip START, so the CLIPPED event's treatment covers the parked case).
SwingStart now classifies feedback off the library's new
reasonargument (library 2.3.0, LibStub MINOR 39): a named "reset"/"clip" START fires the red interrupt, the combat-enter off-hand re-anchor ("attack") stays quiet instead of flashing red on attack-toggle stutter, a named "movement" ranged UPDATE fires the amber delay and a named "haste"/"parry" UPDATE the teal pop. This removes the window-precedence misread where a Rapid Fire-class rescale landing inside the 0.3-0.7 s reschedule window read as a movement delay. The heuristics demote to the fallback for library builds without the reason argument (below the gate's MINOR 38 the bar is disabled entirely, so in practice they are defense in depth); the stop-grace speed comparison keeps serving the "swing"-reason and no-reason paths alike. The effect test morphs now pass the real combat reasons, so the previews exercise the reason dispatch instead of the fallback tree.Embedded LibClassicSwingTimerAPI bumped from the v2.2.0 stable release to v2.3.0 stable (LibStub MINOR 39;
REQUIRED_LIB_MINORfollows). No consumer change: the library's newreasonargument on START/UPDATE is appended afterhand, past the arguments the bars read. The bump picks up the duplicate-dispatch fix (two embedded copies no longer fire every event twice), the swing-timer cancellation at PLAYER_ENTERING_WORLD / PLAYER_TARGET_CHANGED (no stray STOP cancelling a fresh swing), no target swings on Forever, the Multi-Shot / Aimed Shot no-reset for weaving hunters, and the Rapid Fire mid-swing rescale. The stable release is the beta with no code changes, and the library verified the load-bearing pieces in game on it: the reason argument, the single-frame dispatch and the duplicate-dispatch two-copy test (an addon embedding MINOR 37 loaded alongside) - so this addon's reason dispatch now rides verified ground. Still unverified upstream: the Multi-Shot / Aimed no-reset and the Rapid Fire rescale factor (capture plan docs/M1_SESSION_SCRIPT.md B8).docs: SPEC 10.5 and the probe list updated from the 2026-10-04/05 investigation sessions (build 70205; the full record and evidence live in the library repo: HASTE_APPLICATION_FINDINGS section 7.1 item 4, the M1_SESSION_SCRIPT probes B4-B6, and docs/TRANSITION_PARITY_PLAN.md). Resolved: probe 3 (PLAYER_SWING is player-only), probe 6 (by-name aura presence is dark mid-combat), probe 7 (GetAuraCount does not exist; GetComboPoints reads plain mid-combat) - which opened the UNIT_AURA/UNIT_ATTACK_SPEED transition-parity lead, filed as probe 8 and verified end to end on the rogue: SnD applications fire the attack-speed event at the cast instant, fades fire mid-combat at the expiry, refreshes fire nothing, and the fade fire is the fix for the one-swing expiry lag. BENCHMARK's ForeverSwing seal caveat and section-8 item 10 updated to the resolved verdicts. Remaining captures: warrior (Flurry) and hunter (Deadly Aspects).
Fixed
- The "Flat bar colors" tooltip names the haste effect color teal (it read "haste green" since the haste feedback moved to teal).
[1.0.0-beta4] - 2026-10-02
Added
docs/ASSET_LIBRARY.md: a curated catalog of client-side assets the addon can draw on — bar fills, chrome, tick/spark/glow art, client fonts, settings-panel bits — each tagged by verification status (in use / Forever-verified via theref/addons / research), plus a paste-readyAssetLibraryLua table withAtlasOK/ApplyAssethelpers that guard every atlas lookup so a renamed client atlas degrades to fallback art. Documentation only; not shipped in the package (the packager ignoresdocs).
Changed
- Embedded LibClassicSwingTimerAPI bumped from v2.2.0-beta4 to the v2.2.0
stable release (LibStub MINOR 37;
REQUIRED_LIB_MINORfollows). For this addon: with the delta text enabled, the off-hand bar no longer shows a meaningless delta (e.g.-21.63s) for a few seconds after the attack is toggled on - the library now reports a delta only while both hands are swinging. The library's death reset also parks each stopped hand before its STOP (the bars already followed the STOP, so nothing changes on screen). The library release closes its Forever verification items: dual-wield off-hand anchoring and the death reset verified on the Forever beta, plus a Classic Era regression pass.
[1.0.0-beta3] - 2026-10-01
Added
- The addon now ships an icon:
Textures/icon.png(the logo's icon variant), referenced from the TOC's new## IconTexturein the full-path form (Interface\AddOns\4everSwingTimer\Textures\icon.png- the relativeTextures\icon.pngform did not resolve on the Forever beta client), so the addon list and the Settings AddOns tab show the project logo instead of a blank tile. The logo sources remain indocs/, which is not shipped. - Ranged dynamic-haste feedback: a mid-swing ranged
UNIT_SWING_TIMER_UPDATEthat shortens the shot's landing (a Rapid Fire-class rescale, via the library's dynamic-haste rescale) now fires the teal haste pop instead of silently re-anchoring. The movement-reschedule window keeps precedence - a ~0.5 s recast still reads as the amber delay. The haste test preview accordingly covers the ranged bar too, its fake-swing morph nudged past the reschedule window so it lands through the same shortened-landing path.
Changed
- Effect test previews (
/4everswingtimer test interrupt|haste|delay|queuedand the unlocked overlay's effect buttons) now reproduce the full event signature on a fake swing instead of painting the treatment on the bars' current state. With no swing in flight the same fake 2.0 s swing as the plain test command starts first, scoped to the effect's own hands, and the effect lands through the real detection paths: the interrupt restarts the swing mid-flight like a cast reset, the haste shortens the swings - the parry UPDATE on the melee hands, the dynamic-haste rescale on ranged (the bars re-anchor at the early landing) - the delay replays the movement-cancel retry at the ranged swing's own landing - the bar completes, cannot fire, pulls back and lands ~0.5 s later - and the queued highlight rides a swinging main-hand bar until the swing lands and consumes it. On a live combat swing the plain treatment fires, as before - test data never rewrites live state. The queued-state poll no longer resets the fake preview within 0.2 s - it is held off until the preview clears - andtest effectsspaces the four previews one swing apart. Also fixed the first effect trigger after a reload: a bar the library leaves active with a landed swing at login no longer reads as a live swing, so the preview starts its fake swing over the stale state instead of firing the plain treatment on it (previously the effect triggers only worked after a plain Swing test had overwritten the stale bar). - The event trace (
/4everswingtimer trace) now records all sevenUNIT_SWING_TIMER_*callbacks instead of only UPDATE, so a full session's library behavior is capturable from the SavedVariables trace file - e.g. a START or seeded info at login whose landing never produces a STOP. The upstream report for that library issue and the record of its library-side resolution (fixed by a parked login seed, released in the library's v2.2.0-beta4) both live indocs/(not shipped). - Embedded LibClassicSwingTimerAPI bumped from v2.2.0-beta3 to v2.2.0-beta4
(LibStub MINOR 36;
REQUIRED_LIB_MINORfollows): the library's stale login swing - an in-flight seed atPLAYER_ENTERING_WORLDwhose landing the engine can never complete, the issue this addon reported (docs/upstream-stale-swing-report.md) and live-verified fixed (docs/library-stale-swing-fix-report.md) - now parks instead, so no consumer ever receives the uncompletable swing. This addon's own stale-landing convergence stays as the defense for any swing that ends without a STOP on any path or client. - The delay burst is now the haste pop's anti-version. It previously mirrored the pop - anchoring the bar's right side and kicking its left edge out, which read as a leftward lunge; it now keeps the haste pop's own trailing anchor and inverts the scale, so the bar flinches IN and its leading edge is yanked back - the delay reads as time pulled back rather than a second, mirrored lunge. The amber streak still flies backward, the fill tint, spark and glow are unchanged, and the effect inverts with the fill mode exactly as before.
- The haste feedback color is now teal (tint
0.10/0.95/0.65, glow0.05/0.90/0.55), previously green: green reads as health in peripheral vision (heal text, health bars, absorbs) before the bar's shape resolves, so a green pop could be misread as a heal gain; teal keeps the flash distinct via its blue channel while staying far from the warm effect colors (red, amber, yellow) and biased turquoise so the hue jump off the curated palettes' blues on the off-hand and ranged bars still reads. The fill tint, spark, streak, burst and halo all take the new color together. - Stale landings now converge even while the bars are hidden. The
stale-landing self-park lived only in the anchor's OnUpdate, and a hidden
frame receives no OnUpdate - so at a first-time login (out of combat, UI
locked, nothing visible) a login-seeded never-landing swing sat un-parked
and its tick stayed at the bar's far left from the initial anchor until
the first real swing. The convergence now also runs on a 0.5 s C_Timer
ticker (
SweepStaleLandings), independent of frame visibility, parking the bar- value, tick and text paired - before anything is ever shown. Parked bars additionally re-pair their value and tick on every visibility pass, so whatever an init-time pass could miss converges at the next entering-world / equipment / combat pass instead of depending on one call site's ordering.
Fixed
- After a login or
/reloada bar could sit stuck at its swing's end - the tick parked at the fill's edge and the time reading 0.0 - until the next real swing. The shipped library build (v2.2.0-beta3, LibStub MINOR 34) seeds an "in flight" swing at login that the engine can never complete (auto-attack does not survive a reload), so its expiration passes without the STOP that parks a bar; the library-side fix rides its unreleased MINOR 35 (see docs/upstream-stale-swing-report.md). The addon now converges such a bar to the parked state itself once the landing is stale- the expiration has been in the past for over 1 s, a grace that outlasts the ranged movement retry (~0.5 s, so a moving Auto Shot still fires its amber delay burst at the library's re-anchor) and the engine-vs-timer race at a natural landing (whose STOP parks the bar long before the grace elapses). No interrupt feedback fires on the convergence; the bar simply reaches the parked state the missing STOP owed it.
- The parked tick sat at the far left of the bar after a login or
/reloaduntil the first swing.ApplyAllapplied the parked fill value (full in fill mode) but never repositioned the tick afterwards - the lastUpdateTickPositionran insideApplySkin, beforeApplyLayouthad sized the bar (zero width, early return) and before the value change - so the tick stayed at its creation-time LEFT-edge anchor instead of the fill's edge at the "ready" position.SwingStopalready paired its parked value with a reposition, which is why a normally landed swing looked right and only the fresh-login bar was wrong.ApplyAllnow repositions the tick for every bar it re-parks.
[1.0.0-beta2] - 2026-10-01
Changed
- Embedded LibClassicSwingTimerAPI bumped from v2.2.0-beta2 to v2.2.0-beta3: the library's Forever parry fix (ApplyParryHaste) is now in the shipped build, so the real-time parry-haste path - the library's mid-swing UNIT_SWING_TIMER_UPDATE at the player's parry - is active instead of the early-landing fallback. Also picks up the library's dynamic-haste rescale on successful dynamic-family casts and its restricted-content (secret GUID/spell ID) error fixes.
[1.0.0-beta1] - 2026-10-01
Added
- Initial v1 for WoW: Forever: main-hand, off-hand and ranged swing bars driven by the embedded LibClassicSwingTimerAPI, with combat feedback modeled on the 12.x casting bar - a cast clip tints the fill red, shakes the bar and flashes an outer glow; a parry-haste shortening bursts the bar forward - with the fill's direction of travel, so the burst inverts in drain mode - a green speed streak fired from the fill's edge plus a green halo behind the bar; a movement-delayed Auto Shot fires the same burst backward in amber - an amber streak and halo; a paused swing gets a paused tint; a queued next-melee ability (Heroic Strike, Cleave, Raptor Strike, Maul) tints the main-hand fill the cast bar's yellow. Plus an optional main/off-hand delta text and a one-time notice when the game's own swing timer bars are also enabled.
- Parry-haste detection: fires in real time at the parry from the library's mid-swing UNIT_SWING_TIMER_UPDATE when the embedded library includes the parry fix; falls back to early-landing detection (an in-flight STOP followed by a same-speed START) for library builds without it. In-flight stops are classified after a short grace period into early landings, weapon swaps or interrupted swings.
- Bar configuration: three visibility modes mirroring the native timer (While swinging / In combat / Always), a Native skin reusing the client's ui-swingtimerbar-* atlases (label text-shadow and insets match the game's own swing timer) plus a Flat skin with curated color palettes, fill/drain direction, bar height (14 px floor, so the fill and text never collapse), and label/time text that scales with the bar. Per-character settings via the 12.x Settings API.
- Slash commands and test tooling: /4everswingtimer (alias /everswing) with unlock, lock, reset and test subcommands - "test interrupt|haste|delay| queued" triggers each treatment without waiting for combat and "test effects" plays them all - plus a test-bars button and a row of effect buttons on the unlock overlay.
- /4everswingtimer trace: verbatim beta-probe capture recording PLAYER_SWING, UNIT_COMBAT, the player's spellcasts and the library's UNIT_SWING_TIMER_UPDATE with millisecond timestamps into SavedVariables (persisted at logout or /reload), with session markers and the client build number so captures can be read from disk and attributed to a build. Design decisions and the verification checklist: docs/SPEC.md.
Changed
- Internal cleanup, no behavior change: deduplicated the bar-visibility refresh in the event dispatch (Core.lua), extracted the shared center-glow overlay and pip sizing in the bars (Bars.lua), and dropped the settings proxy helper's unused slider-bounds arguments, which duplicated the bounds already passed to CreateSliderOptions (Options.lua).
This mod has no additional files