Files
Brues 5213bd5474 Migrate to ClassicAPI for macro display, auras and timing
Squash of the classicapi_next branch (10 commits). ClassicAPI is now a hard
requirement, and the Lua-side reimplementations it supersedes are gone:
net -1087 lines across 12 files.

Macro display: resolved macro actions are published through
C_Macro.SetMacroDisplay, so the client draws macro buttons and the
action-bar function overrides this addon used to install are removed.
Ownership is per macro -- ClassicAPI keeps the macros we never claim.

Auras and timing: ClassicAPI is the source of truth for non-player aura
timing via the positional C_UnitAuras.UnitAura, replacing the write-only
buff tracking tables and the overflow-slot fallback.

Cleanup: the hasPfUI76 flag and the pfUI 7.6 branches it gated, two
silently-shadowed code paths, and per-call allocations in the event and
publish paths (SPELL_CAST_EVENT merged). Packaging moves to
brues-code/packager@vCAPI.

Macro syntax: a clause may carry a leading run of [group] blocks sharing
one action, Blizzard-style -- groups are OR'd, first pass wins, and []
always passes. The ;-separated form is unchanged and mixes freely. @focus
clauses with no focus set now fail quietly instead of printing
"Invalid target" on the way past.
2026-09-11 00:30:36 -05:00

13 KiB

ClassicAPI Adoption Backlog

Opportunities to use ClassicAPI (C:\Git\ClassicAPI, see docs/API.md) in SuperCleveRoidMacros. ClassicAPI is a client mod (sibling to nampower/SuperWoW) that backports the modern C_* API into the 1.12.1 client.

Policy: ClassicAPI is a hard requirement of this fork — no fallbacks. The load-time requirement check warns when it's missing (Core.lua), the same as nampower/UnitXP. ClassicAPI-backed code may assume the API is present (light pcall/nil guards for runtime safety are fine, but do not maintain alternate non-ClassicAPI implementations). Users who don't want ClassicAPI should run the upstream addon instead.

API references are line numbers into C:\Git\ClassicAPI\docs\API.md.


Tier 1 — high impact (new capabilities, not just refactors)

