FourEverSwingTimer

Swing timer bars for WoW: Forever — main-hand, off-hand and ranged in one moveable stack, with live combat feedback between swings.
Back to Files

v1.0.0-beta5-forever

File name4everSwingTimer-v1.0.0-beta5-forever.zip
Uploader
RalgathorRalgathor
Uploaded
Oct 14, 2026
Downloads
9
Size
124.9 KB
Flavors
Forever
File ID
9183522
Type
B
Beta
Supported game versions
  • 1.60.1

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-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 (EnableRangeCheck is armed when the feature is on); a nil answer falls through to the fallback probes - C_Spell.IsSpellInRange/C_Item.IsItemInRange at 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 native C_SwingTimer.IsTargetWithinSwingRange query for the main-hand and ranged hands (with EnableRangeCheck armed for both at start and PLAYER_SWING_RANGE_UPDATE registered, each fire printed with its payload), and the fallback signals the ref addons use - C_Spell.IsSpellInRange on a melee-band class ability and Auto Shot, C_Item.IsItemInRange on vanilla 10/15/20/30 yd band items, and CheckInteractDistance'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.md at 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; a GLOSSARY-MAP.md is 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 reason argument (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_MINOR follows). No consumer change: the library's new reason argument on START/UPDATE is appended after hand, 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 the ref/ addons / research), plus a paste-ready AssetLibrary Lua table with AtlasOK/ApplyAsset helpers that guard every atlas lookup so a renamed client atlas degrades to fallback art. Documentation only; not shipped in the package (the packager ignores docs).

Changed

  • Embedded LibClassicSwingTimerAPI bumped from v2.2.0-beta4 to the v2.2.0 stable release (LibStub MINOR 37; REQUIRED_LIB_MINOR follows). 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 ## IconTexture in the full-path form (Interface\AddOns\4everSwingTimer\Textures\icon.png - the relative Textures\icon.png form 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 in docs/, which is not shipped.
  • Ranged dynamic-haste feedback: a mid-swing ranged UNIT_SWING_TIMER_UPDATE that 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|queued and 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 - and test effects spaces 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 seven UNIT_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 in docs/ (not shipped).
  • Embedded LibClassicSwingTimerAPI bumped from v2.2.0-beta3 to v2.2.0-beta4 (LibStub MINOR 36; REQUIRED_LIB_MINOR follows): the library's stale login swing - an in-flight seed at PLAYER_ENTERING_WORLD whose 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, glow 0.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 /reload a 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 /reload until the first swing. ApplyAll applied the parked fill value (full in fill mode) but never repositioned the tick afterwards - the last UpdateTickPosition ran inside ApplySkin, before ApplyLayout had 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. SwingStop already paired its parked value with a reposition, which is why a normally landed swing looked right and only the fresh-login bar was wrong. ApplyAll now 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).