Commit Graph
632 Commits
Author SHA1 Message Date
Brues 7bbb8df010 remove pre pfUI 7.6 stuff 2026-09-11 00:27:05 -05:00
Brues 96dbe9aca8 Fail @focus clauses quietly when no focus is set
`[@focus,...] X` with no focus set printed "Invalid target" before falling
through to the next clause, which the new `[@mouseover][@focus][] X` idiom
turned into a message on every press. The noise came from a pre-ClassicAPI
name-based fallback in DoWithConditionals: with no native focus token,
TryTargetFocus resolved UnitName('focus') to nil and could never succeed,
so the branch only ever reached the error print. GetFocusUnitId's own
contract already says @focus should fall through silently.

Both evaluators now fail the clause quietly when GetFocusUnitId is nil,
the same way @mouseover does, so display and execution agree (TestAction
previously evaluated against "target" in that case). GetFocusName,
TryTargetFocus and pfUI's FocusNameHook were only reachable through that
path and are removed.
2026-09-10 17:35:49 -05:00
Brues 1de455f96e Support Blizzard-style bracket groups: [a][b] Spell and [] fall-through
A clause may now carry a leading run of [group] blocks sharing one action;
groups are OR'd, first pass wins, and [] always passes. The existing
;-separated single-block form is unchanged and mixes freely.

The OR loop lives in the two single-clause evaluators, DoWithConditionals
and TestAction, which expand a multi-group clause into one-group variants
and recurse. That covers every ;-split site, /castsequence (where ; is not
a separator and the sequence is keyed by the full args), /cancelaura,
/stopmacro and the editor highlight without rewriting strings, so
action.args, the ParsedMsg/Sequences caches and highlight offsets keep
their identity.

