Skip to content

v0.3.0

Choose a tag to compare

@github-actions github-actions released this 28 May 20:01

Quiver

v0.3.0 (2026-05-28)

Full Changelog Previous Releases

  • Bump version to 0.3.0
  • Widen auto-shot bar to 102px and lower position by 4px
    Bar now spans from the traps button center (210°) to the food button
    center (330°) at radius 59 — both x-extents are ±51px. Offset changed
    from -4 to -8 to sit a smidge lower below the sphere.
  • Auto-shot bar: cyan color on speed proc, gold when normal
    Bar turns cyan when UNIT_ATTACK_SPEED fires with a faster speed (Rapid
    Fire, haste trinkets, Bloodlust etc.) and reverts to gold when speed
    returns to normal. speedBoosted flag resets on START_AUTOREPEAT_SPELL
    and PLAYER_ENTERING_WORLD so the color is always in sync.
  • Add auto-shot swing timer bar
    Gold bar below the sphere tracks time to next Auto Shot. Counts down
    from weapon speed to 0, resets on each Auto Shot proc. Clips to 0.5s
    window when Aimed Shot begins casting. Hides outside combat or without
    a target. Movement detection uses PLAYER_STARTED/STOPPED_MOVING events
    to avoid per-frame GetUnitSpeed; module reference cached as a file-local
    upvalue to keep the OnUpdate path allocation-free.
  • Remove Stings module; fix aspect color update delay
    Stings is unused and unlikely to be needed soon — removed from toc, Core,
    Sphere (sting bar + UpdateStingDisplay), and deleted the module file.
    Aspect delay fix: add UNIT_SPELLCAST_SUCCEEDED to the raw event frame.
    Aspects are instant casts so this fires immediately when an aspect is cast,
    catching the change before UNIT_AURA eventually fires for some other buff.
    UPDATE_SHAPESHIFT_FORM and UNIT_AURA are kept as fallbacks.
  • Aspects: use raw WoW frame for event registration to avoid AceEvent collision
    Quiver:RegisterEvent keys handlers by (addon_object, event). Stings registers
    UNIT_AURA after Aspects, silently replacing it. Aspects now uses a dedicated
    CreateFrame to register both UNIT_AURA and UPDATE_SHAPESHIFT_FORM independently,
    so both events fire regardless of registration order.
    Restore Aspects:Disable() in Core:Disable() to unregister the raw frame events.
  • Fix aspect detection: shapeshift API + correct event, fix UNIT_AURA handler collision
    Two root causes:
    1. AceEvent-3.0 keys handlers by (addon, event). Stings:Enable() registers
      UNIT_AURA after Aspects:Enable(), silently replacing Aspects' handler.
      Aspects now uses UPDATE_SHAPESHIFT_FORM instead, which doesn't conflict.
    2. GetShapeshiftFormInfo returns (icon, active, castable, spellID) — no name.
      Previous UnitBuff scan was looking at buff auras, which may not include
      aspects (they are shapeshifts in TBC). Detection now uses GetShapeshiftForm +
      GetShapeshiftFormInfo + GetSpellInfo to resolve the active aspect by spell ID,
      with a UnitBuff fallback for clients where aspects surface as regular buffs.
  • Fix aspect detection event and food orbit count not updating after feeding
    Aspects: replace UPDATE_SHAPESHIFT_FORM with UNIT_AURA (aspects are regular
    buffs in TBC Anniversary, not shapeshifts, so the shapeshift event never
    fires). Remove the prev != current guard in DetectCurrentAspect so
    UpdateColor is always called and the no-aspect pulse fires on load.
    Food orbit counter: split BAG_UPDATE_DELAYED into two handlers. BAG_UPDATE
    (fires per slot, cheap RefreshFoodOrbitCount) covers the orbit count when
    the picker is closed; BAG_UPDATE_DELAYED (fires once, expensive GetSuitableFood
    scan) covers the picker refresh when it is open. This fixes the count not
    updating after quick-feeding via the orbit button if BAG_UPDATE_DELAYED was
    unreliable in this scenario.
  • Fix aspect detection: use UNIT_AURA and always refresh sphere color
    UPDATE_SHAPESHIFT_FORM does not fire for aspects since they are regular buffs
    in TBC Classic Anniversary, not shapeshifts. Switch to UNIT_AURA filtered to
    the player unit.
    Remove the prev != current guard in DetectCurrentAspect so UpdateColor is
    always called after a scan. This fixes the no-aspect pulse not appearing on
    load (prev and current were both nil so the guard skipped the update).
  • Replace Aspects poller and Stings UNIT_AURA with updateInfo-filtered event handlers
    Aspects was polling UnitBuff every second via an OnUpdate ticker, generating
    15-20 AuraData table allocations/sec that outpaced Lua 5.1's incremental GC.
    Stings fired a full UnitDebuff scan on every UNIT_AURA without filtering.
    Both now use UNIT_AURA with updateInfo filtering: skip the scan entirely when
    the update contains no aspect/sting changes. Falls back to a full scan when
    updateInfo is nil (older Classic clients) or isFullUpdate is set.
    Added ASPECTS_BY_NAME and STINGS_BY_NAME O(1) lookup tables to replace the
    inner ipairs loops in DetectCurrentAspect and ScanTarget.
    Removed Aspects:Disable() (no ticker to stop) and the Core:Disable() call.
  • Fix memory leaks: stop tickers on disable, reuse config panel, drop UNIT_AURA
    • Mana/Aspects/Pet tickers stored as self.ticker so Core:Disable() can stop them
    • Config panel built once and refreshed on open rather than recreated each time
    • UNIT_AURA dropped for aspect detection (10-20 fires/sec in combat, each allocs
      an updateInfo table); 1s poll already handles it
    • BAG_UPDATE -> BAG_UPDATE_DELAYED (fires once after bag settles, not per slot)
    • UpdateTriggerReadiness guards SetScript with _lastSelected to avoid closure churn
    • Aspects:DetectCurrentAspect gates UpdateColor on actual aspect change
    • TENTHS_STR lookup table replaces string.format in 0.1s trap ticker hot path
    • GetContainerItemID fast path in pet food scan before full GetContainerItemInfo
  • Reduce trap cooldown ticker allocation rate
    cdTicker: use 0.5s interval when trap menu is closed (was always 0.1s).
    Menu-open path keeps 0.1s for smooth expansion-button text. This cuts
    UpdateTrapCooldowns calls from 10/sec to 2/sec when menu is closed.
    PopulateMenu: cache b._normalTex = b:GetNormalTexture() after SetNormalTexture.
    UpdateTrapCooldowns: use b._normalTex / triggerBtn._normalTex instead of
    calling GetNormalTexture() on every tick. On some WoW client builds
    GetNormalTexture() allocates a fresh userdata wrapper each call; caching
    eliminates that source entirely. triggerBtn._normalTex is initialised lazily
    on first UpdateTrapCooldowns call so it works without a Sphere.lua change.
    Display text: show tenths only for sub-1s remaining; integer (math.ceil)
    above that. string.format is now only called for the last second of a
    cooldown instead of the last 10 seconds, reducing string churn 10x.
  • Trap trigger button: keep countdown live when menu is closed
    UpdateTrapCooldowns now manages cdTicker lifetime itself:
    • If maxRemaining > 0 and ticker is not running, start it (so the trigger
      button countdown stays current without depending on SPELL_UPDATE_COOLDOWN
      firing between GCDs).
    • If maxRemaining == 0 and menu is closed, stop it.
      HideAll calls UpdateTrapCooldowns after clearing activeMenu so the ticker
      restarts immediately if a trap is still cooling, eliminating the freeze-then-
      resume gap that occurred when closing the menu while a cooldown was active.
      Per-button visual updates remain gated behind menuOpen so the extra ticker
      cycles when the menu is closed are cheap (trigger button only).
  • Fix pet ring visibility; reduce UpdateTrapCooldowns overhead
    Sphere.lua: Move petRing from BACKGROUND to BORDER layer. Both petRing and
    the sphere bg texture (bg) were at BACKGROUND; draw order within the same
    sublayer is non-deterministic so the ring could render behind the sphere.
    BORDER is guaranteed to sit above BACKGROUND, making the happiness ring
    reliably visible.
    Menus.lua: UpdateTrapCooldowns is called on every SPELL_UPDATE_COOLDOWN
    (fires frequently in combat) AND every 0.1s when the trap menu is open.
    Previously it re-called GetSpellCooldown for each of 5 buttons even though
    Traps:UpdateCooldowns had just read and stored that data. Now it reads from
    Traps.cooldowns directly. Also gates per-button visual updates (SetText,
    SetDesaturated, cdFrame, cdDim, cdText) behind an activeMenu == traps check
    so that work is skipped entirely when the trap menu is closed.
  • Memory: pre-allocate trap cooldown tables and pulse color constants
    Traps.lua: Initialize now seeds self.cooldowns with one {start,duration}
    table per trap. UpdateCooldowns updates those tables in-place instead of
    replacing them on every SPELL_UPDATE_COOLDOWN event. This event fires
    every GCD in combat; the old code discarded and re-created 5 tables each
    time, generating continuous GC pressure.
    Sphere.lua: Define PULSE_COLOR_MANA_LOW and PULSE_COLOR_NO_ASPECT as
    module-level constants. UpdateColor (called every second via the aspect
    ticker plus on every UNIT_AURA for the player) now assigns these constant
    references instead of allocating a fresh {r,g,b} table on each call.
  • Fix frame memory leak: pool and reuse menu buttons instead of recreating
    RebuildAll fires on PLAYER_ENTERING_WORLD, SPELLS_CHANGED, and
    PLAYER_REGEN_ENABLED. The old PopulateMenu created ~52 new WoW frames
    per call (26 buttons + 26 blockers) and dropped references to the
    previous ones. WoW cannot destroy frames — they accumulated as hidden
    C-side objects for the entire session, causing steady memory growth
    visible after every zone change.
    Fix: NewMenu pre-allocates a fixed pool of CreateBlankButton() frames
    sized to #entries. PopulateMenu now resets and reconfigures the same
    pool frames on every rebuild instead of creating new ones. Cooldown
    frames (cdFrame/cdDim/cdText) are created once per pool slot on first
    use via a nil-guard and reused thereafter.
    Frame count is now fixed at Initialize time:
    aspects 7 + pet 5 + traps 5 + tracking 9 = 26 buttons + 26 blockers
  • Fix ghost right-clicks on hidden menu buttons (#27)
    Hidden SecureActionButtonTemplate buttons intercept right-clicks even at
    alpha=0. EnableMouse is a protected operation on secure frames and cannot
    be toggled during combat lockdown.
    Fix: each popup menu button gets a companion non-secure blocker frame
    anchored over it (same strata, one frame level higher, EnableMouse=true).
    The blocker is shown when the button is invisible and hidden when the menu
    opens — normal frames can be shown/hidden freely during combat lockdown,
    so this works in all states without tainting the secure button.
  • Fix cdText nil guard; consolidate food selection into nested table
    UI/Menus.lua:
    • UpdateTrapCooldowns: guard now checks triggerBtn.cdText as well as
      cdDim before accessing either — previously cdDim guard passed but
      cdText access was unconditional
    • Food selection keys consolidated from three sibling flat keys
      (menuSelections["food"], ["foodID"], ["foodIsBuff"]) into a single
      nested table (menuSelections.food = {name, itemID, isPetBuff}).
      Eliminates collision risk with menu names and makes the saved state
      self-contained. Existing saves with old flat keys are silently dropped
      (food re-selects on next picker use — acceptable for a UI preference)
  • Remove dead CastSpellByName helpers and unused saved var keys
    All casting goes through SecureActionButtonTemplate attributes; the
    module-level Cast() wrappers were dead code that would taint the UI if
    called during combat lockdown.
    Removed:
    • Aspects:Cast(), Traps:Cast(), Stings:Cast(), Tracking:Cast()
    • Pet:CallPet(), DismissPet(), RevivePet(), MendPet()
    • Tracking:Cast() also wrote char.lastTracking which was never read back
    • Core defaults: lastAspect (never written) and lastTracking (never read)
  • Fix ammo low threshold crossing; gate OnUpdate tickers
    Modules/Ammo.lua:
    • Add wasLow flag: OnAmmoLow now fires once on threshold crossing, not
      on every BAG_UPDATE while below threshold
    • Skip warning when count == 0 (no ammo pouch equipped)
      UI/Menus.lua:
    • selectionTicker starts hidden; shown when a deferred SetAttribute is
      queued, hidden again once the write fires — no longer runs every frame
    • cdTicker moved to module level, starts hidden; shown when the trap
      menu opens, hidden when any menu closes — previously ran unconditionally
      calling GetSpellCooldown x5 every 0.1s for the entire session
  • Fix food sort order; macro bindings on sphere; passive spell filter
    Modules/Pet.lua:
    • Fix GetItemInfo destructuring: itemLevel was capturing itemQuality (slot 3)
      instead of itemLevel (slot 4), causing all common food to tie and shuffle
    • Add name as stable tiebreaker so same-ilvl items never reorder on open
      UI/Menus.lua:
    • Filter passive spells out of knownSpellCache so they don't appear as
      castable entries in menus
    • Show Feed Pet spell icon on orbit button when no food is selected
      UI/Sphere.lua:
    • HasBinding helper supports both spell and macro binding types
    • Error messages updated to 'action' wording to cover macro bindings