Commit Graph
92 Commits
Author SHA1 Message Date
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.
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 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 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 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
Brues 2e8ed33a6a focus/focustarget is provided by classicapi now 2026-07-28 00:02:45 -05:00
Brues 403567b381 Scan nameplateN unit tokens for /target candidates, not SuperWoW WorldFrame
The /target candidate search's last SuperWoW-gated block walked WorldFrame
children and scraped GUIDs via pcall(frame.GetName, frame, 1). ClassicAPI now
provides modern nameplateN unit tokens that work with UnitExists/UnitCanAttack/
TargetUnit directly, so it collapses to iterating nameplate1..40 -- no SuperWoW
dependency, no WorldFrame hackery, and it also picks up default vanilla
nameplates. Scans the full range since assigned slots can be sparse.
2026-07-28 00:00:51 -05:00
Brues 7ecb66a8dc Add left/right modifier variants to kmods via ClassicAPI
kmods gains lctrl/rctrl, lalt/ralt, lshift/rshift (ClassicAPI's side-specific
IsLeft*/IsRight* key checks), so [mod:lshift]/[mod:ralt] and castsequence
reset=lctrl work. mod/nomod now use ClassicAPI's IsModifierKeyDown instead of
OR-ing the three individual checks.
2026-07-27 22:46:18 -05:00
Brues 494aced5bd Resolve [mhimbue:Name] via enchant DBC, not tooltip scan
CheckWeaponImbueByName now resolves the applied temp-enchant ID to its
localized name via ClassicAPI (C_Item.GetEnchantInfo) and matches that
directly -- exact and locale-clean, replacing the green-text tooltip
heuristics for named enchants (poisons, oils, Mongoose, Windfury). Only
nameless enchants (sharpening/weightstones, no DBC name) fall through to
the tooltip scan, so no regression.
2026-07-27 22:44:13 -05:00
Brues 4f76255f73 Mark GetWeaponEnchantInfo TODO item done 2026-07-27 22:42:47 -05:00
Brues fd8050aee1 Add [mhenchant]/[ohenchant] weapon temp-enchant conditionals
Uses ClassicAPI's modern C_Item.GetWeaponEnchantInfo (12-tuple with
enchant IDs) plus C_Item.GetEnchantInfo (ID -> localized name) to match
WHICH temporary weapon enchant is applied -- by SpellItemEnchantment ID or
name -- not just that one exists. Locale-proof and exact, unlike
[mhimbue:Name]'s green-text tooltip scan.

