mirror of
https://github.com/brues-code/SuperCleveRoidMacros.git
synced 2026-09-16 03:38:00 +00:00
Compare commits
42 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| cf47d8caba | |||
| 71f8f74237 | |||
| 1a98a05b7e | |||
| 64e3f93042 | |||
| 0c86ed67e9 | |||
| 60c1b7f235 | |||
| 8c10353567 | |||
| fcd206756d | |||
| 8d86389a03 | |||
| 8c85ce572a | |||
| a2db3ebd42 | |||
| 66d5f0c618 | |||
| 7cf2ad95c3 | |||
| c5bc69560e | |||
| ce10c9f456 | |||
| 08dd3ba4dd | |||
| b1fb2af861 | |||
| 7382e8a55f | |||
| 37434911e4 | |||
| da8ea22998 | |||
| d82eeabf22 | |||
| dd61767eb9 | |||
| 2e8ed33a6a | |||
| 403567b381 | |||
| 7ecb66a8dc | |||
| 494aced5bd | |||
| 4f76255f73 | |||
| fd8050aee1 | |||
| 0e07eafbf9 | |||
| 6aaa8a409b | |||
| 2cf4886b01 | |||
| 77f985a927 | |||
| 7c71f8fc26 | |||
| efc98bcd6a | |||
| 41d9283117 | |||
| 9bebbbb836 | |||
| 586c51b53a | |||
| 2ce4d11698 | |||
| e594fda295 | |||
| 22633aa16c | |||
| 6718a01d79 | |||
| 1770f7b6e1 |
+181
-8
@@ -52,13 +52,13 @@ end
|
||||
-- C_UnitAuras
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
-- Scan a unit's auras (indexFn = C_UnitAuras.GetBuffDataByIndex or
|
||||
-- GetDebuffDataByIndex) for one matching the dispel type. Vanilla descriptors
|
||||
-- hold 32 helpful / 16 harmful slots; cap as a backstop.
|
||||
local function scanDispel(indexFn, unit, dispelType, wantAny)
|
||||
-- Scan one aura range of `unit` (filter = "HELPFUL" or "HARMFUL") for an aura
|
||||
-- matching the dispel type. The filtered index self-terminates at the end of the
|
||||
-- range (nil); 48 is a backstop over vanilla's 32 helpful / 16 harmful slots.
|
||||
local function scanDispel(unit, filter, dispelType, wantAny)
|
||||
local i = 1
|
||||
while i <= 48 do
|
||||
local data = indexFn(unit, i)
|
||||
local data = C_UnitAuras.GetAuraDataByIndex(unit, i, filter)
|
||||
if not data then return false end
|
||||
local dn = data.dispelName
|
||||
if dn and dn ~= "" and (wantAny or dn == dispelType) then
|
||||
@@ -76,10 +76,29 @@ end
|
||||
-- false -> scan debuffs (defensive cleanse, default)
|
||||
function API.UnitHasDispelType(unit, dispelType, helpful)
|
||||
if not unit or not UnitExists(unit) then return false end
|
||||
local indexFn = helpful and C_UnitAuras.GetBuffDataByIndex
|
||||
or C_UnitAuras.GetDebuffDataByIndex
|
||||
local filter = helpful and "HELPFUL" or "HARMFUL"
|
||||
local wantAny = (dispelType == nil or dispelType == "any")
|
||||
return scanDispel(indexFn, unit, dispelType, wantAny)
|
||||
return scanDispel(unit, filter, dispelType, wantAny)
|
||||
end
|
||||
|
||||
-- First matching aura on `unit` by spellID, or nil. With no filter it walks the
|
||||
-- whole aura array (helpful then harmful), so it finds a debuff even when it has
|
||||
-- overflowed into an NPC's buff slots -- no 16+32 slot scan, no UnitIsPlayer
|
||||
-- gate. filter ("HELPFUL"/"HARMFUL") restricts the search. Returns the modern
|
||||
-- AuraData (spellId, name, applications, duration, expirationTime, dispelName, ...).
|
||||
function API.GetUnitAuraBySpellID(unit, spellID, filter)
|
||||
if not unit or not spellID then return nil end
|
||||
return C_UnitAuras.GetUnitAuraBySpellID(unit, spellID, filter)
|
||||
end
|
||||
|
||||
-- First matching aura on `unit` by spell NAME, or nil. Same whole-array search as
|
||||
-- GetUnitAuraBySpellID; the name is case-sensitive and locale-resolved, so pass it
|
||||
-- in the client's locale (what C_Spell.GetSpellName returns). filter
|
||||
-- ("HELPFUL"/"HARMFUL") restricts the search. Prefer the by-ID variant for
|
||||
-- portability where a spellID is known.
|
||||
function API.GetAuraDataBySpellName(unit, spellName, filter)
|
||||
if not unit or not spellName or spellName == "" then return nil end
|
||||
return C_UnitAuras.GetAuraDataBySpellName(unit, spellName, filter)
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
@@ -155,6 +174,76 @@ function API.GetItemSetInfo(setID)
|
||||
return C_Item.GetItemSetInfo(setID)
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Equipment Set
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
-- True if the saved equipment set named `name` is currently equipped -- every
|
||||
-- resolvable item in its target slot (a missing bank-stored piece doesn't
|
||||
-- disqualify, matching C_EquipmentSet.GetEquipmentSetInfo's isEquipped). Name is
|
||||
-- an exact, case-sensitive match per GetEquipmentSetID. Returns false for an
|
||||
-- unknown name or a client without the EquipmentSet API. Powers [equipset]/
|
||||
-- [noequipset]; the swap side is the reclaimed /equipset command.
|
||||
function API.IsEquipmentSetEquipped(name)
|
||||
if not name or name == "" then return false end
|
||||
local setID = C_EquipmentSet.GetEquipmentSetID(name)
|
||||
if not setID then return false end
|
||||
local _, _, _, isEquipped = C_EquipmentSet.GetEquipmentSetInfo(setID)
|
||||
return isEquipped and true or false
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Weapon Enchant
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
-- Temporary weapon-enchant state for a slot: "mh" (main), "oh" (off), or
|
||||
-- "ranged". Returns hasEnchant, expirationMs, charges, enchantID. The enchantID
|
||||
-- comes from ClassicAPI's modern C_Item.GetWeaponEnchantInfo 12-tuple (the
|
||||
-- vanilla global omits it), so [mhenchant] can tell WHICH imbue is applied, not
|
||||
-- just that one exists. Falls back to the vanilla 6-tuple global (enchantID nil,
|
||||
-- no ranged slot) when the C_Item version is unavailable.
|
||||
function API.GetWeaponEnchant(slot)
|
||||
local hasM, mExp, mChg, mID, hasO, oExp, oChg, oID, hasR, rExp, rChg, rID =
|
||||
C_Item.GetWeaponEnchantInfo()
|
||||
if slot == "oh" then
|
||||
return hasO, oExp, oChg, oID
|
||||
elseif slot == "ranged" then
|
||||
return hasR, rExp, rChg, rID
|
||||
end
|
||||
return hasM, mExp, mChg, mID
|
||||
end
|
||||
|
||||
-- Localized name of an item-enchant ID (poison/oil/sharpening stone/permanent),
|
||||
-- read straight from SpellItemEnchantment.dbc via ClassicAPI -- or nil for an
|
||||
-- unknown id / a client without C_Item.GetEnchantInfo. Lets [mhenchant:Name]
|
||||
-- resolve the applied enchant's name without scraping the weapon tooltip.
|
||||
function API.GetEnchantName(enchantID)
|
||||
if not enchantID or enchantID == 0 then return nil end
|
||||
local info = C_Item.GetEnchantInfo(enchantID)
|
||||
return info and info.name or nil
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Loss Of Control
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
-- Locked spell-school mask from an active SCHOOL_INTERRUPT (Counterspell / Kick /
|
||||
-- Pummel / Earth Shock lockout) on the player, or 0 when not kicked. Read from
|
||||
-- C_LossOfControl, which synthesizes the lockout from the server's own
|
||||
-- SMSG_SPELL_COOLDOWN packet -- a state no debuff scan can see. Also returns the
|
||||
-- seconds remaining (nil if ClassicAPI didn't observe the applying cast). Returns
|
||||
-- 0 for a client without C_LossOfControl. Player-only (vanilla LoC is local-only).
|
||||
function API.GetSchoolLockout()
|
||||
local n = C_LossOfControl.GetActiveLossOfControlDataCount() or 0
|
||||
for i = 1, n do
|
||||
local d = C_LossOfControl.GetActiveLossOfControlData(i)
|
||||
if d and d.locType == "SCHOOL_INTERRUPT" then
|
||||
return d.lockoutSchool or 0, d.timeRemaining
|
||||
end
|
||||
end
|
||||
return 0, nil
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Spell
|
||||
--------------------------------------------------------------------------------
|
||||
@@ -168,6 +257,67 @@ function API.GetSpellMechanicByID(spellID)
|
||||
return C_Spell.GetSpellMechanicByID(spellID)
|
||||
end
|
||||
|
||||
-- Per-effect SpellMechanic ids (Spell.dbc EffectMechanic[3]) as {m1, m2, m3},
|
||||
-- or nil for an invalid spell / 0 for an effect with no mechanic. Complements
|
||||
-- GetSpellMechanicByID, which only reads the spell-level Mechanic field: vanilla
|
||||
-- stores some mechanics on an effect instead (e.g. Rake's bleed is effect-level,
|
||||
-- so GetSpellMechanicByID returns 0 but this returns {0,15,0}). Nil-guarded so an
|
||||
-- older ClassicAPI build without the function degrades gracefully.
|
||||
function API.GetSpellEffectMechanics(spellID)
|
||||
return C_Spell.GetSpellEffectMechanics(spellID)
|
||||
end
|
||||
|
||||
-- Flat spell-damage bonus (spell power) for a magic school, as a number.
|
||||
-- school is 1-based: 1=Physical, 2=Holy, 3=Fire, 4=Nature, 5=Frost, 6=Shadow,
|
||||
-- 7=Arcane. Reads the same client field nampower's GetSpellPower does -- exact,
|
||||
-- with gear/enchants/buffs/talents/set bonuses baked in.
|
||||
function API.GetSpellBonusDamage(school)
|
||||
return GetSpellBonusDamage(school)
|
||||
end
|
||||
|
||||
-- Flat healing bonus (+healing), as a number. Vanilla has no healing-done field,
|
||||
-- so ClassicAPI derives it from gear/enchant/buff MOD_HEALING_DONE plus
|
||||
-- stat-conversion talents (e.g. Spiritual Guidance) -- exact, not a holy-damage
|
||||
-- proxy.
|
||||
function API.GetSpellBonusHealing()
|
||||
return GetSpellBonusHealing()
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Unit Health
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
-- Health deficit (max - current) for `unit` in one call. Falls back to
|
||||
-- UnitHealthMax - UnitHealth without ClassicAPI.
|
||||
API.UnitHealthMissing = UnitHealthMissing or function(unit)
|
||||
return (UnitHealthMax(unit) or 0) - (UnitHealth(unit) or 0)
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Unit Power
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
-- Current power for a specific Enum.PowerType (0=Mana, 1=Rage, 2=Focus,
|
||||
-- 3=Energy, 4=Happiness), or the unit's primary power when powerType is omitted.
|
||||
-- Display-divided (rage reads 0..100). Falls back to UnitMana without ClassicAPI.
|
||||
API.UnitPower = UnitPower or function(unit, powerType)
|
||||
return UnitMana(unit)
|
||||
end
|
||||
|
||||
API.UnitPowerMax = UnitPowerMax or function(unit, powerType)
|
||||
return UnitManaMax(unit)
|
||||
end
|
||||
|
||||
-- Power deficit (max - current) for the type / primary power, in one call.
|
||||
API.UnitPowerMissing = UnitPowerMissing or function(unit, powerType)
|
||||
return (UnitManaMax(unit) or 0) - (UnitMana(unit) or 0)
|
||||
end
|
||||
|
||||
-- Unit's primary power type as an integer (0=Mana .. 4=Happiness).
|
||||
function API.UnitPowerType(unit)
|
||||
return UnitPowerType(unit)
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- State
|
||||
--------------------------------------------------------------------------------
|
||||
@@ -182,6 +332,29 @@ function API.IsSwimming()
|
||||
return IsSwimming() and true or false
|
||||
end
|
||||
|
||||
-- True if the player is currently mounted.
|
||||
function API.IsMounted()
|
||||
return IsMounted() and true or false
|
||||
end
|
||||
|
||||
-- True if the player is under a WMO roof (building, cave, instance interior).
|
||||
-- Live engine geometry query, not zone-based; nil (-> false) pre-world.
|
||||
function API.IsIndoors()
|
||||
return IsIndoors() and true or false
|
||||
end
|
||||
|
||||
-- True if the player is outdoors (open sky / not inside a WMO interior).
|
||||
-- Exact complement of IsIndoors for a resolvable player.
|
||||
function API.IsOutdoors()
|
||||
return IsOutdoors() and true or false
|
||||
end
|
||||
|
||||
-- Player's stand state: 0 = standing, non-zero = sitting/sleeping/kneeling/etc.
|
||||
-- (see UnitStandState). Player-only.
|
||||
function API.GetPlayerStandState()
|
||||
return UnitStandState("player") or 0
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Cursor
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
+40
-67
@@ -32,6 +32,35 @@ CleveRoids.lastRakeCast = CleveRoids.lastRakeCast or {
|
||||
timestamp = 0
|
||||
}
|
||||
|
||||
-- Family membership without hardcoded rank lists. C_Spell.GetSpellName resolves
|
||||
-- ANY spellID from the client's Spell.dbc -- every rank (so no enumeration or
|
||||
-- spellbook scan) and TWoW's custom spells alike -- so "is this spellID a Rip?"
|
||||
-- is just a name match against one seed rank. seedNameCache memoizes the seed's
|
||||
-- localized name (locale-safe: derived from the ID, not a hardcoded string; a
|
||||
-- seed absent from the DBC, e.g. a TWoW spell on a stock client, resolves to nil
|
||||
-- and the set simply never matches -- which is correct, that spell can't be cast).
|
||||
local seedNameCache = {}
|
||||
local function SeedName(seedID)
|
||||
local n = seedNameCache[seedID]
|
||||
if n == nil then
|
||||
n = C_Spell.GetSpellName(seedID) or false
|
||||
seedNameCache[seedID] = n
|
||||
end
|
||||
return n or nil
|
||||
end
|
||||
|
||||
-- Stand-in for a hardcoded {spellID=true,...} rank set: t[spellID] is true iff
|
||||
-- spellID is any rank of the family seeded by seedID. Same read shape as the old
|
||||
-- tables, so every membership consumer (`X and X[id]`) keeps working unchanged.
|
||||
-- Only valid for membership tests -- these are not iterable (pairs() sees empty).
|
||||
local function RankSet(seedID)
|
||||
return setmetatable({}, { __index = function(_, spellID)
|
||||
if type(spellID) ~= "number" then return nil end
|
||||
local name = SeedName(seedID)
|
||||
return (name and C_Spell.GetSpellName(spellID) == name) and true or nil
|
||||
end })
|
||||
end
|
||||
|
||||
-- Define spells that scale with combo points by SPELL ID and their duration formulas
|
||||
-- Duration = base + (combo_points - 1) * increment
|
||||
CleveRoids.ComboScalingSpellsByID = {
|
||||
@@ -70,32 +99,9 @@ CleveRoids.FerociousBiteSpellIDs = {
|
||||
[31018] = true, -- Rank 6
|
||||
}
|
||||
|
||||
-- Rip spell IDs (for Carnage talent - refreshed when Carnage procs)
|
||||
CleveRoids.RipSpellIDs = {
|
||||
[1079] = true, -- Rank 1
|
||||
[9492] = true, -- Rank 2
|
||||
[9493] = true, -- Rank 3
|
||||
[9752] = true, -- Rank 4
|
||||
[9894] = true, -- Rank 5
|
||||
[9896] = true, -- Rank 6
|
||||
}
|
||||
|
||||
-- Rake spell IDs (for Carnage talent - refreshed when Carnage procs)
|
||||
CleveRoids.RakeSpellIDs = {
|
||||
[1822] = true, -- Rank 1
|
||||
[1823] = true, -- Rank 2
|
||||
[1824] = true, -- Rank 3
|
||||
[9904] = true, -- Rank 4
|
||||
}
|
||||
|
||||
-- Pounce Bleed spell IDs (for immunity detection - bleed portion of Pounce)
|
||||
-- Note: Pounce (cast) TRIGGERS a separate Pounce Bleed spell with different IDs
|
||||
-- Cast IDs: 9005, 9823, 9827 → Trigger Bleed IDs: 9007, 9824, 9826
|
||||
CleveRoids.PounceBleedSpellIDs = {
|
||||
[9007] = true, -- Rank 1 (triggered by Pounce 9005)
|
||||
[9824] = true, -- Rank 2 (triggered by Pounce 9823)
|
||||
[9826] = true, -- Rank 3 (triggered by Pounce 9827)
|
||||
}
|
||||
-- Rip / Rake families (for Carnage talent). Seeded by Rank 1; matches every rank.
|
||||
CleveRoids.RipSpellIDs = RankSet(1079)
|
||||
CleveRoids.RakeSpellIDs = RankSet(1822)
|
||||
|
||||
-- Combined table for all bleed spells that need immunity detection
|
||||
-- Used when checking if a cast bleed failed to apply (indicates bleed immunity)
|
||||
@@ -132,14 +138,9 @@ CleveRoids.PounceToBleedMapping = {
|
||||
-- When Molten Blast hits, it refreshes Flame Shock duration on the target
|
||||
-- Detection: Monitor combat log for Molten Blast damage, then refresh Flame Shock
|
||||
-- =============================================================================
|
||||
CleveRoids.MoltenBlastSpellIDs = {
|
||||
[36916] = true, -- Rank 1
|
||||
[36917] = true, -- Rank 2
|
||||
[36918] = true, -- Rank 3
|
||||
[36919] = true, -- Rank 4
|
||||
[36920] = true, -- Rank 5
|
||||
[36921] = true, -- Rank 6
|
||||
}
|
||||
-- TWoW custom; seed resolves on the TWoW client (nil/never-matches on stock,
|
||||
-- where Molten Blast can't be cast anyway).
|
||||
CleveRoids.MoltenBlastSpellIDs = RankSet(36916)
|
||||
|
||||
CleveRoids.FlameShockSpellIDs = {
|
||||
[8050] = true, -- Rank 1
|
||||
@@ -154,12 +155,7 @@ CleveRoids.FlameShockSpellIDs = {
|
||||
-- WARLOCK: Conflagrate → Immolate Duration Reduction
|
||||
-- When Conflagrate is cast, it reduces Immolate duration by 3 seconds
|
||||
-- =============================================================================
|
||||
CleveRoids.ConflagrateSpellIDs = {
|
||||
[17962] = true, -- Rank 1
|
||||
[18930] = true, -- Rank 2
|
||||
[18931] = true, -- Rank 3
|
||||
[18932] = true, -- Rank 4
|
||||
}
|
||||
CleveRoids.ConflagrateSpellIDs = RankSet(17962)
|
||||
|
||||
CleveRoids.ImmolateSpellIDs = {
|
||||
[348] = true, -- Rank 1
|
||||
@@ -177,11 +173,8 @@ CleveRoids.ImmolateSpellIDs = {
|
||||
-- Channeled spell that accelerates DoT tick rate by 30% while channeling
|
||||
-- Complex tracking: debuff expires 30% faster while Dark Harvest is active
|
||||
-- =============================================================================
|
||||
CleveRoids.DarkHarvestSpellIDs = {
|
||||
[52550] = true, -- Rank 1
|
||||
[52551] = true, -- Rank 2
|
||||
[52552] = true, -- Rank 3
|
||||
}
|
||||
-- TWoW custom (see MoltenBlast note).
|
||||
CleveRoids.DarkHarvestSpellIDs = RankSet(52550)
|
||||
|
||||
-- =============================================================================
|
||||
-- DRUID: Rake Debuff Cap Boss Whitelist
|
||||
@@ -489,7 +482,7 @@ function CleveRoids.TrackComboPointCastByID(spellID, targetGUID)
|
||||
end
|
||||
else
|
||||
-- Second, check if name-based tracking has recent data for this spell
|
||||
local spellName = GetSpellRecField(spellID, "name")
|
||||
local spellName = C_Spell.GetSpellName(spellID)
|
||||
if spellName then
|
||||
-- Remove rank info for comparison
|
||||
local baseName = CleveRoids.StripRank(spellName)
|
||||
@@ -548,26 +541,6 @@ function CleveRoids.TrackComboPointCastByID(spellID, targetGUID)
|
||||
return duration
|
||||
end
|
||||
|
||||
-- API function to get last tracked combo points for a spell
|
||||
function CleveRoids.GetLastComboPointsForSpell(spellName)
|
||||
if CleveRoids.ComboPointTracking[spellName] then
|
||||
return CleveRoids.ComboPointTracking[spellName].combo_points
|
||||
elseif CleveRoids.spell_tracking[spellName] then
|
||||
return CleveRoids.spell_tracking[spellName].last_combo_points
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
-- API function to get last calculated duration for a spell
|
||||
function CleveRoids.GetLastDurationForSpell(spellName)
|
||||
if CleveRoids.ComboPointTracking[spellName] then
|
||||
return CleveRoids.ComboPointTracking[spellName].duration
|
||||
elseif CleveRoids.spell_tracking[spellName] then
|
||||
return CleveRoids.spell_tracking[spellName].last_duration
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
-- Utility function to display current combo tracking info
|
||||
function CleveRoids.ShowComboTracking()
|
||||
CleveRoids.Print("=== Combo Point Tracking ===")
|
||||
@@ -683,7 +656,7 @@ if _G.UseAction then
|
||||
local spellName = nil
|
||||
|
||||
if actionType == "SPELL" and actionID then
|
||||
spellName = GetSpellRecField(actionID, "name")
|
||||
spellName = C_Spell.GetSpellName(actionID)
|
||||
end
|
||||
|
||||
if currentCP and currentCP > 0 then
|
||||
|
||||
+25
-19
@@ -2,9 +2,10 @@ local _G = _G or getfenv(0)
|
||||
local CleveRoids = _G.CleveRoids or {}
|
||||
|
||||
local Extension = CleveRoids.RegisterExtension("Compatibility_pfUI")
|
||||
Extension.RegisterEvent("ADDON_LOADED", "ADDON_LOADED")
|
||||
Extension.RegisterEvent("PLAYER_LOGIN", "PLAYER_LOGIN")
|
||||
Extension.Debug = false
|
||||
-- pfUI-loaded and player-login handlers are wired via ClassicAPI's EventUtil at
|
||||
-- the bottom of the file (ContinueOnAddOnLoaded fires immediately if pfUI already
|
||||
-- loaded, so no separate "we missed pfUI's ADDON_LOADED" fallback is needed).
|
||||
|
||||
-- Track pfUI state
|
||||
Extension.pfUILoaded = false
|
||||
@@ -99,7 +100,7 @@ local function GetCarnageOverride(effect)
|
||||
end
|
||||
|
||||
for spellID, override in pairs(CleveRoids.carnageDurationOverrides) do
|
||||
local spellName = GetSpellRecField(spellID, "name")
|
||||
local spellName = C_Spell.GetSpellName(spellID)
|
||||
if spellName then
|
||||
local baseName = CleveRoids.StripRank(spellName)
|
||||
if baseName == effect and override.timestamp and (GetTime() - override.timestamp) < 5 then
|
||||
@@ -209,7 +210,7 @@ function Extension.HookPfUILibdebuff()
|
||||
for spellID, rec in pairs(CleveRoids.libdebuff.objects[unitGUID]) do
|
||||
if rec and rec.start and rec.duration then
|
||||
-- Get spell name for this ID
|
||||
local spellName = GetSpellRecField(spellID, "name")
|
||||
local spellName = C_Spell.GetSpellName(spellID)
|
||||
if spellName then
|
||||
local baseName = CleveRoids.StripRank(spellName)
|
||||
if baseName == effect then
|
||||
@@ -380,7 +381,7 @@ function Extension.SyncComboDurationToPfUI(guid, spellID, duration)
|
||||
end
|
||||
|
||||
-- Get spell name from spell ID
|
||||
local spellName = GetSpellRecField(spellID, "name")
|
||||
local spellName = C_Spell.GetSpellName(spellID)
|
||||
if not spellName then
|
||||
if CleveRoids.debug then
|
||||
DEFAULT_CHAT_FRAME:AddMessage("|cffff0000[pfUI Sync]|r Could not find spell name for ID " .. spellID)
|
||||
@@ -726,23 +727,22 @@ function Extension.OnLoad()
|
||||
SLASH_PFUICD1 = "/pfuicd"
|
||||
end
|
||||
|
||||
function Extension.ADDON_LOADED()
|
||||
-- Check if pfUI just loaded AND the global actually exists
|
||||
-- (another addon could be named "pfUI" without being the real UI framework)
|
||||
if arg1 == "pfUI" and pfUI then
|
||||
Extension.pfUILoaded = true
|
||||
-- pfUI modules load after ADDON_LOADED, so schedule a check
|
||||
if CleveRoids.ScheduleTimer then
|
||||
CleveRoids.ScheduleTimer(function()
|
||||
Extension.SetupCompatibility()
|
||||
end, 0.5)
|
||||
end
|
||||
-- Fires once pfUI has loaded (immediately if it loaded before us, via EventUtil).
|
||||
function Extension.OnPfUILoaded()
|
||||
-- Guard: only the real pfUI framework sets this global (another addon could be
|
||||
-- named "pfUI" without being the UI framework).
|
||||
if not pfUI then return end
|
||||
Extension.pfUILoaded = true
|
||||
-- pfUI's submodules initialize after its ADDON_LOADED, so defer the setup.
|
||||
if CleveRoids.ScheduleTimer then
|
||||
CleveRoids.ScheduleTimer(function()
|
||||
Extension.SetupCompatibility()
|
||||
end, 0.5)
|
||||
end
|
||||
end
|
||||
|
||||
function Extension.PLAYER_LOGIN()
|
||||
-- If pfUI loaded before SCRM (alphabetical order), ADDON_LOADED for pfUI was missed.
|
||||
-- Re-run InitPfUIIntegration here to ensure lib.objects is linked correctly.
|
||||
function Extension.OnPlayerLogin()
|
||||
-- Ensure lib.objects is linked correctly (InitPfUIIntegration is idempotent).
|
||||
if pfUI and not CleveRoids.hasPfUI76 then
|
||||
local lib = CleveRoids.libdebuff
|
||||
if lib and lib.InitPfUIIntegration then
|
||||
@@ -797,4 +797,10 @@ if not CleveRoids.ScheduleTimer then
|
||||
end
|
||||
end
|
||||
|
||||
-- Wire handlers via ClassicAPI EventUtil (fires immediately if the event already
|
||||
-- happened, so load order relative to pfUI no longer matters). Registered here,
|
||||
-- after the handlers are defined, since ContinueOnAddOnLoaded may fire inline.
|
||||
EventUtil.ContinueOnAddOnLoaded("pfUI", Extension.OnPfUILoaded)
|
||||
EventUtil.ContinueOnPlayerLogin(Extension.OnPlayerLogin)
|
||||
|
||||
_G["CleveRoids"] = CleveRoids
|
||||
|
||||
+314
-221
@@ -55,15 +55,14 @@ function CleveRoids.GetCachedPlayerHealthPercent()
|
||||
end
|
||||
|
||||
function CleveRoids.GetCachedPlayerPowerPercent()
|
||||
local API = CleveRoids.NampowerAPI
|
||||
local power = API and API.GetUnitPower and API.GetUnitPower("player") or UnitMana("player")
|
||||
local max = API and API.GetUnitMaxPower and API.GetUnitMaxPower("player") or UnitManaMax("player")
|
||||
local API = CleveRoids.ClassicAPI
|
||||
local power = API.UnitPower("player")
|
||||
local max = API.UnitPowerMax("player")
|
||||
return max > 0 and (100 * power / max) or 0
|
||||
end
|
||||
|
||||
function CleveRoids.GetCachedPlayerPower()
|
||||
local API = CleveRoids.NampowerAPI
|
||||
return API and API.GetUnitPower and API.GetUnitPower("player") or UnitMana("player")
|
||||
return CleveRoids.ClassicAPI.UnitPower("player")
|
||||
end
|
||||
|
||||
function CleveRoids.GetCachedTargetHealthPercent()
|
||||
@@ -105,7 +104,7 @@ local function BuildSpellNameCache()
|
||||
|
||||
if lib.personalDebuffs then
|
||||
for sid, _ in pairs(lib.personalDebuffs) do
|
||||
local name = GetSpellRecField(sid, "name")
|
||||
local name = C_Spell.GetSpellName(sid)
|
||||
if name then
|
||||
name = CleveRoids.StripRank(name)
|
||||
if not _spellNameToIDs[name] then
|
||||
@@ -118,7 +117,7 @@ local function BuildSpellNameCache()
|
||||
|
||||
if lib.sharedDebuffs then
|
||||
for sid, _ in pairs(lib.sharedDebuffs) do
|
||||
local name = GetSpellRecField(sid, "name")
|
||||
local name = C_Spell.GetSpellName(sid)
|
||||
if name then
|
||||
name = CleveRoids.StripRank(name)
|
||||
if not _spellNameToIDs[name] then
|
||||
@@ -163,7 +162,7 @@ local function GetSpellIDForRank(baseName, rankNum)
|
||||
if not matchIDs then return nil end
|
||||
local targetRank = "Rank " .. rankNum
|
||||
for _, sid in ipairs(matchIDs) do
|
||||
local rank = GetSpellRecField and GetSpellRecField(sid, "rank")
|
||||
local rank = C_Spell.GetSpellSubtext(sid)
|
||||
if rank and rank == targetRank then
|
||||
return sid
|
||||
end
|
||||
@@ -198,102 +197,6 @@ local function IsSharedDebuffByIdOrName(lib, spellID, debuffName)
|
||||
return false
|
||||
end
|
||||
|
||||
-- PERFORMANCE: Equipment cache for HasGearEquipped (avoids 19-slot scan per call)
|
||||
-- Invalidated on UNIT_INVENTORY_CHANGED via CleveRoids.InvalidateEquipmentCache()
|
||||
-- Enhanced with Nampower v2.18+ GetEquippedItems when available
|
||||
local _equippedItemIDs = {} -- [slot] = itemID (number)
|
||||
local _equippedItemNames = {} -- [slot] = itemName (lowercase string)
|
||||
local _equipmentCacheValid = false
|
||||
|
||||
-- Track if we've warned about GetEquippedItems errors (warn once per session)
|
||||
local _getEquippedItemsErrorWarned = false
|
||||
|
||||
local function BuildEquipmentCache()
|
||||
if _equipmentCacheValid then return end
|
||||
_equipmentCacheValid = true
|
||||
|
||||
-- Clear old data
|
||||
for i = 1, 19 do
|
||||
_equippedItemIDs[i] = nil
|
||||
_equippedItemNames[i] = nil
|
||||
end
|
||||
|
||||
local string_find = string.find
|
||||
local string_lower = string.lower
|
||||
|
||||
-- Try Nampower GetEquippedItems for faster enumeration
|
||||
-- Requires v2.22+ because earlier versions (e.g., v2.19.1) have internal bug
|
||||
local API = CleveRoids.NampowerAPI
|
||||
local hasValidNampower = API and API.HasMinimumVersion and API.HasMinimumVersion(2, 22, 0)
|
||||
|
||||
if hasValidNampower and GetEquippedItems then
|
||||
-- Use pcall to catch any internal Nampower errors and fall back gracefully
|
||||
local success, result = pcall(GetEquippedItems, "player")
|
||||
|
||||
if not success then
|
||||
-- Log the error once per session for debugging
|
||||
if not _getEquippedItemsErrorWarned then
|
||||
_getEquippedItemsErrorWarned = true
|
||||
local errMsg = tostring(result)
|
||||
if CleveRoids.Print then
|
||||
CleveRoids.Print("|cffff6600Warning:|r GetEquippedItems failed: " .. errMsg)
|
||||
CleveRoids.Print("Using fallback equipment detection. Consider updating Nampower.")
|
||||
end
|
||||
end
|
||||
-- Fall through to manual enumeration
|
||||
elseif result and type(result) == "table" then
|
||||
local usedNampower = false
|
||||
for nampowerSlot, itemInfo in pairs(result) do
|
||||
-- Nampower uses 0-indexed slots, WoW API uses 1-indexed
|
||||
-- tonumber() handles both string and numeric keys from different Nampower versions
|
||||
-- Skip non-numeric keys (metadata fields, etc.)
|
||||
local slotNum = tonumber(nampowerSlot)
|
||||
if slotNum and type(itemInfo) == "table" and itemInfo.itemId then
|
||||
local slot = slotNum + 1
|
||||
-- itemInfo must be a table to access .itemId (userdata from some Nampower versions is not indexable)
|
||||
if slot >= 1 and slot <= 19 then
|
||||
_equippedItemIDs[slot] = itemInfo.itemId
|
||||
usedNampower = true
|
||||
|
||||
-- Get item name via Nampower API or GetItemInfo
|
||||
local itemName = API and API.GetItemName and API.GetItemName(itemInfo.itemId)
|
||||
if not itemName then
|
||||
itemName = GetItemInfo(itemInfo.itemId)
|
||||
end
|
||||
if itemName then
|
||||
_equippedItemNames[slot] = string_lower(itemName)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if usedNampower then
|
||||
return -- Done with Nampower path
|
||||
end
|
||||
-- Fall through to manual enumeration if Nampower returned userdata items
|
||||
end
|
||||
end
|
||||
|
||||
-- Fallback: manual slot enumeration via ClassicAPI (id + decorated name),
|
||||
-- no link string built. C_Item.GetItemName carries random-suffix decoration
|
||||
-- and falls back to the base name internally, so it replaces the old
|
||||
-- bracket-name / GetItemInfo two-step in a single call.
|
||||
for slot = 1, 19 do
|
||||
local id = GetInventoryItemID("player", slot)
|
||||
if id then
|
||||
_equippedItemIDs[slot] = id
|
||||
local name = C_Item.GetItemName({ equipmentSlotIndex = slot })
|
||||
if name then
|
||||
_equippedItemNames[slot] = string_lower(name)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Invalidate equipment cache (call on UNIT_INVENTORY_CHANGED)
|
||||
function CleveRoids.InvalidateEquipmentCache()
|
||||
_equipmentCacheValid = false
|
||||
end
|
||||
|
||||
-- ============================================================================
|
||||
-- PERFORMANCE: Unified item location lookup using CleveRoids.Items cache
|
||||
-- Returns: { type="inventory"|"bag", inventoryID=N } or { type="bag", bag=N, slot=N }
|
||||
@@ -444,24 +347,26 @@ local stat_checks = {
|
||||
attackpower = function() local base, pos, neg = UnitAttackPower("player"); return base + pos + neg end,
|
||||
rap = function() local base, pos, neg = UnitRangedAttackPower("player"); return base + pos + neg end,
|
||||
rangedattackpower = function() local base, pos, neg = UnitRangedAttackPower("player"); return base + pos + neg end,
|
||||
healing = function() local _, h = CleveRoids.NampowerAPI.GetSpellPower(); return h or 0 end,
|
||||
healingpower = function() local _, h = CleveRoids.NampowerAPI.GetSpellPower(); return h or 0 end,
|
||||
healing = function() return CleveRoids.ClassicAPI.GetSpellBonusHealing() or 0 end,
|
||||
healingpower = function() return CleveRoids.ClassicAPI.GetSpellBonusHealing() or 0 end,
|
||||
|
||||
-- Bonus Spell Damage by School (Nampower v2.31+ GetSpellPower)
|
||||
-- GetSpellPower() returns: physical, holy, fire, nature, frost, shadow, arcane
|
||||
arcane_power = function() return select(7, CleveRoids.NampowerAPI.GetSpellPower()) or 0 end,
|
||||
fire_power = function() return select(3, CleveRoids.NampowerAPI.GetSpellPower()) or 0 end,
|
||||
frost_power = function() return select(5, CleveRoids.NampowerAPI.GetSpellPower()) or 0 end,
|
||||
nature_power = function() return select(4, CleveRoids.NampowerAPI.GetSpellPower()) or 0 end,
|
||||
shadow_power = function() return select(6, CleveRoids.NampowerAPI.GetSpellPower()) or 0 end,
|
||||
-- Bonus Spell Damage by School (ClassicAPI GetSpellBonusDamage)
|
||||
-- school: 1=Physical, 2=Holy, 3=Fire, 4=Nature, 5=Frost, 6=Shadow, 7=Arcane
|
||||
arcane_power = function() return CleveRoids.ClassicAPI.GetSpellBonusDamage(7) or 0 end,
|
||||
fire_power = function() return CleveRoids.ClassicAPI.GetSpellBonusDamage(3) or 0 end,
|
||||
frost_power = function() return CleveRoids.ClassicAPI.GetSpellBonusDamage(5) or 0 end,
|
||||
nature_power = function() return CleveRoids.ClassicAPI.GetSpellBonusDamage(4) or 0 end,
|
||||
shadow_power = function() return CleveRoids.ClassicAPI.GetSpellBonusDamage(6) or 0 end,
|
||||
|
||||
-- Highest spell power across all schools
|
||||
spell_power = function()
|
||||
local p, h, fi, n, fr, s, a = CleveRoids.NampowerAPI.GetSpellPower()
|
||||
if p then
|
||||
return math.max(p, h, fi, n, fr, s, a)
|
||||
local API = CleveRoids.ClassicAPI
|
||||
local best = 0
|
||||
for s = 1, 7 do
|
||||
local v = API.GetSpellBonusDamage(s) or 0
|
||||
if v > best then best = v end
|
||||
end
|
||||
return 0
|
||||
return best
|
||||
end,
|
||||
|
||||
-- Defensive Stats
|
||||
@@ -916,7 +821,7 @@ function CleveRoids.GetAllCasterAuraTimeRemaining(targetGuid, spellId)
|
||||
local targetData, isPfUI = CleveRoids.GetAuraTrackingData(targetGuid)
|
||||
if not targetData then return nil end
|
||||
|
||||
local spellName = GetSpellRecField and GetSpellRecField(spellId, "name")
|
||||
local spellName = C_Spell.GetSpellName(spellId)
|
||||
if not spellName then return nil end
|
||||
|
||||
local casters = targetData[spellName]
|
||||
@@ -960,7 +865,7 @@ function CleveRoids.FindAllCasterAuraByName(targetGuid, searchName)
|
||||
-- Resolve spell ID to name for direct lookup
|
||||
local searchID = tonumber(searchName)
|
||||
if searchID then
|
||||
local resolvedName = GetSpellRecField and GetSpellRecField(searchID, "name")
|
||||
local resolvedName = C_Spell.GetSpellName(searchID)
|
||||
if not resolvedName then return nil, nil end
|
||||
searchName = resolvedName
|
||||
end
|
||||
@@ -1081,7 +986,7 @@ local function OnAutoAttackOther(attackerGuid, targetGuid, totalDamage, hitInfo,
|
||||
rec.start = GetTime()
|
||||
|
||||
if CleveRoids.debug then
|
||||
local spellName = GetSpellRecField and GetSpellRecField(spellID, "name") or "Unknown"
|
||||
local spellName = C_Spell.GetSpellName(spellID) or "Unknown"
|
||||
local baseName = CleveRoids.StripRank(spellName) or "Unknown"
|
||||
DEFAULT_CHAT_FRAME:AddMessage(
|
||||
string.format("|cff00ffaa[Judgement Refresh]|r Refreshed %s (ID:%d) on melee hit - new duration: %ds",
|
||||
@@ -1091,7 +996,7 @@ local function OnAutoAttackOther(attackerGuid, targetGuid, totalDamage, hitInfo,
|
||||
|
||||
-- Sync to pfUI if loaded (pre-7.6 only)
|
||||
if not CleveRoids.hasPfUI76 and pfUI and pfUI.api and pfUI.api.libdebuff then
|
||||
local spellName = GetSpellRecField and GetSpellRecField(spellID, "name") or nil
|
||||
local spellName = C_Spell.GetSpellName(spellID) or nil
|
||||
local baseName = CleveRoids.StripRank(spellName)
|
||||
local targetName = (lib.guidToName and lib.guidToName[normalizedTarget]) or UnitName("target")
|
||||
local targetLevel = UnitLevel("target") or 0
|
||||
@@ -1236,7 +1141,7 @@ local function OnAuraCastSelf(spellId, casterGuid, targetGuid, effect, effectAur
|
||||
-- Use the isBuffNotDebuff result determined above (avoids redundant slot scanning)
|
||||
local lib = CleveRoids.libdebuff
|
||||
if isBuffNotDebuff and spellId and durationMs and durationMs > 0 and lib and not lib.hasPfUIEnhanced then
|
||||
local spellName = GetSpellRecField and GetSpellRecField(spellId, "name")
|
||||
local spellName = C_Spell.GetSpellName(spellId)
|
||||
if spellName then
|
||||
local playerGuid = CleveRoids.GetGUID("player")
|
||||
if playerGuid then
|
||||
@@ -1269,7 +1174,7 @@ local function OnAuraCastOther(spellId, casterGuid, targetGuid, effect, effectAu
|
||||
-- When pfUI enhanced is active, pfUI writes to pfUI.libdebuff_all_auras with
|
||||
-- full downrank protection — we read from that table via GetAuraTrackingData().
|
||||
if spellId and durationMs and durationMs > 0 then
|
||||
local spellName = GetSpellRecField and GetSpellRecField(spellId, "name")
|
||||
local spellName = C_Spell.GetSpellName(spellId)
|
||||
if spellName and not CleveRoids.hasPfUI76 then
|
||||
CleveRoids._allCasterAuraDirty = true
|
||||
if not CleveRoids.AllCasterAuraTracking[targetGuid] then
|
||||
@@ -1327,7 +1232,7 @@ local function OnAuraCastOther(spellId, casterGuid, targetGuid, effect, effectAu
|
||||
-- (AURA_CAST_ON_OTHER fires for both buffs and debuffs; BUFF_ADDED_OTHER confirms buff)
|
||||
local lib = CleveRoids.libdebuff
|
||||
if lib and not lib.hasPfUIEnhanced then
|
||||
local spellNameForPending = GetSpellRecField and GetSpellRecField(spellId, "name")
|
||||
local spellNameForPending = C_Spell.GetSpellName(spellId)
|
||||
if spellNameForPending then
|
||||
local normTargetGuid = CleveRoids.NormalizeGUID(targetGuid)
|
||||
if normTargetGuid then
|
||||
@@ -1472,7 +1377,7 @@ autoAttackFrame:SetScript("OnEvent", function()
|
||||
|
||||
if spellId and spellId > 0 and durationMs and durationMs > 0 then
|
||||
local playerGUID = CleveRoids.GetGUID("player")
|
||||
local durSpellName = GetSpellRecField and GetSpellRecField(spellId, "name")
|
||||
local durSpellName = C_Spell.GetSpellName(spellId)
|
||||
if playerGUID and durSpellName and not CleveRoids.hasPfUI76 then
|
||||
CleveRoids._allCasterAuraDirty = true
|
||||
if not CleveRoids.AllCasterAuraTracking[playerGUID] then
|
||||
@@ -1524,7 +1429,7 @@ autoAttackFrame:SetScript("OnEvent", function()
|
||||
local state = arg7
|
||||
if state == 2 then return end -- Stack change, not full removal
|
||||
if guid and spellId and CleveRoids.AllCasterAuraTracking[guid] then
|
||||
local removedName = GetSpellRecField and GetSpellRecField(spellId, "name")
|
||||
local removedName = C_Spell.GetSpellName(spellId)
|
||||
if removedName and CleveRoids.AllCasterAuraTracking[guid][removedName] then
|
||||
CleveRoids.AllCasterAuraTracking[guid][removedName] = nil
|
||||
if not next(CleveRoids.AllCasterAuraTracking[guid]) then
|
||||
@@ -1880,7 +1785,7 @@ local function IsPendingDebuffCast(spellName, targetUnit)
|
||||
if arr then
|
||||
for _, pending in pairs(arr) do
|
||||
if pending and pending.targetGUID == targetGuid and pending.spellID then
|
||||
local pendingName = GetSpellRecField and GetSpellRecField(pending.spellID, "name")
|
||||
local pendingName = C_Spell.GetSpellName(pending.spellID)
|
||||
if pendingName then
|
||||
local normalizedPending = NormalizeSpellNameForComparison(pendingName)
|
||||
if normalizedPending == normalizedCheck then
|
||||
@@ -2018,7 +1923,7 @@ function CleveRoids.CancelAura(auraName)
|
||||
if aura_ix == -1 then break end
|
||||
local bid = GetPlayerBuffID(aura_ix)
|
||||
bid = (bid < -1) and (bid + 65536) or bid
|
||||
if string.lower(GetSpellRecField(bid, "name") or "") == auraName then
|
||||
if string.lower(C_Spell.GetSpellName(bid) or "") == auraName then
|
||||
C_Spell.CancelSpellByID(bid)
|
||||
return true
|
||||
end
|
||||
@@ -2031,7 +1936,7 @@ function CleveRoids.CancelAura(auraName)
|
||||
for slot = 0, 31 do
|
||||
local spellId = _G.GetPlayerAuraDuration(slot)
|
||||
if spellId and spellId > 0 then
|
||||
local name = GetSpellRecField(spellId, "name")
|
||||
local name = C_Spell.GetSpellName(spellId)
|
||||
if name and string.lower(name) == auraName then
|
||||
C_Spell.CancelSpellByID(spellId)
|
||||
return true
|
||||
@@ -2047,7 +1952,7 @@ function CleveRoids.CancelAura(auraName)
|
||||
if entry.durationSec and entry.durationSec > 0 and elapsed > entry.durationSec then
|
||||
CleveRoids.OverflowBuffs[spellId] = nil
|
||||
else
|
||||
local name = GetSpellRecField(spellId, "name")
|
||||
local name = C_Spell.GetSpellName(spellId)
|
||||
if name and string.lower(name) == auraName then
|
||||
C_Spell.CancelSpellByID(spellId)
|
||||
CleveRoids.OverflowBuffs[spellId] = nil
|
||||
@@ -2059,26 +1964,12 @@ function CleveRoids.CancelAura(auraName)
|
||||
return false
|
||||
end
|
||||
|
||||
-- ClassicAPI's C_Item.IsEquippedItem walks the 19 equipment slots natively and
|
||||
-- short-circuits on the first match, so it replaces the old Lua-side equipment
|
||||
-- cache entirely -- no per-call scan, no invalidation. Accepts itemID, item
|
||||
-- link, or (case-insensitive, decorated) name.
|
||||
function CleveRoids.HasGearEquipped(gearId)
|
||||
if not gearId then return false end
|
||||
|
||||
-- PERFORMANCE: Build/refresh equipment cache if needed
|
||||
BuildEquipmentCache()
|
||||
|
||||
-- Handle both numeric IDs and string IDs like "5196"
|
||||
local wantId = tonumber(gearId)
|
||||
local wantName = (type(gearId) == "string" and not wantId) and string.lower(gearId) or nil
|
||||
|
||||
-- PERFORMANCE: Use cached data instead of scanning all slots
|
||||
for slot = 1, 19 do
|
||||
if wantId and _equippedItemIDs[slot] == wantId then
|
||||
return true
|
||||
end
|
||||
if wantName and _equippedItemNames[slot] == wantName then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
return (gearId and C_Item.IsEquippedItem(gearId)) or false
|
||||
end
|
||||
|
||||
|
||||
@@ -2952,11 +2843,8 @@ function CleveRoids.CountEnemiesMatching(checkFunc)
|
||||
tryUnit("targettarget")
|
||||
tryUnit("targettargettarget")
|
||||
tryUnit("pettarget")
|
||||
if pfUI and pfUI.uf and pfUI.uf.focus and pfUI.uf.focus.label and pfUI.uf.focus.id then
|
||||
local focusUnit = pfUI.uf.focus.label .. pfUI.uf.focus.id
|
||||
tryUnit(focusUnit)
|
||||
tryUnit(focusUnit .. "target")
|
||||
end
|
||||
tryUnit("focus")
|
||||
tryUnit("focustarget")
|
||||
for i = 1, 4 do
|
||||
tryUnit("party" .. i .. "target")
|
||||
end
|
||||
@@ -2966,22 +2854,8 @@ function CleveRoids.CountEnemiesMatching(checkFunc)
|
||||
end
|
||||
end
|
||||
|
||||
-- 3. Nameplate scan: visible nameplates give live GUIDs without target switching.
|
||||
-- ClassicAPI's C_NamePlate.GetNamePlateGUIDs() lists every unit with an
|
||||
-- allocated nameplate (including default vanilla nameplates), replacing the
|
||||
-- old WorldFrame child-walk + frame:GetName(1) GUID-extraction.
|
||||
local plateGuids = CleveRoids.ClassicAPI.GetNamePlateGUIDs()
|
||||
if plateGuids then
|
||||
for i = 1, table.getn(plateGuids) do
|
||||
local guid = plateGuids[i]
|
||||
if guid and not checked[guid] and UnitExists(guid) and UnitCanAttack("player", guid) then
|
||||
checked[guid] = true
|
||||
CleveRoids.knownEnemyGuids[guid] = true
|
||||
if checkFunc(guid) then
|
||||
count = count + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
for i = 1, 40 do
|
||||
tryUnit("nameplate"..i)
|
||||
end
|
||||
|
||||
-- During tooltip evaluation, skip known-enemy cache iteration.
|
||||
@@ -3297,9 +3171,9 @@ end
|
||||
-- returns: True or false
|
||||
function CleveRoids.ValidatePower(unit, operator, amount)
|
||||
if not unit or not operator or not amount then return false end
|
||||
local API = CleveRoids.NampowerAPI
|
||||
local power = API and API.GetUnitPower and API.GetUnitPower(unit) or UnitMana(unit)
|
||||
local maxPower = API and API.GetUnitMaxPower and API.GetUnitMaxPower(unit) or UnitManaMax(unit)
|
||||
local API = CleveRoids.ClassicAPI
|
||||
local power = API.UnitPower(unit)
|
||||
local maxPower = API.UnitPowerMax(unit)
|
||||
local powerPercent = maxPower > 0 and (100 * power / maxPower) or 0
|
||||
|
||||
if CleveRoids.operators[operator] then
|
||||
@@ -3309,6 +3183,26 @@ function CleveRoids.ValidatePower(unit, operator, amount)
|
||||
return false
|
||||
end
|
||||
|
||||
-- Validates a SPECIFIC power slot (Enum.PowerType: 0=Mana, 1=Rage, 3=Energy) as a
|
||||
-- percentage, regardless of the unit's primary power -- so [mana]/[energy]/[rage]
|
||||
-- read the right pool cross-form (druid mana in cat) and cross-unit (@target mana).
|
||||
-- A unit with no such pool (max <= 0) fails rather than reading as 0% (avoids a
|
||||
-- rageless caster satisfying [rage:<10]).
|
||||
function CleveRoids.ValidateTypedPower(unit, powerType, operator, amount)
|
||||
if not unit or not operator or not amount then return false end
|
||||
if not UnitExists(unit) then return false end
|
||||
local API = CleveRoids.ClassicAPI
|
||||
local maxPower = API.UnitPowerMax(unit, powerType)
|
||||
if not maxPower or maxPower <= 0 then return false end
|
||||
local powerPercent = 100 * API.UnitPower(unit, powerType) / maxPower
|
||||
|
||||
if CleveRoids.operators[operator] then
|
||||
return CleveRoids.comparators[operator](powerPercent, amount)
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
-- Checks whether or not the given unit has current power vs the given amount
|
||||
-- unit: The unit we're checking
|
||||
-- operator: valid comparitive operator symbol
|
||||
@@ -3316,8 +3210,7 @@ end
|
||||
-- returns: True or false
|
||||
function CleveRoids.ValidateRawPower(unit, operator, amount)
|
||||
if not unit or not operator or not amount then return false end
|
||||
local API = CleveRoids.NampowerAPI
|
||||
local power = API and API.GetUnitPower and API.GetUnitPower(unit) or UnitMana(unit)
|
||||
local power = CleveRoids.ClassicAPI.UnitPower(unit)
|
||||
|
||||
if power and CleveRoids.operators[operator] then
|
||||
return CleveRoids.comparators[operator](power, amount)
|
||||
@@ -3326,23 +3219,15 @@ function CleveRoids.ValidateRawPower(unit, operator, amount)
|
||||
return false
|
||||
end
|
||||
|
||||
-- Raw caster-form mana for druids (SuperWoW: 2nd return of UnitMana)
|
||||
-- Raw caster-form mana for druids. Read the mana power slot directly
|
||||
-- (0 = Enum.PowerType.Mana) so it works while shapeshifted, when the druid's
|
||||
-- primary power is rage/energy -- no SuperWoW 2nd-return-of-UnitMana needed.
|
||||
function CleveRoids.ValidateDruidRawMana(unit, operator, amount)
|
||||
unit = unit or "player"
|
||||
if not operator or amount == nil then return false end
|
||||
if (CleveRoids.playerClass ~= "DRUID") then return false end
|
||||
|
||||
-- SuperWoW returns: current-form power, caster-form mana
|
||||
local _, casterMana = UnitMana(unit)
|
||||
|
||||
-- Fallback: if for some reason we didn't get a 2nd value and we're in caster form now
|
||||
if type(casterMana) ~= "number" then
|
||||
if UnitPowerType and UnitPowerType(unit) == 0 then
|
||||
casterMana = UnitMana(unit)
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
local casterMana = CleveRoids.ClassicAPI.UnitPower(unit, 0)
|
||||
|
||||
local cmp = CleveRoids.comparators and CleveRoids.comparators[operator]
|
||||
return cmp and cmp(casterMana, amount) or false
|
||||
@@ -3355,10 +3240,7 @@ end
|
||||
-- returns: True or false
|
||||
function CleveRoids.ValidatePowerLost(unit, operator, amount)
|
||||
if not unit or not operator or not amount then return false end
|
||||
local API = CleveRoids.NampowerAPI
|
||||
local maxPower = API and API.GetUnitMaxPower and API.GetUnitMaxPower(unit) or UnitManaMax(unit)
|
||||
local power = API and API.GetUnitPower and API.GetUnitPower(unit) or UnitMana(unit)
|
||||
local powerLost = maxPower - power
|
||||
local powerLost = CleveRoids.ClassicAPI.UnitPowerMissing(unit)
|
||||
|
||||
if CleveRoids.operators[operator] then
|
||||
return CleveRoids.comparators[operator](powerLost, amount)
|
||||
@@ -3410,10 +3292,7 @@ end
|
||||
-- returns: True or false
|
||||
function CleveRoids.ValidateHpLost(unit, operator, amount)
|
||||
if not unit or not operator or not amount then return false end
|
||||
local API = CleveRoids.NampowerAPI
|
||||
local maxHp = API and API.GetUnitMaxHealth and API.GetUnitMaxHealth(unit) or UnitHealthMax(unit)
|
||||
local hp = API and API.GetUnitHealth and API.GetUnitHealth(unit) or UnitHealth(unit)
|
||||
local hpLost = maxHp - hp
|
||||
local hpLost = CleveRoids.ClassicAPI.UnitHealthMissing(unit)
|
||||
|
||||
if CleveRoids.operators[operator] then
|
||||
return CleveRoids.comparators[operator](hpLost, amount)
|
||||
@@ -3530,7 +3409,7 @@ local function GetLowercaseSpellName(spellID)
|
||||
local cached = _spellNameCache[spellID]
|
||||
if cached then return cached end
|
||||
|
||||
local name = GetSpellRecField(spellID, "name")
|
||||
local name = C_Spell.GetSpellName(spellID)
|
||||
if not name then return nil end
|
||||
|
||||
-- Strip rank and lowercase
|
||||
@@ -3553,7 +3432,7 @@ local function GetSpellNames(spellID)
|
||||
return cached.base, cached.full
|
||||
end
|
||||
|
||||
local fullName = GetSpellRecField(spellID, "name")
|
||||
local fullName = C_Spell.GetSpellName(spellID)
|
||||
if not fullName then return nil, nil end
|
||||
|
||||
local baseName = _string_gsub(fullName, _RANK_PATTERN, "")
|
||||
@@ -4208,7 +4087,7 @@ function CleveRoids.ValidateUnitDebuff(unit, args)
|
||||
local fallbackNameLower = _string_lower(args.name)
|
||||
for sid, rec in pairs(lib.objects[guid]) do
|
||||
if rec and rec.caster == "player" then
|
||||
local n = GetSpellRecField and GetSpellRecField(sid, "name")
|
||||
local n = C_Spell.GetSpellName(sid)
|
||||
if n then
|
||||
n = CleveRoids.StripRank(n)
|
||||
if _string_lower(n) == fallbackNameLower then
|
||||
@@ -4295,7 +4174,7 @@ function CleveRoids.ValidateUnitDebuff(unit, args)
|
||||
local cleanupNameLower = _string_lower(args.name)
|
||||
for sid, rec in pairs(lib.objects[guid]) do
|
||||
if rec and rec.caster == "player" then
|
||||
local n = GetSpellRecField and GetSpellRecField(sid, "name")
|
||||
local n = C_Spell.GetSpellName(sid)
|
||||
if n then
|
||||
n = CleveRoids.StripRank(n)
|
||||
if _string_lower(n) == cleanupNameLower then
|
||||
@@ -4629,19 +4508,9 @@ function CleveRoids.ValidatePlayerDebuff(args)
|
||||
end
|
||||
|
||||
function CleveRoids.ValidateWeaponImbue(slot, args)
|
||||
-- Check if weapon has enchant via API
|
||||
local hasMainEnchant, mainExpiration, mainCharges, hasOffEnchant, offExpiration, offCharges = GetWeaponEnchantInfo()
|
||||
|
||||
local hasEnchant, expiration, charges
|
||||
if slot == "mh" then
|
||||
hasEnchant = hasMainEnchant
|
||||
expiration = mainExpiration
|
||||
charges = mainCharges
|
||||
else
|
||||
hasEnchant = hasOffEnchant
|
||||
expiration = offExpiration
|
||||
charges = offCharges
|
||||
end
|
||||
-- Temp-enchant state for this slot, read via ClassicAPI (centralizes the
|
||||
-- 12-tuple indexing; enchantID unused here but powers [mhenchant]).
|
||||
local hasEnchant, expiration, charges = CleveRoids.ClassicAPI.GetWeaponEnchant(slot)
|
||||
|
||||
-- Only consider temporary enchants (with time or charges)
|
||||
-- This filters out permanent enchants like Crusader, Lifestealing, etc.
|
||||
@@ -4756,6 +4625,19 @@ function CleveRoids.CheckWeaponImbueByName(slot, imbueName)
|
||||
return true -- No name to check
|
||||
end
|
||||
|
||||
-- Fast path: resolve the applied temp-enchant's ID -> localized name via
|
||||
-- ClassicAPI (SpellItemEnchantment.dbc) and match that directly -- exact,
|
||||
-- locale-clean, no green-text tooltip heuristics. Only nameless enchants
|
||||
-- (e.g. sharpening stones, which show "+N Weapon Damage" and carry no DBC
|
||||
-- name) return nil here and fall through to the tooltip scan below.
|
||||
local _, _, _, enchantID = CleveRoids.ClassicAPI.GetWeaponEnchant(slot)
|
||||
local enchantName = enchantID and CleveRoids.ClassicAPI.GetEnchantName(enchantID)
|
||||
if enchantName then
|
||||
local nlower = GetLowerNormalizedName(enchantName)
|
||||
local want = GetLowerNormalizedName(imbueName)
|
||||
return nlower == want or string.find(nlower, want, 1, true) ~= nil
|
||||
end
|
||||
|
||||
-- Create tooltip scanner if needed
|
||||
if not CleveRoidsTooltip then
|
||||
CreateFrame("GameTooltip", "CleveRoidsTooltip", nil, "GameTooltipTemplate")
|
||||
@@ -4796,6 +4678,48 @@ function CleveRoids.CheckWeaponImbueByName(slot, imbueName)
|
||||
return false
|
||||
end
|
||||
|
||||
-- Does a single [mhenchant]/[ohenchant] value match the applied enchant?
|
||||
-- value is a numeric enchant ID (exact) or a name (resolved from the applied
|
||||
-- enchantID via ClassicAPI's SpellItemEnchantment lookup -- no tooltip scan).
|
||||
-- Name match is case-insensitive, exact-or-substring (so "Deadly" matches
|
||||
-- "Deadly Poison"). A parsed-arg table (from an operator form) uses its .name.
|
||||
local function EnchantValueMatches(value, enchantID)
|
||||
if type(value) == "table" then value = value.name end
|
||||
if not value or value == "" then return false end
|
||||
local wantId = tonumber(value)
|
||||
if wantId then
|
||||
return enchantID == wantId
|
||||
end
|
||||
local name = CleveRoids.ClassicAPI.GetEnchantName(enchantID)
|
||||
if not name then return false end
|
||||
local nlower = GetLowerNormalizedName(name)
|
||||
local want = GetLowerNormalizedName(value)
|
||||
return nlower == want or string.find(nlower, want, 1, true) ~= nil
|
||||
end
|
||||
|
||||
-- Matches the temporary weapon enchant on `slot` ("mh"/"oh") by enchant ID or
|
||||
-- localized name, both resolved via ClassicAPI (C_Item.GetWeaponEnchantInfo +
|
||||
-- GetEnchantInfo) -- locale-proof and exact, unlike the tooltip-scanned
|
||||
-- [mhimbue:Name]. value may be true/nil (any temp enchant), a single ID/name,
|
||||
-- or an OR-list array; returns true if the applied enchant matches any entry.
|
||||
function CleveRoids.ValidateWeaponEnchant(slot, value)
|
||||
local hasEnchant, expiration, charges, enchantID = CleveRoids.ClassicAPI.GetWeaponEnchant(slot)
|
||||
local hasTemp = hasEnchant and ((expiration and expiration > 0) or (charges and charges > 0))
|
||||
if not hasTemp then return false end
|
||||
|
||||
-- Bare [mhenchant]: any temporary enchant present.
|
||||
if value == nil or value == true then return true end
|
||||
|
||||
-- OR-list (e.g. [mhenchant:2823/Deadly_Poison]) is an array of strings.
|
||||
if type(value) == "table" and not value.name and table.getn(value) > 0 then
|
||||
for i = 1, table.getn(value) do
|
||||
if EnchantValueMatches(value[i], enchantID) then return true end
|
||||
end
|
||||
return false
|
||||
end
|
||||
return EnchantValueMatches(value, enchantID)
|
||||
end
|
||||
|
||||
-- TODO: Look into https://github.com/Stanzilla/WoWUIBugs/issues/47 if needed
|
||||
-- PERFORMANCE: Uncached version - called by GetCachedCooldown
|
||||
function CleveRoids._GetCooldownUncached(name, ignoreGCD)
|
||||
@@ -5089,7 +5013,7 @@ function CleveRoids.CheckSpellCast(unit, spell)
|
||||
if not CleveRoids.spell_tracking[guid] then
|
||||
return false
|
||||
else
|
||||
if spell == GetSpellRecField(CleveRoids.spell_tracking[guid].spell_id, "name") or (spell == "") then
|
||||
if spell == C_Spell.GetSpellName(CleveRoids.spell_tracking[guid].spell_id) or (spell == "") then
|
||||
return true
|
||||
end
|
||||
return false
|
||||
@@ -5177,6 +5101,33 @@ function CleveRoids.GetSpellMechanic(spellID)
|
||||
return CleveRoids.ClassicAPI.GetSpellMechanicByID(spellID) or 0
|
||||
end
|
||||
|
||||
-- School name -> spell-school mask bit (WoW SPELL_SCHOOL_MASK_*).
|
||||
local LOCKOUT_SCHOOL_MASK = {
|
||||
physical = 1, holy = 2, fire = 4, nature = 8, frost = 16, shadow = 32, arcane = 64,
|
||||
}
|
||||
|
||||
-- Is `bit` set in `mask`? Shift right past `bit`, then test the low bit with
|
||||
-- math.mod (the % operator is 5.1-only; 1.12 is Lua 5.0, but math.mod exists).
|
||||
local function SchoolMaskHasBit(mask, bit)
|
||||
if not mask or mask < bit then return false end
|
||||
return math.mod(math.floor(mask / bit), 2) == 1
|
||||
end
|
||||
|
||||
-- [locked] / [locked:school] -- player is under a spell-school interrupt lockout
|
||||
-- (Counterspell / Kick / Pummel / Earth Shock), read from C_LossOfControl. This
|
||||
-- is the lockout no debuff scan can detect; full silences stay on [mycc:silence].
|
||||
-- school nil/true = any school kicked; a name matches that school's mask bit.
|
||||
function CleveRoids.ValidateSchoolLocked(school)
|
||||
local mask = CleveRoids.ClassicAPI.GetSchoolLockout()
|
||||
if not mask or mask == 0 then return false end
|
||||
if school == nil or school == true or school == "" then
|
||||
return true
|
||||
end
|
||||
local bit = LOCKOUT_SCHOOL_MASK[string.lower(school)]
|
||||
if not bit then return false end
|
||||
return SchoolMaskHasBit(mask, bit)
|
||||
end
|
||||
|
||||
-- Validate CC on a unit (target, focus, player, etc.)
|
||||
-- Returns true if the unit has the specified CC mechanic active
|
||||
function CleveRoids.ValidateUnitCC(unit, ccType)
|
||||
@@ -5425,6 +5376,33 @@ local function ResolveFocusUnit(unit)
|
||||
return unit
|
||||
end
|
||||
|
||||
-- Builds a Keyword validator for a specific power slot (Enum.PowerType) read as a
|
||||
-- percentage, mirroring [power]/[mypower]. condKey is the conditional name;
|
||||
-- unitDefault is "player" (self) or "target" (@unit-overridable); powerType is the
|
||||
-- Enum.PowerType id. Handles the multi-comparison (>50&<80) branch like [power].
|
||||
local function MakeTypedPowerKeyword(condKey, unitDefault, powerType)
|
||||
return function(conditionals)
|
||||
return Multi(conditionals[condKey], function(args)
|
||||
if type(args) ~= "table" then return false end
|
||||
local unit = (unitDefault == "player") and "player" or (conditionals.target or "target")
|
||||
|
||||
if args.comparisons and type(args.comparisons) == "table" then
|
||||
if not UnitExists(unit) then return false end
|
||||
local maxPower = CleveRoids.ClassicAPI.UnitPowerMax(unit, powerType)
|
||||
if not maxPower or maxPower <= 0 then return false end
|
||||
local powerPercent = 100 * CleveRoids.ClassicAPI.UnitPower(unit, powerType) / maxPower
|
||||
for _, comp in ipairs(args.comparisons) do
|
||||
if not CleveRoids.operators[comp.operator] then return false end
|
||||
if not CleveRoids.comparators[comp.operator](powerPercent, comp.amount) then return false end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
return CleveRoids.ValidateTypedPower(unit, powerType, args.operator, args.amount)
|
||||
end, conditionals, condKey)
|
||||
end
|
||||
end
|
||||
|
||||
-- A list of Conditionals and their functions to validate them
|
||||
CleveRoids.Keywords = {
|
||||
exists = function(conditionals)
|
||||
@@ -5608,6 +5586,23 @@ CleveRoids.Keywords = {
|
||||
return not CleveRoids.ClassicAPI.IsStealthed()
|
||||
end,
|
||||
|
||||
mounted = function(conditionals)
|
||||
return CleveRoids.ClassicAPI.IsMounted()
|
||||
end,
|
||||
|
||||
nomounted = function(conditionals)
|
||||
return not CleveRoids.ClassicAPI.IsMounted()
|
||||
end,
|
||||
|
||||
standing = function(conditionals)
|
||||
return CleveRoids.ClassicAPI.GetPlayerStandState() == 0
|
||||
end,
|
||||
|
||||
-- Any non-standing pose (sit / chair / sleep / kneel)
|
||||
sitting = function(conditionals)
|
||||
return CleveRoids.ClassicAPI.GetPlayerStandState() ~= 0
|
||||
end,
|
||||
|
||||
casting = function(conditionals)
|
||||
if type(conditionals.casting) ~= "table" then return CleveRoids.CheckSpellCast(conditionals.target, "") end
|
||||
return Or(conditionals.casting, function (spell)
|
||||
@@ -5786,6 +5781,26 @@ CleveRoids.Keywords = {
|
||||
end, conditionals, "noset")
|
||||
end,
|
||||
|
||||
-- [equipset:Name] — true if the saved equipment set "Name" is currently
|
||||
-- equipped (ClassicAPI equipment manager, distinct from [set] tier pieces).
|
||||
-- [equipset:Raid/PvP] = either set equipped. Names are case-sensitive; use _
|
||||
-- for spaces, e.g. [equipset:My_Raid_Set].
|
||||
equipset = function(conditionals)
|
||||
return Multi(conditionals.equipset, function(v)
|
||||
local name = (type(v) == "table") and v.name or v
|
||||
return CleveRoids.ClassicAPI.IsEquipmentSetEquipped(name)
|
||||
end, conditionals, "equipset")
|
||||
end,
|
||||
|
||||
-- [noequipset:Name] — true if that equipment set is NOT currently equipped.
|
||||
-- [noequipset:Raid/PvP] = neither equipped (De Morgan's).
|
||||
noequipset = function(conditionals)
|
||||
return NegatedMulti(conditionals.noequipset, function(v)
|
||||
local name = (type(v) == "table") and v.name or v
|
||||
return not CleveRoids.ClassicAPI.IsEquipmentSetEquipped(name)
|
||||
end, conditionals, "noequipset")
|
||||
end,
|
||||
|
||||
-- [inbag:Item] — true if item exists in bags or equipped
|
||||
-- [inbag:Item<12] — true if bag count of Item is less than 12
|
||||
-- Supports multi-value: [inbag:Item1/Item2 inbag:Item3] = (Item1 OR Item2) AND Item3
|
||||
@@ -6008,14 +6023,14 @@ CleveRoids.Keywords = {
|
||||
local groupVal = conditionals.group
|
||||
-- Boolean form [group] - check if in any group
|
||||
if groupVal == true then
|
||||
return GetNumPartyMembers() > 0 or GetNumRaidMembers() > 0
|
||||
return IsInGroup()
|
||||
end
|
||||
-- Value form [group:party] or [group:raid] or [group:party/raid]
|
||||
return Multi(groupVal, function(groupType)
|
||||
if groupType == "party" then
|
||||
return GetNumPartyMembers() > 0
|
||||
return IsInGroup()
|
||||
elseif groupType == "raid" then
|
||||
return GetNumRaidMembers() > 0
|
||||
return IsInRaid()
|
||||
end
|
||||
return false
|
||||
end, conditionals, "group")
|
||||
@@ -6027,14 +6042,14 @@ CleveRoids.Keywords = {
|
||||
local groupVal = conditionals.nogroup
|
||||
-- Boolean form [nogroup] - check if not in any group
|
||||
if groupVal == true then
|
||||
return GetNumPartyMembers() == 0 and GetNumRaidMembers() == 0
|
||||
return not IsInGroup()
|
||||
end
|
||||
-- Value form with De Morgan's law via NegatedMulti
|
||||
return NegatedMulti(groupVal, function(groupType)
|
||||
if groupType == "party" then
|
||||
return GetNumPartyMembers() == 0
|
||||
return not IsInGroup()
|
||||
elseif groupType == "raid" then
|
||||
return GetNumRaidMembers() == 0
|
||||
return not IsInRaid()
|
||||
end
|
||||
return true
|
||||
end, conditionals, "nogroup")
|
||||
@@ -6254,6 +6269,17 @@ CleveRoids.Keywords = {
|
||||
end, conditionals, "mypower")
|
||||
end,
|
||||
|
||||
-- Type-specific power reads (percentage), for the SPECIFIC slot rather than the
|
||||
-- unit's primary power. No prefix = target (@unit-overridable), my = player.
|
||||
-- e.g. [@target,mana:<15] catches a near-OOM caster; [myenergy:>50] gates a
|
||||
-- druid's cat rotation regardless of form.
|
||||
mana = MakeTypedPowerKeyword("mana", "target", 0),
|
||||
mymana = MakeTypedPowerKeyword("mymana", "player", 0),
|
||||
rage = MakeTypedPowerKeyword("rage", "target", 1),
|
||||
myrage = MakeTypedPowerKeyword("myrage", "player", 1),
|
||||
energy = MakeTypedPowerKeyword("energy", "target", 3),
|
||||
myenergy = MakeTypedPowerKeyword("myenergy", "player", 3),
|
||||
|
||||
rawpower = function(conditionals)
|
||||
return Multi(conditionals.rawpower, function(args)
|
||||
if type(args) ~= "table" then return false end
|
||||
@@ -7216,6 +7242,26 @@ CleveRoids.Keywords = {
|
||||
return not CleveRoids.ClassicAPI.IsSwimming()
|
||||
end,
|
||||
|
||||
-- [indoors] - Player is under a WMO roof (building, cave, instance interior)
|
||||
indoors = function(conditionals)
|
||||
return CleveRoids.ClassicAPI.IsIndoors()
|
||||
end,
|
||||
|
||||
-- [noindoors] - Player is NOT indoors
|
||||
noindoors = function(conditionals)
|
||||
return not CleveRoids.ClassicAPI.IsIndoors()
|
||||
end,
|
||||
|
||||
-- [outdoors] - Player is outdoors (open sky / not inside a WMO interior)
|
||||
outdoors = function(conditionals)
|
||||
return CleveRoids.ClassicAPI.IsOutdoors()
|
||||
end,
|
||||
|
||||
-- [nooutdoors] - Player is NOT outdoors
|
||||
nooutdoors = function(conditionals)
|
||||
return not CleveRoids.ClassicAPI.IsOutdoors()
|
||||
end,
|
||||
|
||||
-- [rooted] - Player is currently rooted (Nampower v2.36+)
|
||||
rooted = function(conditionals)
|
||||
if not CleveRoids.NampowerAPI.features.hasPlayerIsRooted then
|
||||
@@ -7588,6 +7634,24 @@ CleveRoids.Keywords = {
|
||||
return not CleveRoids.ValidateWeaponImbue("oh", args)
|
||||
end,
|
||||
|
||||
-- [mhenchant:ID] / [mhenchant:Name] - main hand has that SPECIFIC temporary
|
||||
-- weapon enchant, matched by SpellItemEnchantment ID or localized name via
|
||||
-- ClassicAPI (locale-proof, unlike [mhimbue:Name]'s tooltip scan). Bare
|
||||
-- [mhenchant] = any temp enchant; [mhenchant:A/B] = either; [nomhenchant:X]
|
||||
-- = not that enchant. Names use _ for spaces, e.g. [mhenchant:Deadly_Poison].
|
||||
mhenchant = function(conditionals)
|
||||
return CleveRoids.ValidateWeaponEnchant("mh", conditionals.mhenchant)
|
||||
end,
|
||||
nomhenchant = function(conditionals)
|
||||
return not CleveRoids.ValidateWeaponEnchant("mh", conditionals.nomhenchant)
|
||||
end,
|
||||
ohenchant = function(conditionals)
|
||||
return CleveRoids.ValidateWeaponEnchant("oh", conditionals.ohenchant)
|
||||
end,
|
||||
noohenchant = function(conditionals)
|
||||
return not CleveRoids.ValidateWeaponEnchant("oh", conditionals.noohenchant)
|
||||
end,
|
||||
|
||||
immune = function(conditionals)
|
||||
-- Check if target is immune to the spell being cast or damage school
|
||||
-- Usage: [immune] SpellName OR [immune:SpellName] OR [immune:fire]
|
||||
@@ -8247,7 +8311,7 @@ CleveRoids.Keywords = {
|
||||
if not UnitExists(unit) then return false end
|
||||
|
||||
return Or(conditionals.powertype, function(powerTypeName)
|
||||
local powerType = UnitPowerType(unit)
|
||||
local powerType = CleveRoids.ClassicAPI.UnitPowerType(unit)
|
||||
local powerTypeLower = string.lower(powerTypeName or "")
|
||||
|
||||
if powerTypeLower == "mana" then
|
||||
@@ -8270,7 +8334,7 @@ CleveRoids.Keywords = {
|
||||
if not UnitExists(unit) then return true end
|
||||
|
||||
return NegatedMulti(conditionals.nopowertype, function(powerTypeName)
|
||||
local powerType = UnitPowerType(unit)
|
||||
local powerType = CleveRoids.ClassicAPI.UnitPowerType(unit)
|
||||
local powerTypeLower = string.lower(powerTypeName or "")
|
||||
|
||||
if powerTypeLower == "mana" then
|
||||
@@ -8355,6 +8419,33 @@ CleveRoids.Keywords = {
|
||||
end, conditionals, "nomycc")
|
||||
end,
|
||||
|
||||
-- [locked] / [locked:school] - player is under a spell-school interrupt
|
||||
-- lockout (Counterspell/Kick/Pummel/Earth Shock), via ClassicAPI
|
||||
-- C_LossOfControl -- the server lockout no debuff scan can see. Bare = any
|
||||
-- school kicked; [locked:frost] = that school; [locked:fire/frost] = either.
|
||||
-- Full silences are separate ([mycc:silence]). Player-only.
|
||||
locked = function(conditionals)
|
||||
local v = conditionals.locked
|
||||
if v == nil or v == true or (type(v) == "table" and table.getn(v) == 0) then
|
||||
return CleveRoids.ValidateSchoolLocked(nil)
|
||||
end
|
||||
return Or(v, function(school)
|
||||
return CleveRoids.ValidateSchoolLocked(school)
|
||||
end)
|
||||
end,
|
||||
|
||||
-- [nolocked:school] - player is NOT school-locked. AND logic on negation:
|
||||
-- [nolocked:fire/frost] = neither Fire nor Frost is kicked.
|
||||
nolocked = function(conditionals)
|
||||
local v = conditionals.nolocked
|
||||
if v == nil or v == true or (type(v) == "table" and table.getn(v) == 0) then
|
||||
return not CleveRoids.ValidateSchoolLocked(nil)
|
||||
end
|
||||
return NegatedMulti(v, function(school)
|
||||
return not CleveRoids.ValidateSchoolLocked(school)
|
||||
end, conditionals, "nolocked")
|
||||
end,
|
||||
|
||||
-- ========================================================================
|
||||
-- RESIST TRACKING CONDITIONALS
|
||||
-- ========================================================================
|
||||
@@ -9182,6 +9273,7 @@ CleveRoids.STATIC_CONDITIONALS = {
|
||||
combat = true, nocombat = true, ic = true, ooc = true,
|
||||
zone = true, nozone = true,
|
||||
stealth = true, nostealth = true, stl = true, nostl = true,
|
||||
mounted = true, nomounted = true, standing = true, sitting = true,
|
||||
form = true, noform = true, stance = true, nostance = true,
|
||||
equipped = true, noequipped = true, eq = true, noeq = true,
|
||||
set = true, noset = true,
|
||||
@@ -9189,6 +9281,7 @@ CleveRoids.STATIC_CONDITIONALS = {
|
||||
mod = true, nomod = true,
|
||||
keydown = true, nokeydown = true,
|
||||
swimming = true, noswimming = true, swim = true, noswim = true,
|
||||
indoors = true, noindoors = true, outdoors = true, nooutdoors = true,
|
||||
rooted = true, norooted = true,
|
||||
resting = true, noresting = true,
|
||||
}
|
||||
|
||||
+10
@@ -63,6 +63,16 @@ SlashCmdList.EQSLOT13 = CleveRoids.DoEquipTrinket1
|
||||
SLASH_EQSLOT141 = "/equip14"
|
||||
SlashCmdList.EQSLOT14 = CleveRoids.DoEquipTrinket2
|
||||
|
||||
-- Reclaim ClassicAPI's /equipset (command name EQUIP_SET) so it supports
|
||||
-- conditionals. ClassicAPI registers the localized aliases against EQUIP_SET;
|
||||
-- swapping the handler keeps every alias and adds the conditional engine.
|
||||
if SlashCmdList.EQUIP_SET then
|
||||
SlashCmdList.EQUIP_SET = CleveRoids.DoEquipSet
|
||||
else
|
||||
SLASH_EQUIPSET1 = "/equipset"
|
||||
SlashCmdList.EQUIPSET = CleveRoids.DoEquipSet
|
||||
end
|
||||
|
||||
SLASH_UNSHIFT1 = "/unshift"
|
||||
|
||||
SlashCmdList.UNSHIFT = CleveRoids.DoUnshift
|
||||
|
||||
@@ -42,6 +42,9 @@ CleveRoids.spellIdCache = {}
|
||||
-- PERFORMANCE: Spell name construction cache
|
||||
CleveRoids.spellNameCache = {}
|
||||
|
||||
CleveRoids.playerGuid = UnitGUID('player')
|
||||
CleveRoids.playerClass = UnitClassBase("player")
|
||||
|
||||
-- PERFORMANCE: Upvalues for frequently called global functions (avoid global lookups)
|
||||
local GetTime = GetTime
|
||||
local UnitExists = UnitExists
|
||||
@@ -121,6 +124,10 @@ local BOOLEAN_CONDITIONALS = {
|
||||
nocombat = true,
|
||||
stealth = true,
|
||||
nostealth = true,
|
||||
mounted = true,
|
||||
nomounted = true,
|
||||
standing = true,
|
||||
sitting = true,
|
||||
channeled = true,
|
||||
nochanneled = true,
|
||||
checkchanneled = true,
|
||||
@@ -140,6 +147,16 @@ local BOOLEAN_CONDITIONALS = {
|
||||
nomhimbue = true,
|
||||
ohimbue = true,
|
||||
noohimbue = true,
|
||||
mhenchant = true,
|
||||
nomhenchant = true,
|
||||
ohenchant = true,
|
||||
noohenchant = true,
|
||||
locked = true,
|
||||
nolocked = true,
|
||||
indoors = true,
|
||||
noindoors = true,
|
||||
outdoors = true,
|
||||
nooutdoors = true,
|
||||
group = true,
|
||||
nogroup = true,
|
||||
moving = true,
|
||||
@@ -940,19 +957,23 @@ function CleveRoids.TestForActiveAction(actions)
|
||||
-- Prefer IsSpellUsable result if available (Nampower)
|
||||
actions.active.oom = (notEnoughPower == 1)
|
||||
else
|
||||
-- SuperWoW: UnitMana returns (current power, caster mana) for druids
|
||||
local currentPower, casterMana = UnitMana("player")
|
||||
|
||||
-- For druids with SuperWoW, use caster mana for spell cost checks
|
||||
local manaToCheck = currentPower
|
||||
if CleveRoids.playerClass == "DRUID" and type(casterMana) == "number" then
|
||||
manaToCheck = casterMana
|
||||
-- Read caster mana directly for druids (the mana slot survives
|
||||
-- shapeshift, 0 = Enum.PowerType.Mana); other classes use their
|
||||
-- primary power. Replaces the SuperWoW UnitMana 2nd-return hack.
|
||||
local manaToCheck
|
||||
if CleveRoids.playerClass == "DRUID" then
|
||||
manaToCheck = CleveRoids.ClassicAPI.UnitPower("player", 0)
|
||||
else
|
||||
manaToCheck = CleveRoids.ClassicAPI.UnitPower("player")
|
||||
end
|
||||
|
||||
actions.active.oom = (manaToCheck < actions.active.spell.cost)
|
||||
end
|
||||
|
||||
local start, duration = GetSpellCooldown(actions.active.spell.spellSlot, actions.active.spell.bookType)
|
||||
local start, duration = 0, 0
|
||||
if actions.active.spell.spellSlot then
|
||||
start, duration = GetSpellCooldown(actions.active.spell.spellSlot, actions.active.spell.bookType)
|
||||
end
|
||||
local onCooldown = (start > 0 and duration > 0)
|
||||
|
||||
if actions.active.isReactive then
|
||||
@@ -1569,18 +1590,9 @@ function CleveRoids.TryTargetFocus()
|
||||
return true
|
||||
end
|
||||
|
||||
-- Resolves the player's focus to a usable unit token.
|
||||
-- Prefers pfUI's emulated focus (resolves to a real token like "party2"/"raid5")
|
||||
-- for pfUI users, then falls back to ClassicAPI's native "focus" token (which
|
||||
-- every UnitX function accepts) for everyone else.
|
||||
-- Returns the resolved token, or nil when no focus is set so @focus clauses
|
||||
-- silently fall through to the next macro alternative (no warning spam).
|
||||
function CleveRoids.GetFocusUnitId()
|
||||
if pfUI and pfUI.uf and pfUI.uf.focus and pfUI.uf.focus.label and pfUI.uf.focus.id
|
||||
and UnitExists(pfUI.uf.focus.label .. pfUI.uf.focus.id) then
|
||||
return pfUI.uf.focus.label .. pfUI.uf.focus.id
|
||||
end
|
||||
-- ClassicAPI native focus token (set via /focus or the FOCUSTARGET keybind)
|
||||
if UnitExists("focus") then
|
||||
return "focus"
|
||||
end
|
||||
@@ -2827,26 +2839,19 @@ function CleveRoids.DoTarget(msg)
|
||||
|
||||
addCandidate("pettarget")
|
||||
|
||||
if pfUI and pfUI.uf and pfUI.uf.focus and pfUI.uf.focus.label and pfUI.uf.focus.id then
|
||||
local focusTok = pfUI.uf.focus.label .. pfUI.uf.focus.id
|
||||
addCandidate(focusTok)
|
||||
addCandidate(focusTok .. "target")
|
||||
end
|
||||
addCandidate("focus")
|
||||
addCandidate("focustarget")
|
||||
|
||||
if CleveRoids.hasSuperwow then
|
||||
local numChildren = WorldFrame:GetNumChildren()
|
||||
local children = { WorldFrame:GetChildren() }
|
||||
|
||||
for i = 1, numChildren do
|
||||
local frame = children[i]
|
||||
if frame and frame:IsVisible() then
|
||||
local success, guid = pcall(frame.GetName, frame, 1)
|
||||
if success and guid and type(guid) == "string" and string.len(guid) > 0 then
|
||||
if UnitExists(guid) then
|
||||
addCandidate(guid)
|
||||
end
|
||||
end
|
||||
end
|
||||
-- Visible nameplates as targeting candidates, via ClassicAPI's nameplateN
|
||||
-- unit tokens -- no SuperWoW WorldFrame child-walk / GUID scrape, and covers
|
||||
-- default vanilla nameplates too. Slots are sparse (a removed plate leaves
|
||||
-- its slot vacant until reused), so scan the full range rather than breaking
|
||||
-- on the first gap; UnitExists returns false cleanly for a free/out-of-range
|
||||
-- slot.
|
||||
for i = 1, 40 do
|
||||
local plate = "nameplate" .. i
|
||||
if UnitExists(plate) then
|
||||
addCandidate(plate)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -3588,6 +3593,32 @@ function CleveRoids.DoEquipRing2(msg)
|
||||
return false
|
||||
end
|
||||
|
||||
-- Equip a ClassicAPI equipment set by name. msg is the resolved set name (after
|
||||
-- conditional parsing). ClassicAPI's own /equipset is a bare
|
||||
-- GetEquipmentSetID -> UseEquipmentSet with no conditional support; routing it
|
||||
-- through DoWithConditionals adds [combat]/[mod]/@unit/etc. and ";"-separated
|
||||
-- fallthrough (first set whose conditionals pass wins). Nil-guarded so a client
|
||||
-- without the EquipmentSet API just no-ops instead of erroring.
|
||||
local function _equipSetAction(msg)
|
||||
if not msg or msg == "" then return false end
|
||||
if type(C_EquipmentSet) ~= "table" then return false end
|
||||
local setID = C_EquipmentSet.GetEquipmentSetID(msg)
|
||||
if not setID then return false end
|
||||
C_EquipmentSet.UseEquipmentSet(setID)
|
||||
return true
|
||||
end
|
||||
|
||||
function CleveRoids.DoEquipSet(msg)
|
||||
local parts = CleveRoids.splitStringIgnoringQuotes(msg)
|
||||
for i = 1, table.getn(parts) do
|
||||
local v = string.gsub(parts[i], "^%?", "")
|
||||
if CleveRoids.DoWithConditionals(v, _equipSetAction, CleveRoids.FixEmptyTarget, false, _equipSetAction) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function CleveRoids.DoCancelForm(msg)
|
||||
local handled
|
||||
-- PERFORMANCE: Use numeric iteration to avoid pairs() iterator allocation
|
||||
@@ -3935,30 +3966,15 @@ function CleveRoids.OnUpdate(self)
|
||||
|
||||
CR.lastUpdate = time
|
||||
|
||||
-- Reclaim the top of the SendChatMessage hook chain if another addon has
|
||||
-- displaced our #showtooltip filter (see EnsureSendChatMessageHook). Cheap
|
||||
-- identity check; only re-hooks when actually displaced.
|
||||
if CR.EnsureSendChatMessageHook then
|
||||
CR.EnsureSendChatMessageHook()
|
||||
-- LeafVillageAchievements/Legends also hook SendChatMessage and can bump ours
|
||||
-- off the top of the chain (orphaning it / decorating #showtooltip). Reclaim
|
||||
-- the top when they're present so #showtooltip stays filtered.
|
||||
if not CR._watchChatHooks then
|
||||
CR._watchChatHooks = (C_AddOns.IsAddOnLoaded("LeafVillageAchievements")
|
||||
or C_AddOns.IsAddOnLoaded("LeafVillageLegends"))
|
||||
end
|
||||
|
||||
-- Process deferred equipment index updates (for throttled UNIT_INVENTORY_CHANGED)
|
||||
-- PERFORMANCE: Skip check entirely if no pending update
|
||||
local pendingTime = CR.equipIndexPendingTime
|
||||
if pendingTime and not UnitAffectingCombat("player") then
|
||||
if (time - (CR.lastEquipIndexTime or 0)) >= 0.2 then
|
||||
CR.lastEquipIndexTime = time
|
||||
CR.equipIndexPendingTime = nil
|
||||
CR.lastItemIndexTime = time
|
||||
CR.IndexItems()
|
||||
CR.Actions = {}
|
||||
CR.Macros = {}
|
||||
CR.IndexActionBars()
|
||||
|
||||
if CRM.realtime == 0 then
|
||||
CR.QueueActionUpdate()
|
||||
end
|
||||
end
|
||||
if CR._watchChatHooks then
|
||||
CR.EnsureSendChatMessageHook()
|
||||
end
|
||||
|
||||
-- PERFORMANCE: Check for expired reactive procs only if we have any
|
||||
@@ -4176,11 +4192,7 @@ function GameTooltip.SetAction(self, slot)
|
||||
|
||||
local current_spell_data = CleveRoids.GetSpell(action_name)
|
||||
if current_spell_data and current_spell_data.id then
|
||||
if current_spell_data.spellSlot and current_spell_data.bookType then
|
||||
GameTooltip:SetSpell(current_spell_data.spellSlot, current_spell_data.bookType)
|
||||
else
|
||||
GameTooltip:SetSpellByID(current_spell_data.id)
|
||||
end
|
||||
GameTooltip:SetSpellByID(current_spell_data.id)
|
||||
GameTooltip:Show()
|
||||
return
|
||||
end
|
||||
@@ -4613,7 +4625,7 @@ function GetActionCooldown(slot)
|
||||
return GetInventoryItemCooldown("player", slotId)
|
||||
end
|
||||
|
||||
if a.spell then
|
||||
if a.spell and a.spell.spellSlot then
|
||||
return GetSpellCooldown(a.spell.spellSlot, a.spell.bookType)
|
||||
elseif a.item then
|
||||
if a.item.bagID and a.item.slot then
|
||||
@@ -4811,8 +4823,8 @@ CleveRoids.Frame:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||||
CleveRoids.Frame:RegisterEvent("UPDATE_MACROS")
|
||||
CleveRoids.Frame:RegisterEvent("SPELLS_CHANGED")
|
||||
CleveRoids.Frame:RegisterEvent("ACTIONBAR_SLOT_CHANGED")
|
||||
CleveRoids.Frame:RegisterEvent("BAG_UPDATE")
|
||||
CleveRoids.Frame:RegisterEvent("UNIT_INVENTORY_CHANGED")
|
||||
CleveRoids.Frame:RegisterEvent("BAG_UPDATE_DELAYED")
|
||||
CleveRoids.Frame:RegisterEvent("PLAYER_EQUIPMENT_CHANGED")
|
||||
CleveRoids.Frame:RegisterEvent("UNIT_PET")
|
||||
|
||||
-- == STATE CHANGE EVENT REGISTRATION (for performance) ==
|
||||
@@ -4825,6 +4837,13 @@ CleveRoids.Frame:RegisterEvent("PLAYER_REGEN_ENABLED") -- Left actual combat (n
|
||||
CleveRoids.Frame:RegisterEvent("UPDATE_SHAPESHIFT_FORM")
|
||||
CleveRoids.Frame:RegisterEvent("SPELL_UPDATE_COOLDOWN")
|
||||
CleveRoids.Frame:RegisterEvent("PLAYER_STARTED_MOVING")
|
||||
-- ClassicAPI loss-of-control (school-interrupt lockout) for [locked]/[nolocked].
|
||||
-- Gated on the namespace so an older ClassicAPI without it doesn't error on an
|
||||
-- unknown event.
|
||||
if type(C_LossOfControl) == "table" then
|
||||
CleveRoids.Frame:RegisterEvent("LOSS_OF_CONTROL_ADDED")
|
||||
CleveRoids.Frame:RegisterEvent("LOSS_OF_CONTROL_UPDATE")
|
||||
end
|
||||
-- Use GUID events when available (v2.39+), fall back to standard per-token events
|
||||
if CleveRoids.NampowerAPI.features.hasUnitGuidEvents then
|
||||
CleveRoids.Frame:RegisterEvent("UNIT_AURA_GUID")
|
||||
@@ -4939,7 +4958,6 @@ function CleveRoids.Frame:PLAYER_LOGIN()
|
||||
if CleveRoids.disabled then return end
|
||||
|
||||
_, CleveRoids.playerClass = UnitClass("player")
|
||||
_, CleveRoids.playerGuid = UnitExists("player")
|
||||
CleveRoids.IndexSpells()
|
||||
CleveRoids.IndexPetSpells()
|
||||
CleveRoids.initializationTimer = GetTime() + 1.5
|
||||
@@ -5028,7 +5046,7 @@ function CleveRoids.Frame:UNIT_CASTEVENT(caster,target,action,spell_id,cast_time
|
||||
-- Refresh the Judgement by updating the start time
|
||||
rec.start = GetTime()
|
||||
|
||||
local spellName = GetSpellRecField(spellID, "name")
|
||||
local spellName = C_Spell.GetSpellName(spellID)
|
||||
local baseName = CleveRoids.StripRank(spellName) or "Unknown"
|
||||
|
||||
if CleveRoids.debug then
|
||||
@@ -5064,7 +5082,7 @@ function CleveRoids.Frame:UNIT_CASTEVENT(caster,target,action,spell_id,cast_time
|
||||
|
||||
-- Debug channel tracking
|
||||
if CleveRoids.ChannelTimeDebug then
|
||||
local spellName = spell_id and GetSpellRecField and GetSpellRecField(spell_id, "name") or "Unknown"
|
||||
local spellName = spell_id and C_Spell.GetSpellName(spell_id) or "Unknown"
|
||||
if string.find(spellName, "Arcane") then
|
||||
DEFAULT_CHAT_FRAME:AddMessage(string.format("|cff00ff00[UNIT_CASTEVENT]|r %s: %s (ID:%s) caster=%s player=%s",
|
||||
action, spellName, tostring(spell_id), tostring(caster), tostring(CleveRoids.playerGuid)))
|
||||
@@ -5137,8 +5155,8 @@ function CleveRoids.Frame:UNIT_CASTEVENT(caster,target,action,spell_id,cast_time
|
||||
if CleveRoids.currentSequence and caster == CleveRoids.playerGuid then
|
||||
local active = CleveRoids.GetCurrentSequenceAction(CleveRoids.currentSequence)
|
||||
|
||||
local name = GetSpellRecField(spell_id, "name")
|
||||
local rank = GetSpellRecField(spell_id, "rank")
|
||||
local name = C_Spell.GetSpellName(spell_id)
|
||||
local rank = C_Spell.GetSpellSubtext(spell_id)
|
||||
local nameRank = (rank and rank ~= "") and (name .. "(" .. rank .. ")") or nil
|
||||
local isSeqSpell = active and active.action and (
|
||||
active.action == name or
|
||||
@@ -5178,7 +5196,7 @@ function CleveRoids.Frame:SPELL_CAST_EVENT(success, spellId, castType, targetGui
|
||||
CleveRoids.CurrentSpell.type = "channeled"
|
||||
CleveRoids.CurrentSpell.castingSpellId = spellId
|
||||
|
||||
local spellName = GetSpellRecField and GetSpellRecField(spellId, "name")
|
||||
local spellName = C_Spell.GetSpellName(spellId)
|
||||
if spellName then
|
||||
CleveRoids.CurrentSpell.spellName = spellName
|
||||
end
|
||||
@@ -5204,8 +5222,8 @@ function CleveRoids.Frame:SPELL_CAST_EVENT(success, spellId, castType, targetGui
|
||||
if CleveRoids.currentSequence and success == 1 then
|
||||
local active = CleveRoids.GetCurrentSequenceAction(CleveRoids.currentSequence)
|
||||
if active and active.action then
|
||||
local name = GetSpellRecField(spellId, "name")
|
||||
local rank = GetSpellRecField(spellId, "rank")
|
||||
local name = C_Spell.GetSpellName(spellId)
|
||||
local rank = C_Spell.GetSpellSubtext(spellId)
|
||||
local nameRank = (rank and rank ~= "") and (name .. "(" .. rank .. ")") or nil
|
||||
local isSeqSpell = (active.action == name or (nameRank and active.action == nameRank))
|
||||
|
||||
@@ -5222,7 +5240,7 @@ function CleveRoids.Frame:SPELL_CAST_EVENT(success, spellId, castType, targetGui
|
||||
if CleveRoids.currentSequence and success == 0 then
|
||||
local active = CleveRoids.GetCurrentSequenceAction(CleveRoids.currentSequence)
|
||||
if active and active.action then
|
||||
local name = GetSpellRecField(spellId, "name")
|
||||
local name = C_Spell.GetSpellName(spellId)
|
||||
local isSeqSpell = (active.action == name)
|
||||
if isSeqSpell then
|
||||
CleveRoids.currentSequence.status = 1 -- Reset to retry
|
||||
@@ -5280,8 +5298,8 @@ function CleveRoids.Frame:SPELL_START_SELF(casterGuid, targetGuid, spellId, cast
|
||||
if CleveRoids.currentSequence and castTimeMs and castTimeMs > 0 then
|
||||
local active = CleveRoids.GetCurrentSequenceAction(CleveRoids.currentSequence)
|
||||
if active and active.action then
|
||||
local name = GetSpellRecField(spellId, "name")
|
||||
local rank = GetSpellRecField(spellId, "rank")
|
||||
local name = C_Spell.GetSpellName(spellId)
|
||||
local rank = C_Spell.GetSpellSubtext(spellId)
|
||||
local nameRank = (rank and rank ~= "") and (name .. "(" .. rank .. ")") or nil
|
||||
local isSeqSpell = (active.action == name or (nameRank and active.action == nameRank))
|
||||
|
||||
@@ -5318,7 +5336,7 @@ function CleveRoids.Frame:SPELL_FAILED_SELF(casterGuid, targetGuid, spellId, ...
|
||||
if CleveRoids.currentSequence then
|
||||
local active = CleveRoids.GetCurrentSequenceAction(CleveRoids.currentSequence)
|
||||
if active and active.action then
|
||||
local name = GetSpellRecField(spellId, "name")
|
||||
local name = C_Spell.GetSpellName(spellId)
|
||||
local isSeqSpell = name and (active.action == name)
|
||||
if isSeqSpell then
|
||||
CleveRoids.currentSequence.status = 1 -- Reset to retry
|
||||
@@ -5357,7 +5375,7 @@ function CleveRoids.Frame:SPELLCAST_CHANNEL_START()
|
||||
-- Update spell info
|
||||
if spellId then
|
||||
CleveRoids.CurrentSpell.castingSpellId = spellId
|
||||
local spellName = GetSpellRecField(spellId, "name")
|
||||
local spellName = C_Spell.GetSpellName(spellId)
|
||||
if spellName then
|
||||
CleveRoids.CurrentSpell.spellName = spellName
|
||||
end
|
||||
@@ -5437,7 +5455,7 @@ function CleveRoids.Frame:SPELLCAST_START()
|
||||
-- Update spell info
|
||||
if spellId then
|
||||
CleveRoids.CurrentSpell.castingSpellId = spellId
|
||||
local spellName = GetSpellRecField(spellId, "name")
|
||||
local spellName = C_Spell.GetSpellName(spellId)
|
||||
if spellName then
|
||||
CleveRoids.CurrentSpell.spellName = spellName
|
||||
end
|
||||
@@ -5672,7 +5690,7 @@ function CleveRoids.Frame:ACTIONBAR_SLOT_CHANGED()
|
||||
end
|
||||
end
|
||||
|
||||
function CleveRoids.Frame:BAG_UPDATE()
|
||||
function CleveRoids.Frame:BAG_UPDATE_DELAYED()
|
||||
-- In combat: Skip expensive indexing but still queue icon update
|
||||
-- so conditionals like [inbag] re-evaluate (they use live bag APIs)
|
||||
if UnitAffectingCombat("player") then
|
||||
@@ -5701,34 +5719,22 @@ function CleveRoids.Frame:BAG_UPDATE()
|
||||
end
|
||||
end
|
||||
|
||||
function CleveRoids.Frame:UNIT_INVENTORY_CHANGED()
|
||||
if arg1 ~= "player" then return end
|
||||
function CleveRoids.Frame:PLAYER_EQUIPMENT_CHANGED()
|
||||
-- arg1 = inventory slot that changed, arg2 = hasCurrent (slot now holds an item)
|
||||
local slot, hasCurrent = arg1, arg2
|
||||
|
||||
-- PERFORMANCE: Invalidate equipment cache for HasGearEquipped
|
||||
if CleveRoids.InvalidateEquipmentCache then
|
||||
CleveRoids.InvalidateEquipmentCache()
|
||||
end
|
||||
|
||||
-- In combat: Skip ALL processing - EquipBagItem already handles cache invalidation
|
||||
-- This eliminates lag from IndexEquippedItems during rapid gear swapping
|
||||
-- In combat: skip the slot reindex to avoid lag during rapid gear swapping.
|
||||
-- [equipped] reads live engine state (C_Item.IsEquippedItem) so it stays
|
||||
-- correct regardless; the Items table self-heals on the next BAG_UPDATE.
|
||||
if UnitAffectingCombat("player") then
|
||||
return
|
||||
end
|
||||
|
||||
-- Out of combat: Full indexing with throttle
|
||||
local now = GetTime()
|
||||
if (now - (CleveRoids.lastEquipIndexTime or 0)) < 0.2 then
|
||||
CleveRoids.equipIndexPendingTime = now
|
||||
return
|
||||
end
|
||||
|
||||
CleveRoids.lastEquipIndexTime = now
|
||||
CleveRoids.equipIndexPendingTime = nil
|
||||
CleveRoids.lastItemIndexTime = now
|
||||
CleveRoids.IndexItems()
|
||||
CleveRoids.Actions = {}
|
||||
CleveRoids.Macros = {}
|
||||
CleveRoids.IndexActionBars()
|
||||
-- Out of combat: reindex only the one slot that changed (the bag side of any
|
||||
-- swap is covered by BAG_UPDATE_DELAYED). No action-bar rebuild is needed -
|
||||
-- gear swaps don't change what's on the bars; QueueActionUpdate re-evaluates
|
||||
-- [equipped:...] icons/conditionals.
|
||||
CleveRoids.IndexEquipSlot(slot, hasCurrent)
|
||||
|
||||
if CleveRoidMacros.realtime == 0 then
|
||||
CleveRoids.QueueActionUpdate()
|
||||
@@ -5765,6 +5771,13 @@ function CleveRoids.Frame:PLAYER_FOCUS_CHANGED()
|
||||
CleveRoids.QueueActionUpdate()
|
||||
end
|
||||
end
|
||||
-- School-interrupt lockout applied/changed -> refresh [locked]/[nolocked] icons.
|
||||
function CleveRoids.Frame:LOSS_OF_CONTROL_ADDED()
|
||||
if CleveRoidMacros.realtime == 0 then
|
||||
CleveRoids.QueueActionUpdate()
|
||||
end
|
||||
end
|
||||
CleveRoids.Frame.LOSS_OF_CONTROL_UPDATE = CleveRoids.Frame.LOSS_OF_CONTROL_ADDED
|
||||
function CleveRoids.Frame:UPDATE_SHAPESHIFT_FORM()
|
||||
if CleveRoidMacros.realtime == 0 then
|
||||
CleveRoids.QueueActionUpdate()
|
||||
@@ -5887,11 +5900,9 @@ function CleveRoids.Frame:SPELL_QUEUE_EVENT()
|
||||
queueType = eventCode,
|
||||
queueTime = GetTime()
|
||||
}
|
||||
if GetSpellRecField then
|
||||
local name = GetSpellRecField(spellId, "name")
|
||||
if name then
|
||||
CleveRoids.queuedSpell.spellName = name
|
||||
end
|
||||
local name = C_Spell.GetSpellName(spellId)
|
||||
if name then
|
||||
CleveRoids.queuedSpell.spellName = name
|
||||
end
|
||||
-- BUGFIX: Update casting state when spell is queued (for [casting] conditional)
|
||||
if CleveRoids.UpdateCastingState then
|
||||
@@ -5928,11 +5939,9 @@ function CleveRoids.Frame:SPELL_CAST_EVENT()
|
||||
targetGuid = targetGuid,
|
||||
timestamp = GetTime()
|
||||
}
|
||||
if GetSpellRecField then
|
||||
local name = GetSpellRecField(spellId, "name")
|
||||
if name then
|
||||
CleveRoids.lastCastSpell.spellName = name
|
||||
end
|
||||
local name = C_Spell.GetSpellName(spellId)
|
||||
if name then
|
||||
CleveRoids.lastCastSpell.spellName = name
|
||||
end
|
||||
|
||||
-- Track pending cast for SPELL_GO correlation (reactive ability detection)
|
||||
@@ -5962,7 +5971,7 @@ function CleveRoids.Frame:SPELL_CAST_EVENT()
|
||||
if cp > 0 then
|
||||
CleveRoids.pendingCasts[spellId].comboPoints = cp
|
||||
if CleveRoids.debug then
|
||||
local castSpellName = GetSpellRecField and GetSpellRecField(spellId, "name") or "Unknown"
|
||||
local castSpellName = C_Spell.GetSpellName(spellId) or "Unknown"
|
||||
DEFAULT_CHAT_FRAME:AddMessage(
|
||||
string.format("|cff00ff88[SPELL_CAST_EVENT]|r Captured %d CP for %s (ID:%d)",
|
||||
cp, castSpellName, spellId)
|
||||
@@ -5997,27 +6006,27 @@ function CleveRoids.Frame:KEY_UP()
|
||||
CleveRoids.isActionUpdateQueued = true
|
||||
end
|
||||
|
||||
-- Filter out the #showtooltip line that Blizzard's native macro executor sends
|
||||
-- to chat when a macro runs from an action button. Named (not anonymous) so we
|
||||
-- can detect displacement and reclaim the top of the hook chain.
|
||||
-- Base SendChatMessage captured at first hook; used to break a hook cycle.
|
||||
local baseSendChatMessage = SendChatMessage
|
||||
local sendingChatMessage = false
|
||||
|
||||
-- Swallow #showtooltip lines Blizzard's macro executor sends to chat.
|
||||
local function CleveRoids_SendChatMessage(msg, ...)
|
||||
if msg and string.find(msg, "^#showtooltip") then
|
||||
return
|
||||
end
|
||||
-- Call whatever we chained over (another addon's hook, or the real function)
|
||||
CleveRoids.Hooks.SendChatMessage(msg, unpack(arg))
|
||||
if sendingChatMessage then
|
||||
-- Re-entered via a hook cycle; go straight to base to avoid recursion.
|
||||
return baseSendChatMessage(msg, unpack(arg))
|
||||
end
|
||||
sendingChatMessage = true
|
||||
local ok, err = pcall(CleveRoids.Hooks.SendChatMessage, msg, unpack(arg))
|
||||
sendingChatMessage = false
|
||||
if not ok and geterrorhandler then geterrorhandler()(err) end
|
||||
end
|
||||
|
||||
-- (Re-)assert our filter as the OUTERMOST SendChatMessage hook. This is a no-op
|
||||
-- once we're already on top. It matters because some addons snapshot
|
||||
-- SendChatMessage at their file-load and later install a hook that calls that
|
||||
-- snapshot DIRECTLY -- if they loaded before us, their snapshot predates our
|
||||
-- filter, so calling it directly orphans us and #showtooltip leaks to chat.
|
||||
-- (LeafVillageAchievements does exactly this at PLAYER_ENTERING_WORLD+3s; and
|
||||
-- because the fork sorts after it alphabetically, we load too late to be in its
|
||||
-- snapshot -- upstream "CleveRoidMacros" sorted before it and wasn't affected.)
|
||||
-- OnUpdate calls this so we reclaim the top within a frame of being displaced;
|
||||
-- being outermost also guarantees we see the pristine line for the anchor match.
|
||||
-- Reassert our filter as the outermost SendChatMessage hook (no-op if already on
|
||||
-- top). See the OnUpdate caller for why this is needed.
|
||||
function CleveRoids.EnsureSendChatMessageHook()
|
||||
if SendChatMessage ~= CleveRoids_SendChatMessage then
|
||||
CleveRoids.Hooks.SendChatMessage = SendChatMessage
|
||||
@@ -6226,7 +6235,7 @@ SlashCmdList["CLEVEROID"] = function(msg)
|
||||
CleveRoids_LearnedDurations = CleveRoids_LearnedDurations or {}
|
||||
CleveRoids_LearnedDurations[spellID] = CleveRoids_LearnedDurations[spellID] or {}
|
||||
CleveRoids_LearnedDurations[spellID][playerGUID] = duration
|
||||
local spellName = GetSpellRecField(spellID, "name") or "Unknown"
|
||||
local spellName = C_Spell.GetSpellName(spellID) or "Unknown"
|
||||
CleveRoids.Print("Set " .. spellName .. " (ID:" .. spellID .. ") duration to " .. duration .. "s")
|
||||
else
|
||||
CleveRoids.Print("Usage: /cleveroid learn <spellID> <duration> - Manually set spell duration")
|
||||
@@ -6243,7 +6252,7 @@ SlashCmdList["CLEVEROID"] = function(msg)
|
||||
else
|
||||
local spellID = tonumber(val)
|
||||
if spellID and CleveRoids_LearnedDurations and CleveRoids_LearnedDurations[spellID] then
|
||||
local spellName = GetSpellRecField(spellID, "name") or "Unknown"
|
||||
local spellName = C_Spell.GetSpellName(spellID) or "Unknown"
|
||||
CleveRoids_LearnedDurations[spellID] = nil
|
||||
CleveRoids.Print("Forgot " .. spellName .. " (ID:" .. spellID .. ") duration")
|
||||
elseif spellID then
|
||||
@@ -6429,7 +6438,7 @@ SlashCmdList["CLEVEROID"] = function(msg)
|
||||
|
||||
CleveRoids.Print(schoolColor .. string.upper(school) .. "|r (" .. table.getn(spellIDs) .. " spells):")
|
||||
for _, spellID in ipairs(spellIDs) do
|
||||
local spellName = GetSpellRecField and GetSpellRecField(spellID, "name") or "Unknown"
|
||||
local spellName = C_Spell.GetSpellName(spellID) or "Unknown"
|
||||
CleveRoids.Print(" " .. spellName .. " (ID:" .. spellID .. ")")
|
||||
end
|
||||
end
|
||||
@@ -6472,7 +6481,7 @@ SlashCmdList["CLEVEROID"] = function(msg)
|
||||
CleveRoids.Print("No learned combo durations yet. Cast finishers and let them expire!")
|
||||
else
|
||||
for spellID, cpData in pairs(CleveRoids_ComboDurations) do
|
||||
local spellName = GetSpellRecField(spellID, "name") or ("Spell " .. spellID)
|
||||
local spellName = C_Spell.GetSpellName(spellID) or ("Spell " .. spellID)
|
||||
CleveRoids.Print(spellName .. " (ID:" .. spellID .. "):")
|
||||
for cp = 1, 5 do
|
||||
if cpData[cp] then
|
||||
@@ -6554,7 +6563,7 @@ SlashCmdList["CLEVEROID"] = function(msg)
|
||||
return
|
||||
end
|
||||
|
||||
local spellName = GetSpellRecField(spellID, "name") or ("Spell " .. spellID)
|
||||
local spellName = C_Spell.GetSpellName(spellID) or ("Spell " .. spellID)
|
||||
local modifier = CleveRoids.talentModifiers and CleveRoids.talentModifiers[spellID]
|
||||
|
||||
if not modifier then
|
||||
@@ -6630,7 +6639,7 @@ SlashCmdList["CLEVEROID"] = function(msg)
|
||||
for spellID, rec in pairs(lib.objects[guid]) do
|
||||
if rec and rec.start and rec.duration then
|
||||
local timeRemaining = rec.duration + rec.start - GetTime()
|
||||
local spellName = GetSpellRecField and GetSpellRecField(spellID, "name") or "Unknown"
|
||||
local spellName = C_Spell.GetSpellName(spellID) or "Unknown"
|
||||
local caster = rec.caster or "unknown"
|
||||
local stacks = rec.stacks or 0
|
||||
if timeRemaining > 0 then
|
||||
@@ -6657,7 +6666,7 @@ SlashCmdList["CLEVEROID"] = function(msg)
|
||||
for i = 1, 16 do
|
||||
local texture, stacks, debuffType, spellID = UnitDebuff("target", i)
|
||||
if texture then
|
||||
local spellName = GetSpellRecField and GetSpellRecField(spellID, "name") or "slot" .. i
|
||||
local spellName = C_Spell.GetSpellName(spellID) or "slot" .. i
|
||||
CleveRoids.Print(string.format(" Slot %d: [%d] %s (stacks: %d)",
|
||||
i, spellID or 0, spellName, stacks or 0))
|
||||
debuffCount = debuffCount + 1
|
||||
@@ -6677,7 +6686,7 @@ SlashCmdList["CLEVEROID"] = function(msg)
|
||||
-- Check if this might be an overflow debuff by checking libdebuff durations
|
||||
local isDebuff = lib and lib.durations and lib.durations[spellID]
|
||||
if isDebuff then
|
||||
local spellName = GetSpellRecField and GetSpellRecField(spellID, "name") or "slot" .. i
|
||||
local spellName = C_Spell.GetSpellName(spellID) or "slot" .. i
|
||||
CleveRoids.Print(string.format(" Buff Slot %d (=Debuff %d): [%d] %s (stacks: %d) |cffff8800OVERFLOW|r",
|
||||
i, i + 16, spellID, spellName, stacks or 0))
|
||||
overflowCount = overflowCount + 1
|
||||
@@ -6718,7 +6727,7 @@ SlashCmdList["CLEVEROID"] = function(msg)
|
||||
-- Also check GetSpellRecField
|
||||
if GetSpellRecField then
|
||||
for id = 1, 30000 do
|
||||
local name = GetSpellRecField(id, "name")
|
||||
local name = C_Spell.GetSpellName(id)
|
||||
if name and string.lower(name) == string.lower(searchName) then
|
||||
local found = false
|
||||
for _, existingID in ipairs(foundIDs) do
|
||||
|
||||
@@ -209,8 +209,8 @@ local function InjectCustomSpells()
|
||||
local count = 0
|
||||
for spellID, data in pairs(CleveRoids.CustomCursiveSpells) do
|
||||
-- Get texture from GetSpellRecField + GetSpellIconTexture
|
||||
local name = GetSpellRecField(spellID, "name")
|
||||
local rank = GetSpellRecField(spellID, "rank")
|
||||
local name = C_Spell.GetSpellName(spellID)
|
||||
local rank = C_Spell.GetSpellSubtext(spellID)
|
||||
local texture = CleveRoids.libdebuff and CleveRoids.libdebuff:GetCachedIcon(spellID)
|
||||
if texture then
|
||||
-- Always update/add (in case Cursive reloaded and cleared them)
|
||||
@@ -524,8 +524,8 @@ CleveRoids.HandleConsoleCommand = function(msg)
|
||||
return
|
||||
end
|
||||
|
||||
local name = GetSpellRecField(spellID, "name")
|
||||
local rank = GetSpellRecField(spellID, "rank")
|
||||
local name = C_Spell.GetSpellName(spellID)
|
||||
local rank = C_Spell.GetSpellSubtext(spellID)
|
||||
local texture = CleveRoids.libdebuff and CleveRoids.libdebuff:GetCachedIcon(spellID)
|
||||
if not name then
|
||||
DEFAULT_CHAT_FRAME:AddMessage("|cffff0000Error:|r Spell ID " .. spellID .. " not found.")
|
||||
|
||||
@@ -1391,12 +1391,6 @@ end
|
||||
-- Extension Entry Points
|
||||
-- ============================================================================
|
||||
|
||||
function Extension.OnAddonLoaded()
|
||||
if arg1 == "Blizzard_MacroUI" then
|
||||
InstallHooks()
|
||||
end
|
||||
end
|
||||
|
||||
function Extension.OnLoad()
|
||||
-- Skip if macro checker is disabled
|
||||
if CleveRoidMacros and CleveRoidMacros.macrocheck == 0 then return end
|
||||
@@ -1404,13 +1398,9 @@ function Extension.OnLoad()
|
||||
-- Skip if SuperMacro is loaded (detected at load time)
|
||||
if SuperMacroFrame ~= nil then return end
|
||||
|
||||
-- Listen for macro UI loading
|
||||
Extension.RegisterEvent("ADDON_LOADED", "OnAddonLoaded")
|
||||
|
||||
-- If MacroFrame already exists (unlikely but safe), hook immediately
|
||||
if MacroFrame and MacroFrameText then
|
||||
InstallHooks()
|
||||
end
|
||||
-- Install once Blizzard's macro UI is available (fires immediately if already
|
||||
-- loaded), replacing the ADDON_LOADED listener + manual "already loaded" check.
|
||||
EventUtil.ContinueOnAddOnLoaded("Blizzard_MacroUI", InstallHooks)
|
||||
end
|
||||
|
||||
_G["CleveRoids"] = CleveRoids
|
||||
|
||||
@@ -133,12 +133,6 @@ function Extension.OnMacroFrameLoad()
|
||||
end
|
||||
end
|
||||
|
||||
function Extension.OnAddonLoaded()
|
||||
if arg1 == "Blizzard_MacroUI" then
|
||||
Extension.OnMacroFrameLoad()
|
||||
end
|
||||
end
|
||||
|
||||
function Extension.OnLoad()
|
||||
-- Schedule messages to show after UI is ready
|
||||
local function ShowMessages()
|
||||
@@ -178,16 +172,11 @@ function Extension.OnLoad()
|
||||
end
|
||||
end
|
||||
|
||||
-- Listen for macro UI loading
|
||||
Extension.RegisterEvent("ADDON_LOADED", "OnAddonLoaded")
|
||||
-- Hook the macro UI once available (fires immediately if already loaded).
|
||||
EventUtil.ContinueOnAddOnLoaded("Blizzard_MacroUI", Extension.OnMacroFrameLoad)
|
||||
|
||||
-- Also try to hook MacroFrame_SaveMacro if it already exists
|
||||
if MacroFrame_SaveMacro then
|
||||
Extension.OnMacroFrameLoad()
|
||||
end
|
||||
|
||||
-- Register PLAYER_LOGIN to show status messages
|
||||
Extension.RegisterEvent("PLAYER_LOGIN", "OnPlayerLogin")
|
||||
-- Status messages on login (currently disabled inside OnPlayerLogin).
|
||||
EventUtil.ContinueOnPlayerLogin(Extension.OnPlayerLogin)
|
||||
|
||||
-- Store the message function for later
|
||||
Extension.ShowMessages = ShowMessages
|
||||
|
||||
@@ -6,7 +6,6 @@ local _G = _G or getfenv(0)
|
||||
local CleveRoids = _G.CleveRoids or {}
|
||||
|
||||
local Extension = CleveRoids.RegisterExtension("CT_RaidAssist")
|
||||
Extension.RegisterEvent("ADDON_LOADED", "OnLoad")
|
||||
|
||||
function Extension.OnEnter()
|
||||
local tempOptions = CT_RAMenu_Options["temp"]
|
||||
@@ -48,15 +47,14 @@ function Extension.OnLeave()
|
||||
CleveRoids.ClearMouseoverFrom("native")
|
||||
end
|
||||
|
||||
function Extension.OnLoad()
|
||||
if arg1 ~= "CT_RaidAssist" then
|
||||
return
|
||||
end
|
||||
function Extension.OnLoad() end
|
||||
|
||||
function Extension.OnAddOnLoad()
|
||||
if not CT_RA_MemberFrame_OnEnter then return end
|
||||
Extension.Hook("CT_RA_MemberFrame_OnEnter", "OnEnter")
|
||||
Extension.HookMethod(_G["GameTooltip"], "Hide", "OnLeave")
|
||||
Extension.HookMethod(_G["GameTooltip"], "FadeOut", "OnLeave")
|
||||
|
||||
end
|
||||
|
||||
_G["CleveRoids"] = CleveRoids
|
||||
EventUtil.ContinueOnAddOnLoaded("CT_RaidAssist", Extension.OnAddOnLoad)
|
||||
|
||||
@@ -6,7 +6,6 @@ local _G = _G or getfenv(0)
|
||||
local CleveRoids = _G.CleveRoids or {}
|
||||
|
||||
local Extension = CleveRoids.RegisterExtension("CT_UnitFrames")
|
||||
Extension.RegisterEvent("ADDON_LOADED", "OnLoad")
|
||||
|
||||
function Extension.SetHook(widget)
|
||||
local hookedOnEnter = widget:GetScript("OnEnter")
|
||||
@@ -24,8 +23,10 @@ function Extension.SetHook(widget)
|
||||
end)
|
||||
end
|
||||
|
||||
function Extension.OnLoad()
|
||||
if arg1 ~= "CT_UnitFrames" or not CT_AssistFrame then
|
||||
function Extension.OnLoad() end
|
||||
|
||||
function Extension.OnAddOnLoad()
|
||||
if not CT_AssistFrame then
|
||||
return
|
||||
end
|
||||
CleveRoids.Print("CT_UnitFrames module loaded.")
|
||||
@@ -36,4 +37,4 @@ function Extension.OnLoad()
|
||||
Extension.SetHook(CT_AssistFrame_Drag)
|
||||
end
|
||||
|
||||
_G["CleveRoids"] = CleveRoids
|
||||
EventUtil.ContinueOnAddOnLoaded("CT_UnitFrames", Extension.OnAddOnLoad)
|
||||
|
||||
@@ -9,7 +9,6 @@ local CleveRoids = _G.CleveRoids or {}
|
||||
CleveRoids.Hooks = CleveRoids.Hooks or {}
|
||||
|
||||
local Extension = CleveRoids.RegisterExtension("CursiveMouseover")
|
||||
Extension.RegisterEvent("ADDON_LOADED", "OnLoad")
|
||||
|
||||
local hooked = false
|
||||
|
||||
@@ -66,21 +65,13 @@ local function HookCursiveUI()
|
||||
end
|
||||
|
||||
function Extension.OnLoad()
|
||||
-- Try to hook when Cursive loads
|
||||
if arg1 == "Cursive" then
|
||||
-- Delay slightly to ensure Cursive.ui is initialized
|
||||
local frame = CreateFrame("Frame")
|
||||
frame:SetScript("OnUpdate", function()
|
||||
if HookCursiveUI() then
|
||||
this:Hide()
|
||||
end
|
||||
end)
|
||||
end
|
||||
-- Delay slightly to ensure Cursive.ui is initialized
|
||||
local frame = CreateFrame("Frame")
|
||||
frame:SetScript("OnUpdate", function()
|
||||
if HookCursiveUI() then
|
||||
this:Hide()
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
-- Also try to hook immediately in case Cursive is already loaded
|
||||
if Cursive and Cursive.ui then
|
||||
HookCursiveUI()
|
||||
end
|
||||
|
||||
_G["CleveRoids"] = CleveRoids
|
||||
EventUtil.ContinueOnAddOnLoaded("Cursive", Extension.OnLoad)
|
||||
|
||||
@@ -6,7 +6,6 @@ local _G = _G or getfenv(0)
|
||||
local CleveRoids = _G.CleveRoids or {}
|
||||
|
||||
local Extension = CleveRoids.RegisterExtension("DiscordUnitFrames")
|
||||
Extension.RegisterEvent("ADDON_LOADED", "OnLoad")
|
||||
|
||||
function Extension.OnEnterFrame()
|
||||
CleveRoids.SetMouseoverFrom("duf", this.unit)
|
||||
@@ -26,11 +25,10 @@ function Extension.OnLeaveElement()
|
||||
CleveRoids.ClearMouseoverFrom("native")
|
||||
end
|
||||
|
||||
function Extension.OnLoad()
|
||||
if arg1 ~= "DiscordUnitFrames" then
|
||||
return
|
||||
end
|
||||
function Extension.OnLoad() end
|
||||
|
||||
function Extension.OnAddOnLoad()
|
||||
if not DUF_UnitFrame_OnEnter then return end
|
||||
CleveRoids.ClearHooks()
|
||||
Extension.Hook("DUF_UnitFrame_OnEnter", "OnEnterFrame")
|
||||
Extension.Hook("DUF_UnitFrame_OnLeave", "OnLeaveFrame")
|
||||
@@ -39,4 +37,4 @@ function Extension.OnLoad()
|
||||
Extension.Hook("DUF_Element_OnLeave", "OnLeaveElement")
|
||||
end
|
||||
|
||||
_G["CleveRoids"] = CleveRoids
|
||||
EventUtil.ContinueOnAddOnLoaded("DiscordUnitFrames", Extension.OnAddOnLoad)
|
||||
|
||||
@@ -8,7 +8,6 @@ local CleveRoids = _G.CleveRoids or {}
|
||||
CleveRoids.Hooks = CleveRoids.Hooks or {}
|
||||
|
||||
local Extension = CleveRoids.RegisterExtension("Grid")
|
||||
Extension.RegisterEvent("ADDON_LOADED", "OnLoad")
|
||||
|
||||
function Extension.OnEnter(unit)
|
||||
CleveRoids.SetMouseoverFrom("grid", unit)
|
||||
@@ -19,11 +18,10 @@ function Extension.OnLeave()
|
||||
CleveRoids.ClearMouseoverFrom("native")
|
||||
end
|
||||
|
||||
function Extension.OnLoad()
|
||||
if arg1 ~= "Grid" then
|
||||
return
|
||||
end
|
||||
function Extension.OnLoad() end
|
||||
|
||||
function Extension.OnAddOnLoad()
|
||||
if not GridFrame then return end
|
||||
CleveRoids.Hooks.Grid = { CreateFrames = GridFrame.frameClass.prototype.CreateFrames}
|
||||
GridFrame.frameClass.prototype.CreateFrames = CleveRoids.GrdCreateFrames
|
||||
end
|
||||
@@ -111,4 +109,4 @@ function CleveRoids:GrdCreateFrames()
|
||||
ClickCastFrames[self.frame] = true
|
||||
end
|
||||
|
||||
_G["CleveRoids"] = CleveRoids
|
||||
EventUtil.ContinueOnAddOnLoaded("Grid", Extension.OnAddOnLoad)
|
||||
|
||||
@@ -8,7 +8,6 @@ local CleveRoids = _G.CleveRoids or {}
|
||||
local CreateFrames = nil
|
||||
|
||||
local Extension = CleveRoids.RegisterExtension("NotGrid")
|
||||
Extension.RegisterEvent("ADDON_LOADED", "OnLoad")
|
||||
|
||||
function Extension.OnEnter()
|
||||
CleveRoids.SetMouseoverFrom("ngrid", this.unit)
|
||||
@@ -39,7 +38,9 @@ function CleveRoids:NotGrid_CreateFrames()
|
||||
end
|
||||
end
|
||||
|
||||
function Extension.OnLoad()
|
||||
function Extension.OnLoad() end
|
||||
|
||||
function Extension.OnAddOnLoad()
|
||||
-- NotGrid loads before CleveRoids, so if NotGrid is enabled, then it's global will exist.
|
||||
if not NotGrid then
|
||||
return
|
||||
@@ -48,7 +49,6 @@ function Extension.OnLoad()
|
||||
CreateFrames = NotGrid.CreateFrames
|
||||
NotGrid.CreateFrames = CleveRoids.NotGrid_CreateFrames
|
||||
|
||||
Extension.UnregisterEvent("ADDON_LOADED", "Onload")
|
||||
end
|
||||
|
||||
_G["CleveRoids"] = CleveRoids
|
||||
EventUtil.ContinueOnAddOnLoaded("NotGrid", Extension.OnAddOnLoad)
|
||||
|
||||
@@ -8,7 +8,6 @@ local CleveRoids = _G.CleveRoids or {}
|
||||
CleveRoids.Hooks = CleveRoids.Hooks or {}
|
||||
|
||||
local Extension = CleveRoids.RegisterExtension("PerfectRaid")
|
||||
Extension.RegisterEvent("ADDON_LOADED", "OnLoad")
|
||||
|
||||
function Extension.OnEnter(unit)
|
||||
CleveRoids.SetMouseoverFrom("praid", unit)
|
||||
@@ -19,11 +18,10 @@ function Extension.OnLeave()
|
||||
CleveRoids.ClearMouseoverFrom("native")
|
||||
end
|
||||
|
||||
function Extension.OnLoad()
|
||||
if arg1 ~= "PerfectRaid" then
|
||||
return
|
||||
end
|
||||
function Extension.OnLoad() end
|
||||
|
||||
function Extension.OnAddOnLoad()
|
||||
if not PerfectRaid then return end
|
||||
CleveRoids.Hooks.PerfectRaid = { CreateFrame = PerfectRaid.CreateFrame }
|
||||
PerfectRaid.CreateFrame = CleveRoids.PerfectRaidCreateFrame
|
||||
end
|
||||
@@ -134,4 +132,4 @@ function CleveRoids.PerfectRaidCreateFrame(self, num)
|
||||
--]]
|
||||
end
|
||||
|
||||
_G["CleveRoids"] = CleveRoids
|
||||
EventUtil.ContinueOnAddOnLoaded("PerfectRaid", Extension.OnAddOnLoad)
|
||||
|
||||
@@ -4,7 +4,6 @@ local CleveRoids = _G.CleveRoids or {}
|
||||
CleveRoids.Hooks = CleveRoids.Hooks or {}
|
||||
|
||||
local Extension = CleveRoids.RegisterExtension("ag_UnitFrames")
|
||||
Extension.RegisterEvent("ADDON_LOADED", "OnLoad")
|
||||
|
||||
function Extension.OnEnter(unit)
|
||||
CleveRoids.SetMouseoverFrom("aguf", unit)
|
||||
@@ -23,7 +22,6 @@ function Extension.OnLoad()
|
||||
CleveRoids.Hooks.ag_UnitFrames = { OnEnter = aUF.classes.aUFunit.prototype.OnEnter, OnLeave = aUF.classes.aUFunit.prototype.OnLeave}
|
||||
aUF.classes.aUFunit.prototype.OnEnter = CleveRoids.aUFOnEnter
|
||||
aUF.classes.aUFunit.prototype.OnLeave = CleveRoids.aUFOnLeave
|
||||
Extension.UnregisterEvent("ADDON_LOADED", "Onload")
|
||||
end
|
||||
|
||||
-- Taken from ag_UnitClass.lua
|
||||
@@ -41,4 +39,4 @@ function CleveRoids:aUFOnLeave()
|
||||
end
|
||||
|
||||
|
||||
_G["CleveRoids"] = CleveRoids
|
||||
EventUtil.ContinueOnAddOnLoaded("ag_UnitFrames", Extension.OnLoad)
|
||||
|
||||
@@ -8,6 +8,9 @@
|
||||
- Resolving a real UnitID when .unit isn't set
|
||||
- Properly hooking party group[0] (your own party slot) with a safe closure and defaulting to "player"
|
||||
]]
|
||||
|
||||
if pfPlayer and pfPlayer.GetAttribute and pfPlayer:GetAttribute('unit') == 'player' then return end
|
||||
|
||||
local _G = _G or getfenv(0)
|
||||
local CleveRoids = _G.CleveRoids or {}
|
||||
|
||||
@@ -31,7 +34,6 @@ local function ResolvePfUnit(frame, fallbackName)
|
||||
name = strlower(name)
|
||||
|
||||
local candidates = { "target", "targettarget", "player", "pet" }
|
||||
local i
|
||||
for i = 1, 4 do
|
||||
table.insert(candidates, "party"..i)
|
||||
table.insert(candidates, "partypet"..i)
|
||||
@@ -153,7 +155,6 @@ end
|
||||
function Extension.RegisterPartyScripts()
|
||||
if not pfUI or not pfUI.uf or not pfUI.uf.group then return end
|
||||
|
||||
local i
|
||||
for i = 0, 4 do
|
||||
local frame = pfUI.uf.group[i]
|
||||
if frame then
|
||||
@@ -181,7 +182,6 @@ end
|
||||
function Extension.RegisterRaidScripts()
|
||||
if not pfUI or not pfUI.uf or not pfUI.uf.raid then return end
|
||||
|
||||
local i
|
||||
for i = 1, 40 do
|
||||
local frame = pfUI.uf.raid[i]
|
||||
if frame then
|
||||
@@ -333,7 +333,6 @@ end
|
||||
function Extension.RegisterRaidMarkScripts()
|
||||
if not pfUI or not pfUI.raidmarkers or not pfUI.raidmarkers.rows then return end
|
||||
|
||||
local i
|
||||
for i = 1, 8 do
|
||||
local row = pfUI.raidmarkers.rows[i]
|
||||
if row then
|
||||
|
||||
@@ -6,7 +6,6 @@ local _G = _G or getfenv(0)
|
||||
local CleveRoids = _G.CleveRoids or {}
|
||||
|
||||
local Extension = CleveRoids.RegisterExtension("sRaidFrames")
|
||||
Extension.RegisterEvent("ADDON_LOADED", "OnLoad")
|
||||
|
||||
function Extension:OnEnter(frame)
|
||||
CleveRoids.SetMouseoverFrom("sraid", frame.unit)
|
||||
@@ -17,14 +16,13 @@ function Extension.OnLeave()
|
||||
CleveRoids.ClearMouseoverFrom("native")
|
||||
end
|
||||
|
||||
function Extension.OnLoad()
|
||||
if arg1 ~= "sRaidFrames" then
|
||||
return
|
||||
end
|
||||
function Extension.OnLoad() end
|
||||
|
||||
function Extension.OnAddOnLoad()
|
||||
if not sRaidFrames then return end
|
||||
Extension.HookMethod(sRaidFrames, "UnitTooltip", "OnEnter")
|
||||
Extension.HookMethod(_G["GameTooltip"], "Hide", "OnLeave")
|
||||
Extension.HookMethod(_G["GameTooltip"], "FadeOut", "OnLeave")
|
||||
end
|
||||
|
||||
_G["CleveRoids"] = CleveRoids
|
||||
EventUtil.ContinueOnAddOnLoaded("sRaidFrames", Extension.OnAddOnLoad)
|
||||
|
||||
@@ -122,7 +122,7 @@ local function CreateIconButton(parent, index, iconTable)
|
||||
GameTooltip:SetOwner(btn, "ANCHOR_BOTTOMLEFT")
|
||||
local spellName
|
||||
if data.spellId then
|
||||
spellName = GetSpellRecField and GetSpellRecField(data.spellId, "name") or ("Spell " .. data.spellId)
|
||||
spellName = C_Spell.GetSpellName(data.spellId) or ("Spell " .. data.spellId)
|
||||
else
|
||||
spellName = data.displayName
|
||||
end
|
||||
@@ -649,7 +649,7 @@ local function InjectTestTargetData()
|
||||
for i = 1, table.getn(TEST_TARGET_SPELL_IDS) do
|
||||
local spellId = TEST_TARGET_SPELL_IDS[i]
|
||||
local dur = targetDurations[i] or 60
|
||||
local spellName = GetSpellRecField and GetSpellRecField(spellId, "name") or ("TestSpell" .. spellId)
|
||||
local spellName = C_Spell.GetSpellName(spellId) or ("TestSpell" .. spellId)
|
||||
if not CleveRoids.AllCasterAuraTracking[targetGuid][spellName] then
|
||||
CleveRoids.AllCasterAuraTracking[targetGuid][spellName] = {}
|
||||
end
|
||||
|
||||
@@ -156,53 +156,27 @@ end
|
||||
|
||||
-- Lightweight equipment-only indexing for combat situations
|
||||
-- Updates existing cache rather than rebuilding it
|
||||
function CleveRoids.IndexEquippedItems()
|
||||
local items = CleveRoids.Items or {}
|
||||
|
||||
for inventoryID = 1, 19 do
|
||||
local itemID = GetInventoryItemID("player", inventoryID)
|
||||
if itemID then
|
||||
local name, link, _, _, itemType, itemSubType, _, _, texture = GetItemInfo(itemID)
|
||||
if name then
|
||||
local count = GetInventoryItemCount("player", inventoryID)
|
||||
if not items[name] then
|
||||
items[name] = {
|
||||
inventoryID = inventoryID,
|
||||
id = itemID,
|
||||
name = name,
|
||||
count = count,
|
||||
texture = texture,
|
||||
link = link,
|
||||
}
|
||||
items[itemID] = name
|
||||
local lowerName = string.lower(name)
|
||||
if lowerName ~= name then
|
||||
items[lowerName] = name
|
||||
end
|
||||
else
|
||||
-- Update existing entry with current equipment state
|
||||
items[name].inventoryID = inventoryID
|
||||
items[name].count = count
|
||||
end
|
||||
end
|
||||
else
|
||||
-- Slot is now empty - clear inventoryID from any item that was there
|
||||
-- This is handled lazily by GetItem() fallback, so we skip expensive iteration
|
||||
end
|
||||
end
|
||||
|
||||
CleveRoids.lastGetItem = nil
|
||||
CleveRoids.Items = items
|
||||
end
|
||||
|
||||
-- PERFORMANCE: Index a single equipment slot instead of all 20
|
||||
-- Use when we know exactly which slot changed (e.g., from EquipBagItem)
|
||||
function CleveRoids.IndexEquipSlot(inventoryID)
|
||||
-- Use when we know exactly which slot changed (e.g., from EquipBagItem or
|
||||
-- PLAYER_EQUIPMENT_CHANGED). hasCurrent is the event's arg2 (does the slot now
|
||||
-- hold an item); pass false to skip the API probe on a slot we know is empty.
|
||||
function CleveRoids.IndexEquipSlot(inventoryID, hasCurrent)
|
||||
if not inventoryID then return end
|
||||
|
||||
local items = CleveRoids.Items or {}
|
||||
local itemID = GetInventoryItemID("player", inventoryID)
|
||||
|
||||
-- Clear any stale inventoryID still pointing at this slot: the item that was
|
||||
-- here is now unequipped or swapped out (its real location comes from the
|
||||
-- paired BAG_UPDATE rebuild). Equip changes are user-paced, so this table
|
||||
-- scan is off the hot path.
|
||||
for _, entry in pairs(items) do
|
||||
if type(entry) == "table" and entry.inventoryID == inventoryID then
|
||||
entry.inventoryID = nil
|
||||
end
|
||||
end
|
||||
|
||||
-- hasCurrent == false (arg2) => slot is now empty, nothing to add.
|
||||
local itemID = hasCurrent ~= false and GetInventoryItemID("player", inventoryID)
|
||||
if itemID then
|
||||
local name, itemLink, _, _, itemType, itemSubType, _, _, texture = GetItemInfo(itemID)
|
||||
if name then
|
||||
@@ -378,9 +352,9 @@ function CleveRoids.GetActionButtonInfo(slot)
|
||||
if not actionType then return end
|
||||
|
||||
if actionType == "spell" and id then
|
||||
local rank = GetSpellRecField(id, "rank")
|
||||
local rank = C_Spell.GetSpellSubtext(id)
|
||||
if rank == "" then rank = nil end
|
||||
return "SPELL", id, GetSpellRecField(id, "name"), rank
|
||||
return "SPELL", id, C_Spell.GetSpellName(id), rank
|
||||
elseif actionType == "item" and id then
|
||||
local item = CleveRoids.GetItem(id)
|
||||
return "ITEM", id, (item and item.name)
|
||||
@@ -447,6 +421,27 @@ end
|
||||
|
||||
function CleveRoids.GetSpell(text)
|
||||
text = CleveRoids.Trim(text)
|
||||
|
||||
-- Explicit "spell:<id>" form. If the player knows the spell, reuse its cached
|
||||
-- spellbook entry (full cost/cooldown/usability). FindSpellBookSlotByID
|
||||
-- (ClassicAPI) resolves per-rank IDs and pet spells natively and returns the
|
||||
-- same bookType string CleveRoids.Spells is keyed by. If the spell is not in
|
||||
-- either book, fall back to an id-only entry that SetAction renders via
|
||||
-- SetSpellByID; cost=0 keeps the downstream active-action checks nil-safe.
|
||||
local _, _, spellId = string.find(text, "^spell:(%d+)$")
|
||||
if spellId then
|
||||
spellId = tonumber(spellId)
|
||||
local slot, book = FindSpellBookSlotByID(spellId)
|
||||
if slot then
|
||||
local name, rank = GetSpellInfo(slot, book)
|
||||
local byName = name and CleveRoids.Spells[book] and CleveRoids.Spells[book][name]
|
||||
if byName then
|
||||
return (rank and rank ~= "" and byName[rank]) or byName.highest or byName
|
||||
end
|
||||
end
|
||||
return { id = spellId, texture = C_Spell.GetSpellTexture(spellId) or CleveRoids.unknownTexture, cost = 0 }
|
||||
end
|
||||
|
||||
local rs, _, rank = string.find(text, "[^%s]%((Rank %d+)%)$")
|
||||
local name = rank and string.sub(text, 1, rs) or text
|
||||
|
||||
@@ -534,6 +529,12 @@ end
|
||||
function CleveRoids.GetItem(text)
|
||||
if not text or text == "" then return end
|
||||
|
||||
-- Explicit "item:<id>" form: force resolution by item ID. Reuses the numeric
|
||||
-- lookup below (equipped -> bags -> GetItemInfo), so location-aware tooltip
|
||||
-- rendering (SetInventoryItem/SetBagItem) still applies when the player has it.
|
||||
local _, _, prefixedId = string.find(text, "^item:(%d+)$")
|
||||
if prefixedId then text = prefixedId end
|
||||
|
||||
local Items = CleveRoids.Items
|
||||
local item = Items[text] or Items[tostring(text)]
|
||||
if not item then
|
||||
|
||||
@@ -651,6 +651,8 @@ local function validateConditional(conditional, args, action)
|
||||
combo = true,
|
||||
hp = true, myhp = true, rawhp = true, myrawhp = true,
|
||||
power = true, mypower = true, rawpower = true, myrawpower = true,
|
||||
mana = true, mymana = true, rage = true, myrage = true,
|
||||
energy = true, myenergy = true,
|
||||
hplost = true, myhplost = true,
|
||||
powerlost = true, mypowerlost = true,
|
||||
stat = true,
|
||||
@@ -678,6 +680,8 @@ local function validateConditional(conditional, args, action)
|
||||
-- Check operator syntax for numeric comparisons
|
||||
if args and type(args) == "string" then
|
||||
local hasHpOrPower = safeStringFind(baseCond, "hp") or safeStringFind(baseCond, "power") or
|
||||
safeStringFind(baseCond, "mana") or safeStringFind(baseCond, "energy") or
|
||||
safeStringFind(baseCond, "rage") or
|
||||
safeStringFind(baseCond, "combo") or baseCond == "stat"
|
||||
if hasHpOrPower then
|
||||
local hasOperator = safeStringFind(args, "[<>=~]+")
|
||||
|
||||
+2
-41
@@ -71,7 +71,6 @@
|
||||
|
||||
Spell Miss Events (v2.31+):
|
||||
- SPELL_MISS_SELF / SPELL_MISS_OTHER - Spell miss/resist/immune/dodge/etc.
|
||||
- GetSpellPower([mode]) - Player mod damage done for all 7 schools
|
||||
|
||||
Aura Event State Parameter (v2.32+):
|
||||
- Buff/debuff events include 7th `state` parameter (0=added, 1=removed, 2=modified)
|
||||
@@ -364,9 +363,8 @@ API.VERSION_REQUIREMENTS = {
|
||||
["AuraDurationEvents"] = { 2, 30, 0 },
|
||||
["GetPlayerAuraDuration"] = { 2, 30, 0, "GetPlayerAuraDuration" },
|
||||
|
||||
-- v2.31+ - Spell miss events and spell power query
|
||||
-- v2.31+ - Spell miss events
|
||||
["SpellMissEvents"] = { 2, 31, 0 }, -- SPELL_MISS_SELF/OTHER events
|
||||
["GetSpellPower"] = { 2, 31, 0, "GetSpellPower" },
|
||||
|
||||
-- v2.32+ - Aura event state parameter and stack removal fix
|
||||
["AuraEventState"] = { 2, 32, 0 },
|
||||
@@ -575,9 +573,8 @@ local function InitializeFeatures()
|
||||
f.hasAuraDurationEvents = API.HasFeature("AuraDurationEvents")
|
||||
f.hasGetPlayerAuraDuration = API.HasFeature("GetPlayerAuraDuration")
|
||||
|
||||
-- v2.31+ Spell miss events and spell power
|
||||
-- v2.31+ Spell miss events
|
||||
f.hasSpellMissEvents = API.HasFeature("SpellMissEvents")
|
||||
f.hasGetSpellPower = API.HasFeature("GetSpellPower")
|
||||
|
||||
-- v2.32+ Aura event state parameter
|
||||
f.hasAuraEventState = API.HasFeature("AuraEventState")
|
||||
@@ -3387,15 +3384,6 @@ API.MISS_INFO = {
|
||||
-- SPELL POWER QUERY (v2.31+)
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
-- Get spell power for all 7 damage schools (v2.31+)
|
||||
-- mode: optional mode parameter passed to GetSpellPower
|
||||
-- Returns: physical, holy, fire, nature, frost, shadow, arcane (or nil if unavailable)
|
||||
function API.GetSpellPower(mode)
|
||||
if not API.features.hasGetSpellPower or not _G.GetSpellPower then
|
||||
return nil, nil, nil, nil, nil, nil, nil
|
||||
end
|
||||
return _G.GetSpellPower(mode)
|
||||
end
|
||||
|
||||
-- Get duration of a spell in milliseconds (v2.38+)
|
||||
-- For channeling spells: returns the channel duration.
|
||||
@@ -3504,32 +3492,5 @@ function API.GetUnitMaxHealth(unitToken)
|
||||
return UnitHealthMax(unitToken)
|
||||
end
|
||||
|
||||
-- powerType: nil=current, 0=mana, 1=rage, 2=focus, 3=energy
|
||||
-- GetUnitField uses power1-power4 fields
|
||||
local POWER_FIELDS = { [0] = "power1", [1] = "power2", [2] = "power3", [3] = "power4" }
|
||||
local MAX_POWER_FIELDS = { [0] = "maxPower1", [1] = "maxPower2", [2] = "maxPower3", [3] = "maxPower4" }
|
||||
|
||||
function API.GetUnitPower(unitToken, powerType)
|
||||
if API.features.hasGetUnitField and GetUnitField and powerType then
|
||||
local field = POWER_FIELDS[powerType]
|
||||
if field then
|
||||
local val = GetUnitField(unitToken, field)
|
||||
if val then return val end
|
||||
end
|
||||
end
|
||||
return UnitMana(unitToken)
|
||||
end
|
||||
|
||||
function API.GetUnitMaxPower(unitToken, powerType)
|
||||
if API.features.hasGetUnitField and GetUnitField and powerType then
|
||||
local field = MAX_POWER_FIELDS[powerType]
|
||||
if field then
|
||||
local val = GetUnitField(unitToken, field)
|
||||
if val then return val end
|
||||
end
|
||||
end
|
||||
return UnitManaMax(unitToken)
|
||||
end
|
||||
|
||||
-- Expose API globally for other addons
|
||||
_G.CleveRoidsNampowerAPI = API
|
||||
|
||||
+180
-226
File diff suppressed because it is too large
Load Diff
+9
-10
@@ -49,16 +49,15 @@ API references are line numbers into `C:\Git\ClassicAPI\docs\API.md`.
|
||||
- class-aware `[dispellable]` (only types this character can actually remove).
|
||||
- optional name filtering on the type keywords (e.g. `[magic:Polymorph]`).
|
||||
|
||||
### 2. `C_Item.GetWeaponEnchantInfo()` — temp-enchant IDs
|
||||
- **API:** `API.md:7159` — returns 12-tuple including `enchantID` for main/off/ranged.
|
||||
- **Unlocks:** detect *which* temp enchant (poison/oil/sharpening stone) is
|
||||
applied to a weapon, not just that one exists.
|
||||
- **Naming:** `[poison]` is now taken by the target dispel-type conditional
|
||||
(slice 1 above). Use weapon-specific keywords for this — e.g.
|
||||
`[mhenchant:<id>]` / `[ohenchant:<id>]` (or `[mhpoison:<id>]`/`[ohpoison:<id>]`),
|
||||
not a bare `[poison]`.
|
||||
- Vanilla's global only reports presence; this is a genuinely new capability
|
||||
(rogue/shaman/enhance).
|
||||
### 2. ~~`C_Item.GetWeaponEnchantInfo()` — temp-enchant IDs~~ — DONE
|
||||
- Shipped as `[mhenchant]` / `[ohenchant]` (+ `no` variants). Matches the applied
|
||||
temp enchant by SpellItemEnchantment ID *or* localized name — the name path
|
||||
resolves via `C_Item.GetEnchantInfo(id).name` (the ID→name table this doc
|
||||
assumed we lacked), so no tooltip scan. Wrappers `ClassicAPI.GetWeaponEnchant`
|
||||
/ `GetEnchantName`; `ValidateWeaponImbue` now reads through the former.
|
||||
- Bare = any temp enchant; OR-lists supported (`[mhenchant:2823/Deadly_Poison]`).
|
||||
- Follow-up (optional): route `[mhimbue:Name]`'s match through `GetEnchantName`
|
||||
too, retiring the green-text tooltip scan in `CheckWeaponImbueByName`.
|
||||
|
||||
### 3. `GetUnitSpeed(unit)` + `IsFalling()` / `IsSwimming()`
|
||||
- **API:** `API.md:8312`, `API.md:7629`.
|
||||
|
||||
Reference in New Issue
Block a user