ParseMsg scans the leading group run (quote-aware) instead of the greedy
%[(.+)%], which previously turned [a][b] into the bogus key a][b and made
such clauses fail silently on both the cast and icon paths. The flag
pattern still runs on the whole clause when there are no groups, so !Spell
is byte-identical.

TestAction reports which variant passed and TestForActiveAction stores its
conditionals, so range/usable colouring follows the passing group's @unit
rather than group 1's. /target gains ; and group support; /pfcast injects
the resolved unit into every group, not just the first. The macro checker
validates every leading block, drops the EMPTY_CONDITIONAL error, and
catches a missing action after the last group.
2026-09-10 17:17:08 -05:00
Brues d93d97a469 Delete the write-only buff tracking tables and the overflow fallback
Following the aura data source to ClassicAPI exposed a chain of machinery that
nothing consumed any more.

ValidateAura's non-player overflow fallback is gone. It existed because a buff
can occupy server slots 33-48 with no client slot, so it read presence and
duration out of AllCasterAuraTracking behind a UnitDebuff slot scan that stopped
[buff:Name] matching a debuff. ClassicAPI settles this at the source: its
HELPFUL/HARMFUL filters select on each aura's real polarity flag rather than the
slot range it happens to sit in, so per docs/API.md "a debuff parked in a buff
slot still reads harmful" -- and an overflowed buff still reads helpful. The
ClassicAPI resolution already covers the case, and classifies it more accurately
than the slot-range guard did.

That removed the last caller of FindAllCasterAuraByName (67 lines), and the
player-side allBuffAuras timing lookup went with it: for the player, C_Spell...
C_UnitAuras reads expirationTime out of the engine's own player-buff table, so it
is the more authoritative source rather than a fallback.

With no readers left, three tables turn out to be pure overhead -- populated on
every buff aura event, swept periodically, cleared on removal and death, and
never read for aura state:
  - lib.allBuffAuras   [guid][name][caster] = {startTime, duration, rank}
  - lib.ownBuffCasts   [guid][name]         = {startTime, duration, spellId, ...}
  - lib.pendingBuffCasts [guid][spellId]    = {casterGuid, duration, name, time}
pendingBuffCasts only existed to correlate AURA_CAST_ON_OTHER with
BUFF_ADDED_OTHER so the other two could be filled, so removing them emptied the
BUFF_ADDED_OTHER handler entirely. That event is no longer registered, and pfUI
compat no longer unregisters an event we never ask for.

Fixes a regression from the previous commit in this series: removing the
allBuffAuras/ownBuffCasts blocks from BUFF_REMOVED_SELF and BUFF_REMOVED_OTHER
also deleted their `local spellName = C_Spell.GetSpellName(spellId)`, leaving
later `if spellName` guards reading a nil global. The OverflowBuffsByName and
AllCasterAuraTracking prunes in those branches had silently stopped running.
Both declarations are restored.

AllCasterAuraTracking itself stays: OverflowBuffFrame and the aura-tracking
writers still use it.
2026-09-10 14:40:16 -05:00
Brues 2a562e0550 Make ClassicAPI the source of truth for non-player aura timing
ValidateAura resolved remote aura timers from libdebuff's allBuffAuras and then
from AllCasterAuraTracking via FindAllCasterAuraByName. Both existed for one
reason: vanilla cannot report a timer for an aura on a unit other than the
player. ClassicAPI's Aura::Source cache does, reconstructing duration and
expirationTime for any unit from the observed SMSG_SPELL_GO -- caster-modified,
so talent extensions and combo-point finisher scaling are already applied, and
Carnage's roll-gated Rip/Rake refresh is handled in the DLL rather than mirrored
in Lua.

Replaced that ~70-line cascade with ResolveUnitAuraViaClassicAPI, a positional
C_UnitAuras.UnitAura scan over the relevant range. Matching stays ours -- by
spellID when the conditional supplied a number, else by lowercased name, since
C_UnitAuras' own by-name lookup is case-sensitive and exact and would miss
[debuff:thunder_clap]. It runs after the existing scans and overrides them, which
is what "source of truth" means here.

Also drops the libdebuff UnitBuff timeleft bug the old comments worked around
(timeleft reporting ~1000s), since nothing reads it on this path any more.

Presence and stacks from C_UnitAuras are reliable. Timing is best-effort by
design: an aura cast before login, or one refreshed at max stacks, yields no
expirationTime, and remaining is left nil so the caller keeps its found-and-0
default. A permanent aura (duration 0) reports -1, matching the existing
convention.

Still on the old machinery and not yet migrated: the non-player overflow-slot
buff path (AllCasterAuraTracking, guarded by a debuff-slot check) and the player
branches. AllCasterAuraTracking still has 56 references across 5 files, mostly
OverflowBuffFrame and Conditionals.
2026-09-10 14:40:16 -05:00
Brues daaab2fd11 Retire the hasPfUI76 flag and the pfUI 7.6 branches it gated
Assume a modern pfUI, and drop the version-tiered branching entirely: 31 call
sites across five files, plus the flag and its detector.

Note which side was actually dead. HasPfUI76() required four things -- pfUI
version >= 7.6, Nampower >= 2.40, pfUI.libdebuff_objects_guid, and
pfUI.libdebuff_casts. The last one does not exist: pfUI exports 28 libdebuff_*
tables and libdebuff_casts is not among them (libdebuff_recent_casts is a
dedup table keyed [targetGuid][spellName][casterGuid], a different shape). So
the detector returned false at any version, and it was the hasPfUI76-TRUE
branches that never ran, not the fallbacks. Removing those is behaviour-
preserving; keeping SCRM as the owner of castTracking is what the code has
always actually done.

Removed accordingly:
- lib:HasPfUI76(), both flag declarations, and the block that would have aliased
  castTracking to pfUI.libdebuff_casts and re-pointed lib.objects/iconCache.
- HookPfUILibdebuff's "7.6 handles durations internally" early return, so the
  GetDuration/AddEffect hooks below it are visibly the live path again.
- The SPELL_START_OTHER / SPELL_FAILED_OTHER unregister arm, the pfUI branch of
  GetAuraTrackingData, the pfUI76 arm of the libdebuff zone/death cleanup, the
  pfUI backing table in the /cleveroid aura dump, and two status-string suffixes.
- SyncComboDurationToPfUI (77 lines) with its two call sites: it returned
  immediately whenever hasPfUI76, and is inert once the tier split is gone.

Unconditionalised the paths that were gated on `not hasPfUI76`: the castTracking
populate/clear/sweep, InitPfUIIntegration on login, and ten libdebuff duration
lookups.

Verified no hasPfUI76 reference remains, and that per-file block balance is
unchanged from HEAD.
2026-09-10 14:40:16 -05:00
Brues 43f1ba378a use brues-code/packager@vCAPI for packaging 2026-09-10 14:40:16 -05:00
Brues 828acc0c4b Remove two silently-shadowed code paths
Both were invisible: no error, no warning, just logic that never ran.

Console.lua no longer defines the global RunMacro. Core.lua installs a hook of
the same name and loads first, so this file's name-only wrapper replaced it. What
that cost, whenever SuperMacro was absent: macro *index* arguments stopped
working (Blizzard's RunMacro accepts an index or a name, and ExecuteMacroByName
takes only a name), the delegation to SuperMacro went away, so did the fallback
to the saved Blizzard original when a macro would not resolve, and so did
clearing the stop/skip flags at the start of a top-level run -- which is what
lets /stopmacro, /skipmacro and /firstaction work across parent/child macro
boundaries. With SuperMacro loaded, Compatibility/SuperMacro.lua reassigns the
global afterwards anyway, so this definition only ever did harm.
SlashCmdList.RUNMACRO keeps using ExecuteMacroByName, which is correct there.

Utility.lua drops ClearReactiveProcOnCast and the wrappers it existed for, which
tried to clear a reactive proc when its spell was cast. Three independent bugs
meant it never worked: the wrappers guarded on CleveRoids.Frame, which Core.lua
creates after this file loads, so the guard was always false and neither wrapper
installed; the UNIT_CASTEVENT wrapper tested arg2 == "START" when that signature
is (caster, target, action, spell_id, cast_time), so it compared the target; and
both passed a spell ID to a lookup keyed by spell name. They also allocated an
arg table per call and unpack()'d it. Deleting is behaviour-preserving.

Swept for both patterns: no other duplicate global definitions, and no remaining
file-scope use of CleveRoids.Frame in a file that loads before Core.lua.
2026-09-10 14:40:16 -05:00
Brues f296425c94 Cut per-call allocations in the event and publish paths; merge SPELL_CAST_EVENT
Chasing high memory churn (~188 kb/s with pfUI). Four allocation sources and one
correctness bug found along the way.

The recurring cause: in 1.12's Lua 5.0 a function declared `function(...)` builds
a fresh `arg` table on every call. Three handlers were declared that way and none
of them read the vararg.

- Compatibility/pfUI.lua: the registered action handler. This was the dominant
  one. UpdateAllManagedCooldowns fans ACTIONBAR_UPDATE_COOLDOWN across every
  managed slot (up to 120) on each SPELL_UPDATE_COOLDOWN, so every GCD and
  cooldown tick allocated ~120 tables that the handler then discarded, because
  its whole body only ever applied to ACTIONBAR_SLOT_CHANGED. Dropped the vararg
  and added an early return before any work. Bongos' and UltimaMacros' handlers
  were already vararg-free.

- Core.lua SendEventForAction: dropped the vararg (every caller passes exactly
  one extra value) and replaced the inline "arg" .. i concatenations -- 30 per
  call across three loops -- with a prebuilt name table. The eight-branch arg.n
  fan-out collapses to one loop.

- Core.lua Frame OnEvent dispatcher: dropped the vararg. It fires for all ~48
  registered events, including the UNIT_HEALTH / UNIT_AURA / UNIT_*_GUID streams.

PublishDisplay now skips no-op publishes. Publishing is not free: the client
repaints holders of the macro through its own notifier, which returns as
ACTIONBAR_SLOT_CHANGED -> ClearAction + IndexActionSlot -> TestForActiveAction ->
publish. Publishing an unchanged value therefore tore down and re-resolved slots
for nothing and fed itself, since GetAction and IndexActionSlot both publish on
paths that fire constantly. This was the C_Macro.SetMacroDisplay regression.

Also merges the two same-named SPELL_CAST_EVENT handlers. Lua assigns in file
order, so the later definition silently replaced the earlier one and its half
never ran: channel-start detection, spell_tracking cleanup, and cast-sequence
advancement. AdvanceSequence has only two callers and the other is inside
UNIT_CASTEVENT, which is registered only under SuperWoW -- so /castsequence had
no way to advance on a successful cast for Nampower-only users.
2026-09-10 14:40:16 -05:00
Brues 4dd5326172 Drop the action-bar overrides now that ClassicAPI draws macro buttons
Removes the nine Lua replacements the macro display integration makes
redundant: GameTooltip.SetAction, GetActionCooldown, GetActionCount,
IsConsumableAction, IsUsableAction, IsActionInRange, ActionHasRange,
IsCurrentAction and GetActionTexture. The client now derives all of it from the
value published via C_Macro.SetMacroDisplay, including the drag cursor and macro
window grid that Lua could never reach, and answers range for the macro slot
itself instead of via a borrowed proxy slot. Also drops GetSlotMacroTexture,
which only GetActionTexture used.

Behavior change: a macro with a hand-picked icon now keeps that icon. The
deleted GetActionTexture substituted the active action's icon unconditionally;
the client only substitutes when the macro's own icon is the question mark.
That is the engine's rule, matching 3.3.5's macro icon getter.

Two things the integration guide expected to fall out did not, and are kept:

- GetProxyActionSlot still has six callers unrelated to macro display
  (/startattack, [stopattack], and [channeled] for Attack/Auto Shot/Shoot).
  Only three of its callers lived in the deleted range.
- Hooks.IsCurrentAction and Hooks.OriginalIsUsableAction are used outside the
  overrides they were saved for, so the aliases stay. They now simply name the
  unmodified globals. The auto-attack drift check specifically needs the real
  current-action state, which the old override could not give it.

PickupAction is untouched: it invalidates our per-slot caches on drag, which is
not display and is still needed.
2026-09-10 14:40:16 -05:00
Brues c5ddc6c313 Publish resolved macro actions to ClassicAPI's C_Macro.SetMacroDisplay
Hands ClassicAPI the action this addon resolved for each macro, so the icon,
tooltip, cooldown sweep, range and usable state come from the client instead of
from replaced action-bar globals. That also reaches the drag cursor and the macro
window grid, which Lua cannot touch, and lets the client answer range for a macro
slot directly rather than borrowing a proxy slot.

- CleveRoids.useClassicAPIDisplay feature-detects C_Macro.SetMacroDisplay rather
  than checking CLASSIC_API_VERSION, which reports a dev sentinel while the API is
  unreleased. ClassicAPIMacroDisplay tells ClassicAPI we drive macro display, which
  it otherwise stands down from when it sees this addon.
- macro.actions.macroID back-references the Blizzard index, since the update loop
  walks actions objects rather than macros. nil for SuperMacro macros, which have
  no index and so cannot be published.
- PublishDisplay sends false, not nothing, when no action matched: that claims the
  macro and shows the question mark, where silence would hand it back to
  ClassicAPI's own #showtooltip parser.
- PublishAllDisplays runs once the addon is ready and after every re-parse, since
  ClassicAPI re-evaluates nothing for us. It covers macros that aren't on a bar,
  which is what keeps the macro window grid correct. Deferred past load because
  SetMacroDisplay returns false until the player is in the world.
- ReleaseDisplays hands every macro back on DisableAddon.

The per-slot ACTIONBAR_SLOT_CHANGED fan-out is kept for the non-integrated path.
Publishing repaints every slot holding the macro through the client's own notifier,
so it is redundant when the API is present, but removing it outright would leave
buttons never repainting on a ClassicAPI build without the API.
2026-09-10 14:40:16 -05:00
liioraandBrues 9f8a5553bc feat: add /stopchanneling command (#5)
* feat: add /stopchannel command

* review: give /stopchanneling a real handler and warn when nampower can't honor it

---------

Co-authored-by: Brues <5278969+brues-code@users.noreply.github.com>
2026-09-10 14:39:16 -05:00
Brues 00caff0c9b Bump the README conditional count to match the wiki 2026-09-10 14:24:46 -05:00
liioraandBrues 080c1243b6 feat: add myspellhaste conditional (#4)
* feat: add myspellhaste conditional

* review: fix myspellhaste proxy fallback, document it, register with macrocheck

---------

Co-authored-by: Brues <5278969+brues-code@users.noreply.github.com>
2026-09-10 14:06:27 -05:00
Brues 6f78be8394 Detect the relic slot via UnitHasRelicSlot instead of hardcoded classes
IsRelicSlot no longer tests playerClass against PALADIN/DRUID/SHAMAN. It now
reads CleveRoids.hasRelicSlot, sampled once in PLAYER_LOGIN.

Also folds PerformEquipSwap's slot-18 check into the same helper. Its class list
(HUNTER/WARRIOR/ROGUE/MAGE/WARLOCK/PRIEST) was the exact complement of the relic
classes, so it becomes `not IsRelicSlot(inventoryId)` and the two lists can no
longer drift apart.
2026-09-09 12:50:36 -05:00
Brues 10ad9eb980 Add pkgmeta and luarc 2026-09-09 12:46:48 -05:00
Artur MorozovandBrues 0ef4fe1818 add support for 'button' conditional (#3)
* add support for 'button' conditional

* Route [button] through Multi, add [nobutton], register as static

- [button] now reads conditionals.button via Multi(...) instead of indexing
  _groups.button[1].values[1] directly, so OR/AND lists and repeated groups
  behave like every other argument conditional. [button:1/2] now matches left
  or right; previously only the first value was ever checked and the rest were
  silently dropped. This also removes the unguarded _groups reach-in, which
  Multi itself defends against (it checks _groups is present and falls back to
  _operators).

- Adds [nobutton:N] via NegatedMulti, following the convention that every
  conditional ships its negation (nomod, nostance, nokeydown, ...).

- Bare [button] / [nobutton] now mean "any / no mapped mouse button held",
  mirroring bare [mod]. Bare [nobutton] doubles as an "activated by a keybind
  rather than a click" test.

- Registers button/nobutton in STATIC_CONDITIONALS next to mod and keydown, so
  they are checked once up front rather than re-evaluated for every candidate
  unit during multiscan target scanning.

Still open: IsMouseButtonDown reports live physical state, so a keybound macro
has no button held and [button:N] is false for it, and whether the button still
reads down at macro-execution time depends on whether the action button fires on
mouse-down or mouse-up. Needs in-game verification.

---------

Co-authored-by: Brues <5278969+brues-code@users.noreply.github.com>
2026-09-09 02:51:51 -05:00
Brues 269c6ba67e Fix the same 0-based slot assumption in the equipment Lua fallbacks
#2 corrected IsItemInSlot's native path, but the same 0-based assumption was
still present in the two Lua fallbacks, which run on nampower < v2.18. Per
nampower's SCRIPTS.md the slot is 1-19 (16=MainHand, 17=OffHand), and
ClassicAPI's GetInventoryItemID is 1-based as well:

- GetEquippedItem's fallback called GetInventoryItemID(slot + 1), contradicting
  both its own native path and its doc comment.
- GetEquippedItems' fallback built 0-18 keys while the native path returns
  1-19, so the two branches handed back differently-keyed tables.

Neither wrapper currently has any callers, so this was latent rather than
user-visible. Also records the 1-indexed convention at the IsItemInSlot call
site, where the misleading "0-indexed" comment used to be.
2026-09-09 02:40:57 -05:00
Sami Lappalainen c7905c2a70 fix for off-by-one error (#2) 2026-09-09 02:38:57 -05:00
Brues 77c0dba2c4 Alias [mouseuse] to [cursor] via a conditional alias table
Adds CleveRoids.conditionalAliases, applied in ParseMsg so a deprecated name
is rewritten to its current keyword before anything downstream (evaluation,
_groups) sees it. This is the mechanism for renamed modifiers, which -- unlike
predicates such as [stl] -> [stealth] -- have no Keywords function an alias
could simply point at.

[mouseuse] now resolves to [cursor], routing old macros through ClassicAPI's
C_Spell.CastAtCursor / C_Item.UseAtCursor. Drops the post-cast block that cast
normally and then faked a mouse click via CameraOrSelectOrMoveStart/Stop to
place the AoE circle, along with its workaround for that call spuriously
starting auto-attack.

MacroErrorChecker seeds VALID_CONDITIONALS from the alias table, so the old
names stay valid syntax without needing a placeholder ignoreKeywords entry.
2026-09-09 02:18:36 -05:00
Brues 61bab6c734 Require ClassicAPI v1.12.1; scan dispel auras via positional UnitAura
scanDispel now reads each slot through the positional C_UnitAuras.UnitAura
(added in ClassicAPI v1.12.1) instead of the table-return GetAuraDataByIndex,
dropping a throwaway AuraData table per slot on the up-to-48-slot scan behind
the [magic]/[curse]/[disease]/[poison]/[dispellable] conditionals.

Since UnitAura is called directly with no fallback, gate on it: the load-time
check in Core.lua warns (matching its warn-don't-disable design) when
ClassicAPI is older than v1.12.1, wiring up the previously-unused
ClassicAPI.HasMinimumVersion.
2026-09-02 14:40:21 -05:00
brues-code daa7d6f2bf Merge pull request #1 from Seraphic8x2244/feedpet-pethappiness
new command /feedpet, new conditional [pethappiness]
2026-08-25 09:43:41 -05:00
Brues 6d73c6b006 Localize Feed Pet via spell ID and fix IsReactive param
- Resolve Feed Pet from spell ID 6991 (C_Spell.GetSpellName) so /feedpet works on non-enUS clients without a Localized.Spells entry
- Fix CleveRoids.IsReactive referencing undefined global spellName instead of its name parameter
2026-08-25 09:42:36 -05:00
Seraphic8x2244 d5baecaf41 new command feedpet, new conditional pethappiness 2026-08-25 09:26:59 -05:00
Brues 5c31232b52 LoadSavedVariablesFirst 2026-08-23 14:40:01 -05:00
Brues d5cae2136c added CleveRoids_LearnedDurations nil check 2026-08-23 14:33:29 -05:00
Brues 004eebc9f7 Coalesce macro/spell rebuilds to cut the login event storm
UPDATE_MACROS and SPELLS_CHANGED each did a full spell + talent + pet +
120-slot action-bar rebuild inline, and both fire several times during
login as the spellbook and macros populate - so the rebuild ran 4-6
times in the first ~2s, most of it duplicated work fanning 120 button
updates out to Blizzard/pfUI/Bongos each pass.

Defer the rebuild instead: UPDATE_MACROS, SPELLS_CHANGED and PLAYER_LOGIN
arm a 0.3s debounce (macroRebuildTime); the update loop runs RebuildMacros
once when the burst settles. RebuildMacros skips the action-bar pass until
`ready` (GetAction early-returns before then, and the +1.5s init timer
builds the bars once anyway), and BAG_UPDATE_DELAYED's bar rebuild is
gated on `ready` too. Net: IndexActionBars runs once at login (the
suppressed init-timer pass) instead of 4-6 times, IndexSpells ~2x instead
of ~5x. Runtime macro edits and bag changes still do a full unsuppressed
rebuild, just debounced.
v3.1.10
2026-08-21 21:39:11 -05:00
Brues 63b484b1ea Replace WDB tooltip warmup with GET_ITEM_INFO_RECEIVED handler
ClassicAPI hooks the global GetItemInfo to auto-warm the item cache on a
miss and fires GET_ITEM_INFO_RECEIVED when the async fill lands, so the
tooltip-scan warmup is obsolete: IndexItems's own GetItemInfo calls
already trigger the same warmup, and owned items (bags + equipped) are
priority-prefetched by the engine. The old warmup also assumed the fill
was synchronous, which no longer holds.

Drop DoWDBWarmup and its login scheduling; instead listen for
GET_ITEM_INFO_RECEIVED and run a debounced re-index. IndexItems records
owned itemIDs it could not resolve into pendingItemInfo, and the handler
ignores any fill not in that set (quest DB scans, AH sweeps, chat-link
hovers, inspects) in O(1) so unrelated bursts do not cause reindex churn.
2026-08-21 21:02:59 -05:00
Brues cf47d8caba Use SetSpellByID for tooltip v3.1.9 2026-08-06 00:33:12 -05:00
Brues 71f8f74237 Support #showtooltip spell:<id> and item:<id>
GetSpell/GetItem now resolve explicit spell:/item: ID forms. spell:<id>
prefers the player's spellbook entry via FindSpellBookSlotByID (per-rank
and pet aware) for full cost/cooldown/usability, falling back to an
id-only entry rendered via SetSpellByID for spells not in the book.
item:<id> reuses the existing numeric lookup for location-aware tooltips.

Guard the id-only path against nil spellSlot/cost in TestForActiveAction
and GetActionCooldown so display-only spell references don't crash.

Remove the dead, no-op GetSpellSlotByID stub.
2026-08-06 00:15:19 -05:00
Brues 1a98a05b7e Remove redundant local 'i' declarations
Delete unnecessary local 'i' declarations in Extensions/Mouseover/pfUI.lua (ResolvePfUnit, RegisterPartyScripts, RegisterRaidScripts, RegisterRaidMarkScripts). The for-loop headers already provide a local loop variable, so the explicit locals were redundant and could shadow variables. No functional change.
2026-08-02 15:18:48 -05:00
Brues 64e3f93042 Specifically disable pfUI mouseover hooks when unit attribute exists v3.1.8 2026-08-02 15:11:46 -05:00
Brues 0c86ed67e9 Revert "Drop redundant pfUI mouseover frame hooks; pfUI sets native mouseover"
This reverts commit c5bc69560e.
2026-08-02 15:07:35 -05:00
Brues 60c1b7f235 dont bother hooking into brues-code pfUI 2026-08-01 20:21:37 -05:00
Brues 8c10353567 removed useless GetSpellRecField checks 2026-07-30 20:41:23 -05:00
Brues fcd206756d Replace equipment cache with C_Item.IsEquippedItem
HasGearEquipped now wraps ClassicAPI's native slot-walk, which
short-circuits on first match. Removes BuildEquipmentCache, the
_equipped* cache tables/invalidation, and the InvalidateEquipmentCache
call from PLAYER_EQUIPMENT_CHANGED. [equipped] reads live engine state,
so no cache staleness surface remains; also gains item-link support.
2026-07-30 20:35:39 -05:00
Brues 8d86389a03 Remove dead IndexEquippedItems (no callers) 2026-07-30 20:28:39 -05:00
Brues 8c85ce572a Scope PLAYER_EQUIPMENT_CHANGED to the changed slot
Replace the full IndexItems + action-bar rebuild with a single-slot
IndexEquipSlot using the event's arg1 (slot) and arg2 (hasCurrent),
and drop the now-pointless throttle/deferral. Bag-side deltas remain
covered by BAG_UPDATE_DELAYED; [equipped] runs off its own cache.
2026-07-30 20:28:05 -05:00
Brues a2db3ebd42 Add indoors/outdoors conditionals via ClassicAPI v3.1.7 2026-07-30 19:59:50 -05:00
Brues 66d5f0c618 Switch to PLAYER_EQUIPMENT_CHANGED event
Replace UNIT_INVENTORY_CHANGED with PLAYER_EQUIPMENT_CHANGED in event registration and handler
2026-07-30 19:59:38 -05:00
Brues 7cf2ad95c3 Use BAG_UPDATE_DELAYED 2026-07-30 19:51:24 -05:00
Brues c5bc69560e Drop redundant pfUI mouseover frame hooks; pfUI sets native mouseover
pfUI.uf:EnableScripts binds pfUI.uf.OnEnter to every unitframe, which calls
Nampower's SetMouseoverUnit -- so the native "mouseover" token already resolves
to the hovered pfUI frame (player/target/party/raid/focus/pettarget/partytarget/
...). Every conditional consumer checks UnitExists("mouseover") before the
CleveRoids.mouseoverUnit fallback, so the 10 per-frame OnEnter/OnLeave hookers
here were pure duplication (they even chained pfUI's OnEnter).

Removed all of them. Kept RegisterRaidMarkScripts (raid-marker rows aren't
unitframes, so pfUI sets no mouseover for them -> still needed for mark1..mark8)
and HookPfCast (unrelated /pfcast conditional wrapper), plus the PfSet/PfClear/
resolve helpers the raid-mark path uses.
2026-07-30 00:28:00 -05:00
Brues ce10c9f456 Derive membership-only spell families by name, not hardcoded rank lists
Rip/Rake/Conflagrate/MoltenBlast/DarkHarvest were hardcoded {rankID=true} sets
used only for membership tests. Replaced with a metatable-backed RankSet(seedID):
t[spellID] name-matches the spellID against one seed rank via C_Spell.GetSpellName,
which resolves every rank (no enumeration) and TWoW's custom spells (confirmed
36916 -> 'Molten Blast' on the TWoW client). Read shape is unchanged, so all
consumers work untouched. Locale-safe (name derived from the seed ID). Custom
seeds absent on a stock client resolve to nil and simply never match -- correct,
those spells can't be cast there.

Left hardcoded: the pairs()-iterated sets (FerociousBite/FlameShock/Immolate),
BleedSpellIDs (Pounce cast/bleed name collision), ComboScalingSpellsByID (carries
base/increment), PounceToBleedMapping (trigger relationship), MobsThatBleed (GUIDs).
2026-07-28 12:21:38 -05:00
Brues 08dd3ba4dd Remove dead code from ComboPointTracker
PounceBleedSpellIDs, GetLastComboPointsForSpell, and GetLastDurationForSpell
were each definition-only with zero references anywhere in the addon. The
Pounce bleed IDs are still covered by BleedSpellIDs (detection) and
PounceToBleedMapping (cast->trigger).
2026-07-28 12:10:54 -05:00
Brues b1fb2af861 remove fallbacks 2026-07-28 10:39:41 -05:00
Brues 7382e8a55f Unify dispel scan on GetAuraDataByIndex with a filter
scanDispel now calls C_UnitAuras.GetAuraDataByIndex(unit, i, filter) instead of
selecting GetBuffDataByIndex/GetDebuffDataByIndex -- the two are documented as
GetAuraDataByIndex with a HELPFUL/HARMFUL filter, and the index runs within the
filtered range identically. Drops the helpful-and-A-or-B function pick.
2026-07-28 09:55:05 -05:00
Brues 37434911e4 Resolve tracked aura texture/stacks via C_UnitAuras, not slot scans
FindPlayerDebuff and FindPlayerBuff hand-scanned debuff/buff slots for a
spellID to pull icon + stacks. Both now use ClassicAPI.GetUnitAuraBySpellID
(AuraData.icon / .applications) -- FindPlayerDebuff unfiltered (walks both
ranges, was debuff-then-buff), FindPlayerBuff HELPFUL-filtered (buff-only).
Search order is immaterial since a spellID is only ever a buff or a debuff.
2026-07-28 09:20:23 -05:00
Brues da8ea22998 Match buff-based immunity via C_UnitAuras.GetAuraDataBySpellName
Replaces the 32-slot UnitBuff + C_Spell.GetSpellName name-compare loop with a
single ClassicAPI.GetAuraDataBySpellName(unit, name, "HELPFUL") lookup.
Locale-identical to the old compare (both use the localized Spell.dbc name).
Wrapper added next to GetUnitAuraBySpellID.
2026-07-28 09:16:33 -05:00
Brues d82eeabf22 Detect Banish via C_UnitAuras.GetUnitAuraBySpellID, not slot scan
The Banish immunity check hand-scanned 16 debuff slots plus 32 buff slots
(NPC overflow) for spellID 710/18647. ClassicAPI.GetUnitAuraBySpellID walks
the whole aura array in one call per rank, so it finds the debuff wherever it
lands -- including an NPC's overflow buff slots -- dropping the two loops and
the UnitIsPlayer gate. Wrapper added alongside UnitHasDispelType.
2026-07-28 09:12:29 -05:00
Brues dd61767eb9 Add [locked]/[nolocked] school-interrupt conditional via C_LossOfControl
Detects a spell-school interrupt lockout (Counterspell/Kick/Pummel/Earth
Shock) on the player -- the server-side lockout that no debuff scan can see,
since it's a SMSG_SPELL_COOLDOWN packet, not an aura. ClassicAPI's
C_LossOfControl aggregates it as SCHOOL_INTERRUPT with a lockoutSchool mask.

ClassicAPI.GetSchoolLockout() returns the locked school mask; ValidateSchoolLocked
tests a school name against it (bitmask check without the 5.1-only % operator).
[locked] = any school kicked, [locked:frost] = that school, [locked:fire/frost]
= either; [nolocked:...] negates with AND (neither). Registered in
BOOLEAN_CONDITIONALS for the bare form; LOSS_OF_CONTROL_ADDED/UPDATE wired to
refresh the icon. Full silences remain [mycc:silence].
2026-07-28 08:57:37 -05:00