ClassicAPI.lua gains GetWeaponEnchant(slot) (centralizes the tuple
indexing, vanilla-global fallback) and GetEnchantName(id). ValidateWeaponImbue
now reads through GetWeaponEnchant. New keywords mhenchant/nomhenchant/
ohenchant/noohenchant support bare (any enchant), single ID/name, and
OR-lists ([mhenchant:2823/Deadly_Poison]); registered in BOOLEAN_CONDITIONALS
for the bare form and auto-added to VALID_CONDITIONALS via Keywords.
2026-07-27 22:42:09 -05:00
Brues 0e07eafbf9 Add [equipset]/[noequipset] conditionals for equipment sets
Checks whether a saved ClassicAPI equipment set is currently equipped, via
C_EquipmentSet.GetEquipmentSetInfo's isEquipped flag (wrapped as
ClassicAPI.IsEquipmentSetEquipped). Named [equipset] rather than [set] to avoid
colliding with the existing tier/item-set piece-count conditional. Supports
OR-lists ([equipset:Raid/Farm]) and negation ([noequipset:PvP]). Pairs with the
conditional /equipset command: swap with one, branch on the other.
2026-07-27 22:32:01 -05:00
Brues 6aaa8a409b Add conditional support to /equipset via ClassicAPI equipment sets
ClassicAPI registers a bare /equipset that resolves a name and calls
C_EquipmentSet.UseEquipmentSet with no conditional layer. DoEquipSet routes
it through DoWithConditionals like the other /equip* commands, so it gains
[combat]/[mod]/@unit/etc. and ';'-separated fallthrough (first set whose
conditionals pass wins). Console.lua reclaims the EQUIP_SET handler (keeping
ClassicAPI's localized aliases) and falls back to registering /equipset itself
if the API command isn't present.
2026-07-27 22:28:09 -05:00
Brues 2cf4886b01 Add [mana]/[rage]/[energy] type-specific power conditionals
Read a SPECIFIC power slot (Enum.PowerType) as a percentage via ClassicAPI
UnitPower/UnitPowerMax, rather than the unit's primary power. Adds mana/
mymana, rage/myrage, energy/myenergy (no-prefix = target, @unit-overridable;
my = player), each supporting operators and multi-comparison like [power]/
[mypower]. Works cross-form (druid mana in Cat) and cross-unit -- the
standout being [@target,mana:<15] to catch a caster near OOM. A unit with no
such pool (max <= 0) fails rather than reading 0%. Registered in the macro
error checker (needsArgs + operator hint); VALID_CONDITIONALS auto-populates
from Keywords.
2026-07-27 20:10:20 -05:00
Brues 77f985a927 Read HP deficit via ClassicAPI UnitHealthMissing
ValidateHpLost now gets the health deficit in one call through
CleveRoids.ClassicAPI.UnitHealthMissing, mirroring how ValidatePowerLost
uses UnitPowerMissing, instead of hand-computing max - current via
NampowerAPI.
2026-07-27 19:48:36 -05:00
Brues 7c71f8fc26 Use ClassicAPI UnitPower* for power reads; drop GetUnitField/SuperWoW paths
Add ClassicAPI UnitPower/UnitPowerMax/UnitPowerMissing/UnitPowerType wrappers
and route the power conditionals through them:
- ValidatePower/ValidateRawPower/GetCachedPlayerPower(Percent) use UnitPower /
  UnitPowerMax (omitted type = primary power, matching the old UnitMana path).
- ValidatePowerLost uses UnitPowerMissing (one call vs max - current).
- powertype/nopowertype use ClassicAPI.UnitPowerType.

Replace the SuperWoW "2nd return of UnitMana = caster mana" druid hack in
ValidateDruidRawMana and the #showtooltip OOM check with UnitPower(unit, 0),
which reads the mana slot directly and survives shapeshift. Verified in Cat
Form: UnitPower('player', 0) returns caster mana while UnitPower('player', 3)
returns energy.

Remove the now-dead Nampower GetUnitField-based GetUnitPower/GetUnitMaxPower
wrappers and POWER_FIELDS tables (no caller passed a powerType, so that path
never ran).
2026-07-27 19:27:25 -05:00
Brues efc98bcd6a Use ClassicAPI GetSpellBonusDamage/Healing for spell-power conditionals
Migrate the healing/*_power/spell_power conditionals off Nampower's
GetSpellPower to ClassicAPI GetSpellBonusDamage(school) / GetSpellBonusHealing.
Both read the same client field, so the per-school damage values are identical
while dropping the Nampower v2.31 gate.

Fixes healing/healingpower: GetSpellPower returns no healing value, so they were
returning the 2nd school (holy spell damage) as a stand-in for +healing.
GetSpellBonusHealing returns the real (derived) +healing.

Prune the now-unused Nampower GetSpellPower wrapper, feature-table entry, and
feature flag.
2026-07-27 18:57:58 -05:00
Brues 41d9283117 Use IsInGroup/IsInRaid for group checks
Replace numeric GetNumPartyMembers/GetNumRaidMembers checks with IsInGroup() and IsInRaid() in Conditionals.lua for the group and nogroup conditionals.
2026-07-27 18:45:02 -05:00
Brues 9bebbbb836 Fix addon initialization order with guard checks
Refactor mouseover extensions to split initialization into two phases: OnLoad (empty) and OnAddOnLoad (actual setup). This ensures hooks are only registered when the target addon's globals are defined. Add guard clauses to check for required functions before hooking. Also remove Cursive's premature initialization attempt.
2026-07-27 14:32:12 -05:00
Brues 586c51b53a Detect bleeds authoritatively via Spell.dbc mechanics
Add a GetSpellSchool bleed check (Priority 0.5) backed by DBC SpellMechanic
data instead of damage-event learning / name patterns:
- GetSpellMechanicByID == 15 catches spell-level bleeds (Garrote, Rupture,
  Rend, Rip, Pounce, Deep Wounds).
- New ClassicAPI GetSpellEffectMechanics catches effect-level bleeds that the
  spell-level field misses -- Rake is spell-level 0 with EffectMechanic[2]=15.

Wrap C_Spell.GetSpellEffectMechanics in ClassicAPI.lua (nil-guarded so older
builds fall back to spell-level only). Verified against the client Spell.dbc.

Also drop Hemorrhage from the bleed name-pattern fallback: the DBC gives it no
bleed mechanic (physical damage), so bleed-immune mobs don't resist it -- it was
a false positive. Existing learning / split-damage / patterns remain as fallback.
2026-07-26 15:40:26 -05:00
Brues 2ce4d11698 Use ClassicAPI EventUtil for addon-load/login extension wiring
Replace hand-rolled ADDON_LOADED/PLAYER_LOGIN handlers with
EventUtil.ContinueOnAddOnLoaded / ContinueOnPlayerLogin, which fire
immediately if the event already happened -- removing the "we loaded before
the target addon and missed its ADDON_LOADED" workarounds.

- pfUI compat: ContinueOnAddOnLoaded("pfUI") + ContinueOnPlayerLogin; drops
  the missed-event fallback (login path still re-runs SetupCompatibility).
- MacroErrorUI / MacroLengthWarn: ContinueOnAddOnLoaded("Blizzard_MacroUI"),
  folding their manual "already loaded" checks.
- 9 Mouseover extensions (ag_UnitFrames, CT_RaidAssist, CT_UnitFrames,
  DiscordUnitFrames, Grid, NotGrid, Cursive, sRaidFrames, PerfectRaid):
  ContinueOnAddOnLoaded("<AddonName>", OnLoad). Since immediate-fire passes no
  event args, the old `arg1 == "X"` checks are replaced by the addon-name gate
  (global guards kept where present); also removes the buggy
  UnregisterEvent("ADDON_LOADED", "Onload") no-ops that never fired.

Addon names match file names; drops support for renamed folders (e.g. -master).
2026-07-26 14:53:34 -05:00
Brues e594fda295 Use C_Spell.GetSpellTexture in GetCachedIcon 2026-07-26 14:20:14 -05:00
Brues 22633aa16c Replace GetSpellRecField name/rank reads with C_Spell equivalents
Swap 166 call sites from GetSpellRecField(id, "name") to
C_Spell.GetSpellName(id) and GetSpellRecField(id, "rank") to
C_Spell.GetSpellSubtext(id) across Core, Conditionals, Utility,
ComboPointTracker, CursiveCustomSpells, pfUI, OverflowBuffFrame, and
Generic. Guard forms and the _GetSpellRecField alias calls collapse to the
direct C_Spell call.

GetSpellRecField stays for fields with no C_Spell equivalent (school,
spellIconID, mechanic, effectMechanic, effectApplyAuraName, stackAmount,
rangeIndex) and inside the NampowerAPI wrapper layer.
2026-07-26 14:07:31 -05:00
Brues 6718a01d79 Add [mounted]/[nomounted] and [standing]/[sitting] conditionals
Back them with ClassicAPI IsMounted() and UnitStandState("player").
mounted/nomounted are player mount state; standing = stand state 0,
sitting = any non-standing pose (its complement). Player-only, registered
in Keywords, BOOLEAN_CONDITIONALS, and STATIC_CONDITIONALS like [stealth].
2026-07-26 13:44:16 -05:00
Brues 1770f7b6e1 Make SendChatMessage reclaim cycle-safe and cover LVL
The previous reclaim stack-overflowed when LeafVillageAchievements loaded
after us (its captured "original" was our own function, so reclaiming onto
the top created an ours<->LVA loop). Add a reentrancy guard plus a base
SendChatMessage reference so a hook cycle routes straight to the base
function instead of recursing.

Gate the reclaim on LeafVillageAchievements OR LeafVillageLegends (both hook
SendChatMessage and can bump ours off the top -- LVA orphans it, LVL wraps
and re-installs on timers). Keep the anchored ^#showtooltip match since being
on top means we see the pristine line.
2026-07-24 14:09:36 -05:00
Brues d060b04908 Reclaim SendChatMessage hook so #showtooltip stays filtered
Our #showtooltip filter hooks the global SendChatMessage. An addon that
snapshots SendChatMessage at its own file-load and later calls that
snapshot directly (e.g. LeafVillageAchievements at PLAYER_ENTERING_WORLD+3s)
orphans our hook if it loaded before us -- its snapshot predates our filter,
so #showtooltip leaks to chat. This bites the fork specifically: it sorts
after LeafVillageAchievements alphabetically, so it loads too late to be in
the snapshot, whereas upstream "CleveRoidMacros" sorted before it.

Make the filter a named function and add EnsureSendChatMessageHook, which
re-asserts it as the outermost SendChatMessage hook (no-op once on top).
OnUpdate calls it each throttled tick via a cheap identity check, so we
reclaim the top of the chain within a frame of being displaced. Normal
messages still flow through the chained-over hook untouched.
2026-07-24 09:41:26 -05:00
Brues 6ac69501f5 pass base object rather than ItemLocation 2026-07-20 10:35:09 -05:00
Brues 782c685fba Replace tooltip-scanning GetSpellCost with ClassicAPI DBC reads
GetSpellCost now reads power cost and reagents straight from Spell.dbc via
C_Spell.GetSpellPowerCost (effective, talent-modified cost) and
C_Spell.GetSpellReagents (itemID), dropping the GameTooltip owner/scan
frames, their font strings, and all locale-dependent line parsing.

Reagent counting is now itemID-based end to end: GetReagentCount takes an
itemID and matches by id in the Items cache / bag scan. This removes the
hardcoded English reagent tables (_ReagentBySpell, _ReagentIdByName) and
the name-matching bag-scan tooltip, so it works on any client locale.
Verified in-game that GetSpellReagents covers DBC reagent spells (Vanish
-> Flash Powder), which the hand table previously special-cased.

The localized reagent name is still used for countedItemTypes registration
(recognizing a reagent item placed on the action bar); when that name
isn't cached yet, warm it via the ClassicAPI Item mixin
(Item:CreateFromItemID/ContinueOnItemLoad) and register it once it lands.
2026-07-19 16:34:19 -05:00
Brues 97e898f14c Add /cancelform command and CancelShapeshiftForm
Adds a new /cancelform slash command (Console.lua) wired to CleveRoids.DoCancelForm. Replaces the previous _unshiftAction with _cancelFormAction that calls CancelShapeshiftForm, and updates DoUnshift to delegate to DoCancelForm for backwards compatibility. Also fixes a unit name check by replacing GetUnitName with UnitName in DoRetarget.
2026-07-19 14:32:13 -05:00
Brues b84835a567 Use GetInventoryItemID instead of string parsing 2026-07-17 21:25:25 -05:00
Brues 40cf2c8010 More ClassicAPI porting 2026-07-15 19:45:31 -05:00
Brues a5a0b9c00a Adopt ClassicAPI C_Item.GetItemName in Utility.lua equip-modifier debug
Replace the debug-only GetInventoryItemLink name scrape with the
decorated C_Item.GetItemName location form.
2026-07-15 19:36:18 -05:00
Brues 6e57ff01dc Adopt ClassicAPI C_Item item reads in Tooltip/Generic.lua indexers
Migrate the link-scrape-then-discard sites to direct ClassicAPI reads:

- IndexEquippedItems / IndexEquipSlot: GetInventoryItemID for the id
  (GetItemInfo still supplies the stored base link/texture)
- IndexItems bag+equip scans: GetContainerItemID/GetInventoryItemID +
  C_Item.GetItemName (decorated) for the dedup fast-path; drop the now
  dead GetContainerItemLink/GetInventoryItemLink locals
- GetItemFast cache validation: alloc-free id compare via
  GetInventoryItemID/GetContainerItemID, decorated-name compare otherwise
- IsItemEquipped: GetInventoryItemID + C_Item.GetItemName

Left untouched: makeInventoryItem/makeBagItem and the GetItem/GetItemFast
scan loops, which build a real link that is stored on the item and later
fed to GameTooltip:SetHyperlink.

Note: the equipped-scan loops now start at slot 1 (GetInventoryItemID
requires slot>=1), so the ammo slot (0) is no longer walked by these
indexers -- ammo is not referenced by name anywhere in the addon.
2026-07-15 19:33:07 -05:00
Brues 9cd79d682d Adopt ClassicAPI C_Item item reads in Conditionals.lua
Replace GetInventoryItemLink/GetContainerItemLink link-scraping with
direct ClassicAPI reads across the item conditionals:

- [equipped] cache build: GetInventoryItemID + C_Item.GetItemName
  (decorated name replaces the old bracket-name / GetItemInfo two-step)
- slot-number -> item name resolves ([cd], [usable]/[nousable]):
  C_Item.GetItemName location form
- HasItem / GetItemCooldown substring fallbacks: match the decorated
  name instead of the raw link string
- numeric-slot presence check: GetInventoryItemID

Suffix decoration is preserved via the location form, so partial/
suffixed [equipped:...] matches behave as before.
2026-07-15 19:17:00 -05:00
Brues f54eda5ae3 Adopt ClassicAPI C_Item/C_Container item reads in Core.lua
Replace GetInventoryItemLink/GetContainerItemLink link-scraping (built
solely to regex out item:ID or the bracketed name) with direct ClassicAPI
reads:

- id/presence: GetInventoryItemID, C_Container.GetContainerItemID
- suffix-sensitive names (equip-by-name on gear): C_Item.GetItemName
  location form, which now carries random-suffix decoration
- suffix-free names (consumables/reagents/poisons) in hot bag scans:
  id + C_Item.GetItemNameByID (base name, zero table allocation)

Also covers /use equipped-slot resolution and the WDB warm scans. Drops
the now-dead GetContainerItemLink/GetInventoryItemLink/string_find
upvalue aliases. No behavior change; equip-by-name keeps full suffixed
matching via the decorated location form.
2026-07-15 19:12:56 -05:00
Brues 653f8db3d7 Reconcile /startattack against real action-bar state, not cached flag
The autoAttack flag could drift stale-true (optimistic set after
AttackTarget, or a target dying without PLAYER_LEAVE_COMBAT), making
/startattack skip the attack until the target was dropped and reselected.
The old fallback called the overridden IsCurrentAction, which just echoes
the cached flag for the attack slot, so it never detected drift. Use the
original Hooks.IsCurrentAction as ground truth and sync the flag to it.
2026-07-04 19:38:37 -05:00
Brues 86fb0254e6 Show autoattack icon and glow 2026-06-30 12:34:34 -05:00
Brues fc640abb07 Render known-spell action tooltips via spellbook slot, not spellID
GameTooltip.SetAction used GameTooltip:SetSpellByID for spell actions, which
renders the static DBC tooltip. For spells we resolved from the player's own
spellbook, use GameTooltip:SetSpell(spellSlot, bookType) instead so the
tooltip shows live player-accurate data (mana cost, cooldown, range coloring,
reagent counts). spellSlot/bookType/id all come from the same spellbook index
entry, so it renders the identical spell and rank. SetSpellByID remains as a
defensive fallback for entries without a slot. Applied to both the direct and
nested-macro tooltip paths.
2026-06-23 12:41:17 -05:00
Brues 3384765faa dont need pcall around registering events 2026-06-22 13:04:05 -05:00
Brues ceaf7c2c43 Refresh [moving] macro icons on movement via ClassicAPI
Register PLAYER_STARTED_MOVING (ClassicAPI, edge-detected off the WASD/
autorun key state) and queue an action update so [moving]/[nomoving] macro
icons repaint when movement starts.

PLAYER_STOPPED_MOVING is deliberately NOT used -- it's key-release based and
misses real stops (running into geometry, click-to-move, roots, knockback).
Instead, STARTED kicks off a 0.1s C_Timer.NewTicker that watches
IsPlayerMoving() (the same speed>0/falling signal [moving] evaluates) and, on
the actual stop, refreshes once and cancels itself. No timer exists while
stopped, so there's no idle cost; guarded on isShuttingDown and event-driven
mode (realtime==0).
2026-06-22 12:58:59 -05:00
Brues 963ad21297 Show resolved icon for question-mark macros in the macro list
Macros with no chosen icon (e.g. "#showtooltip Shoot") display Blizzard's
default question mark in the macro UI. After each MacroFrame_Update, swap
that placeholder for the icon the action bar would show, resolved from the
macro's #showtooltip/first action via GetMacroByIndex.

- Only replaces icons that are currently the question mark, so user-chosen
  icons are never touched.
- Covers both the list buttons (MacroButtonNIcon) and the selected-macro
  detail icon.
- Purely cosmetic: never changes the saved icon; Blizzard repaints the
  default on the next refresh if resolution fails or the macro changes.
- Installed in MacroErrorUI's existing hook path (gated on Blizzard_MacroUI
  load and the SuperMacro guard).
2026-06-22 00:35:19 -05:00
Brues ae09b23afd Resolve item sets via ClassicAPI; drop Reliquary dependency
Item-set membership and set info now read ItemSet.dbc directly through
ClassicAPI instead of the nampower/Reliquary glue, so set features no
longer require the Reliquary DLL to be installed.

- ClassicAPI.lua: add GetItemSetIDByID and GetItemSetInfo wrappers.
- Utility.lua: repoint ResolveSetItems, CountEquippedSetItemsBySetId,
  GetEquippedItemSetInfo, and GetEquippedSetPieceCount at the ClassicAPI
  accessors. Set-name matching now uses the DBC name (localized; identical
  to the old enUS path on English clients).
- NampowerAPI.lua: remove the now-unused GetItemSet/GetItemSetId/
  GetItemSetItems/GetItemSetBonuses, the dead GetSpellEffectRadius (no
  callers, only radius source was Reliquary), and the orphaned RQ_SafeCall.
- Init.lua: drop hasReliquary detection and its startup feature line.
- Conditionals.lua: update the [set:] comment (no longer Reliquary-gated).
2026-06-21 14:29:28 -05:00
Brues a8bf7fbc0f Identify equipped weapon type by item subclass, not localized name
HasWeaponEquipped matched the localized GetItemInfo subtype string, which
required a hand-maintained translation of all 13 weapon/shield subtypes per
locale plus brittle "^Fist"/last-word string parsing.

Use the locale-independent class/subclass IDs from C_Item.GetItemInfoInstant
instead:
- Init.lua: WeaponTypeNames entries now carry numeric class + subClass set
  (Axes/Swords/Maces span their 1H+2H subclasses; Shields are armor class 4).
- Conditionals.lua: HasWeaponEquipped resolves the slot's itemID via
  ClassicAPI GetInventoryItemID, then compares classID/subClassID -- no link
  parsing, no string munging.
- Localization.lua: drop the now-unused weapon-subtype strings from every
  locale block (112 lines).
2026-06-21 14:16:56 -05:00
Brues f0f57330c5 Drop dead localization entries
Remove localization data with no consumers anywhere in the addon:
- ItemTypes tables (Consumable/Reagent/Projectile/Trade Goods) - unused
- SpellRank patterns - unused (the live GetSpellRank is a BuffLib method)
- Spells keys Revenge, Overpower, Riposte, Surprise Attack, Lacerate,
  Baited Shot, Counterattack, Arcane Surge - unused; only Shadowform,
  Stealth, Prowl, Shadowmeld are referenced.

133 lines removed across all locale blocks; no behavior change.
2026-06-21 14:12:33 -05:00
Brues f21f90d0f1 Adopt ClassicAPI accessors: cursor, item IDs, spell mechanic
Lean on ClassicAPI's backported readers instead of link-string parsing
and a hand-maintained mechanic table.

- ClassicAPI.lua: add wrappers GetCursorInfo/CursorHoldsItemID,
  GetContainerItemID, GetInventoryItemID, and GetSpellMechanicByID.
- Core.lua: in the manual equip fallback, verify the cursor holds the
  intended item (CursorHoldsItemID) before EquipCursorItem, aborting only
  on a definitive mismatch so empty/unknown cursors behave as before.
- NampowerAPI.lua / Utility.lua: replace GetContainerItemLink /
  GetInventoryItemLink + "item:(%d+)" parsing with direct C_Container/
  inventory ID lookups at the sites that only need the itemID; route the
  equipped-set scans through the GetEquippedItemID helper.
- Conditionals.lua / Utility.lua: replace the 785-entry CCSpellMechanics
  fallback table with C_Spell.GetSpellMechanicByID in GetSpellMechanic and
  GetSpellCCType (BuffLib / nampower paths unchanged); delete the table.
2026-06-21 14:02:49 -05:00
Brues d435d90871 useless comment 2026-06-21 03:38:50 -05:00
Brues af7e575d4a Set version to dynamic 2026-06-21 03:21:59 -05:00
Brues 0d910e81f1 Create release.yml 2026-06-21 03:21:46 -05:00
Brues 9f10dab560 Use ClassicAPI CastSpellNoToggle for the ! anti-toggle prefix
Unify both ! mechanisms (bare !spell gate-injection and bracketed
[cond] !spell dispatch skip) on ClassicAPI's CastSpellNoToggle, which
no-ops auto-repeat (Shoot/Auto Shot/Wand) and toggle auras
(forms/stances/aspects/seals) instead of toggling them off.

- Bare !spell now just sets conditionals.noSpam (no injected gate
  conditional); removed spamConditions and GetSpammableConditional.
- Dispatch: melee Attack still uses AttackTarget (CastSpellNoToggle
  doesn't cover the melee swing). For everything else under !, keep the
  ValidatePlayerBuff anti-refresh skip (CastSpellNoToggle only covers
  true toggle auras, not regular self-buffs like Mark of the Wild),
  otherwise CastSpellNoToggle. This is more reliable for auto-repeat
  than the old CheckChanneled path.

CheckChanneled is retained (still used by the [channeled]/[checkchanneled]
conditionals).
2026-06-21 02:55:40 -05:00
Brues a561d0ed20 Make cursor a [cursor] modifier instead of an @cursor token
Per design preference, drop the @ and the ParseMsg special-casing:
[cursor] is now a normal modifier keyword (added to ignoreKeywords,
mirroring [mouseuse]) rather than a pseudo unit token. The cast/use
dispatch still routes through ClassicAPI's CastAtCursor / UseAtCursor
(checked via conditionals.cursor). Reverts the @cursor ParseMsg branch.
2026-06-21 02:36:09 -05:00
Brues b9895471f0 Add @cursor token for ground-target spells/items
@cursor places a ground-target spell or on-use item at the cursor via
ClassicAPI's engine-level placement instead of the [mouseuse] reticle-
click simulation (so no auto-attack side effect).

- ParseMsg flags @cursor as conditionals.atCursor rather than setting a
  bogus "cursor" unit target, so it doesn't affect help/harm/exists/range
  checks or pass an invalid token to CastSpellByName.
- DoWithConditionals routes atCursor casts through C_Spell.CastAtCursor
  (spellID resolved from the spell index / GetSpellIdForName) and item
  uses through C_Item.UseAtCursor. Both fall back to a normal cast/use
  for non-ground actions.

New syntax, so existing macros are unaffected; MacroErrorChecker already
accepts @cursor.
2026-06-21 02:34:12 -05:00
Brues a220dbd003 [swimming] via ClassicAPI IsSwimming; drop nampower 2.36 gate
Add ClassicAPI.IsSwimming wrapper and route [swimming]/[noswimming] and
the swim/noswim aliases through it. Removes the Nampower v2.36
hasPlayerIsSwimming version gate and the "requires Nampower 2.36"
warning (ClassicAPI's IsSwimming is always available). Same semantics.
2026-06-21 02:17:19 -05:00
Brues 416a716d8c Remove dead AuraScanTooltip frame
The AuraScanTooltip scanning frame was only used by CancelAura's legacy
tooltip path, which was removed when CancelAura moved to ClassicAPI's
C_Spell.CancelSpellByID. Nothing references it now.
2026-06-21 02:13:12 -05:00
Brues d4df0a3b26 CancelAura via C_Spell.CancelSpellByID; stop [focus] no-focus warning
- CancelAura: cancel matched buffs through ClassicAPI's
  C_Spell.CancelSpellByID instead of nampower CancelPlayerAuraSpellId
  and the legacy slot-based CancelPlayerBuff + tooltip-scan path. The
  buff scan (SuperWoW GetPlayerBuffID / nampower raw GetPlayerAuraDuration
  + overflow tracking) and the boolean return are preserved, so the
  ~spell cancel-vs-cast toggle still works. CancelSpellByName isn't usable
  here because it reports no match.
- Focus: GetFocusUnitId no longer warns when no focus is set. In a
  fallback macro ([@focus] as one alternative) that's a normal state;
  the clause now silently falls through like any unresolved @unit.
  Dropped the unused warn parameter and updated both callers.
2026-06-21 02:10:52 -05:00
Brues f926ac5521 Replace custom/UnitXP timers with ClassicAPI C_Timer
- ScheduleTimer now wraps C_Timer.After, removing the dedicated timer
  frame + per-frame OnUpdate loop + timers queue.
- Aura-tracking cleanup -> C_Timer.NewTicker(5) (_auraCleanupTicker);
  drop the UnitXP arm, the named global CleveRoids_AuraTrackingCleanupTimer,
  and the inline "not hasUnitXP" fallback in OnAuraCastOther.
- Libdebuff cleanup -> C_Timer.NewTicker(30) (_cleanupTicker); drop the
  UnitXP arm and CleveRoids_LibDebuffCleanupTimer.
- Shutdown cancels the tickers instead of UnitXP timer disarm.

Cleanup now always runs (was gated on hasUnitXP) and no longer depends
on UnitXP threaded timers or named global callbacks. Tickers self-guard
on isShuttingDown and are cancelled on shutdown.
2026-06-21 01:59:54 -05:00
Brues 75a6371f52 keep active local 2026-06-21 01:57:29 -05:00
Brues cca94fc9d7 Record Conditionals.lua ClassicAPI audit (wins + do-not-migrate traps)
Document the audit results in the backlog: the do-not-migrate traps
(C_UnitAuras expirationTime player-only, GetShapeshiftFormID != bar
index, DBC cast time base-only, UnitXP range superiority, nampower
FindPlayerItemSlot, [known]/[usable] richer than ClassicAPI), the wins
already done, and the marginal follow-ups. Also correct the stale
"graceful fallback" cross-cutting note to the mandatory/no-fallbacks
policy.
2026-06-21 01:52:14 -05:00
Brues a9c031538b Use native ClassicAPI focus token; add [focus]/[nofocus]
ClassicAPI exposes a native "focus" unit token (set via its own /focus
/ FOCUSTARGET keybind), accepted by every UnitX call.

- GetFocusUnitId falls back to the native "focus" token after the pfUI
  emulated-focus check, so @focus / [cond:focus] / focus range checks
  now work for non-pfUI users (previously focus only worked via pfUI).
- TryTargetFocus uses TargetUnit("focus") for an exact switch before the
  fragile name-based TargetByName path.
- Add [focus]/[nofocus] conditionals (GetFocusUnitId ~= nil), covering
  both pfUI and native focus; registered as boolean conditionals.

No addon /focus command is needed: ClassicAPI's companion addon already
registers /focus and /clearfocus.
2026-06-21 01:50:34 -05:00
Brues 5d5b46b0a6 Adopt ClassicAPI in Conditionals: item cooldown, stealth, nameplates
- GetItemCooldownCached: resolve via ClassicAPI GetItemCooldown (by item
  id/name) instead of FindItemLocation + nampower GetItemIdCooldown +
  link parsing; equipment slots (1-19) still query the inventory slot.
- [stealth]/[nostealth] and [stl]/[nostl], and the Rogue branch of
  GetCurrentShapeshiftIndex, now use ClassicAPI IsStealthed() (covers
  Rogue Stealth + Druid Prowl) instead of class-gated localized buff
  lookups. Note: [nostealth] is now true for non-stealth classes.
- CountEnemiesMatching nameplate scan uses C_NamePlate.GetNamePlateGUIDs()
  instead of walking WorldFrame children and extracting GUIDs via
  pcall(frame.GetName).
- Add ClassicAPI.IsStealthed / GetNamePlateGUIDs wrappers.
2026-06-21 01:05:28 -05:00
Brues af38410249 use GetItemCooldown from classicapi 2026-06-21 00:56:52 -05:00
Brues c710ccf832 Identify action-bar macros by slot/index; drop name restrictions
Resolve a slot's macro by its Blizzard macro index via ClassicAPI's
GetActionInfo instead of GetActionText -> name -> GetMacro, so action-bar
macros no longer depend on the macro name.

- Core.lua: GetAction uses GetActionInfo(slot) -> macro index ->
  GetMacroByIndex; falls back to the name path for SuperMacro (no index).
  ParseMacro split into a shared BuildMacro + ParseMacroByIndex (caches by
  index) + ParseMacro(name). Added GetMacroByIndex. The macro cache is now
  keyed by index for action-bar macros, so blank/duplicate names no longer
  collide. GetMacroIndexByName remains only for genuine name references
  ({MacroName}, /runmacro, /macrocheck).
- MacroErrorUI.lua: remove the now-obsolete macro-name warnings (duplicate,
  blank, spell-conflict, item-conflict) from both the live editor validation
  and the on-close report; delete the unused name-set helpers.
- README: update the known-issue to note only name-referenced macros need
  unique names.
2026-06-21 00:30:30 -05:00
Brues 3267db6be6 Resolve macro icon via ClassicAPI GetActionInfo (no name round-trip)
Replace the three duplicated GetActionText -> GetMacroIndexByName ->
GetMacroInfo blocks in the GetActionTexture hook with a single
GetSlotMacroTexture helper that gets the macro slot directly from
ClassicAPI's GetActionInfo. More robust (no dependence on macro-name
lookups) and DRYs the fallback logic.
2026-06-21 00:15:34 -05:00
Brues 73e8938d76 Use ClassicAPI for action-bar mapping, spell IDs, and dynamic pfUI icons
GetActionInfo:
- Add ClassicAPI.GetActionInfo wrapper; rewrite GetActionButtonInfo
  (Generic.lua) to use it and resolve names via GetSpellRecField /
  GetItem / GetMacroInfo, replacing the per-slot GameTooltip scan +
  texture heuristic. Powers reactive-ability slot detection.
- Delete the dead SuperWoW-GetActionText copy of GetActionButtonInfo.

Spell IDs in the index:
- IndexSpells now uses ClassicAPI GetSpellInfo(slot, bookType), whose
  10th return is the spellID, so every Spells entry carries .id (one
  call also replaces GetSpellName + GetSpellTexture).

Dynamic pfUI action-button icons (pairs with the pfUI fork change):
- GameTooltip.SetAction hook renders spells via ClassicAPI
  SetSpellByID(spell.id) instead of SetSpell(spellSlot, bookType) +
  manual rank text. Items stay location-based (instance data).
- Drop the redundant GetActionSpellSlot -> GetSpellCooldown cooldown
  shim in the pfUI handler; pfUI now routes through the hooked
  GetActionCooldown when it defers macro scanning to us.
2026-06-21 00:11:46 -05:00
Brues b28f4452b9 Make ClassicAPI a hard requirement; remove movement fallbacks
ClassicAPI is now required, not optional. The load-time requirement
check warns when it's missing (Core.lua), the same as Nampower/UnitXP,
and ClassicAPI-backed code calls the API directly with no fallbacks.

- ClassicAPI.lua: drop the HasUnitAuras/HasUnitSpeed/HasIsFalling
  capability gates and pcall hedging; wrappers call C_UnitAuras /
  GetUnitSpeed / IsFalling directly. Keep IsAvailable (used by the
  requirement check) and the version helpers.
- [moving]: ClassicAPI GetUnitSpeed is the only source. Removed the
  MonkeySpeed integration, the Nampower PlayerIsMoving branch, and the
  100 Hz position-tracking buffer in Core.lua's OnUpdate (with its
  POS_TRACK_INTERVAL constant and UnitPosition upvalue). Removed the
  now-dead speed==nil guards and WarnNoSpeedSource.
- README: ClassicAPI moved to Required; MonkeySpeed dropped from
  Supported Addons.
- docs/CLASSICAPI-TODO.md: record the mandatory/no-fallbacks policy.
2026-06-20 23:47:23 -05:00
Brues 59aad5b662 correct links 2026-06-20 23:11:38 -05:00
Brues 0ec082992d Add ClassicAPI (optional) to README requirements
List ClassicAPI in the Requirements table as Optional — it enables the
dispel-type conditionals ([magic], [curse], [disease], [poison],
[dispellable], [magicbuff]), which degrade gracefully without it.
2026-06-20 23:05:08 -05:00
Brues c05fc754e6 Add ClassicAPI dispel-type conditionals via C_UnitAuras
Introduce ClassicAPI.lua, a feature-detection layer for the ClassicAPI
client mod that mirrors NampowerAPI's HasMinimumVersion pattern
(IsAvailable / HasMinimumVersion / HasUnitAuras). UnitHasDispelType
scans a unit's auras via C_UnitAuras and reports the dispel type,
scanning debuffs or buffs based on a helpful flag.

Add conditionals backed by it:
- Debuff side (defensive cleanse): [magic] [curse] [disease] [poison]
  [dispellable] + negations
- Buff side (offensive dispel/strip): [magicbuff] [dispellablebuff]
  + negations

All are no-arg booleans, default to @target (honor @unit), and degrade
gracefully without ClassicAPI: positive checks return false, negated
return true, since vanilla cannot read aura dispel types. The existing
ValidateAura path is untouched. MacroErrorChecker auto-accepts them via
the Keywords table; they're registered in BOOLEAN_CONDITIONALS so they
parse as flags. Loaded after NampowerAPI in the TOC.
2026-06-20 23:03:23 -05:00
Brues 86d16f507b Point README wiki links to brues-code fork
The wiki lives on the brues-code fork; update all 10 Full Documentation
links from jrc13245 to brues-code. SP_SwingTimer download links (a
separate project) are left untouched.
2026-06-20 23:02:09 -05:00
Brues 69318572c7 Add ClassicAPI adoption backlog
Document opportunities to use ClassicAPI (modern C_* API backport for
the 1.12 client) in the addon: C_UnitAuras for target aura/dispel-type
conditionals, GetWeaponEnchantInfo enchant IDs for poison/imbue checks,
GetUnitSpeed to drop the MonkeySpeed dependency, plus C_Spell, C_Timer,
UnitGUID, native focus, and Tier-3 items.

Each entry cites API.md line refs and the addon files it touches, and
notes the cross-cutting requirement to feature-detect with fallback
(mirroring the nampower HasMinimumVersion pattern).
2026-06-20 22:22:27 -05:00
Brues 51b6b26985 Auto-discover registered slash commands in macro checker
The macro syntax checker validated commands against a hardcoded
VALID_COMMANDS table, so any command not on the list (e.g. /dump) was
flagged "Unknown command" even when the client/addon accepted it.

Add CleveRoids.IsRegisteredCommand: scans SlashCmdList and its
SLASH_<KEY>N globals into a cache (rebuilt on PLAYER_ENTERING_WORLD,
lazy-built on first use) so any command registered the addon way is
accepted automatically. Wire it into the validator alongside the
hardcoded table and the user whitelist.

Trim now-redundant hardcoded entries: the third-party addon block
(/aux, /rinse, /cursive, ...) and this addon's own self-registering
commands. Keep only commands NOT in SlashCmdList: Blizzard builtins
(/cast, /target, /run, ...), /focus (pfUI, only present when loaded),
and the chat/social/emote builtins FrameXML handles internally.
2026-06-20 22:11:47 -05:00
Brues 0d95c4a0a6 update nampower link 2026-06-20 21:58:02 -05:00