1. C_UnitAuras — native aura data for any unit

  • API: API.md:8670GetAuraDataBySpellName(unit, name, filter), GetUnitAuraBySpellID(unit, spellID, filter), GetAuraDataByIndex.

  • Returns spellId, dispelName ("Magic"/"Curse"/"Disease"/"Poison"), applications (stacks), duration.

  • Unlocks: [dispellable] / [curse] / [magic] target conditionals and spellID-based (rank/locale-proof) aura matching.

  • Caveat: expirationTime is player-only — NO LONGER TRUE. This note said target/focus expirationTime was always 0, so debuff timers had to stay on libdebuff. ClassicAPI has since added the Aura::Source cache: for a non-player unit expirationTime is reconstructed from the observed SMSG_SPELL_GO, and duration is the caster-modified value (talent extensions like Improved Shadow Word: Pain included). It also handles combo-point finisher scaling automatically (Rupture at 4 CP reads 14s, no registration) and ships Carnage's roll-gated Rip/Rake refresh in the DLL (src/turtle/Carnage.cpp, exposed as RegisterAuraDurationModifierByTrigger).

  • Real remaining caveats (all best-effort, from docs/API.md):

    • Only auras observed after login carry caster/timing; older ones report expirationTime 0 and sourceUnit nil.
    • Max-stack refresh is a blind spot: re-applying at max stacks (Shadow Weaving 5→5) emits no client-visible change, so the entry elapses and evicts.
    • Out-of-range group members are spell-ID only, with applications always 1.
  • Consequence: most of libdebuff is now redundant — see "libdebuff retirement" below.

  • DONE (slice 1 — dispel-type conditionals): added ClassicAPI.lua detection module (CleveRoids.ClassicAPI, mirrors NampowerAPI's HasMinimumVersion) and the [magic]/[curse]/[disease]/[poison]/ [dispellable] conditionals + negations, backed by C_UnitAuras.GetDebuffDataByIndex(...).dispelName. No-arg booleans, honor @unit, graceful fallback (positive→false, negated→true) when ClassicAPI is absent. Purely additive — the existing ValidateAura path is untouched.

  • DONE (slice 2 — buff-side / offensive dispel): UnitHasDispelType now takes a helpful flag (scans GetBuffDataByIndex), exposed as [magicbuff]/[dispellablebuff] + negations — detect a dispellable buff to strip off an enemy (e.g. [harm,magicbuff] Dispel Magic). Magic-only is the practical set since 1.12 offensive dispel removes Magic buffs.

  • Follow-ups (still TODO):

    • spellID-based aura matching via GetUnitAuraBySpellID (rank/locale-proof), optionally wired into ValidateAura as a fast path with libdebuff fallback.
    • more reliable cross-unit stacks via applications.
    • class-aware [dispellable] (only types this character can actually remove).
    • optional name filtering on the type keywords (e.g. [magic:Polymorph]).

2. C_Item.GetWeaponEnchantInfo() — temp-enchant IDs — DONE

  • Shipped as [mhenchant] / [ohenchant] (+ no variants). Matches the applied temp enchant by SpellItemEnchantment ID or localized name — the name path resolves via C_Item.GetEnchantInfo(id).name (the ID→name table this doc assumed we lacked), so no tooltip scan. Wrappers ClassicAPI.GetWeaponEnchant / GetEnchantName; ValidateWeaponImbue now reads through the former.
  • Bare = any temp enchant; OR-lists supported ([mhenchant:2823/Deadly_Poison]).
  • Follow-up (optional): route [mhimbue:Name]'s match through GetEnchantName too, retiring the green-text tooltip scan in CheckWeaponImbueByName.

3. GetUnitSpeed(unit) + IsFalling() / IsSwimming()

  • API: API.md:8312, API.md:7629.

  • Replaces today: MonkeySpeed addon dependency (which needs SuperWoW UnitPosition) for [moving]Conditionals.lua:2981.

  • DONE: ClassicAPI.lua exposes HasUnitSpeed / GetUnitCurrentSpeed / GetPlayerSpeedPercent (100% = 7.0 yd/s) plus IsPlayerFalling (IsFalling). GetPlayerSpeed and IsPlayerMoving are ClassicAPI-only now; the boolean [moving] ORs in IsPlayerFalling so jumping/falling registers even though currentSpeed is horizontal-only.

  • Fallbacks removed (ClassicAPI mandatory): deleted the MonkeySpeed integration (HasMonkeySpeed, RequireMonkeySpeed), the Nampower PlayerIsMoving branch, and the 100 Hz position-tracking buffer in Core.lua's OnUpdate (_positionHistory, POS_TRACK_INTERVAL, UnitPosition upvalue). MonkeySpeed dropped from README "Supported Addons".

  • IsSwimming deliberately not used: swim state is true while treading water motionless, which would falsely report [moving].


Tier 2 — replace custom scanning / extra deps

GetActionInfo(slot) — action-slot → spell/macro/item mapping

  • API: API.md:489 — returns actionType, id, subType.
  • DONE: ClassicAPI.GetActionInfo wrapper added; GetActionButtonInfo (Extensions/Tooltip/Generic.lua) rewritten to use it and resolve names via GetSpellRecField / GetItem / GetMacroInfo, replacing the per-slot GameTooltip scan + texture heuristic (which couldn't tell spell from item and never recognized macros). Powers reactive-ability slot detection (IndexActionSlot). Deleted the dead SuperWoW-GetActionText copy of GetActionButtonInfo in Conditionals.lua.
  • Known limitation: GetActionInfo returns id = nil for bag-instance items (items dragged from bags rather than placed by itemID), so those slots get no name. The consumers (actionSlots for /startattack·/shoot·auto-shot proxies, reactive spells) only need spells, so this is acceptable.
  • Follow-up (optional): migrate the remaining GetActionText macro-slot detection in Core.lua / ComboPointTracker.lua to GetActionInfo too.

4. C_Spell.* / GetSpellInfo(spellID)

  • API: API.md:6649+GetSpellCooldown, IsUsableSpell, IsSpellKnown, SpellHasRange, GetSpellSchool, FindSpellBookSlotByID.
  • Replaces today: spellbook scanning via GetSpellName(i, BOOKTYPE_SPELL) (Conditionals.lua:2500) and the Slam-slot hack. Simplifies [known], [usable], range checks.

5. C_Timer.After / NewTicker (+ coroutines)

  • API: API.md:8008+.
  • Replaces today: manual OnUpdate frames (Core.lua:5082, :80, :460) and the UnitXP aura-cleanup timer (Conditionals.lua:1474).

6. UnitGUID / UnitTokenFromGUID

  • API: API.md:8206+.
  • Replaces today: SuperWoW GUIDs in ComboPointTracker (ComboPointTracker.lua).
  • Verify first: confirm GUID string format matches SuperWoW's before swapping.

7. Native focus token + FocusUnit/ClearFocus + PLAYER_FOCUS_CHANGED

  • API: API.md:2624+.
  • Replaces today: custom/pfUI focus resolution (Conditionals.lua:5799, :3148). Gives engine-backed @focus for non-pfUI users.

Tier 3 — situational

  • C_NamePlate.GetNamePlates() / nameplateN tokens (API.md:5742+) — alternative to UnitXP enemy enumeration for AoE counting (Conditionals.lua:3099).
  • GetShapeshiftFormID() + UPDATE_SHAPESHIFT_FORM (API.md:7792) — cleaner stance/form conditionals.
  • CastSpellNoToggle / numeric spellIDs in /cast (API.md:5336+) — rank-specific casting by ID, no-toggle behavior; could back a /cast <spellID>.
  • State helpers IsStealthed / IsMounted / UnitStandState (API.md:7574+) — back simple [stealth] / [mounted] / [standing] conditionals.

Cross-cutting policy

ClassicAPI is a hard requirement (see policy at the top) — ClassicAPI-backed code calls the API directly, no fallbacks. Light pcall/nil guards for runtime safety are fine; do not maintain alternate non-ClassicAPI implementations. The load-time requirement check (Core.lua) warns when it's missing, the same as nampower/UnitXP.


Conditionals.lua audit — do NOT migrate (traps)

Findings from the full Conditionals.lua audit. These look like ClassicAPI candidates but are worse than the current implementation — recorded so we don't re-investigate.

  • Auras / ValidateAuraC_UnitAuras.expirationTime is player-onlySTALE, do not trust this entry. It was written before the Aura::Source cache landed. Non-player expirationTime/duration now work (caster-modified, talent extensions included), so this is no longer a reason to keep libdebuff's remote-duration tracking. See Tier 1 §1 and "libdebuff retirement" for the current picture and the real caveats. The nampower GetUnitField batch read is a separate question and has not been re-examined.
  • GetCurrentShapeshiftIndex form loop / [stance]·[form]GetShapeshiftFormID() returns the DBC form id (Cat=1, Bear=5, Shadowform=28…), NOT the 1-based bar index these conditionals compare against. Swapping would silently break every [stance:N]/[form:N] macro. (Only the Rogue stealth branch was migrated, via IsStealthed.)
  • Cast-time scans (Slam-clip, GetSpellCastTime) — ClassicAPI/DBC cast time is base only (no haste/talents/buffs), which defeats the reason these scan the tooltip.
  • Range / distance / behind / facing — UnitXP_SP3 gives precise yards + facing; UnitInRange is a binary 40yd and has no facing/behind. Keep UnitXP.
  • FindItemLocation — nampower FindPlayerItemSlot finds an item by id/name across bags in one call; ClassicAPI has no equivalent (would need a manual C_Container bag scan). Keep nampower.
  • [known] (name+rank+talent logic richer than IsSpellKnown/IsPlayerSpell's current-rank/no-talent semantics) and [usable]/reactive (nampower IsSpellUsable == IsUsableSpell). Keep.

Conditionals.lua audit — done

  • GetItemCooldownCached → ClassicAPI GetItemCooldown(item) (slots 1-19 still inventory-based).
  • [stealth]/[nostealth]/[stl]/[nostl] + Rogue branch of GetCurrentShapeshiftIndexIsStealthed().
  • CountEnemiesMatching nameplate scan → C_NamePlate.GetNamePlateGUIDs().
  • Focus: GetFocusUnitId/TryTargetFocus use the native "focus" token; added [focus]/[nofocus]. /focus is provided by ClassicAPI's companion addon.

libdebuff retirement

libdebuff exists because vanilla cannot report debuff durations on units other than the player. ClassicAPI's Aura::Source cache now does exactly that, so most of the library is redundant. This is a staged replacement, not a delete: measure first, then remove per group.

Current footprint: 28 public lib: methods, ~726 internal references in Utility.lua, consumers in 8 files (Conditionals.lua 45, Compatibility/pfUI.lua 39, Core.lua 9). Note pfUI did not delete its own libdebuff — v9.0.25 still ships ~1695 lines of it, re-based on C_UnitAuras.GetAuraDataByIndex / GetAuraDataBySpellName. "Re-base on C_UnitAuras", not "remove", is the precedent.

Group A — replaceable by C_UnitAuras (do these first): GetDuration, GetDebuffCaster, IsOurDebuff, UnitBuff/UnitDebuff, FindPlayerDebuff/FindPlayerBuff, GetAllDebuffsOnTarget, GetCachedIcon, ApplyCarnageRefresh, and the Dark Harvest trio (ApplyDarkHarvestStart/End, GetDarkHarvestReduction, GetTimeRemainingWithDarkHarvest) — ClassicAPI ships Carnage refresh and Dark Harvest tick compression in the DLL.

Group B — no C_UnitAuras equivalent, keep: ShouldApplyDebuffRank, DidSpellFail, WasSpellReflected, DidTargetEvade, ProcessMissReason, IsPersonalDebuff, GetSpellRank/GetSpellBaseName, HasPendingCast. These are miss/rank/learning logic, not aura state.

Gate before removing Group A: confirm parity in-game on one spell where ClassicAPI does the hard part — Rip under Carnage. Compare CleveRoids.libdebuff:GetDuration(spellID) against C_UnitAuras.GetUnitAuraBySpellID(unit, spellID).duration and the derived remaining (expirationTime - GetTime()), on a target you have debuffed. If those agree across a Carnage proc, Group A can go. Watch the documented best-effort gaps: an aura cast before you logged in, and refresh-at-max-stacks.

Marginal / optional follow-ups

  • [swimming]IsSwimming() would drop the nampower-2.36 version gate + warning (same semantics). [rooted] has no ClassicAPI equivalent — keep nampower.
  • Weapon-imbue name match ([imbue:Mongoose]) → C_Item.GetWeaponEnchantInfo gives enchant IDs, so it needs an enchantID→name table we don't ship.