Two handlers opened by testing arg1 against a fixed set of unit tokens, so
every other unit's copy of UNIT_AURA reached Lua only to be compared away
-- in a raid that is the bulk of them. ClassicAPI's RegisterUnitEvent
pushes that filter into the client, so the handler only runs for the units
named. Filtering is per (frame, event), so the other events sharing the
libdebuff frame are unaffected.
Utility.lua UNIT_AURA -> "target" (libdebuff seeding)
ComboPointTracker UNIT_AURA -> "target", "player"
Extensions register through CleveRoids.RegisterEvent, so ExtensionsManager
gains a RegisterUnitEvent alongside it.
Separately, the main frame's unit state streams (UNIT_AURA / UNIT_HEALTH /
UNIT_POWER, or the _GUID variants under Nampower v2.39+) are now registered
only in event-driven mode. Their handlers are wholly wrapped in
`realtime == 0`, so with realtime on every one of them crossed into Lua and
returned immediately while the OnUpdate did the refreshing. Registration is
applied at load, again at VARIABLES_LOADED once the saved setting is
readable, and whenever /cleveroid realtime flips it.
Those three stay plain RegisterEvent calls: their handlers ignore the unit
and refresh every macro, and a conditional may name any unit
([@party3,hp:<50], @focus, @mouseover), so narrowing the token set would
leave those icons stale. The _GUID variants could not be filtered anyway --
their first argument is a GUID, not a unit token.
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.
* 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>
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.
* 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>
#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.
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.
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.
- 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
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.
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.
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.
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.
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.
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.
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.
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).
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).
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.
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.
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.
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.
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].
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.
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.
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.
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.
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.
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.
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.