mirror of
https://github.com/brues-code/SuperCleveRoidMacros.git
synced 2026-09-16 03:38:00 +00:00
Compare commits
81
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7bbb8df010 | ||
|
|
96dbe9aca8 | ||
|
|
1de455f96e | ||
|
|
d93d97a469 | ||
|
|
2a562e0550 | ||
|
|
daaab2fd11 | ||
|
|
43f1ba378a | ||
|
|
828acc0c4b | ||
|
|
f296425c94 | ||
|
|
4dd5326172 | ||
|
|
c5ddc6c313 | ||
|
|
9f8a5553bc | ||
|
|
00caff0c9b | ||
|
|
080c1243b6 | ||
|
|
6f78be8394 | ||
|
|
10ad9eb980 | ||
|
|
0ef4fe1818 | ||
|
|
269c6ba67e | ||
|
|
c7905c2a70 | ||
|
|
77c0dba2c4 | ||
|
|
61bab6c734 | ||
|
|
daa7d6f2bf | ||
|
|
6d73c6b006 | ||
|
|
d5baecaf41 | ||
|
|
5c31232b52 | ||
|
|
d5cae2136c | ||
|
|
004eebc9f7 | ||
|
|
63b484b1ea | ||
|
|
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 | ||
|
|
d060b04908 | ||
|
|
6ac69501f5 | ||
|
|
782c685fba | ||
|
|
97e898f14c | ||
|
|
b84835a567 | ||
|
|
40cf2c8010 | ||
|
|
a5a0b9c00a | ||
|
|
6e57ff01dc | ||
|
|
9cd79d682d | ||
|
|
f54eda5ae3 | ||
|
|
653f8db3d7 |
@@ -18,6 +18,6 @@ jobs:
|
|||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Package and release to GitHub
|
- name: Package and release to GitHub
|
||||||
uses: BigWigsMods/packager@v2
|
uses: brues-code/packager@vCAPI
|
||||||
env:
|
env:
|
||||||
GITHUB_OAUTH: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_OAUTH: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|||||||
+22
@@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"runtime": {
|
||||||
|
"version": "Lua 5.1"
|
||||||
|
},
|
||||||
|
"diagnostics": {
|
||||||
|
"disable": ["deprecated"],
|
||||||
|
"globals": [
|
||||||
|
"this",
|
||||||
|
"event",
|
||||||
|
"arg",
|
||||||
|
"arg1",
|
||||||
|
"arg2",
|
||||||
|
"arg3",
|
||||||
|
"arg4",
|
||||||
|
"arg5",
|
||||||
|
"arg6",
|
||||||
|
"arg7",
|
||||||
|
"arg8",
|
||||||
|
"arg9"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
+199
-14
@@ -3,9 +3,11 @@
|
|||||||
|
|
||||||
ClassicAPI is a client mod (sibling to Nampower/SuperWoW) that backports the
|
ClassicAPI is a client mod (sibling to Nampower/SuperWoW) that backports the
|
||||||
modern C_* API into the 1.12.1 Lua environment. It is a HARD REQUIREMENT of
|
modern C_* API into the 1.12.1 Lua environment. It is a HARD REQUIREMENT of
|
||||||
this addon, so the wrappers below call the API directly — no fallbacks. The
|
this addon (ClassicAPI v1.12.1+, which added the positional
|
||||||
load-time requirement check (Core.lua) uses IsAvailable() to warn when the
|
C_UnitAuras.UnitAura), so the wrappers below call the API directly — no
|
||||||
DLL is missing; users who don't want ClassicAPI should run the upstream addon.
|
fallbacks. The load-time requirement check (Core.lua) uses IsAvailable() to
|
||||||
|
warn when the DLL is missing and HasMinimumVersion() when it's too old; users
|
||||||
|
who don't want ClassicAPI should run the upstream addon.
|
||||||
|
|
||||||
Detection: the global CLASSIC_API_VERSION is defined once the client has
|
Detection: the global CLASSIC_API_VERSION is defined once the client has
|
||||||
booted, encoded as X*10000 + Y*100 + Z for a vX.Y.Z tag (untagged dev builds
|
booted, encoded as X*10000 + Y*100 + Z for a vX.Y.Z tag (untagged dev builds
|
||||||
@@ -52,16 +54,17 @@ end
|
|||||||
-- C_UnitAuras
|
-- C_UnitAuras
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|
||||||
-- Scan a unit's auras (indexFn = C_UnitAuras.GetBuffDataByIndex or
|
-- Scan one aura range of `unit` (filter = "HELPFUL" or "HARMFUL") for an aura
|
||||||
-- GetDebuffDataByIndex) for one matching the dispel type. Vanilla descriptors
|
-- matching the dispel type. Uses the positional C_UnitAuras.UnitAura (added in
|
||||||
-- hold 32 helpful / 16 harmful slots; cap as a backstop.
|
-- ClassicAPI v1.12.1, this addon's minimum) -- no table allocated per slot, with
|
||||||
local function scanDispel(indexFn, unit, dispelType, wantAny)
|
-- dispelName as the 4th return. The filtered index self-terminates at the end of
|
||||||
|
-- the range (nil name); 48 is a backstop over vanilla's 32 helpful / 16 harmful slots.
|
||||||
|
local function scanDispel(unit, filter, dispelType, wantAny)
|
||||||
local i = 1
|
local i = 1
|
||||||
while i <= 48 do
|
while i <= 48 do
|
||||||
local data = indexFn(unit, i)
|
local name, _, _, dispelName = C_UnitAuras.UnitAura(unit, i, filter)
|
||||||
if not data then return false end
|
if not name then return false end
|
||||||
local dn = data.dispelName
|
if dispelName and dispelName ~= "" and (wantAny or dispelName == dispelType) then
|
||||||
if dn and dn ~= "" and (wantAny or dn == dispelType) then
|
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
i = i + 1
|
i = i + 1
|
||||||
@@ -76,10 +79,29 @@ end
|
|||||||
-- false -> scan debuffs (defensive cleanse, default)
|
-- false -> scan debuffs (defensive cleanse, default)
|
||||||
function API.UnitHasDispelType(unit, dispelType, helpful)
|
function API.UnitHasDispelType(unit, dispelType, helpful)
|
||||||
if not unit or not UnitExists(unit) then return false end
|
if not unit or not UnitExists(unit) then return false end
|
||||||
local indexFn = helpful and C_UnitAuras.GetBuffDataByIndex
|
local filter = helpful and "HELPFUL" or "HARMFUL"
|
||||||
or C_UnitAuras.GetDebuffDataByIndex
|
|
||||||
local wantAny = (dispelType == nil or dispelType == "any")
|
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
|
end
|
||||||
|
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
@@ -155,6 +177,76 @@ function API.GetItemSetInfo(setID)
|
|||||||
return C_Item.GetItemSetInfo(setID)
|
return C_Item.GetItemSetInfo(setID)
|
||||||
end
|
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
|
-- Spell
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
@@ -168,6 +260,76 @@ function API.GetSpellMechanicByID(spellID)
|
|||||||
return C_Spell.GetSpellMechanicByID(spellID)
|
return C_Spell.GetSpellMechanicByID(spellID)
|
||||||
end
|
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
|
||||||
|
|
||||||
|
-- Spell haste for `unit` as a percentage: 0 unhasted, positive when casts are
|
||||||
|
-- sped up, negative while slowed (Curse of Tongues). 0 for a unit that doesn't
|
||||||
|
-- resolve. Vanilla has no haste API at all -- ClassicAPI derives this from the
|
||||||
|
-- UNIT_MOD_CAST_SPEED descriptor field the server folds into the cast time, so
|
||||||
|
-- it's exact and needs no nampower GetUnitField dependency.
|
||||||
|
function API.UnitSpellHaste(unit)
|
||||||
|
return UnitSpellHaste(unit)
|
||||||
|
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
|
-- State
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
@@ -182,6 +344,29 @@ function API.IsSwimming()
|
|||||||
return IsSwimming() and true or false
|
return IsSwimming() and true or false
|
||||||
end
|
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
|
-- Cursor
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
|
|||||||
+40
-67
@@ -32,6 +32,35 @@ CleveRoids.lastRakeCast = CleveRoids.lastRakeCast or {
|
|||||||
timestamp = 0
|
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
|
-- Define spells that scale with combo points by SPELL ID and their duration formulas
|
||||||
-- Duration = base + (combo_points - 1) * increment
|
-- Duration = base + (combo_points - 1) * increment
|
||||||
CleveRoids.ComboScalingSpellsByID = {
|
CleveRoids.ComboScalingSpellsByID = {
|
||||||
@@ -70,32 +99,9 @@ CleveRoids.FerociousBiteSpellIDs = {
|
|||||||
[31018] = true, -- Rank 6
|
[31018] = true, -- Rank 6
|
||||||
}
|
}
|
||||||
|
|
||||||
-- Rip spell IDs (for Carnage talent - refreshed when Carnage procs)
|
-- Rip / Rake families (for Carnage talent). Seeded by Rank 1; matches every rank.
|
||||||
CleveRoids.RipSpellIDs = {
|
CleveRoids.RipSpellIDs = RankSet(1079)
|
||||||
[1079] = true, -- Rank 1
|
CleveRoids.RakeSpellIDs = RankSet(1822)
|
||||||
[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)
|
|
||||||
}
|
|
||||||
|
|
||||||
-- Combined table for all bleed spells that need immunity detection
|
-- Combined table for all bleed spells that need immunity detection
|
||||||
-- Used when checking if a cast bleed failed to apply (indicates bleed immunity)
|
-- 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
|
-- When Molten Blast hits, it refreshes Flame Shock duration on the target
|
||||||
-- Detection: Monitor combat log for Molten Blast damage, then refresh Flame Shock
|
-- Detection: Monitor combat log for Molten Blast damage, then refresh Flame Shock
|
||||||
-- =============================================================================
|
-- =============================================================================
|
||||||
CleveRoids.MoltenBlastSpellIDs = {
|
-- TWoW custom; seed resolves on the TWoW client (nil/never-matches on stock,
|
||||||
[36916] = true, -- Rank 1
|
-- where Molten Blast can't be cast anyway).
|
||||||
[36917] = true, -- Rank 2
|
CleveRoids.MoltenBlastSpellIDs = RankSet(36916)
|
||||||
[36918] = true, -- Rank 3
|
|
||||||
[36919] = true, -- Rank 4
|
|
||||||
[36920] = true, -- Rank 5
|
|
||||||
[36921] = true, -- Rank 6
|
|
||||||
}
|
|
||||||
|
|
||||||
CleveRoids.FlameShockSpellIDs = {
|
CleveRoids.FlameShockSpellIDs = {
|
||||||
[8050] = true, -- Rank 1
|
[8050] = true, -- Rank 1
|
||||||
@@ -154,12 +155,7 @@ CleveRoids.FlameShockSpellIDs = {
|
|||||||
-- WARLOCK: Conflagrate → Immolate Duration Reduction
|
-- WARLOCK: Conflagrate → Immolate Duration Reduction
|
||||||
-- When Conflagrate is cast, it reduces Immolate duration by 3 seconds
|
-- When Conflagrate is cast, it reduces Immolate duration by 3 seconds
|
||||||
-- =============================================================================
|
-- =============================================================================
|
||||||
CleveRoids.ConflagrateSpellIDs = {
|
CleveRoids.ConflagrateSpellIDs = RankSet(17962)
|
||||||
[17962] = true, -- Rank 1
|
|
||||||
[18930] = true, -- Rank 2
|
|
||||||
[18931] = true, -- Rank 3
|
|
||||||
[18932] = true, -- Rank 4
|
|
||||||
}
|
|
||||||
|
|
||||||
CleveRoids.ImmolateSpellIDs = {
|
CleveRoids.ImmolateSpellIDs = {
|
||||||
[348] = true, -- Rank 1
|
[348] = true, -- Rank 1
|
||||||
@@ -177,11 +173,8 @@ CleveRoids.ImmolateSpellIDs = {
|
|||||||
-- Channeled spell that accelerates DoT tick rate by 30% while channeling
|
-- Channeled spell that accelerates DoT tick rate by 30% while channeling
|
||||||
-- Complex tracking: debuff expires 30% faster while Dark Harvest is active
|
-- Complex tracking: debuff expires 30% faster while Dark Harvest is active
|
||||||
-- =============================================================================
|
-- =============================================================================
|
||||||
CleveRoids.DarkHarvestSpellIDs = {
|
-- TWoW custom (see MoltenBlast note).
|
||||||
[52550] = true, -- Rank 1
|
CleveRoids.DarkHarvestSpellIDs = RankSet(52550)
|
||||||
[52551] = true, -- Rank 2
|
|
||||||
[52552] = true, -- Rank 3
|
|
||||||
}
|
|
||||||
|
|
||||||
-- =============================================================================
|
-- =============================================================================
|
||||||
-- DRUID: Rake Debuff Cap Boss Whitelist
|
-- DRUID: Rake Debuff Cap Boss Whitelist
|
||||||
@@ -489,7 +482,7 @@ function CleveRoids.TrackComboPointCastByID(spellID, targetGUID)
|
|||||||
end
|
end
|
||||||
else
|
else
|
||||||
-- Second, check if name-based tracking has recent data for this spell
|
-- 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
|
if spellName then
|
||||||
-- Remove rank info for comparison
|
-- Remove rank info for comparison
|
||||||
local baseName = CleveRoids.StripRank(spellName)
|
local baseName = CleveRoids.StripRank(spellName)
|
||||||
@@ -548,26 +541,6 @@ function CleveRoids.TrackComboPointCastByID(spellID, targetGUID)
|
|||||||
return duration
|
return duration
|
||||||
end
|
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
|
-- Utility function to display current combo tracking info
|
||||||
function CleveRoids.ShowComboTracking()
|
function CleveRoids.ShowComboTracking()
|
||||||
CleveRoids.Print("=== Combo Point Tracking ===")
|
CleveRoids.Print("=== Combo Point Tracking ===")
|
||||||
@@ -683,7 +656,7 @@ if _G.UseAction then
|
|||||||
local spellName = nil
|
local spellName = nil
|
||||||
|
|
||||||
if actionType == "SPELL" and actionID then
|
if actionType == "SPELL" and actionID then
|
||||||
spellName = GetSpellRecField(actionID, "name")
|
spellName = C_Spell.GetSpellName(actionID)
|
||||||
end
|
end
|
||||||
|
|
||||||
if currentCP and currentCP > 0 then
|
if currentCP and currentCP > 0 then
|
||||||
|
|||||||
+47
-388
@@ -2,9 +2,10 @@ local _G = _G or getfenv(0)
|
|||||||
local CleveRoids = _G.CleveRoids or {}
|
local CleveRoids = _G.CleveRoids or {}
|
||||||
|
|
||||||
local Extension = CleveRoids.RegisterExtension("Compatibility_pfUI")
|
local Extension = CleveRoids.RegisterExtension("Compatibility_pfUI")
|
||||||
Extension.RegisterEvent("ADDON_LOADED", "ADDON_LOADED")
|
|
||||||
Extension.RegisterEvent("PLAYER_LOGIN", "PLAYER_LOGIN")
|
|
||||||
Extension.Debug = false
|
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
|
-- Track pfUI state
|
||||||
Extension.pfUILoaded = false
|
Extension.pfUILoaded = false
|
||||||
@@ -21,19 +22,6 @@ function Extension.DLOG(msg)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function Extension.FocusNameHook()
|
|
||||||
local hook = Extension.internal.memberHooks[CleveRoids]["GetFocusName"]
|
|
||||||
local target = hook.original()
|
|
||||||
|
|
||||||
if pfUI and pfUI.uf and pfUI.uf.focus and pfUI.uf.focus.unitname then
|
|
||||||
target = pfUI.uf.focus.unitname
|
|
||||||
end
|
|
||||||
|
|
||||||
--Extension.DLOG(target)
|
|
||||||
|
|
||||||
return target
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Check if pfUI's macrotweak module is loaded
|
-- Check if pfUI's macrotweak module is loaded
|
||||||
function Extension.IsPfUIMacrotweakLoaded()
|
function Extension.IsPfUIMacrotweakLoaded()
|
||||||
if not pfUI then return false end
|
if not pfUI then return false end
|
||||||
@@ -91,334 +79,6 @@ function Extension.HandleSendChatMessageHook()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Helper function to check for Carnage duration override
|
|
||||||
-- Returns override duration and timeleft if found, nil otherwise
|
|
||||||
local function GetCarnageOverride(effect)
|
|
||||||
if not effect or not CleveRoids.carnageDurationOverrides then
|
|
||||||
return nil, nil
|
|
||||||
end
|
|
||||||
|
|
||||||
for spellID, override in pairs(CleveRoids.carnageDurationOverrides) do
|
|
||||||
local spellName = GetSpellRecField(spellID, "name")
|
|
||||||
if spellName then
|
|
||||||
local baseName = CleveRoids.StripRank(spellName)
|
|
||||||
if baseName == effect and override.timestamp and (GetTime() - override.timestamp) < 5 then
|
|
||||||
local timeleft = override.duration - (GetTime() - override.timestamp)
|
|
||||||
if timeleft < 0 then timeleft = 0 end
|
|
||||||
return override.duration, timeleft
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return nil, nil
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Hook pfUI's libdebuff to use our combo-aware durations
|
|
||||||
-- NOTE: pfUI 7.6+ (GetUnitField edition) handles combo durations and Carnage internally.
|
|
||||||
-- We only inject when there's a mismatch between pfUI's data and ours.
|
|
||||||
function Extension.HookPfUILibdebuff()
|
|
||||||
if not pfUI or not pfUI.api or not pfUI.api.libdebuff then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
local pflib = pfUI.api.libdebuff
|
|
||||||
|
|
||||||
-- Check if pfUI 7.6+ with GetUnitField-based libdebuff is active
|
|
||||||
-- If so, pfUI handles combo durations and Carnage internally - we only override on mismatch
|
|
||||||
local hasPfUI76 = CleveRoids.hasPfUI76
|
|
||||||
|
|
||||||
-- pfUI 7.6+ handles all durations internally - no hooks needed
|
|
||||||
if hasPfUI76 then
|
|
||||||
Extension.DLOG("Skipped all libdebuff hooks (pfUI 7.6+ handles internally)")
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Hook GetDuration if it exists
|
|
||||||
-- pfUI's GetDuration signature: function(effect, rank) where effect is spell NAME
|
|
||||||
if pflib.GetDuration and not Extension.pfLibDebuffHooked then
|
|
||||||
local originalGetDuration = pflib.GetDuration
|
|
||||||
|
|
||||||
pflib.GetDuration = function(self, effect, rank)
|
|
||||||
local pfuiDuration = originalGetDuration(self, effect, rank)
|
|
||||||
|
|
||||||
-- Check for Carnage duration overrides (only if pfUI doesn't have it)
|
|
||||||
local carnageDuration = GetCarnageOverride(effect)
|
|
||||||
if carnageDuration then
|
|
||||||
-- Only override if pfUI's duration is significantly different (>1s difference)
|
|
||||||
if not pfuiDuration or math.abs(carnageDuration - pfuiDuration) > 1 then
|
|
||||||
if Extension.Debug then
|
|
||||||
DEFAULT_CHAT_FRAME:AddMessage(
|
|
||||||
string.format("|cff00aaff[pfUI Duration Override]|r %s: Carnage %.1fs (pfUI: %.1fs)",
|
|
||||||
effect, carnageDuration, pfuiDuration or 0)
|
|
||||||
)
|
|
||||||
end
|
|
||||||
return carnageDuration
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Check name-based tracking for fresh combo casts (only if pfUI returned 0 or nil)
|
|
||||||
if (not pfuiDuration or pfuiDuration == 0) and CleveRoids.ComboPointTracking and CleveRoids.ComboPointTracking[effect] then
|
|
||||||
local tracking = CleveRoids.ComboPointTracking[effect]
|
|
||||||
if tracking.duration and tracking.confirmed and (GetTime() - tracking.cast_time) < 0.5 then
|
|
||||||
if Extension.Debug then
|
|
||||||
DEFAULT_CHAT_FRAME:AddMessage(
|
|
||||||
string.format("|cff00aaff[pfUI Duration Override]|r %s: Combo tracking %.1fs (pfUI: %.1fs)",
|
|
||||||
effect, tracking.duration, pfuiDuration or 0)
|
|
||||||
)
|
|
||||||
end
|
|
||||||
return tracking.duration
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return pfuiDuration
|
|
||||||
end
|
|
||||||
|
|
||||||
Extension.pfLibDebuffHooked = true
|
|
||||||
Extension.DLOG("Hooked pfUI.api.libdebuff.GetDuration (mismatch-only mode)")
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Hook AddEffect if it exists (pre-7.6 only - 7.6+ returns early above)
|
|
||||||
if pflib.AddEffect and not Extension.pfLibAddEffectHooked then
|
|
||||||
local originalAddEffect = pflib.AddEffect
|
|
||||||
|
|
||||||
pflib.AddEffect = function(self, unit, unitlevel, effect, duration, caster)
|
|
||||||
-- RANK CHECKING: Preserve higher rank's remaining time if lower rank was cast
|
|
||||||
-- NOTE: 'unit' is a unit NAME (e.g., "Expert Training Dummy"), not a unit ID
|
|
||||||
-- Defensive: verify libdebuff is a table, not a function
|
|
||||||
if caster == "player" and type(CleveRoids.libdebuff) == "table" and duration and duration > 0 then
|
|
||||||
-- Try to find the GUID for this unit name
|
|
||||||
local unitGUID = nil
|
|
||||||
|
|
||||||
-- Check if this is the current target
|
|
||||||
if UnitName("target") == unit then
|
|
||||||
unitGUID = CleveRoids.GetGUID("target")
|
|
||||||
end
|
|
||||||
|
|
||||||
-- If we couldn't match to current target, check guidToName mapping
|
|
||||||
if not unitGUID and CleveRoids.libdebuff.guidToName then
|
|
||||||
for guid, name in pairs(CleveRoids.libdebuff.guidToName) do
|
|
||||||
if name == unit then
|
|
||||||
unitGUID = guid
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Check if a higher rank of this spell is already active
|
|
||||||
if unitGUID and CleveRoids.libdebuff.objects and CleveRoids.libdebuff.objects[unitGUID] then
|
|
||||||
-- Find all spell IDs that match this effect name
|
|
||||||
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")
|
|
||||||
if spellName then
|
|
||||||
local baseName = CleveRoids.StripRank(spellName)
|
|
||||||
if baseName == effect then
|
|
||||||
-- Same spell - check if still active
|
|
||||||
local remaining = rec.duration + rec.start - GetTime()
|
|
||||||
if remaining > 0 then
|
|
||||||
-- If incoming duration > remaining time, we're trying to add more time
|
|
||||||
-- This means either a refresh or lower rank cast - preserve existing timer
|
|
||||||
if duration > remaining then
|
|
||||||
if Extension.Debug then
|
|
||||||
DEFAULT_CHAT_FRAME:AddMessage(
|
|
||||||
string.format("|cff00aaff[pfUI Rank Preserve]|r %s: Preserving timer (%.1fs remaining vs %.1fs incoming)",
|
|
||||||
effect, remaining, duration)
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Preserve the existing timer
|
|
||||||
duration = remaining
|
|
||||||
end
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Check for Carnage duration overrides FIRST (highest priority)
|
|
||||||
local carnageDuration = GetCarnageOverride(effect)
|
|
||||||
if carnageDuration then
|
|
||||||
duration = duration or carnageDuration
|
|
||||||
caster = caster or "player" -- Ensure caster is set for UnitOwnDebuff filtering
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Check if this is a combo scaling spell by name
|
|
||||||
if not duration and CleveRoids.IsComboScalingSpell and CleveRoids.IsComboScalingSpell(effect) then
|
|
||||||
if CleveRoids.ComboPointTracking and CleveRoids.ComboPointTracking[effect] then
|
|
||||||
local tracking = CleveRoids.ComboPointTracking[effect]
|
|
||||||
if tracking.duration and tracking.confirmed and (GetTime() - tracking.cast_time) < 0.5 then
|
|
||||||
duration = tracking.duration
|
|
||||||
caster = caster or "player"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return originalAddEffect(self, unit, unitlevel, effect, duration, caster)
|
|
||||||
end
|
|
||||||
|
|
||||||
Extension.pfLibAddEffectHooked = true
|
|
||||||
Extension.DLOG("Hooked pfUI.api.libdebuff.AddEffect")
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Hook UnitDebuff to return Carnage override duration to display code
|
|
||||||
-- Only override when pfUI's duration differs significantly from ours
|
|
||||||
if pflib.UnitDebuff and not Extension.pfLibUnitDebuffHooked then
|
|
||||||
local originalUnitDebuff = pflib.UnitDebuff
|
|
||||||
|
|
||||||
pflib.UnitDebuff = function(self, unit, id)
|
|
||||||
local effect, rank, texture, stacks, dtype, duration, timeleft, caster = originalUnitDebuff(self, unit, id)
|
|
||||||
|
|
||||||
-- Only check Carnage override if pfUI returned data but duration might be wrong
|
|
||||||
if effect then
|
|
||||||
local carnageDuration, carnageTimeleft = GetCarnageOverride(effect)
|
|
||||||
if carnageDuration then
|
|
||||||
-- Only override if there's a significant difference (>1s)
|
|
||||||
if not duration or math.abs(carnageDuration - duration) > 1 then
|
|
||||||
if Extension.Debug then
|
|
||||||
DEFAULT_CHAT_FRAME:AddMessage(
|
|
||||||
string.format("|cff00aaff[pfUI UnitDebuff Override]|r %s: Carnage %.1fs/%.1fs (pfUI: %.1fs/%.1fs)",
|
|
||||||
effect, carnageDuration, carnageTimeleft, duration or 0, timeleft or 0)
|
|
||||||
)
|
|
||||||
end
|
|
||||||
duration = carnageDuration
|
|
||||||
timeleft = carnageTimeleft
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return effect, rank, texture, stacks, dtype, duration, timeleft, caster
|
|
||||||
end
|
|
||||||
|
|
||||||
Extension.pfLibUnitDebuffHooked = true
|
|
||||||
Extension.DLOG("Hooked pfUI.api.libdebuff.UnitDebuff (mismatch-only mode)")
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Hook UnitOwnDebuff to return Carnage override duration when selfdebuff is enabled
|
|
||||||
-- Only override when pfUI's duration differs significantly from ours
|
|
||||||
if pflib.UnitOwnDebuff and not Extension.pfLibUnitOwnDebuffHooked then
|
|
||||||
local originalUnitOwnDebuff = pflib.UnitOwnDebuff
|
|
||||||
|
|
||||||
pflib.UnitOwnDebuff = function(self, unit, id)
|
|
||||||
local effect, rank, texture, stacks, dtype, duration, timeleft, caster = originalUnitOwnDebuff(self, unit, id)
|
|
||||||
|
|
||||||
if effect then
|
|
||||||
local carnageDuration, carnageTimeleft = GetCarnageOverride(effect)
|
|
||||||
if carnageDuration then
|
|
||||||
-- Only override if there's a significant difference (>1s)
|
|
||||||
if not duration or math.abs(carnageDuration - duration) > 1 then
|
|
||||||
if Extension.Debug then
|
|
||||||
DEFAULT_CHAT_FRAME:AddMessage(
|
|
||||||
string.format("|cff00aaff[pfUI UnitOwnDebuff Override]|r %s: Carnage %.1fs/%.1fs (pfUI: %.1fs/%.1fs)",
|
|
||||||
effect, carnageDuration, carnageTimeleft, duration or 0, timeleft or 0)
|
|
||||||
)
|
|
||||||
end
|
|
||||||
duration = carnageDuration
|
|
||||||
timeleft = carnageTimeleft
|
|
||||||
end
|
|
||||||
end
|
|
||||||
-- If UnitOwnDebuff returned nil but we have a Carnage override, synthesize from UnitDebuff
|
|
||||||
-- This fallback is only needed for edge cases where pfUI doesn't track the debuff yet
|
|
||||||
elseif not effect and CleveRoids.carnageDurationOverrides then
|
|
||||||
-- Use pflib:UnitDebuff which includes our Carnage override hook
|
|
||||||
local baseEffect, baseRank, baseTex, baseStacks, baseDtype, baseDur, baseLeft, _ = pflib:UnitDebuff(unit, id)
|
|
||||||
if baseEffect then
|
|
||||||
local carnageDuration2, carnageTimeleft2 = GetCarnageOverride(baseEffect)
|
|
||||||
if carnageDuration2 then
|
|
||||||
return baseEffect, baseRank, baseTex, baseStacks, baseDtype, carnageDuration2, carnageTimeleft2, "player"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return effect, rank, texture, stacks, dtype, duration, timeleft, caster
|
|
||||||
end
|
|
||||||
|
|
||||||
Extension.pfLibUnitOwnDebuffHooked = true
|
|
||||||
Extension.DLOG("Hooked pfUI.api.libdebuff.UnitOwnDebuff (mismatch-only mode)")
|
|
||||||
end
|
|
||||||
|
|
||||||
return Extension.pfLibDebuffHooked or Extension.pfLibAddEffectHooked or Extension.pfLibUnitDebuffHooked or Extension.pfLibUnitOwnDebuffHooked
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Synchronize combo durations to pfUI's libdebuff objects
|
|
||||||
-- NOTE: pfUI 7.6+ handles combo durations internally - skip sync entirely
|
|
||||||
function Extension.SyncComboDurationToPfUI(guid, spellID, duration)
|
|
||||||
if not pfUI or not pfUI.api or not pfUI.api.libdebuff then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- pfUI 7.6+ handles all durations internally
|
|
||||||
if CleveRoids.hasPfUI76 then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Get unit name from GUID
|
|
||||||
local unitName = nil
|
|
||||||
local unitLevel = 0
|
|
||||||
|
|
||||||
-- Check if this is the current target
|
|
||||||
local targetGUID = CleveRoids.GetGUID("target")
|
|
||||||
if targetGUID == guid then
|
|
||||||
unitName = UnitName("target")
|
|
||||||
unitLevel = UnitLevel("target") or 0
|
|
||||||
end
|
|
||||||
|
|
||||||
-- If we couldn't find the unit, use GUID to name mapping from libdebuff
|
|
||||||
if not unitName and CleveRoids.libdebuff and CleveRoids.libdebuff.guidToName then
|
|
||||||
unitName = CleveRoids.libdebuff.guidToName[guid]
|
|
||||||
-- Default to level 0 if we don't have the unit targeted
|
|
||||||
unitLevel = 0
|
|
||||||
end
|
|
||||||
|
|
||||||
if not unitName then
|
|
||||||
if CleveRoids.debug then
|
|
||||||
DEFAULT_CHAT_FRAME:AddMessage("|cffff0000[pfUI Sync]|r Could not find unit name for GUID")
|
|
||||||
end
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Get spell name from spell ID
|
|
||||||
local spellName = GetSpellRecField(spellID, "name")
|
|
||||||
if not spellName then
|
|
||||||
if CleveRoids.debug then
|
|
||||||
DEFAULT_CHAT_FRAME:AddMessage("|cffff0000[pfUI Sync]|r Could not find spell name for ID " .. spellID)
|
|
||||||
end
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Remove rank from spell name to match pfUI's format
|
|
||||||
local effectName = CleveRoids.StripRank(spellName)
|
|
||||||
|
|
||||||
-- Update pfUI's stored debuff duration
|
|
||||||
local pflib = pfUI.api.libdebuff
|
|
||||||
if pflib.objects and pflib.objects[unitName] then
|
|
||||||
-- Try both the specific level and level 0 (fallback)
|
|
||||||
for _, level in ipairs({unitLevel, 0}) do
|
|
||||||
if pflib.objects[unitName][level] and pflib.objects[unitName][level][effectName] then
|
|
||||||
local old_duration = pflib.objects[unitName][level][effectName].duration
|
|
||||||
pflib.objects[unitName][level][effectName].duration = duration
|
|
||||||
|
|
||||||
if CleveRoids.debug then
|
|
||||||
DEFAULT_CHAT_FRAME:AddMessage(
|
|
||||||
string.format("|cff00ffaa[pfUI Sync]|r Updated %s on %s (L%d): %ds -> %ds",
|
|
||||||
effectName, unitName, level, old_duration or 0, duration)
|
|
||||||
)
|
|
||||||
end
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if CleveRoids.debug then
|
|
||||||
DEFAULT_CHAT_FRAME:AddMessage(
|
|
||||||
string.format("|cffaaaa00[pfUI Sync]|r Effect not found in pfUI storage: %s on %s",
|
|
||||||
effectName, unitName)
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Register action event handler for pfUI button updates
|
-- Register action event handler for pfUI button updates
|
||||||
function Extension.RegisterPfUIActionEventHandler()
|
function Extension.RegisterPfUIActionEventHandler()
|
||||||
if not pfUI or Extension.actionHandlerRegistered then
|
if not pfUI or Extension.actionHandlerRegistered then
|
||||||
@@ -428,7 +88,17 @@ function Extension.RegisterPfUIActionEventHandler()
|
|||||||
-- Register a handler that will be called whenever CleveRoids updates macro states
|
-- Register a handler that will be called whenever CleveRoids updates macro states
|
||||||
if CleveRoids.RegisterActionEventHandler then
|
if CleveRoids.RegisterActionEventHandler then
|
||||||
Extension.DLOG("Registering pfUI action event handler")
|
Extension.DLOG("Registering pfUI action event handler")
|
||||||
CleveRoids.RegisterActionEventHandler(function(slot, event, ...)
|
-- Declared without `...` on purpose: in 1.12's Lua 5.0 a vararg function
|
||||||
|
-- allocates an `arg` table on every call, and this handler never read it.
|
||||||
|
-- That mattered because UpdateAllManagedCooldowns fans
|
||||||
|
-- ACTIONBAR_UPDATE_COOLDOWN out across every managed slot -- up to 120 calls
|
||||||
|
-- -- on each SPELL_UPDATE_COOLDOWN, which fires on every GCD and cooldown
|
||||||
|
-- tick. Those calls all allocated a table and then did nothing, because the
|
||||||
|
-- whole body only ever applied to ACTIONBAR_SLOT_CHANGED. Hence the early
|
||||||
|
-- return before any work.
|
||||||
|
CleveRoids.RegisterActionEventHandler(function(slot, event)
|
||||||
|
if event ~= "ACTIONBAR_SLOT_CHANGED" then return end
|
||||||
|
|
||||||
local button = pfUI.bars and pfUI.bars.buttons and pfUI.bars.buttons[slot]
|
local button = pfUI.bars and pfUI.bars.buttons and pfUI.bars.buttons[slot]
|
||||||
|
|
||||||
if Extension.Debug then
|
if Extension.Debug then
|
||||||
@@ -440,23 +110,21 @@ function Extension.RegisterPfUIActionEventHandler()
|
|||||||
))
|
))
|
||||||
end
|
end
|
||||||
|
|
||||||
-- For slot change events, do a full button update so the icon,
|
-- Full button update so the icon, cooldown and tooltip refresh.
|
||||||
-- cooldown, and tooltip refresh through CleveRoids' hooked
|
|
||||||
-- GetActionTexture / GetActionCooldown / GameTooltip:SetAction.
|
|
||||||
-- pfUI's ButtonMacroScan defers to us for managed macros (leaves
|
-- pfUI's ButtonMacroScan defers to us for managed macros (leaves
|
||||||
-- spellslot nil), so its ButtonFullUpdate routes through those hooks
|
-- spellslot nil), so ButtonFullUpdate reads the stock action-bar
|
||||||
-- and follows the active conditional — no manual cooldown override
|
-- functions -- which now resolve through the value we publish with
|
||||||
-- needed.
|
-- C_Macro.SetMacroDisplay, rather than the Lua overrides this addon
|
||||||
if event == "ACTIONBAR_SLOT_CHANGED" then
|
-- used to install.
|
||||||
-- Mark the slot for update in pfUI's cache (processed next OnUpdate)
|
|
||||||
if pfUI.bars and pfUI.bars.update then
|
|
||||||
pfUI.bars.update[slot] = true
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Also directly call ButtonFullUpdate if the button exists
|
-- Mark the slot for update in pfUI's cache (processed next OnUpdate)
|
||||||
if button and pfUI.bars.ButtonFullUpdate then
|
if pfUI.bars and pfUI.bars.update then
|
||||||
pfUI.bars.ButtonFullUpdate(button)
|
pfUI.bars.update[slot] = true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Also directly call ButtonFullUpdate if the button exists
|
||||||
|
if button and pfUI.bars.ButtonFullUpdate then
|
||||||
|
pfUI.bars.ButtonFullUpdate(button)
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
@@ -473,9 +141,6 @@ function Extension.SetupCompatibility()
|
|||||||
if Extension.pfUILoaded then
|
if Extension.pfUILoaded then
|
||||||
Extension.DLOG("pfUI detected")
|
Extension.DLOG("pfUI detected")
|
||||||
|
|
||||||
-- Hook libdebuff for combo duration support
|
|
||||||
Extension.HookPfUILibdebuff()
|
|
||||||
|
|
||||||
-- Register action event handler for button updates
|
-- Register action event handler for button updates
|
||||||
Extension.RegisterPfUIActionEventHandler()
|
Extension.RegisterPfUIActionEventHandler()
|
||||||
|
|
||||||
@@ -630,16 +295,9 @@ function Extension.SetupPfUIEventHooks(lib)
|
|||||||
ev:UnregisterEvent("AURA_CAST_ON_OTHER")
|
ev:UnregisterEvent("AURA_CAST_ON_OTHER")
|
||||||
ev:UnregisterEvent("DEBUFF_ADDED_OTHER")
|
ev:UnregisterEvent("DEBUFF_ADDED_OTHER")
|
||||||
ev:UnregisterEvent("DEBUFF_REMOVED_OTHER")
|
ev:UnregisterEvent("DEBUFF_REMOVED_OTHER")
|
||||||
ev:UnregisterEvent("BUFF_ADDED_OTHER")
|
|
||||||
ev:UnregisterEvent("BUFF_REMOVED_SELF")
|
ev:UnregisterEvent("BUFF_REMOVED_SELF")
|
||||||
ev:UnregisterEvent("BUFF_REMOVED_OTHER")
|
ev:UnregisterEvent("BUFF_REMOVED_OTHER")
|
||||||
|
|
||||||
-- pfUI 7.6+ also handles cast tracking internally
|
|
||||||
if lib.hasPfUI76 then
|
|
||||||
ev:UnregisterEvent("SPELL_START_OTHER")
|
|
||||||
ev:UnregisterEvent("SPELL_FAILED_OTHER")
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Keep registered: SPELL_START_SELF (channel duration capture before early return),
|
-- Keep registered: SPELL_START_SELF (channel duration capture before early return),
|
||||||
-- UNIT_DIED (AllCasterAuraTracking + OverflowBuff cleanup), UNIT_CASTEVENT (SuperWoW),
|
-- UNIT_DIED (AllCasterAuraTracking + OverflowBuff cleanup), UNIT_CASTEVENT (SuperWoW),
|
||||||
-- PLAYER_TARGET_CHANGED, UNIT_AURA (SeedUnit)
|
-- PLAYER_TARGET_CHANGED, UNIT_AURA (SeedUnit)
|
||||||
@@ -699,7 +357,6 @@ end
|
|||||||
|
|
||||||
function Extension.OnLoad()
|
function Extension.OnLoad()
|
||||||
Extension.DLOG("Extension pfUI Loaded.")
|
Extension.DLOG("Extension pfUI Loaded.")
|
||||||
Extension.HookMethod(CleveRoids, "GetFocusName", "FocusNameHook", true)
|
|
||||||
|
|
||||||
-- Export extension for external access
|
-- Export extension for external access
|
||||||
CleveRoids.Compatibility_pfUI = Extension
|
CleveRoids.Compatibility_pfUI = Extension
|
||||||
@@ -726,24 +383,23 @@ function Extension.OnLoad()
|
|||||||
SLASH_PFUICD1 = "/pfuicd"
|
SLASH_PFUICD1 = "/pfuicd"
|
||||||
end
|
end
|
||||||
|
|
||||||
function Extension.ADDON_LOADED()
|
-- Fires once pfUI has loaded (immediately if it loaded before us, via EventUtil).
|
||||||
-- Check if pfUI just loaded AND the global actually exists
|
function Extension.OnPfUILoaded()
|
||||||
-- (another addon could be named "pfUI" without being the real UI framework)
|
-- Guard: only the real pfUI framework sets this global (another addon could be
|
||||||
if arg1 == "pfUI" and pfUI then
|
-- named "pfUI" without being the UI framework).
|
||||||
Extension.pfUILoaded = true
|
if not pfUI then return end
|
||||||
-- pfUI modules load after ADDON_LOADED, so schedule a check
|
Extension.pfUILoaded = true
|
||||||
if CleveRoids.ScheduleTimer then
|
-- pfUI's submodules initialize after its ADDON_LOADED, so defer the setup.
|
||||||
CleveRoids.ScheduleTimer(function()
|
if CleveRoids.ScheduleTimer then
|
||||||
Extension.SetupCompatibility()
|
CleveRoids.ScheduleTimer(function()
|
||||||
end, 0.5)
|
Extension.SetupCompatibility()
|
||||||
end
|
end, 0.5)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function Extension.PLAYER_LOGIN()
|
function Extension.OnPlayerLogin()
|
||||||
-- If pfUI loaded before SCRM (alphabetical order), ADDON_LOADED for pfUI was missed.
|
-- Ensure lib.objects is linked correctly (InitPfUIIntegration is idempotent).
|
||||||
-- Re-run InitPfUIIntegration here to ensure lib.objects is linked correctly.
|
if pfUI then
|
||||||
if pfUI and not CleveRoids.hasPfUI76 then
|
|
||||||
local lib = CleveRoids.libdebuff
|
local lib = CleveRoids.libdebuff
|
||||||
if lib and lib.InitPfUIIntegration then
|
if lib and lib.InitPfUIIntegration then
|
||||||
lib:InitPfUIIntegration()
|
lib:InitPfUIIntegration()
|
||||||
@@ -775,9 +431,6 @@ function Extension.PLAYER_LOGIN()
|
|||||||
-- Print startup status only if pfUI global exists and compatibility was set up
|
-- Print startup status only if pfUI global exists and compatibility was set up
|
||||||
if Extension.pfUILoaded and pfUI then
|
if Extension.pfUILoaded and pfUI then
|
||||||
local statusMsg = "|cff00ff00[SCRM]|r pfUI compatibility loaded"
|
local statusMsg = "|cff00ff00[SCRM]|r pfUI compatibility loaded"
|
||||||
if CleveRoids.hasPfUI76 then
|
|
||||||
statusMsg = statusMsg .. " (7.6+ GUID cast tracking)"
|
|
||||||
end
|
|
||||||
-- statusMsg = statusMsg .. ". Use /pfuicd for debug."
|
-- statusMsg = statusMsg .. ". Use /pfuicd for debug."
|
||||||
DEFAULT_CHAT_FRAME:AddMessage(statusMsg)
|
DEFAULT_CHAT_FRAME:AddMessage(statusMsg)
|
||||||
if not Extension.actionHandlerRegistered then
|
if not Extension.actionHandlerRegistered then
|
||||||
@@ -797,4 +450,10 @@ if not CleveRoids.ScheduleTimer then
|
|||||||
end
|
end
|
||||||
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
|
_G["CleveRoids"] = CleveRoids
|
||||||
|
|||||||
+554
-532
File diff suppressed because it is too large
Load Diff
+47
-8
@@ -63,10 +63,24 @@ SlashCmdList.EQSLOT13 = CleveRoids.DoEquipTrinket1
|
|||||||
SLASH_EQSLOT141 = "/equip14"
|
SLASH_EQSLOT141 = "/equip14"
|
||||||
SlashCmdList.EQSLOT14 = CleveRoids.DoEquipTrinket2
|
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"
|
SLASH_UNSHIFT1 = "/unshift"
|
||||||
|
|
||||||
SlashCmdList.UNSHIFT = CleveRoids.DoUnshift
|
SlashCmdList.UNSHIFT = CleveRoids.DoUnshift
|
||||||
|
|
||||||
|
SLASH_CANCELFORM1 = "/cancelform"
|
||||||
|
|
||||||
|
SlashCmdList.CANCELFORM = CleveRoids.DoCancelForm
|
||||||
|
|
||||||
SLASH_UNQUEUE1 = "/unqueue"
|
SLASH_UNQUEUE1 = "/unqueue"
|
||||||
SlashCmdList.UNQUEUE = SpellStopCasting
|
SlashCmdList.UNQUEUE = SpellStopCasting
|
||||||
|
|
||||||
@@ -116,6 +130,12 @@ local StopAttack = function(msg)
|
|||||||
CleveRoids.DeferStopAttack()
|
CleveRoids.DeferStopAttack()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local StopChanneling = function(msg)
|
||||||
|
-- No Blizzard equivalent: 1.12 has SpellStopCasting (immediate) and nothing
|
||||||
|
-- that waits for the next channel tick, so this is nampower-only.
|
||||||
|
CleveRoids.StopChanneling()
|
||||||
|
end
|
||||||
|
|
||||||
-- Register slash commands and assign original handlers.
|
-- Register slash commands and assign original handlers.
|
||||||
-- These will be hooked immediately after.
|
-- These will be hooked immediately after.
|
||||||
SLASH_STARTATTACK1 = "/startattack"
|
SLASH_STARTATTACK1 = "/startattack"
|
||||||
@@ -127,6 +147,9 @@ SlashCmdList.STOPATTACK = StopAttack
|
|||||||
SLASH_STOPCASTING1 = "/stopcasting"
|
SLASH_STOPCASTING1 = "/stopcasting"
|
||||||
SlashCmdList.STOPCASTING = SpellStopCasting
|
SlashCmdList.STOPCASTING = SpellStopCasting
|
||||||
|
|
||||||
|
SLASH_STOPCHANNELING1 = "/stopchanneling"
|
||||||
|
SlashCmdList.STOPCHANNELING = StopChanneling
|
||||||
|
|
||||||
SLASH_CLEARTARGET1 = "/cleartarget"
|
SLASH_CLEARTARGET1 = "/cleartarget"
|
||||||
SlashCmdList.CLEARTARGET = ClearTarget
|
SlashCmdList.CLEARTARGET = ClearTarget
|
||||||
|
|
||||||
@@ -188,6 +211,21 @@ SlashCmdList.STOPCASTING = function(msg)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- /stopchanneling hook
|
||||||
|
CleveRoids.Hooks.STOPCHANNELING_SlashCmd = SlashCmdList.STOPCHANNELING
|
||||||
|
SlashCmdList.STOPCHANNELING = function(msg)
|
||||||
|
if CleveRoids.stopMacroFlag then return end
|
||||||
|
msg = msg or ""
|
||||||
|
if string.find(msg, "%[") then
|
||||||
|
-- If conditionals are present, let the function handle it.
|
||||||
|
-- It will only stop the channel if the conditions are met.
|
||||||
|
CleveRoids.DoConditionalStopChanneling(msg)
|
||||||
|
else
|
||||||
|
-- If no conditionals, run the original command.
|
||||||
|
CleveRoids.Hooks.STOPCHANNELING_SlashCmd(msg)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
-- /unqueue hook
|
-- /unqueue hook
|
||||||
CleveRoids.Hooks.UNQUEUE_SlashCmd = SlashCmdList.UNQUEUE
|
CleveRoids.Hooks.UNQUEUE_SlashCmd = SlashCmdList.UNQUEUE
|
||||||
SlashCmdList.UNQUEUE = function(msg)
|
SlashCmdList.UNQUEUE = function(msg)
|
||||||
@@ -320,14 +358,15 @@ SlashCmdList.RUNMACRO = function(msg)
|
|||||||
return CleveRoids.ExecuteMacroByName(CleveRoids.Trim(msg))
|
return CleveRoids.ExecuteMacroByName(CleveRoids.Trim(msg))
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Global RunMacro wrapper for user convenience (delegates to namespaced internal function)
|
-- The global RunMacro is installed by Core.lua, which loads before this file. Do not
|
||||||
-- This pattern ensures internal logic uses CleveRoids.ExecuteMacroByName and won't break
|
-- redefine it here: Core's hook accepts a macro index as well as a name (Blizzard's
|
||||||
-- if another addon overwrites the global RunMacro
|
-- RunMacro takes either), delegates to SuperMacro when that is driving execution,
|
||||||
-- NOTE: When SuperMacro is also loaded, Compatibility/SuperMacro.lua redirects this to
|
-- falls back to the saved Blizzard original when a macro will not resolve, and clears
|
||||||
-- SuperMacro_RunMacro so macros go through RunLine (where CRM commands are intercepted)
|
-- the stop/skip flags at the start of a top-level run, which is what lets /stopmacro,
|
||||||
function RunMacro(name)
|
-- /skipmacro and /firstaction work across parent/child macro boundaries. A plain
|
||||||
return CleveRoids.ExecuteMacroByName(name)
|
-- name-only wrapper here silently replaced all of that.
|
||||||
end
|
-- When SuperMacro is loaded, Compatibility/SuperMacro.lua redirects the global to
|
||||||
|
-- SuperMacro_RunMacro so macros go through RunLine (where CRM commands are intercepted).
|
||||||
|
|
||||||
SLASH_RETARGET1 = "/retarget"
|
SLASH_RETARGET1 = "/retarget"
|
||||||
SlashCmdList.RETARGET = function(msg)
|
SlashCmdList.RETARGET = function(msg)
|
||||||
|
|||||||
@@ -209,8 +209,8 @@ local function InjectCustomSpells()
|
|||||||
local count = 0
|
local count = 0
|
||||||
for spellID, data in pairs(CleveRoids.CustomCursiveSpells) do
|
for spellID, data in pairs(CleveRoids.CustomCursiveSpells) do
|
||||||
-- Get texture from GetSpellRecField + GetSpellIconTexture
|
-- Get texture from GetSpellRecField + GetSpellIconTexture
|
||||||
local name = GetSpellRecField(spellID, "name")
|
local name = C_Spell.GetSpellName(spellID)
|
||||||
local rank = GetSpellRecField(spellID, "rank")
|
local rank = C_Spell.GetSpellSubtext(spellID)
|
||||||
local texture = CleveRoids.libdebuff and CleveRoids.libdebuff:GetCachedIcon(spellID)
|
local texture = CleveRoids.libdebuff and CleveRoids.libdebuff:GetCachedIcon(spellID)
|
||||||
if texture then
|
if texture then
|
||||||
-- Always update/add (in case Cursive reloaded and cleared them)
|
-- Always update/add (in case Cursive reloaded and cleared them)
|
||||||
@@ -524,8 +524,8 @@ CleveRoids.HandleConsoleCommand = function(msg)
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local name = GetSpellRecField(spellID, "name")
|
local name = C_Spell.GetSpellName(spellID)
|
||||||
local rank = GetSpellRecField(spellID, "rank")
|
local rank = C_Spell.GetSpellSubtext(spellID)
|
||||||
local texture = CleveRoids.libdebuff and CleveRoids.libdebuff:GetCachedIcon(spellID)
|
local texture = CleveRoids.libdebuff and CleveRoids.libdebuff:GetCachedIcon(spellID)
|
||||||
if not name then
|
if not name then
|
||||||
DEFAULT_CHAT_FRAME:AddMessage("|cffff0000Error:|r Spell ID " .. spellID .. " not found.")
|
DEFAULT_CHAT_FRAME:AddMessage("|cffff0000Error:|r Spell ID " .. spellID .. " not found.")
|
||||||
|
|||||||
+43
-37
@@ -697,30 +697,9 @@ local function EnsureCondHighlightPool()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Test whether conditionals pass for a given command + alternative text.
|
-- Evaluate one single-group clause of a non-dynamic command against Keywords.
|
||||||
-- Returns: true (passes), false (fails), nil (unconditional / no conditionals)
|
-- Returns: true (passes), false (fails), nil (could not parse)
|
||||||
local function TestConditionalPasses(cmd, alternative)
|
local function EvaluateNonDynamic(alternative)
|
||||||
-- Strip ? tooltip hints (irrelevant for conditional evaluation)
|
|
||||||
if string.find(alternative, "?", 1, true) then
|
|
||||||
alternative = string.gsub(alternative, "%?", "")
|
|
||||||
end
|
|
||||||
|
|
||||||
local hasConditional = string.find(alternative, "%[") ~= nil
|
|
||||||
|
|
||||||
-- Dynamic commands: delegate to TestAction
|
|
||||||
if CleveRoids.dynamicCmds[cmd] then
|
|
||||||
local result = CleveRoids.TestAction(cmd, alternative)
|
|
||||||
if not hasConditional then
|
|
||||||
return nil -- unconditional
|
|
||||||
end
|
|
||||||
return result ~= nil and result ~= false
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Non-dynamic commands: parse and evaluate Keywords manually
|
|
||||||
if not hasConditional then
|
|
||||||
return nil -- unconditional
|
|
||||||
end
|
|
||||||
|
|
||||||
local ok, action, conditionals = pcall(CleveRoids.GetParsedMsg, alternative)
|
local ok, action, conditionals = pcall(CleveRoids.GetParsedMsg, alternative)
|
||||||
if not ok or not conditionals then
|
if not ok or not conditionals then
|
||||||
return nil
|
return nil
|
||||||
@@ -745,6 +724,43 @@ local function TestConditionalPasses(cmd, alternative)
|
|||||||
return passes
|
return passes
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Test whether conditionals pass for a given command + alternative text.
|
||||||
|
-- Returns: true (passes), false (fails), nil (unconditional / no conditionals)
|
||||||
|
local function TestConditionalPasses(cmd, alternative)
|
||||||
|
-- Strip ? tooltip hints (irrelevant for conditional evaluation)
|
||||||
|
if string.find(alternative, "?", 1, true) then
|
||||||
|
alternative = string.gsub(alternative, "%?", "")
|
||||||
|
end
|
||||||
|
|
||||||
|
local hasConditional = string.find(alternative, "%[") ~= nil
|
||||||
|
|
||||||
|
-- Dynamic commands: delegate to TestAction
|
||||||
|
if CleveRoids.dynamicCmds[cmd] then
|
||||||
|
local result = CleveRoids.TestAction(cmd, alternative)
|
||||||
|
if not hasConditional then
|
||||||
|
return nil -- unconditional
|
||||||
|
end
|
||||||
|
return result ~= nil and result ~= false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Non-dynamic commands: evaluate Keywords manually. `[a][b] X` passes when
|
||||||
|
-- any of its groups does.
|
||||||
|
if not hasConditional then
|
||||||
|
return nil -- unconditional
|
||||||
|
end
|
||||||
|
|
||||||
|
local variants = CleveRoids.ExpandBracketGroups(alternative)
|
||||||
|
if not variants then
|
||||||
|
return EvaluateNonDynamic(alternative)
|
||||||
|
end
|
||||||
|
for i = 1, variants.n do
|
||||||
|
if EvaluateNonDynamic(variants[i]) then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
-- Find character offset ranges for each semicolon-separated alternative.
|
-- Find character offset ranges for each semicolon-separated alternative.
|
||||||
-- Returns array of { start, finish } pairs (1-based indices within argsText).
|
-- Returns array of { start, finish } pairs (1-based indices within argsText).
|
||||||
local function FindAlternativeOffsets(argsText)
|
local function FindAlternativeOffsets(argsText)
|
||||||
@@ -1391,12 +1407,6 @@ end
|
|||||||
-- Extension Entry Points
|
-- Extension Entry Points
|
||||||
-- ============================================================================
|
-- ============================================================================
|
||||||
|
|
||||||
function Extension.OnAddonLoaded()
|
|
||||||
if arg1 == "Blizzard_MacroUI" then
|
|
||||||
InstallHooks()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function Extension.OnLoad()
|
function Extension.OnLoad()
|
||||||
-- Skip if macro checker is disabled
|
-- Skip if macro checker is disabled
|
||||||
if CleveRoidMacros and CleveRoidMacros.macrocheck == 0 then return end
|
if CleveRoidMacros and CleveRoidMacros.macrocheck == 0 then return end
|
||||||
@@ -1404,13 +1414,9 @@ function Extension.OnLoad()
|
|||||||
-- Skip if SuperMacro is loaded (detected at load time)
|
-- Skip if SuperMacro is loaded (detected at load time)
|
||||||
if SuperMacroFrame ~= nil then return end
|
if SuperMacroFrame ~= nil then return end
|
||||||
|
|
||||||
-- Listen for macro UI loading
|
-- Install once Blizzard's macro UI is available (fires immediately if already
|
||||||
Extension.RegisterEvent("ADDON_LOADED", "OnAddonLoaded")
|
-- loaded), replacing the ADDON_LOADED listener + manual "already loaded" check.
|
||||||
|
EventUtil.ContinueOnAddOnLoaded("Blizzard_MacroUI", InstallHooks)
|
||||||
-- If MacroFrame already exists (unlikely but safe), hook immediately
|
|
||||||
if MacroFrame and MacroFrameText then
|
|
||||||
InstallHooks()
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
_G["CleveRoids"] = CleveRoids
|
_G["CleveRoids"] = CleveRoids
|
||||||
|
|||||||
@@ -133,12 +133,6 @@ function Extension.OnMacroFrameLoad()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function Extension.OnAddonLoaded()
|
|
||||||
if arg1 == "Blizzard_MacroUI" then
|
|
||||||
Extension.OnMacroFrameLoad()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function Extension.OnLoad()
|
function Extension.OnLoad()
|
||||||
-- Schedule messages to show after UI is ready
|
-- Schedule messages to show after UI is ready
|
||||||
local function ShowMessages()
|
local function ShowMessages()
|
||||||
@@ -178,16 +172,11 @@ function Extension.OnLoad()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Listen for macro UI loading
|
-- Hook the macro UI once available (fires immediately if already loaded).
|
||||||
Extension.RegisterEvent("ADDON_LOADED", "OnAddonLoaded")
|
EventUtil.ContinueOnAddOnLoaded("Blizzard_MacroUI", Extension.OnMacroFrameLoad)
|
||||||
|
|
||||||
-- Also try to hook MacroFrame_SaveMacro if it already exists
|
-- Status messages on login (currently disabled inside OnPlayerLogin).
|
||||||
if MacroFrame_SaveMacro then
|
EventUtil.ContinueOnPlayerLogin(Extension.OnPlayerLogin)
|
||||||
Extension.OnMacroFrameLoad()
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Register PLAYER_LOGIN to show status messages
|
|
||||||
Extension.RegisterEvent("PLAYER_LOGIN", "OnPlayerLogin")
|
|
||||||
|
|
||||||
-- Store the message function for later
|
-- Store the message function for later
|
||||||
Extension.ShowMessages = ShowMessages
|
Extension.ShowMessages = ShowMessages
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ local _G = _G or getfenv(0)
|
|||||||
local CleveRoids = _G.CleveRoids or {}
|
local CleveRoids = _G.CleveRoids or {}
|
||||||
|
|
||||||
local Extension = CleveRoids.RegisterExtension("CT_RaidAssist")
|
local Extension = CleveRoids.RegisterExtension("CT_RaidAssist")
|
||||||
Extension.RegisterEvent("ADDON_LOADED", "OnLoad")
|
|
||||||
|
|
||||||
function Extension.OnEnter()
|
function Extension.OnEnter()
|
||||||
local tempOptions = CT_RAMenu_Options["temp"]
|
local tempOptions = CT_RAMenu_Options["temp"]
|
||||||
@@ -48,15 +47,14 @@ function Extension.OnLeave()
|
|||||||
CleveRoids.ClearMouseoverFrom("native")
|
CleveRoids.ClearMouseoverFrom("native")
|
||||||
end
|
end
|
||||||
|
|
||||||
function Extension.OnLoad()
|
function Extension.OnLoad() end
|
||||||
if arg1 ~= "CT_RaidAssist" then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
|
function Extension.OnAddOnLoad()
|
||||||
|
if not CT_RA_MemberFrame_OnEnter then return end
|
||||||
Extension.Hook("CT_RA_MemberFrame_OnEnter", "OnEnter")
|
Extension.Hook("CT_RA_MemberFrame_OnEnter", "OnEnter")
|
||||||
Extension.HookMethod(_G["GameTooltip"], "Hide", "OnLeave")
|
Extension.HookMethod(_G["GameTooltip"], "Hide", "OnLeave")
|
||||||
Extension.HookMethod(_G["GameTooltip"], "FadeOut", "OnLeave")
|
Extension.HookMethod(_G["GameTooltip"], "FadeOut", "OnLeave")
|
||||||
|
|
||||||
end
|
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 CleveRoids = _G.CleveRoids or {}
|
||||||
|
|
||||||
local Extension = CleveRoids.RegisterExtension("CT_UnitFrames")
|
local Extension = CleveRoids.RegisterExtension("CT_UnitFrames")
|
||||||
Extension.RegisterEvent("ADDON_LOADED", "OnLoad")
|
|
||||||
|
|
||||||
function Extension.SetHook(widget)
|
function Extension.SetHook(widget)
|
||||||
local hookedOnEnter = widget:GetScript("OnEnter")
|
local hookedOnEnter = widget:GetScript("OnEnter")
|
||||||
@@ -24,8 +23,10 @@ function Extension.SetHook(widget)
|
|||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
function Extension.OnLoad()
|
function Extension.OnLoad() end
|
||||||
if arg1 ~= "CT_UnitFrames" or not CT_AssistFrame then
|
|
||||||
|
function Extension.OnAddOnLoad()
|
||||||
|
if not CT_AssistFrame then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
CleveRoids.Print("CT_UnitFrames module loaded.")
|
CleveRoids.Print("CT_UnitFrames module loaded.")
|
||||||
@@ -36,4 +37,4 @@ function Extension.OnLoad()
|
|||||||
Extension.SetHook(CT_AssistFrame_Drag)
|
Extension.SetHook(CT_AssistFrame_Drag)
|
||||||
end
|
end
|
||||||
|
|
||||||
_G["CleveRoids"] = CleveRoids
|
EventUtil.ContinueOnAddOnLoaded("CT_UnitFrames", Extension.OnAddOnLoad)
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ local CleveRoids = _G.CleveRoids or {}
|
|||||||
CleveRoids.Hooks = CleveRoids.Hooks or {}
|
CleveRoids.Hooks = CleveRoids.Hooks or {}
|
||||||
|
|
||||||
local Extension = CleveRoids.RegisterExtension("CursiveMouseover")
|
local Extension = CleveRoids.RegisterExtension("CursiveMouseover")
|
||||||
Extension.RegisterEvent("ADDON_LOADED", "OnLoad")
|
|
||||||
|
|
||||||
local hooked = false
|
local hooked = false
|
||||||
|
|
||||||
@@ -66,21 +65,13 @@ local function HookCursiveUI()
|
|||||||
end
|
end
|
||||||
|
|
||||||
function Extension.OnLoad()
|
function Extension.OnLoad()
|
||||||
-- Try to hook when Cursive loads
|
-- Delay slightly to ensure Cursive.ui is initialized
|
||||||
if arg1 == "Cursive" then
|
local frame = CreateFrame("Frame")
|
||||||
-- Delay slightly to ensure Cursive.ui is initialized
|
frame:SetScript("OnUpdate", function()
|
||||||
local frame = CreateFrame("Frame")
|
if HookCursiveUI() then
|
||||||
frame:SetScript("OnUpdate", function()
|
this:Hide()
|
||||||
if HookCursiveUI() then
|
end
|
||||||
this:Hide()
|
end)
|
||||||
end
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Also try to hook immediately in case Cursive is already loaded
|
EventUtil.ContinueOnAddOnLoaded("Cursive", Extension.OnLoad)
|
||||||
if Cursive and Cursive.ui then
|
|
||||||
HookCursiveUI()
|
|
||||||
end
|
|
||||||
|
|
||||||
_G["CleveRoids"] = CleveRoids
|
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ local _G = _G or getfenv(0)
|
|||||||
local CleveRoids = _G.CleveRoids or {}
|
local CleveRoids = _G.CleveRoids or {}
|
||||||
|
|
||||||
local Extension = CleveRoids.RegisterExtension("DiscordUnitFrames")
|
local Extension = CleveRoids.RegisterExtension("DiscordUnitFrames")
|
||||||
Extension.RegisterEvent("ADDON_LOADED", "OnLoad")
|
|
||||||
|
|
||||||
function Extension.OnEnterFrame()
|
function Extension.OnEnterFrame()
|
||||||
CleveRoids.SetMouseoverFrom("duf", this.unit)
|
CleveRoids.SetMouseoverFrom("duf", this.unit)
|
||||||
@@ -26,11 +25,10 @@ function Extension.OnLeaveElement()
|
|||||||
CleveRoids.ClearMouseoverFrom("native")
|
CleveRoids.ClearMouseoverFrom("native")
|
||||||
end
|
end
|
||||||
|
|
||||||
function Extension.OnLoad()
|
function Extension.OnLoad() end
|
||||||
if arg1 ~= "DiscordUnitFrames" then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
|
function Extension.OnAddOnLoad()
|
||||||
|
if not DUF_UnitFrame_OnEnter then return end
|
||||||
CleveRoids.ClearHooks()
|
CleveRoids.ClearHooks()
|
||||||
Extension.Hook("DUF_UnitFrame_OnEnter", "OnEnterFrame")
|
Extension.Hook("DUF_UnitFrame_OnEnter", "OnEnterFrame")
|
||||||
Extension.Hook("DUF_UnitFrame_OnLeave", "OnLeaveFrame")
|
Extension.Hook("DUF_UnitFrame_OnLeave", "OnLeaveFrame")
|
||||||
@@ -39,4 +37,4 @@ function Extension.OnLoad()
|
|||||||
Extension.Hook("DUF_Element_OnLeave", "OnLeaveElement")
|
Extension.Hook("DUF_Element_OnLeave", "OnLeaveElement")
|
||||||
end
|
end
|
||||||
|
|
||||||
_G["CleveRoids"] = CleveRoids
|
EventUtil.ContinueOnAddOnLoaded("DiscordUnitFrames", Extension.OnAddOnLoad)
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ local CleveRoids = _G.CleveRoids or {}
|
|||||||
CleveRoids.Hooks = CleveRoids.Hooks or {}
|
CleveRoids.Hooks = CleveRoids.Hooks or {}
|
||||||
|
|
||||||
local Extension = CleveRoids.RegisterExtension("Grid")
|
local Extension = CleveRoids.RegisterExtension("Grid")
|
||||||
Extension.RegisterEvent("ADDON_LOADED", "OnLoad")
|
|
||||||
|
|
||||||
function Extension.OnEnter(unit)
|
function Extension.OnEnter(unit)
|
||||||
CleveRoids.SetMouseoverFrom("grid", unit)
|
CleveRoids.SetMouseoverFrom("grid", unit)
|
||||||
@@ -19,11 +18,10 @@ function Extension.OnLeave()
|
|||||||
CleveRoids.ClearMouseoverFrom("native")
|
CleveRoids.ClearMouseoverFrom("native")
|
||||||
end
|
end
|
||||||
|
|
||||||
function Extension.OnLoad()
|
function Extension.OnLoad() end
|
||||||
if arg1 ~= "Grid" then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
|
function Extension.OnAddOnLoad()
|
||||||
|
if not GridFrame then return end
|
||||||
CleveRoids.Hooks.Grid = { CreateFrames = GridFrame.frameClass.prototype.CreateFrames}
|
CleveRoids.Hooks.Grid = { CreateFrames = GridFrame.frameClass.prototype.CreateFrames}
|
||||||
GridFrame.frameClass.prototype.CreateFrames = CleveRoids.GrdCreateFrames
|
GridFrame.frameClass.prototype.CreateFrames = CleveRoids.GrdCreateFrames
|
||||||
end
|
end
|
||||||
@@ -111,4 +109,4 @@ function CleveRoids:GrdCreateFrames()
|
|||||||
ClickCastFrames[self.frame] = true
|
ClickCastFrames[self.frame] = true
|
||||||
end
|
end
|
||||||
|
|
||||||
_G["CleveRoids"] = CleveRoids
|
EventUtil.ContinueOnAddOnLoaded("Grid", Extension.OnAddOnLoad)
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ local CleveRoids = _G.CleveRoids or {}
|
|||||||
local CreateFrames = nil
|
local CreateFrames = nil
|
||||||
|
|
||||||
local Extension = CleveRoids.RegisterExtension("NotGrid")
|
local Extension = CleveRoids.RegisterExtension("NotGrid")
|
||||||
Extension.RegisterEvent("ADDON_LOADED", "OnLoad")
|
|
||||||
|
|
||||||
function Extension.OnEnter()
|
function Extension.OnEnter()
|
||||||
CleveRoids.SetMouseoverFrom("ngrid", this.unit)
|
CleveRoids.SetMouseoverFrom("ngrid", this.unit)
|
||||||
@@ -39,7 +38,9 @@ function CleveRoids:NotGrid_CreateFrames()
|
|||||||
end
|
end
|
||||||
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.
|
-- NotGrid loads before CleveRoids, so if NotGrid is enabled, then it's global will exist.
|
||||||
if not NotGrid then
|
if not NotGrid then
|
||||||
return
|
return
|
||||||
@@ -48,7 +49,6 @@ function Extension.OnLoad()
|
|||||||
CreateFrames = NotGrid.CreateFrames
|
CreateFrames = NotGrid.CreateFrames
|
||||||
NotGrid.CreateFrames = CleveRoids.NotGrid_CreateFrames
|
NotGrid.CreateFrames = CleveRoids.NotGrid_CreateFrames
|
||||||
|
|
||||||
Extension.UnregisterEvent("ADDON_LOADED", "Onload")
|
|
||||||
end
|
end
|
||||||
|
|
||||||
_G["CleveRoids"] = CleveRoids
|
EventUtil.ContinueOnAddOnLoaded("NotGrid", Extension.OnAddOnLoad)
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ local CleveRoids = _G.CleveRoids or {}
|
|||||||
CleveRoids.Hooks = CleveRoids.Hooks or {}
|
CleveRoids.Hooks = CleveRoids.Hooks or {}
|
||||||
|
|
||||||
local Extension = CleveRoids.RegisterExtension("PerfectRaid")
|
local Extension = CleveRoids.RegisterExtension("PerfectRaid")
|
||||||
Extension.RegisterEvent("ADDON_LOADED", "OnLoad")
|
|
||||||
|
|
||||||
function Extension.OnEnter(unit)
|
function Extension.OnEnter(unit)
|
||||||
CleveRoids.SetMouseoverFrom("praid", unit)
|
CleveRoids.SetMouseoverFrom("praid", unit)
|
||||||
@@ -19,11 +18,10 @@ function Extension.OnLeave()
|
|||||||
CleveRoids.ClearMouseoverFrom("native")
|
CleveRoids.ClearMouseoverFrom("native")
|
||||||
end
|
end
|
||||||
|
|
||||||
function Extension.OnLoad()
|
function Extension.OnLoad() end
|
||||||
if arg1 ~= "PerfectRaid" then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
|
function Extension.OnAddOnLoad()
|
||||||
|
if not PerfectRaid then return end
|
||||||
CleveRoids.Hooks.PerfectRaid = { CreateFrame = PerfectRaid.CreateFrame }
|
CleveRoids.Hooks.PerfectRaid = { CreateFrame = PerfectRaid.CreateFrame }
|
||||||
PerfectRaid.CreateFrame = CleveRoids.PerfectRaidCreateFrame
|
PerfectRaid.CreateFrame = CleveRoids.PerfectRaidCreateFrame
|
||||||
end
|
end
|
||||||
@@ -134,4 +132,4 @@ function CleveRoids.PerfectRaidCreateFrame(self, num)
|
|||||||
--]]
|
--]]
|
||||||
end
|
end
|
||||||
|
|
||||||
_G["CleveRoids"] = CleveRoids
|
EventUtil.ContinueOnAddOnLoaded("PerfectRaid", Extension.OnAddOnLoad)
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ local CleveRoids = _G.CleveRoids or {}
|
|||||||
CleveRoids.Hooks = CleveRoids.Hooks or {}
|
CleveRoids.Hooks = CleveRoids.Hooks or {}
|
||||||
|
|
||||||
local Extension = CleveRoids.RegisterExtension("ag_UnitFrames")
|
local Extension = CleveRoids.RegisterExtension("ag_UnitFrames")
|
||||||
Extension.RegisterEvent("ADDON_LOADED", "OnLoad")
|
|
||||||
|
|
||||||
function Extension.OnEnter(unit)
|
function Extension.OnEnter(unit)
|
||||||
CleveRoids.SetMouseoverFrom("aguf", 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}
|
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.OnEnter = CleveRoids.aUFOnEnter
|
||||||
aUF.classes.aUFunit.prototype.OnLeave = CleveRoids.aUFOnLeave
|
aUF.classes.aUFunit.prototype.OnLeave = CleveRoids.aUFOnLeave
|
||||||
Extension.UnregisterEvent("ADDON_LOADED", "Onload")
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Taken from ag_UnitClass.lua
|
-- Taken from ag_UnitClass.lua
|
||||||
@@ -41,4 +39,4 @@ function CleveRoids:aUFOnLeave()
|
|||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
_G["CleveRoids"] = CleveRoids
|
EventUtil.ContinueOnAddOnLoaded("ag_UnitFrames", Extension.OnLoad)
|
||||||
|
|||||||
@@ -8,6 +8,9 @@
|
|||||||
- Resolving a real UnitID when .unit isn't set
|
- 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"
|
- 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 _G = _G or getfenv(0)
|
||||||
local CleveRoids = _G.CleveRoids or {}
|
local CleveRoids = _G.CleveRoids or {}
|
||||||
|
|
||||||
@@ -31,7 +34,6 @@ local function ResolvePfUnit(frame, fallbackName)
|
|||||||
name = strlower(name)
|
name = strlower(name)
|
||||||
|
|
||||||
local candidates = { "target", "targettarget", "player", "pet" }
|
local candidates = { "target", "targettarget", "player", "pet" }
|
||||||
local i
|
|
||||||
for i = 1, 4 do
|
for i = 1, 4 do
|
||||||
table.insert(candidates, "party"..i)
|
table.insert(candidates, "party"..i)
|
||||||
table.insert(candidates, "partypet"..i)
|
table.insert(candidates, "partypet"..i)
|
||||||
@@ -153,7 +155,6 @@ end
|
|||||||
function Extension.RegisterPartyScripts()
|
function Extension.RegisterPartyScripts()
|
||||||
if not pfUI or not pfUI.uf or not pfUI.uf.group then return end
|
if not pfUI or not pfUI.uf or not pfUI.uf.group then return end
|
||||||
|
|
||||||
local i
|
|
||||||
for i = 0, 4 do
|
for i = 0, 4 do
|
||||||
local frame = pfUI.uf.group[i]
|
local frame = pfUI.uf.group[i]
|
||||||
if frame then
|
if frame then
|
||||||
@@ -181,7 +182,6 @@ end
|
|||||||
function Extension.RegisterRaidScripts()
|
function Extension.RegisterRaidScripts()
|
||||||
if not pfUI or not pfUI.uf or not pfUI.uf.raid then return end
|
if not pfUI or not pfUI.uf or not pfUI.uf.raid then return end
|
||||||
|
|
||||||
local i
|
|
||||||
for i = 1, 40 do
|
for i = 1, 40 do
|
||||||
local frame = pfUI.uf.raid[i]
|
local frame = pfUI.uf.raid[i]
|
||||||
if frame then
|
if frame then
|
||||||
@@ -333,7 +333,6 @@ end
|
|||||||
function Extension.RegisterRaidMarkScripts()
|
function Extension.RegisterRaidMarkScripts()
|
||||||
if not pfUI or not pfUI.raidmarkers or not pfUI.raidmarkers.rows then return end
|
if not pfUI or not pfUI.raidmarkers or not pfUI.raidmarkers.rows then return end
|
||||||
|
|
||||||
local i
|
|
||||||
for i = 1, 8 do
|
for i = 1, 8 do
|
||||||
local row = pfUI.raidmarkers.rows[i]
|
local row = pfUI.raidmarkers.rows[i]
|
||||||
if row then
|
if row then
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ local _G = _G or getfenv(0)
|
|||||||
local CleveRoids = _G.CleveRoids or {}
|
local CleveRoids = _G.CleveRoids or {}
|
||||||
|
|
||||||
local Extension = CleveRoids.RegisterExtension("sRaidFrames")
|
local Extension = CleveRoids.RegisterExtension("sRaidFrames")
|
||||||
Extension.RegisterEvent("ADDON_LOADED", "OnLoad")
|
|
||||||
|
|
||||||
function Extension:OnEnter(frame)
|
function Extension:OnEnter(frame)
|
||||||
CleveRoids.SetMouseoverFrom("sraid", frame.unit)
|
CleveRoids.SetMouseoverFrom("sraid", frame.unit)
|
||||||
@@ -17,14 +16,13 @@ function Extension.OnLeave()
|
|||||||
CleveRoids.ClearMouseoverFrom("native")
|
CleveRoids.ClearMouseoverFrom("native")
|
||||||
end
|
end
|
||||||
|
|
||||||
function Extension.OnLoad()
|
function Extension.OnLoad() end
|
||||||
if arg1 ~= "sRaidFrames" then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
|
function Extension.OnAddOnLoad()
|
||||||
|
if not sRaidFrames then return end
|
||||||
Extension.HookMethod(sRaidFrames, "UnitTooltip", "OnEnter")
|
Extension.HookMethod(sRaidFrames, "UnitTooltip", "OnEnter")
|
||||||
Extension.HookMethod(_G["GameTooltip"], "Hide", "OnLeave")
|
Extension.HookMethod(_G["GameTooltip"], "Hide", "OnLeave")
|
||||||
Extension.HookMethod(_G["GameTooltip"], "FadeOut", "OnLeave")
|
Extension.HookMethod(_G["GameTooltip"], "FadeOut", "OnLeave")
|
||||||
end
|
end
|
||||||
|
|
||||||
_G["CleveRoids"] = CleveRoids
|
EventUtil.ContinueOnAddOnLoaded("sRaidFrames", Extension.OnAddOnLoad)
|
||||||
|
|||||||
@@ -122,7 +122,7 @@ local function CreateIconButton(parent, index, iconTable)
|
|||||||
GameTooltip:SetOwner(btn, "ANCHOR_BOTTOMLEFT")
|
GameTooltip:SetOwner(btn, "ANCHOR_BOTTOMLEFT")
|
||||||
local spellName
|
local spellName
|
||||||
if data.spellId then
|
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
|
else
|
||||||
spellName = data.displayName
|
spellName = data.displayName
|
||||||
end
|
end
|
||||||
@@ -649,7 +649,7 @@ local function InjectTestTargetData()
|
|||||||
for i = 1, table.getn(TEST_TARGET_SPELL_IDS) do
|
for i = 1, table.getn(TEST_TARGET_SPELL_IDS) do
|
||||||
local spellId = TEST_TARGET_SPELL_IDS[i]
|
local spellId = TEST_TARGET_SPELL_IDS[i]
|
||||||
local dur = targetDurations[i] or 60
|
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
|
if not CleveRoids.AllCasterAuraTracking[targetGuid][spellName] then
|
||||||
CleveRoids.AllCasterAuraTracking[targetGuid][spellName] = {}
|
CleveRoids.AllCasterAuraTracking[targetGuid][spellName] = {}
|
||||||
end
|
end
|
||||||
|
|||||||
+127
-139
@@ -58,11 +58,7 @@ function CleveRoids.IndexSpells()
|
|||||||
bookType = CleveRoids.bookTypes[book]
|
bookType = CleveRoids.bookTypes[book]
|
||||||
spells[bookType] = {}
|
spells[bookType] = {}
|
||||||
else
|
else
|
||||||
local cost, reagent = CleveRoids.GetSpellCost(i, bookType)
|
local cost, reagent, reagentId = CleveRoids.GetSpellCost(i, bookType, spellId)
|
||||||
-- Fallback for known reagent spells if tooltip scan failed
|
|
||||||
if (not reagent or reagent == "") and CleveRoids.ReagentBySpell then
|
|
||||||
reagent = CleveRoids.ReagentBySpell[spellName]
|
|
||||||
end
|
|
||||||
if not spells[bookType][spellName] then
|
if not spells[bookType][spellName] then
|
||||||
spells[bookType][spellName] = {
|
spells[bookType][spellName] = {
|
||||||
spellSlot = i,
|
spellSlot = i,
|
||||||
@@ -71,7 +67,7 @@ function CleveRoids.IndexSpells()
|
|||||||
bookType = bookType,
|
bookType = bookType,
|
||||||
texture = texture,
|
texture = texture,
|
||||||
cost = cost,
|
cost = cost,
|
||||||
reagent = reagent,
|
reagentId = reagentId,
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
if spellRank and not spells[bookType][spellName][spellRank] then
|
if spellRank and not spells[bookType][spellName][spellRank] then
|
||||||
@@ -83,7 +79,7 @@ function CleveRoids.IndexSpells()
|
|||||||
bookType = bookType,
|
bookType = bookType,
|
||||||
texture = texture,
|
texture = texture,
|
||||||
cost = cost,
|
cost = cost,
|
||||||
reagent = reagent
|
reagentId = reagentId,
|
||||||
}
|
}
|
||||||
spells[bookType][spellName].highest = spells[bookType][spellName][spellRank]
|
spells[bookType][spellName].highest = spells[bookType][spellName][spellRank]
|
||||||
end
|
end
|
||||||
@@ -94,6 +90,13 @@ function CleveRoids.IndexSpells()
|
|||||||
|
|
||||||
if reagent then
|
if reagent then
|
||||||
CleveRoids.countedItemTypes[reagent] = true
|
CleveRoids.countedItemTypes[reagent] = true
|
||||||
|
elseif reagentId and Item then
|
||||||
|
Item:CreateFromItemID(reagentId):ContinueOnItemLoad(function()
|
||||||
|
local loadedName = C_Item.GetItemNameByID(reagentId)
|
||||||
|
if loadedName and loadedName ~= "" then
|
||||||
|
CleveRoids.countedItemTypes[loadedName] = true
|
||||||
|
end
|
||||||
|
end)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -153,56 +156,28 @@ end
|
|||||||
|
|
||||||
-- Lightweight equipment-only indexing for combat situations
|
-- Lightweight equipment-only indexing for combat situations
|
||||||
-- Updates existing cache rather than rebuilding it
|
-- Updates existing cache rather than rebuilding it
|
||||||
function CleveRoids.IndexEquippedItems()
|
|
||||||
local items = CleveRoids.Items or {}
|
|
||||||
|
|
||||||
for inventoryID = 0, 19 do
|
|
||||||
local link = GetInventoryItemLink("player", inventoryID)
|
|
||||||
if link then
|
|
||||||
local _, _, itemID = string.find(link, "item:(%d+)")
|
|
||||||
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
|
-- PERFORMANCE: Index a single equipment slot instead of all 20
|
||||||
-- Use when we know exactly which slot changed (e.g., from EquipBagItem)
|
-- Use when we know exactly which slot changed (e.g., from EquipBagItem or
|
||||||
function CleveRoids.IndexEquipSlot(inventoryID)
|
-- 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
|
if not inventoryID then return end
|
||||||
|
|
||||||
local items = CleveRoids.Items or {}
|
local items = CleveRoids.Items or {}
|
||||||
local link = GetInventoryItemLink("player", inventoryID)
|
|
||||||
|
|
||||||
if link then
|
-- Clear any stale inventoryID still pointing at this slot: the item that was
|
||||||
local _, _, itemID = string.find(link, "item:(%d+)")
|
-- 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)
|
local name, itemLink, _, _, itemType, itemSubType, _, _, texture = GetItemInfo(itemID)
|
||||||
if name then
|
if name then
|
||||||
local count = GetInventoryItemCount("player", inventoryID)
|
local count = GetInventoryItemCount("player", inventoryID)
|
||||||
@@ -263,23 +238,27 @@ function CleveRoids.IndexItems()
|
|||||||
local items = {}
|
local items = {}
|
||||||
local NUM_BAG_SLOTS = NUM_BAG_SLOTS -- Upvalue for bag constant
|
local NUM_BAG_SLOTS = NUM_BAG_SLOTS -- Upvalue for bag constant
|
||||||
|
|
||||||
|
-- Rebuilt each pass: itemIDs the player owns that GetItemInfo couldn't
|
||||||
|
-- resolve yet (cold cache under ClassicAPI's async warmup). The
|
||||||
|
-- GET_ITEM_INFO_RECEIVED handler consults this so it only re-indexes for
|
||||||
|
-- our own uncached items and ignores the flood of unrelated fills (quest
|
||||||
|
-- DB scans, AH, chat-link hovers, inspects) in O(1).
|
||||||
|
local pendingItemInfo = {}
|
||||||
|
CleveRoids.pendingItemInfo = pendingItemInfo
|
||||||
|
|
||||||
-- PERFORMANCE: Local function references
|
-- PERFORMANCE: Local function references
|
||||||
local GetContainerNumSlots = GetContainerNumSlots
|
local GetContainerNumSlots = GetContainerNumSlots
|
||||||
local GetContainerItemLink = GetContainerItemLink
|
|
||||||
local GetContainerItemInfo = GetContainerItemInfo
|
local GetContainerItemInfo = GetContainerItemInfo
|
||||||
local GetInventoryItemLink = GetInventoryItemLink
|
|
||||||
local GetInventoryItemCount = GetInventoryItemCount
|
local GetInventoryItemCount = GetInventoryItemCount
|
||||||
|
|
||||||
-- Scan bags (reverse order to prefer first stack)
|
-- Scan bags (reverse order to prefer first stack)
|
||||||
for bagID = 0, NUM_BAG_SLOTS do
|
for bagID = 0, NUM_BAG_SLOTS do
|
||||||
local numSlots = GetContainerNumSlots(bagID)
|
local numSlots = GetContainerNumSlots(bagID)
|
||||||
for slot = numSlots, 1, -1 do
|
for slot = numSlots, 1, -1 do
|
||||||
local link = GetContainerItemLink(bagID, slot)
|
local itemID = C_Container.GetContainerItemID(bagID, slot)
|
||||||
if link then
|
if itemID then
|
||||||
local _, _, itemID = string_find(link, "item:(%d+)")
|
-- Decorated name for the duplicate fast-path (no link string built)
|
||||||
|
local linkName = C_Item.GetItemName({ bagID = bagID, slotIndex = slot })
|
||||||
-- PERFORMANCE: Try to extract name from link first to check for duplicates
|
|
||||||
local _, _, linkName = string_find(link, "%[(.+)%]")
|
|
||||||
local existing = linkName and items[linkName]
|
local existing = linkName and items[linkName]
|
||||||
|
|
||||||
if existing then
|
if existing then
|
||||||
@@ -310,6 +289,9 @@ function CleveRoids.IndexItems()
|
|||||||
if lowerName ~= name then
|
if lowerName ~= name then
|
||||||
items[lowerName] = name
|
items[lowerName] = name
|
||||||
end
|
end
|
||||||
|
else
|
||||||
|
-- Owned but not cached yet; wait for GET_ITEM_INFO_RECEIVED.
|
||||||
|
pendingItemInfo[itemID] = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -317,13 +299,11 @@ function CleveRoids.IndexItems()
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- Scan equipped items
|
-- Scan equipped items
|
||||||
for inventoryID = 0, 19 do
|
for inventoryID = 1, 19 do
|
||||||
local link = GetInventoryItemLink("player", inventoryID)
|
local itemID = GetInventoryItemID("player", inventoryID)
|
||||||
if link then
|
if itemID then
|
||||||
local _, _, itemID = string_find(link, "item:(%d+)")
|
-- Decorated name for the duplicate fast-path (no link string built)
|
||||||
|
local linkName = C_Item.GetItemName({ equipmentSlotIndex = inventoryID })
|
||||||
-- PERFORMANCE: Try to extract name from link first
|
|
||||||
local _, _, linkName = string_find(link, "%[(.+)%]")
|
|
||||||
local existing = linkName and items[linkName]
|
local existing = linkName and items[linkName]
|
||||||
|
|
||||||
if existing then
|
if existing then
|
||||||
@@ -349,6 +329,9 @@ function CleveRoids.IndexItems()
|
|||||||
if lowerName ~= name then
|
if lowerName ~= name then
|
||||||
items[lowerName] = name
|
items[lowerName] = name
|
||||||
end
|
end
|
||||||
|
else
|
||||||
|
-- Owned but not cached yet; wait for GET_ITEM_INFO_RECEIVED.
|
||||||
|
pendingItemInfo[itemID] = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -383,9 +366,9 @@ function CleveRoids.GetActionButtonInfo(slot)
|
|||||||
if not actionType then return end
|
if not actionType then return end
|
||||||
|
|
||||||
if actionType == "spell" and id then
|
if actionType == "spell" and id then
|
||||||
local rank = GetSpellRecField(id, "rank")
|
local rank = C_Spell.GetSpellSubtext(id)
|
||||||
if rank == "" then rank = nil end
|
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
|
elseif actionType == "item" and id then
|
||||||
local item = CleveRoids.GetItem(id)
|
local item = CleveRoids.GetItem(id)
|
||||||
return "ITEM", id, (item and item.name)
|
return "ITEM", id, (item and item.name)
|
||||||
@@ -440,7 +423,9 @@ function CleveRoids.IndexActionSlot(slot)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
CleveRoids.TestForActiveAction(CleveRoids.GetAction(slot))
|
local actions = CleveRoids.GetAction(slot)
|
||||||
|
CleveRoids.TestForActiveAction(actions)
|
||||||
|
CleveRoids.PublishDisplay(actions)
|
||||||
CleveRoids.SendEventForAction(slot, "ACTIONBAR_SLOT_CHANGED", slot)
|
CleveRoids.SendEventForAction(slot, "ACTIONBAR_SLOT_CHANGED", slot)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -452,6 +437,27 @@ end
|
|||||||
|
|
||||||
function CleveRoids.GetSpell(text)
|
function CleveRoids.GetSpell(text)
|
||||||
text = CleveRoids.Trim(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 rs, _, rank = string.find(text, "[^%s]%((Rank %d+)%)$")
|
||||||
local name = rank and string.sub(text, 1, rs) or text
|
local name = rank and string.sub(text, 1, rs) or text
|
||||||
|
|
||||||
@@ -477,10 +483,8 @@ local function makeInventoryItem(inventoryID, link, Items)
|
|||||||
if not link then link = GetInventoryItemLink("player", inventoryID) end
|
if not link then link = GetInventoryItemLink("player", inventoryID) end
|
||||||
if not link then return end
|
if not link then return end
|
||||||
|
|
||||||
local _, _, itemID = string_find(link, "item:(%d+)")
|
local itemID = GetInventoryItemID("player", inventoryID)
|
||||||
itemID = itemID and tonumber(itemID) or nil
|
local name = C_Item.GetItemName({ equipmentSlotIndex = inventoryID })
|
||||||
|
|
||||||
local name = itemID and GetItemInfo(itemID) or nil
|
|
||||||
local texture = GetInventoryItemTexture("player", inventoryID)
|
local texture = GetInventoryItemTexture("player", inventoryID)
|
||||||
local count = GetInventoryItemCount("player", inventoryID)
|
local count = GetInventoryItemCount("player", inventoryID)
|
||||||
|
|
||||||
@@ -509,14 +513,12 @@ local function makeBagItem(bagID, slot, link, Items)
|
|||||||
end
|
end
|
||||||
if not link then return end
|
if not link then return end
|
||||||
|
|
||||||
local _, _, itemID = string_find(link, "item:(%d+)")
|
local itemID = C_Container.GetContainerItemID(bagID, slot)
|
||||||
itemID = itemID and tonumber(itemID) or nil
|
|
||||||
|
|
||||||
local name, _, _, _, _, _, _, _, texture = GetItemInfo(itemID)
|
local name = C_Item.GetItemName({bagID = bagID, slotIndex = slot})
|
||||||
local count = 0
|
local count = 0
|
||||||
local tex, itemCount = GetContainerItemInfo(bagID, slot)
|
local texture, itemCount = GetContainerItemInfo(bagID, slot)
|
||||||
if itemCount then count = itemCount end
|
if itemCount then count = itemCount end
|
||||||
if not texture then texture = tex end
|
|
||||||
|
|
||||||
local it = {
|
local it = {
|
||||||
bagID = bagID,
|
bagID = bagID,
|
||||||
@@ -543,6 +545,12 @@ end
|
|||||||
function CleveRoids.GetItem(text)
|
function CleveRoids.GetItem(text)
|
||||||
if not text or text == "" then return end
|
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 Items = CleveRoids.Items
|
||||||
local item = Items[text] or Items[tostring(text)]
|
local item = Items[text] or Items[tostring(text)]
|
||||||
if not item then
|
if not item then
|
||||||
@@ -574,9 +582,7 @@ function CleveRoids.GetItem(text)
|
|||||||
for inv = 1, 19 do
|
for inv = 1, 19 do
|
||||||
local link = GetInventoryItemLink("player", inv)
|
local link = GetInventoryItemLink("player", inv)
|
||||||
if link then
|
if link then
|
||||||
local _, _, itemID = string_find(link, "item:(%d+)")
|
local itemID = GetInventoryItemID("player", inv)
|
||||||
itemID = itemID and tonumber(itemID) or nil
|
|
||||||
|
|
||||||
if qid and itemID and qid == itemID then
|
if qid and itemID and qid == itemID then
|
||||||
return makeInventoryItem(inv, link, Items)
|
return makeInventoryItem(inv, link, Items)
|
||||||
elseif qname then
|
elseif qname then
|
||||||
@@ -595,9 +601,7 @@ function CleveRoids.GetItem(text)
|
|||||||
for slot = 1, slots do
|
for slot = 1, slots do
|
||||||
local link = GetContainerItemLink(bag, slot)
|
local link = GetContainerItemLink(bag, slot)
|
||||||
if link then
|
if link then
|
||||||
local _, _, itemID = string_find(link, "item:(%d+)")
|
local itemID = C_Container.GetContainerItemID(bag, slot)
|
||||||
itemID = itemID and tonumber(itemID) or nil
|
|
||||||
|
|
||||||
if qid and itemID and qid == itemID then
|
if qid and itemID and qid == itemID then
|
||||||
return makeBagItem(bag, slot, link, Items)
|
return makeBagItem(bag, slot, link, Items)
|
||||||
elseif qname then
|
elseif qname then
|
||||||
@@ -720,18 +724,16 @@ function CleveRoids.FindItemQuick(text)
|
|||||||
if cached then
|
if cached then
|
||||||
-- Validate: check if item is actually at the cached location
|
-- Validate: check if item is actually at the cached location
|
||||||
if cached.inventoryID then
|
if cached.inventoryID then
|
||||||
local link = GetInventoryItemLink("player", cached.inventoryID)
|
if qid then
|
||||||
if link then
|
if GetInventoryItemID("player", cached.inventoryID) == qid then
|
||||||
local nm = GetNameFromLink(link)
|
cached._validated = true
|
||||||
|
return cached -- Cache is valid
|
||||||
|
end
|
||||||
|
else
|
||||||
|
local nm = C_Item.GetItemName({ equipmentSlotIndex = cached.inventoryID })
|
||||||
if nm and qname and string_lower(nm) == qname then
|
if nm and qname and string_lower(nm) == qname then
|
||||||
cached._validated = true
|
cached._validated = true
|
||||||
return cached -- Cache is valid
|
return cached -- Cache is valid
|
||||||
elseif qid then
|
|
||||||
local _, _, itemID = string_find(link, "item:(%d+)")
|
|
||||||
if itemID and tonumber(itemID) == qid then
|
|
||||||
cached._validated = true
|
|
||||||
return cached -- Cache is valid
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
-- Cache is stale - item not at cached equipped slot, invalidate
|
-- Cache is stale - item not at cached equipped slot, invalidate
|
||||||
@@ -740,18 +742,16 @@ function CleveRoids.FindItemQuick(text)
|
|||||||
Items[string_lower(cached.name)] = nil
|
Items[string_lower(cached.name)] = nil
|
||||||
end
|
end
|
||||||
elseif cached.bagID and cached.slot then
|
elseif cached.bagID and cached.slot then
|
||||||
local link = GetContainerItemLink(cached.bagID, cached.slot)
|
if qid then
|
||||||
if link then
|
if C_Container.GetContainerItemID(cached.bagID, cached.slot) == qid then
|
||||||
local nm = GetNameFromLink(link)
|
cached._validated = true
|
||||||
|
return cached -- Cache is valid
|
||||||
|
end
|
||||||
|
else
|
||||||
|
local nm = C_Item.GetItemName({ bagID = cached.bagID, slotIndex = cached.slot })
|
||||||
if nm and qname and string_lower(nm) == qname then
|
if nm and qname and string_lower(nm) == qname then
|
||||||
cached._validated = true
|
cached._validated = true
|
||||||
return cached -- Cache is valid
|
return cached -- Cache is valid
|
||||||
elseif qid then
|
|
||||||
local _, _, itemID = string_find(link, "item:(%d+)")
|
|
||||||
if itemID and tonumber(itemID) == qid then
|
|
||||||
cached._validated = true
|
|
||||||
return cached -- Cache is valid
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
-- Cache is stale - item not at cached bag slot, invalidate
|
-- Cache is stale - item not at cached bag slot, invalidate
|
||||||
@@ -767,20 +767,17 @@ function CleveRoids.FindItemQuick(text)
|
|||||||
for inv = 1, 19 do
|
for inv = 1, 19 do
|
||||||
local link = GetInventoryItemLink("player", inv)
|
local link = GetInventoryItemLink("player", inv)
|
||||||
if link then
|
if link then
|
||||||
local _, _, itemID = string_find(link, "item:(%d+)")
|
local itemID = GetInventoryItemID("player", inv)
|
||||||
if itemID then
|
-- ID match: fast path
|
||||||
itemID = tonumber(itemID)
|
if qid and qid == itemID then
|
||||||
-- ID match: fast path
|
return makeInventoryItem(inv, link, Items)
|
||||||
if qid and qid == itemID then
|
end
|
||||||
|
-- Name match: extract name from link (faster than GetItemInfo)
|
||||||
|
if qname then
|
||||||
|
local nm = GetNameFromLink(link)
|
||||||
|
if nm and string_lower(nm) == qname then
|
||||||
return makeInventoryItem(inv, link, Items)
|
return makeInventoryItem(inv, link, Items)
|
||||||
end
|
end
|
||||||
-- Name match: extract name from link (faster than GetItemInfo)
|
|
||||||
if qname then
|
|
||||||
local nm = GetNameFromLink(link)
|
|
||||||
if nm and string_lower(nm) == qname then
|
|
||||||
return makeInventoryItem(inv, link, Items)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -791,20 +788,17 @@ function CleveRoids.FindItemQuick(text)
|
|||||||
for slot = 1, slots do
|
for slot = 1, slots do
|
||||||
local link = GetContainerItemLink(bag, slot)
|
local link = GetContainerItemLink(bag, slot)
|
||||||
if link then
|
if link then
|
||||||
local _, _, itemID = string_find(link, "item:(%d+)")
|
local itemID = C_Container.GetContainerItemID(bag, slot)
|
||||||
if itemID then
|
-- ID match: fast path
|
||||||
itemID = tonumber(itemID)
|
if qid and qid == itemID then
|
||||||
-- ID match: fast path
|
return makeBagItem(bag, slot, link, Items)
|
||||||
if qid and qid == itemID then
|
end
|
||||||
|
-- Name match: extract name from link (faster than GetItemInfo)
|
||||||
|
if qname then
|
||||||
|
local nm = GetNameFromLink(link)
|
||||||
|
if nm and string_lower(nm) == qname then
|
||||||
return makeBagItem(bag, slot, link, Items)
|
return makeBagItem(bag, slot, link, Items)
|
||||||
end
|
end
|
||||||
-- Name match: extract name from link (faster than GetItemInfo)
|
|
||||||
if qname then
|
|
||||||
local nm = GetNameFromLink(link)
|
|
||||||
if nm and string_lower(nm) == qname then
|
|
||||||
return makeBagItem(bag, slot, link, Items)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -818,25 +812,19 @@ end
|
|||||||
function CleveRoids.IsItemEquipped(text, inventoryId)
|
function CleveRoids.IsItemEquipped(text, inventoryId)
|
||||||
if not text or not inventoryId then return false end
|
if not text or not inventoryId then return false end
|
||||||
|
|
||||||
local link = GetInventoryItemLink("player", inventoryId)
|
local currentID = GetInventoryItemID("player", inventoryId)
|
||||||
if not link then return false end
|
|
||||||
|
|
||||||
local _, _, currentID = string_find(link, "item:(%d+)")
|
|
||||||
if not currentID then return false end
|
if not currentID then return false end
|
||||||
|
|
||||||
-- Check by ID (fast path)
|
-- Check by ID (fast path)
|
||||||
local textId = tonumber(text)
|
local textId = tonumber(text)
|
||||||
if textId and textId == tonumber(currentID) then
|
if textId and textId == currentID then
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Check by name - extract from link instead of GetItemInfo for performance
|
-- Check by name (decorated, so suffixed gear still matches)
|
||||||
local currentName = GetNameFromLink(link)
|
local currentName = C_Item.GetItemName({ equipmentSlotIndex = inventoryId })
|
||||||
if currentName then
|
if currentName and string_lower(currentName) == string_lower(text) then
|
||||||
local textLower = string_lower(text)
|
return true
|
||||||
if string_lower(currentName) == textLower then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return false
|
return false
|
||||||
|
|||||||
@@ -24,7 +24,22 @@ CleveRoids.hasSuperwow = SetAutoloot and true or false
|
|||||||
CleveRoids.hasTurtle = (type(_G.TURTLE_WOW_VERSION) ~= "nil")
|
CleveRoids.hasTurtle = (type(_G.TURTLE_WOW_VERSION) ~= "nil")
|
||||||
CleveRoids.supported = CleveRoids.hasTurtle
|
CleveRoids.supported = CleveRoids.hasTurtle
|
||||||
|
|
||||||
|
-- ClassicAPI macro display (C_Macro.SetMacroDisplay): hand ClassicAPI the resolved
|
||||||
|
-- action per macro instead of replacing the action-bar globals in Lua, so the icon,
|
||||||
|
-- tooltip, cooldown sweep, range and usable state all come from the client -- including
|
||||||
|
-- the drag cursor and the macro window grid, which Lua cannot reach.
|
||||||
|
--
|
||||||
|
-- Feature-detect rather than version-check: the API is unreleased, so
|
||||||
|
-- CLASSIC_API_VERSION reports the dev sentinel. ClassicAPI stands down from macro
|
||||||
|
-- display entirely when it sees this addon loaded; ClassicAPIMacroDisplay is what
|
||||||
|
-- tells it we drive it instead. A fork that leaves the flag unset keeps the old
|
||||||
|
-- behavior -- both must never drive the same buttons.
|
||||||
|
CleveRoids.useClassicAPIDisplay =
|
||||||
|
(type(C_Macro) == "table" and C_Macro.SetMacroDisplay ~= nil) and true or false
|
||||||
|
CleveRoids.ClassicAPIMacroDisplay = CleveRoids.useClassicAPIDisplay
|
||||||
|
|
||||||
CleveRoids.ParsedMsg = {}
|
CleveRoids.ParsedMsg = {}
|
||||||
|
CleveRoids.ExpandedGroups = {}
|
||||||
CleveRoids.Items = {}
|
CleveRoids.Items = {}
|
||||||
CleveRoids.Spells = {}
|
CleveRoids.Spells = {}
|
||||||
CleveRoids.PetSpells = {}
|
CleveRoids.PetSpells = {}
|
||||||
@@ -43,13 +58,11 @@ CleveRoids.unknownTexture = "Interface\\Icons\\INV_Misc_QuestionMark"
|
|||||||
|
|
||||||
CleveRoids.spell_tracking = {}
|
CleveRoids.spell_tracking = {}
|
||||||
|
|
||||||
-- GUID-based cast tracking (populated by pfUI 7.6 or standalone SPELL_START events)
|
-- GUID-based cast tracking, populated from our own SPELL_START handlers and pruned
|
||||||
-- Format: [casterGuid] = {spellID, spellName, icon, startTime, duration, endTime}
|
-- in OnUpdate. Format: [casterGuid] = {spellID, spellName, icon, startTime, duration,
|
||||||
|
-- endTime}
|
||||||
CleveRoids.castTracking = {}
|
CleveRoids.castTracking = {}
|
||||||
|
|
||||||
-- pfUI 7.6+ with Nampower 2.31.0+ detected (GUID-based cast tracking available)
|
|
||||||
CleveRoids.hasPfUI76 = false
|
|
||||||
|
|
||||||
-- Combo point tracking (initialized early for /cast hook)
|
-- Combo point tracking (initialized early for /cast hook)
|
||||||
CleveRoids.lastComboPoints = 0
|
CleveRoids.lastComboPoints = 0
|
||||||
CleveRoids.lastComboPointsTime = 0
|
CleveRoids.lastComboPointsTime = 0
|
||||||
@@ -124,6 +137,7 @@ CleveRoids.dynamicCmds = {
|
|||||||
["/castpet"] = true,
|
["/castpet"] = true,
|
||||||
["/castsequence"] = true,
|
["/castsequence"] = true,
|
||||||
["/use"] = true,
|
["/use"] = true,
|
||||||
|
["/feedpet"] = true,
|
||||||
["/equip"] = true,
|
["/equip"] = true,
|
||||||
["/equipmh"] = true,
|
["/equipmh"] = true,
|
||||||
["/equipoh"] = true,
|
["/equipoh"] = true,
|
||||||
@@ -186,11 +200,19 @@ CleveRoids.ignoreKeywords = {
|
|||||||
_operators = true, -- Metadata for AND/OR operator tracking
|
_operators = true, -- Metadata for AND/OR operator tracking
|
||||||
_groups = true, -- Grouped conditional values for AND/OR evaluation
|
_groups = true, -- Grouped conditional values for AND/OR evaluation
|
||||||
multiscan = true, -- Processed before Keywords loop (target resolution)
|
multiscan = true, -- Processed before Keywords loop (target resolution)
|
||||||
mouseuse = true, -- Post-cast modifier: auto-click AOE targeting circle at cursor
|
|
||||||
cursor = true, -- Modifier: place ground-target spell/item at cursor (CastAtCursor)
|
cursor = true, -- Modifier: place ground-target spell/item at cursor (CastAtCursor)
|
||||||
stopattack = true, -- Post-cast modifier: stop autoattack after cast (CheapShot pattern)
|
stopattack = true, -- Post-cast modifier: stop autoattack after cast (CheapShot pattern)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
-- Deprecated conditional names, rewritten to their current keyword in ParseMsg
|
||||||
|
-- before anything downstream (evaluation, _groups) sees them. This is the path
|
||||||
|
-- for renamed *modifiers* -- ignoreKeywords entries, which have no Keywords
|
||||||
|
-- predicate that an alias could just point at the way [stl] does for [stealth].
|
||||||
|
-- MacroErrorChecker also reads this table, so the old names stay valid syntax.
|
||||||
|
CleveRoids.conditionalAliases = {
|
||||||
|
mouseuse = "cursor", -- pre-ClassicAPI name, back when it clicked the AoE reticle
|
||||||
|
}
|
||||||
|
|
||||||
-- TODO: Localize?
|
-- TODO: Localize?
|
||||||
CleveRoids.countedItemTypes = {
|
CleveRoids.countedItemTypes = {
|
||||||
["Consumable"] = true,
|
["Consumable"] = true,
|
||||||
|
|||||||
+41
-16
@@ -35,6 +35,14 @@ if CleveRoids.ignoreKeywords then
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Deprecated names still valid in macros; ParseMsg rewrites them to the current
|
||||||
|
-- keyword, so they never reach Keywords/ignoreKeywords under their old name.
|
||||||
|
if CleveRoids.conditionalAliases then
|
||||||
|
for alias, _ in pairs(CleveRoids.conditionalAliases) do
|
||||||
|
VALID_CONDITIONALS[alias] = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
-- Known valid commands
|
-- Known valid commands
|
||||||
local VALID_COMMANDS = {
|
local VALID_COMMANDS = {
|
||||||
-- Core commands NOT registered via SlashCmdList (so not auto-discoverable):
|
-- Core commands NOT registered via SlashCmdList (so not auto-discoverable):
|
||||||
@@ -498,6 +506,7 @@ local COMMANDS_NO_ACTION_NEEDED = {
|
|||||||
["/startattack"] = true,
|
["/startattack"] = true,
|
||||||
["/stopattack"] = true,
|
["/stopattack"] = true,
|
||||||
["/stopcasting"] = true,
|
["/stopcasting"] = true,
|
||||||
|
["/stopchanneling"] = true,
|
||||||
["/unqueue"] = true,
|
["/unqueue"] = true,
|
||||||
["/retarget"] = true,
|
["/retarget"] = true,
|
||||||
["/stopmacro"] = true,
|
["/stopmacro"] = true,
|
||||||
@@ -552,7 +561,6 @@ end
|
|||||||
local ERROR_TYPES = {
|
local ERROR_TYPES = {
|
||||||
INVALID_CONDITIONAL = "Invalid conditional",
|
INVALID_CONDITIONAL = "Invalid conditional",
|
||||||
MISMATCHED_BRACKETS = "Mismatched brackets",
|
MISMATCHED_BRACKETS = "Mismatched brackets",
|
||||||
EMPTY_CONDITIONAL = "Empty conditional block",
|
|
||||||
INVALID_OPERATOR = "Invalid operator",
|
INVALID_OPERATOR = "Invalid operator",
|
||||||
MISSING_ARGUMENT = "Missing argument",
|
MISSING_ARGUMENT = "Missing argument",
|
||||||
INVALID_COMMAND = "Unknown command",
|
INVALID_COMMAND = "Unknown command",
|
||||||
@@ -651,6 +659,8 @@ local function validateConditional(conditional, args, action)
|
|||||||
combo = true,
|
combo = true,
|
||||||
hp = true, myhp = true, rawhp = true, myrawhp = true,
|
hp = true, myhp = true, rawhp = true, myrawhp = true,
|
||||||
power = true, mypower = true, rawpower = true, myrawpower = 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,
|
hplost = true, myhplost = true,
|
||||||
powerlost = true, mypowerlost = true,
|
powerlost = true, mypowerlost = true,
|
||||||
stat = true,
|
stat = true,
|
||||||
@@ -659,6 +669,7 @@ local function validateConditional(conditional, args, action)
|
|||||||
button = true,
|
button = true,
|
||||||
form = true, stance = true,
|
form = true, stance = true,
|
||||||
level = true, mylevel = true,
|
level = true, mylevel = true,
|
||||||
|
myspellhaste = true,
|
||||||
distance = true, nodistance = true,
|
distance = true, nodistance = true,
|
||||||
swingtimer = true, stimer = true,
|
swingtimer = true, stimer = true,
|
||||||
rangedtimer = true, rtimer = true,
|
rangedtimer = true, rtimer = true,
|
||||||
@@ -678,6 +689,8 @@ local function validateConditional(conditional, args, action)
|
|||||||
-- Check operator syntax for numeric comparisons
|
-- Check operator syntax for numeric comparisons
|
||||||
if args and type(args) == "string" then
|
if args and type(args) == "string" then
|
||||||
local hasHpOrPower = safeStringFind(baseCond, "hp") or safeStringFind(baseCond, "power") or
|
local hasHpOrPower = safeStringFind(baseCond, "hp") or safeStringFind(baseCond, "power") or
|
||||||
|
safeStringFind(baseCond, "mana") or safeStringFind(baseCond, "energy") or
|
||||||
|
safeStringFind(baseCond, "rage") or safeStringFind(baseCond, "haste") or
|
||||||
safeStringFind(baseCond, "combo") or baseCond == "stat"
|
safeStringFind(baseCond, "combo") or baseCond == "stat"
|
||||||
if hasHpOrPower then
|
if hasHpOrPower then
|
||||||
local hasOperator = safeStringFind(args, "[<>=~]+")
|
local hasOperator = safeStringFind(args, "[<>=~]+")
|
||||||
@@ -835,16 +848,19 @@ local function validateLine(line, lineNum)
|
|||||||
actionPart = "/" .. actionPart -- Add leading slash if missing after split
|
actionPart = "/" .. actionPart -- Add leading slash if missing after split
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Parse conditionals if present - use non-greedy match
|
-- Parse the leading run of [conditional] blocks, if any. `[a][b] X` is
|
||||||
local condStart = safeStringFind(actionPart, "%[")
|
-- Blizzard-style OR chaining: every block is validated, and the action
|
||||||
|
-- is what follows the last one.
|
||||||
|
local blocks = {}
|
||||||
local condEnd = nil
|
local condEnd = nil
|
||||||
local conditionBlock = nil
|
local condStart = safeStringFind(actionPart, "%[")
|
||||||
|
local len = safeStringLen(actionPart)
|
||||||
|
|
||||||
if condStart then
|
while condStart do
|
||||||
-- Find matching closing bracket
|
-- Find matching closing bracket
|
||||||
local depth = 0
|
local depth = 0
|
||||||
local inQuotes = false
|
local inQuotes = false
|
||||||
local len = safeStringLen(actionPart)
|
local closePos = nil
|
||||||
|
|
||||||
for i = condStart, len do
|
for i = condStart, len do
|
||||||
local char = safeStringSub(actionPart, i, i)
|
local char = safeStringSub(actionPart, i, i)
|
||||||
@@ -858,23 +874,30 @@ local function validateLine(line, lineNum)
|
|||||||
elseif char == "]" then
|
elseif char == "]" then
|
||||||
depth = depth - 1
|
depth = depth - 1
|
||||||
if depth == 0 then
|
if depth == 0 then
|
||||||
condEnd = i
|
closePos = i
|
||||||
conditionBlock = safeStringSub(actionPart, condStart + 1, i - 1)
|
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
if not closePos then break end
|
||||||
|
|
||||||
|
table.insert(blocks, safeStringSub(actionPart, condStart + 1, closePos - 1))
|
||||||
|
condEnd = closePos
|
||||||
|
|
||||||
|
-- Another block directly after this one (whitespace allowed)?
|
||||||
|
local _, wsEnd = safeStringFind(actionPart, "^%s*", closePos + 1)
|
||||||
|
local nextPos = (wsEnd or closePos) + 1
|
||||||
|
if safeStringSub(actionPart, nextPos, nextPos) == "[" then
|
||||||
|
condStart = nextPos
|
||||||
|
else
|
||||||
|
condStart = nil
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if conditionBlock then
|
for _, conditionBlock in ipairs(blocks) do
|
||||||
if safeTrim(conditionBlock) == "" then
|
-- `[]` is the always-true group; nothing to validate
|
||||||
table.insert(localErrors, {
|
if safeTrim(conditionBlock) ~= "" then
|
||||||
type = ERROR_TYPES.EMPTY_CONDITIONAL,
|
|
||||||
line = lineNum,
|
|
||||||
message = "Empty conditional block []"
|
|
||||||
})
|
|
||||||
else
|
|
||||||
-- Check for invalid @ target syntax
|
-- Check for invalid @ target syntax
|
||||||
local _, _, target = safeStringFind(conditionBlock, "(@[^%s,]+)")
|
local _, _, target = safeStringFind(conditionBlock, "(@[^%s,]+)")
|
||||||
if target and not safeStringFind(target, "^@[a-z]+%d*") then
|
if target and not safeStringFind(target, "^@[a-z]+%d*") then
|
||||||
@@ -922,7 +945,9 @@ local function validateLine(line, lineNum)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if condEnd then
|
||||||
-- Check for action after conditionals
|
-- Check for action after conditionals
|
||||||
-- Extract the command from this action part
|
-- Extract the command from this action part
|
||||||
local _, _, cmdFromAction = safeStringFind(actionPart, "^(/[a-z]+%d*)")
|
local _, _, cmdFromAction = safeStringFind(actionPart, "^(/[a-z]+%d*)")
|
||||||
|
|||||||
+7
-46
@@ -71,7 +71,6 @@
|
|||||||
|
|
||||||
Spell Miss Events (v2.31+):
|
Spell Miss Events (v2.31+):
|
||||||
- SPELL_MISS_SELF / SPELL_MISS_OTHER - Spell miss/resist/immune/dodge/etc.
|
- 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+):
|
Aura Event State Parameter (v2.32+):
|
||||||
- Buff/debuff events include 7th `state` parameter (0=added, 1=removed, 2=modified)
|
- Buff/debuff events include 7th `state` parameter (0=added, 1=removed, 2=modified)
|
||||||
@@ -364,9 +363,8 @@ API.VERSION_REQUIREMENTS = {
|
|||||||
["AuraDurationEvents"] = { 2, 30, 0 },
|
["AuraDurationEvents"] = { 2, 30, 0 },
|
||||||
["GetPlayerAuraDuration"] = { 2, 30, 0, "GetPlayerAuraDuration" },
|
["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
|
["SpellMissEvents"] = { 2, 31, 0 }, -- SPELL_MISS_SELF/OTHER events
|
||||||
["GetSpellPower"] = { 2, 31, 0, "GetSpellPower" },
|
|
||||||
|
|
||||||
-- v2.32+ - Aura event state parameter and stack removal fix
|
-- v2.32+ - Aura event state parameter and stack removal fix
|
||||||
["AuraEventState"] = { 2, 32, 0 },
|
["AuraEventState"] = { 2, 32, 0 },
|
||||||
@@ -575,9 +573,8 @@ local function InitializeFeatures()
|
|||||||
f.hasAuraDurationEvents = API.HasFeature("AuraDurationEvents")
|
f.hasAuraDurationEvents = API.HasFeature("AuraDurationEvents")
|
||||||
f.hasGetPlayerAuraDuration = API.HasFeature("GetPlayerAuraDuration")
|
f.hasGetPlayerAuraDuration = API.HasFeature("GetPlayerAuraDuration")
|
||||||
|
|
||||||
-- v2.31+ Spell miss events and spell power
|
-- v2.31+ Spell miss events
|
||||||
f.hasSpellMissEvents = API.HasFeature("SpellMissEvents")
|
f.hasSpellMissEvents = API.HasFeature("SpellMissEvents")
|
||||||
f.hasGetSpellPower = API.HasFeature("GetSpellPower")
|
|
||||||
|
|
||||||
-- v2.32+ Aura event state parameter
|
-- v2.32+ Aura event state parameter
|
||||||
f.hasAuraEventState = API.HasFeature("AuraEventState")
|
f.hasAuraEventState = API.HasFeature("AuraEventState")
|
||||||
@@ -1477,7 +1474,7 @@ end
|
|||||||
|
|
||||||
-- Get all equipped items for a unit
|
-- Get all equipped items for a unit
|
||||||
-- Requires v2.18+ for native GetEquippedItems (with Lua fallback for player only)
|
-- Requires v2.18+ for native GetEquippedItems (with Lua fallback for player only)
|
||||||
-- Returns table with slot indices (0-18) as keys, item info tables as values
|
-- Returns table with slot indices (1-19) as keys, item info tables as values
|
||||||
function API.GetEquippedItems(unitToken)
|
function API.GetEquippedItems(unitToken)
|
||||||
unitToken = unitToken or "player"
|
unitToken = unitToken or "player"
|
||||||
|
|
||||||
@@ -1492,8 +1489,8 @@ function API.GetEquippedItems(unitToken)
|
|||||||
end
|
end
|
||||||
|
|
||||||
local items = {}
|
local items = {}
|
||||||
for slot = 0, 18 do
|
for slot = 1, 19 do
|
||||||
local itemId = CleveRoids.ClassicAPI.GetInventoryItemID("player", slot + 1)
|
local itemId = CleveRoids.ClassicAPI.GetInventoryItemID("player", slot)
|
||||||
if itemId then
|
if itemId then
|
||||||
items[slot] = {
|
items[slot] = {
|
||||||
itemId = itemId,
|
itemId = itemId,
|
||||||
@@ -1521,7 +1518,7 @@ function API.GetEquippedItem(unitToken, slot)
|
|||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
local itemId = CleveRoids.ClassicAPI.GetInventoryItemID("player", slot + 1) -- 1-indexed
|
local itemId = CleveRoids.ClassicAPI.GetInventoryItemID("player", slot) -- both are 1-indexed
|
||||||
if itemId then
|
if itemId then
|
||||||
return {
|
return {
|
||||||
itemId = itemId,
|
itemId = itemId,
|
||||||
@@ -1675,7 +1672,7 @@ function API.IsItemInSlot(itemIdOrName, inventorySlot)
|
|||||||
|
|
||||||
-- Use native v2.18 GetEquippedItem if available
|
-- Use native v2.18 GetEquippedItem if available
|
||||||
if GetEquippedItem then
|
if GetEquippedItem then
|
||||||
local slotInfo = GetEquippedItem("player", inventorySlot - 1) -- 0-indexed
|
local slotInfo = GetEquippedItem("player", inventorySlot) -- 1-indexed (1-19)
|
||||||
if slotInfo and slotInfo.itemId then
|
if slotInfo and slotInfo.itemId then
|
||||||
local checkId = tonumber(itemIdOrName)
|
local checkId = tonumber(itemIdOrName)
|
||||||
if checkId then
|
if checkId then
|
||||||
@@ -3387,15 +3384,6 @@ API.MISS_INFO = {
|
|||||||
-- SPELL POWER QUERY (v2.31+)
|
-- 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+)
|
-- Get duration of a spell in milliseconds (v2.38+)
|
||||||
-- For channeling spells: returns the channel duration.
|
-- For channeling spells: returns the channel duration.
|
||||||
@@ -3504,32 +3492,5 @@ function API.GetUnitMaxHealth(unitToken)
|
|||||||
return UnitHealthMax(unitToken)
|
return UnitHealthMax(unitToken)
|
||||||
end
|
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
|
-- Expose API globally for other addons
|
||||||
_G.CleveRoidsNampowerAPI = API
|
_G.CleveRoidsNampowerAPI = API
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ Enhanced macro addon for World of Warcraft 1.12.1 (Vanilla/Turtle WoW) with dyna
|
|||||||
- **Arguments** use colon: `[mod:alt]`, `[hp:>50]`
|
- **Arguments** use colon: `[mod:alt]`, `[hp:>50]`
|
||||||
- **Negation** with `no` prefix: `[nobuff]`, `[nomod:alt]`
|
- **Negation** with `no` prefix: `[nobuff]`, `[nomod:alt]`
|
||||||
- **Target** with `@`: `[@mouseover,help]`, `[@party1,hp:<50]`
|
- **Target** with `@`: `[@mouseover,help]`, `[@party1,hp:<50]`
|
||||||
|
- **Fallback groups**, Blizzard-style: `[@mouseover,help][@focus,help][] Rejuvenation` tries each `[...]` in order and the first that passes casts the shared spell; `[]` always passes. Mixes freely with `;`
|
||||||
- **Spell names** with spaces: `"Mark of the Wild"` or `Mark_of_the_Wild`
|
- **Spell names** with spaces: `"Mark of the Wild"` or `Mark_of_the_Wild`
|
||||||
|
|
||||||
**Multi-value logic:**
|
**Multi-value logic:**
|
||||||
@@ -46,7 +47,7 @@ See the **[Wiki](https://github.com/brues-code/SuperCleveRoidMacros/wiki)** for
|
|||||||
|
|
||||||
- **[Quick Start](https://github.com/brues-code/SuperCleveRoidMacros/wiki/Quick-Start)** — Syntax, multi-value logic, comparisons, special prefixes
|
- **[Quick Start](https://github.com/brues-code/SuperCleveRoidMacros/wiki/Quick-Start)** — Syntax, multi-value logic, comparisons, special prefixes
|
||||||
- **[Slash Commands](https://github.com/brues-code/SuperCleveRoidMacros/wiki/Slash-Commands)** — All 35+ commands, priority macros, UnitXP scanning
|
- **[Slash Commands](https://github.com/brues-code/SuperCleveRoidMacros/wiki/Slash-Commands)** — All 35+ commands, priority macros, UnitXP scanning
|
||||||
- **[Conditionals](https://github.com/brues-code/SuperCleveRoidMacros/wiki/Conditionals)** — 70+ conditionals (player & target), extended unit tokens, multiscan
|
- **[Conditionals](https://github.com/brues-code/SuperCleveRoidMacros/wiki/Conditionals)** — 150+ conditionals (player & target), short aliases, extended unit tokens, multiscan
|
||||||
- **[Reference Tables](https://github.com/brues-code/SuperCleveRoidMacros/wiki/Reference-Tables)** — CC types, damage schools, stat types, swing types
|
- **[Reference Tables](https://github.com/brues-code/SuperCleveRoidMacros/wiki/Reference-Tables)** — CC types, damage schools, stat types, swing types
|
||||||
- **[Features](https://github.com/brues-code/SuperCleveRoidMacros/wiki/Features)** — Debuff timers, combo tracking, talent modifiers, TWoW mechanics
|
- **[Features](https://github.com/brues-code/SuperCleveRoidMacros/wiki/Features)** — Debuff timers, combo tracking, talent modifiers, TWoW mechanics
|
||||||
- **[Overflow Buff Frame](https://github.com/brues-code/SuperCleveRoidMacros/wiki/Overflow-Buff-Frame)** — Hidden buff display for 32+ buffs
|
- **[Overflow Buff Frame](https://github.com/brues-code/SuperCleveRoidMacros/wiki/Overflow-Buff-Frame)** — Hidden buff display for 32+ buffs
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
## Version: @project-version@
|
## Version: @project-version@
|
||||||
## OptionalDeps: ClassicFocus, FocusFrame, pfUI, SuperMacro, Bongos_ActionBar, Cursive, XPerl, LunaUnitFrames, DragonflightReloaded, -Dragonflight3
|
## OptionalDeps: ClassicFocus, FocusFrame, pfUI, SuperMacro, Bongos_ActionBar, Cursive, XPerl, LunaUnitFrames, DragonflightReloaded, -Dragonflight3
|
||||||
## SavedVariables: CleveRoidMacros, CleveRoids_LearnedDurations, CleveRoids_AuraTextures, CleveRoids_ImmunityData, CleveRoids_ComboDurations, CleveRoids_SpellSchools
|
## SavedVariables: CleveRoidMacros, CleveRoids_LearnedDurations, CleveRoids_AuraTextures, CleveRoids_ImmunityData, CleveRoids_ComboDurations, CleveRoids_SpellSchools
|
||||||
|
## LoadSavedVariablesFirst: 1
|
||||||
Localization.lua
|
Localization.lua
|
||||||
Init.lua
|
Init.lua
|
||||||
NampowerAPI.lua
|
NampowerAPI.lua
|
||||||
|
|||||||
+301
-563
File diff suppressed because it is too large
Load Diff
+66
-19
@@ -25,10 +25,23 @@ API references are line numbers into `C:\Git\ClassicAPI\docs\API.md`.
|
|||||||
`applications` (stacks), `duration`.
|
`applications` (stacks), `duration`.
|
||||||
- **Unlocks:** `[dispellable]` / `[curse]` / `[magic]` target conditionals and
|
- **Unlocks:** `[dispellable]` / `[curse]` / `[magic]` target conditionals and
|
||||||
spellID-based (rank/locale-proof) aura matching.
|
spellID-based (rank/locale-proof) aura matching.
|
||||||
- **Caveat:** `expirationTime` is only populated for `unit=="player"`; it's `0`
|
- **~~Caveat: `expirationTime` is player-only~~ — NO LONGER TRUE.** This note said
|
||||||
for target/focus (vanilla server limitation). Target debuff *timers* still
|
target/focus `expirationTime` was always `0`, so debuff *timers* had to stay on
|
||||||
need the existing libdebuff tracking — only presence/stacks/school/spellId
|
libdebuff. ClassicAPI has since added the `Aura::Source` cache: for a non-player
|
||||||
are reliable cross-unit.
|
unit `expirationTime` is reconstructed from the observed `SMSG_SPELL_GO`, and
|
||||||
|
`duration` is the **caster-modified** value (talent extensions like Improved
|
||||||
|
Shadow Word: Pain included). It also handles combo-point finisher scaling
|
||||||
|
automatically (Rupture at 4 CP reads 14s, no registration) and ships Carnage's
|
||||||
|
roll-gated Rip/Rake refresh in the DLL (`src/turtle/Carnage.cpp`, exposed as
|
||||||
|
`RegisterAuraDurationModifierByTrigger`).
|
||||||
|
- **Real remaining caveats** (all best-effort, from `docs/API.md`):
|
||||||
|
- Only auras observed *after login* carry caster/timing; older ones report
|
||||||
|
`expirationTime` 0 and `sourceUnit` nil.
|
||||||
|
- Max-stack refresh is a blind spot: re-applying at max stacks (Shadow Weaving
|
||||||
|
5→5) emits no client-visible change, so the entry elapses and evicts.
|
||||||
|
- Out-of-range group members are spell-ID only, with `applications` always 1.
|
||||||
|
- **Consequence:** most of libdebuff is now redundant — see
|
||||||
|
"libdebuff retirement" below.
|
||||||
|
|
||||||
- **DONE (slice 1 — dispel-type conditionals):** added `ClassicAPI.lua`
|
- **DONE (slice 1 — dispel-type conditionals):** added `ClassicAPI.lua`
|
||||||
detection module (`CleveRoids.ClassicAPI`, mirrors NampowerAPI's
|
detection module (`CleveRoids.ClassicAPI`, mirrors NampowerAPI's
|
||||||
@@ -49,16 +62,15 @@ API references are line numbers into `C:\Git\ClassicAPI\docs\API.md`.
|
|||||||
- class-aware `[dispellable]` (only types this character can actually remove).
|
- class-aware `[dispellable]` (only types this character can actually remove).
|
||||||
- optional name filtering on the type keywords (e.g. `[magic:Polymorph]`).
|
- optional name filtering on the type keywords (e.g. `[magic:Polymorph]`).
|
||||||
|
|
||||||
### 2. `C_Item.GetWeaponEnchantInfo()` — temp-enchant IDs
|
### 2. ~~`C_Item.GetWeaponEnchantInfo()` — temp-enchant IDs~~ — DONE
|
||||||
- **API:** `API.md:7159` — returns 12-tuple including `enchantID` for main/off/ranged.
|
- Shipped as `[mhenchant]` / `[ohenchant]` (+ `no` variants). Matches the applied
|
||||||
- **Unlocks:** detect *which* temp enchant (poison/oil/sharpening stone) is
|
temp enchant by SpellItemEnchantment ID *or* localized name — the name path
|
||||||
applied to a weapon, not just that one exists.
|
resolves via `C_Item.GetEnchantInfo(id).name` (the ID→name table this doc
|
||||||
- **Naming:** `[poison]` is now taken by the target dispel-type conditional
|
assumed we lacked), so no tooltip scan. Wrappers `ClassicAPI.GetWeaponEnchant`
|
||||||
(slice 1 above). Use weapon-specific keywords for this — e.g.
|
/ `GetEnchantName`; `ValidateWeaponImbue` now reads through the former.
|
||||||
`[mhenchant:<id>]` / `[ohenchant:<id>]` (or `[mhpoison:<id>]`/`[ohpoison:<id>]`),
|
- Bare = any temp enchant; OR-lists supported (`[mhenchant:2823/Deadly_Poison]`).
|
||||||
not a bare `[poison]`.
|
- Follow-up (optional): route `[mhimbue:Name]`'s match through `GetEnchantName`
|
||||||
- Vanilla's global only reports presence; this is a genuinely new capability
|
too, retiring the green-text tooltip scan in `CheckWeaponImbueByName`.
|
||||||
(rogue/shaman/enhance).
|
|
||||||
|
|
||||||
### 3. `GetUnitSpeed(unit)` + `IsFalling()` / `IsSwimming()`
|
### 3. `GetUnitSpeed(unit)` + `IsFalling()` / `IsSwimming()`
|
||||||
- **API:** `API.md:8312`, `API.md:7629`.
|
- **API:** `API.md:8312`, `API.md:7629`.
|
||||||
@@ -151,11 +163,13 @@ Findings from the full Conditionals.lua audit. These look like ClassicAPI
|
|||||||
candidates but are **worse** than the current implementation — recorded so we
|
candidates but are **worse** than the current implementation — recorded so we
|
||||||
don't re-investigate.
|
don't re-investigate.
|
||||||
|
|
||||||
- **Auras / `ValidateAura`** — `C_UnitAuras.expirationTime` is **player-only**
|
- ~~**Auras / `ValidateAura`** — `C_UnitAuras.expirationTime` is player-only~~ —
|
||||||
(`0` for every other unit). It cannot replace the nampower `GetUnitField`
|
**STALE, do not trust this entry.** It was written before the `Aura::Source`
|
||||||
batch read or the remote-duration tracking (libdebuff / overflow slots). The
|
cache landed. Non-player `expirationTime`/`duration` now work (caster-modified,
|
||||||
dispel-*type* path already uses `C_UnitAuras` (that's the one thing it's good
|
talent extensions included), so this is no longer a reason to keep libdebuff's
|
||||||
for); aura *timing/stacks* on non-player units must stay on nampower/libdebuff.
|
remote-duration tracking. See Tier 1 §1 and "libdebuff retirement" for the
|
||||||
|
current picture and the real caveats. The nampower `GetUnitField` batch read is
|
||||||
|
a separate question and has not been re-examined.
|
||||||
- **`GetCurrentShapeshiftIndex` form loop / `[stance]`·`[form]`** —
|
- **`GetCurrentShapeshiftIndex` form loop / `[stance]`·`[form]`** —
|
||||||
`GetShapeshiftFormID()` returns the **DBC form id** (Cat=1, Bear=5,
|
`GetShapeshiftFormID()` returns the **DBC form id** (Cat=1, Bear=5,
|
||||||
Shadowform=28…), NOT the 1-based **bar index** these conditionals compare
|
Shadowform=28…), NOT the 1-based **bar index** these conditionals compare
|
||||||
@@ -184,6 +198,39 @@ don't re-investigate.
|
|||||||
added `[focus]`/`[nofocus]`. `/focus` is provided by ClassicAPI's companion
|
added `[focus]`/`[nofocus]`. `/focus` is provided by ClassicAPI's companion
|
||||||
addon.
|
addon.
|
||||||
|
|
||||||
|
## libdebuff retirement
|
||||||
|
|
||||||
|
libdebuff exists because vanilla cannot report debuff durations on units other
|
||||||
|
than the player. ClassicAPI's `Aura::Source` cache now does exactly that, so most
|
||||||
|
of the library is redundant. This is a staged replacement, not a delete: measure
|
||||||
|
first, then remove per group.
|
||||||
|
|
||||||
|
Current footprint: 28 public `lib:` methods, ~726 internal references in
|
||||||
|
`Utility.lua`, consumers in 8 files (`Conditionals.lua` 45, `Compatibility/pfUI.lua`
|
||||||
|
39, `Core.lua` 9). Note pfUI did **not** delete its own libdebuff — v9.0.25 still
|
||||||
|
ships ~1695 lines of it, re-based on `C_UnitAuras.GetAuraDataByIndex` /
|
||||||
|
`GetAuraDataBySpellName`. "Re-base on C_UnitAuras", not "remove", is the precedent.
|
||||||
|
|
||||||
|
**Group A — replaceable by `C_UnitAuras` (do these first):**
|
||||||
|
`GetDuration`, `GetDebuffCaster`, `IsOurDebuff`, `UnitBuff`/`UnitDebuff`,
|
||||||
|
`FindPlayerDebuff`/`FindPlayerBuff`, `GetAllDebuffsOnTarget`, `GetCachedIcon`,
|
||||||
|
`ApplyCarnageRefresh`, and the Dark Harvest trio (`ApplyDarkHarvestStart`/`End`,
|
||||||
|
`GetDarkHarvestReduction`, `GetTimeRemainingWithDarkHarvest`) — ClassicAPI ships
|
||||||
|
Carnage refresh and Dark Harvest tick compression in the DLL.
|
||||||
|
|
||||||
|
**Group B — no `C_UnitAuras` equivalent, keep:**
|
||||||
|
`ShouldApplyDebuffRank`, `DidSpellFail`, `WasSpellReflected`, `DidTargetEvade`,
|
||||||
|
`ProcessMissReason`, `IsPersonalDebuff`, `GetSpellRank`/`GetSpellBaseName`,
|
||||||
|
`HasPendingCast`. These are miss/rank/learning logic, not aura state.
|
||||||
|
|
||||||
|
**Gate before removing Group A:** confirm parity in-game on one spell where
|
||||||
|
ClassicAPI does the hard part — Rip under Carnage. Compare
|
||||||
|
`CleveRoids.libdebuff:GetDuration(spellID)` against
|
||||||
|
`C_UnitAuras.GetUnitAuraBySpellID(unit, spellID).duration` and the derived
|
||||||
|
remaining (`expirationTime - GetTime()`), on a target you have debuffed. If those
|
||||||
|
agree across a Carnage proc, Group A can go. Watch the documented best-effort
|
||||||
|
gaps: an aura cast before you logged in, and refresh-at-max-stacks.
|
||||||
|
|
||||||
## Marginal / optional follow-ups
|
## Marginal / optional follow-ups
|
||||||
|
|
||||||
- **`[swimming]`** → `IsSwimming()` would drop the nampower-2.36 version gate +
|
- **`[swimming]`** → `IsSwimming()` would drop the nampower-2.36 version gate +
|
||||||
|
|||||||
Reference in New Issue
Block a user