mirror of
https://github.com/brues-code/SuperCleveRoidMacros.git
synced 2026-09-16 03:38:00 +00:00
Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| eddc49fc60 | |||
| 54e3c3b3f4 | |||
| 4fa254b4e6 | |||
| b82d06d061 | |||
| 891467cd7e | |||
| 87ea8bdd41 | |||
| 53a1cf1441 | |||
| 073621e233 | |||
| 3e2b3db31f | |||
| 7101ad167a | |||
| afc5e513ca |
+14
-14
@@ -3,7 +3,8 @@
|
||||
|
||||
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
|
||||
this addon (ClassicAPI v1.15.0+, which added frame:RegisterUnitEvent), so the
|
||||
this addon (ClassicAPI v1.15.8+, which scoped GetMouseButtonClicked to the
|
||||
click dispatch; v1.15.0 added frame:RegisterUnitEvent), so the
|
||||
wrappers below call the API directly — no 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
|
||||
@@ -247,8 +248,8 @@ end
|
||||
-- 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).
|
||||
-- seconds remaining (nil if ClassicAPI didn't observe the applying cast).
|
||||
-- Player-only (vanilla LoC is local-only).
|
||||
function API.GetSchoolLockout()
|
||||
local n = C_LossOfControl.GetActiveLossOfControlDataCount() or 0
|
||||
for i = 1, n do
|
||||
@@ -312,10 +313,9 @@ 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)
|
||||
-- Health deficit (max - current) for `unit` in one call.
|
||||
function API.UnitHealthMissing(unit)
|
||||
return UnitHealthMissing(unit)
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
@@ -324,18 +324,18 @@ end
|
||||
|
||||
-- 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)
|
||||
-- Display-divided (rage reads 0..100).
|
||||
function API.UnitPower(unit, powerType)
|
||||
return UnitPower(unit, powerType)
|
||||
end
|
||||
|
||||
API.UnitPowerMax = UnitPowerMax or function(unit, powerType)
|
||||
return UnitManaMax(unit)
|
||||
function API.UnitPowerMax(unit, powerType)
|
||||
return UnitPowerMax(unit, powerType)
|
||||
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)
|
||||
function API.UnitPowerMissing(unit, powerType)
|
||||
return UnitPowerMissing(unit, powerType)
|
||||
end
|
||||
|
||||
-- Unit's primary power type as an integer (0=Mana .. 4=Happiness).
|
||||
|
||||
+2
-82
@@ -15,23 +15,6 @@ CleveRoids.ComboPointTracking = CleveRoids.ComboPointTracking or {}
|
||||
-- Structure: CleveRoids_ComboDurations[spellID][comboPoints] = duration
|
||||
CleveRoids_ComboDurations = CleveRoids_ComboDurations or {}
|
||||
|
||||
-- Storage for last Rip cast (for Carnage talent mechanic)
|
||||
-- Carnage talent: When Ferocious Bite procs Carnage, it refreshes Rip and Rake to their original duration
|
||||
-- Detection: When combo points don't drop to 0 after FB (they stay at 1 = Carnage proc)
|
||||
-- Talent Position: Tab 2 (Feral Combat), Talent 17
|
||||
-- Rank 1: 10% per CP, Rank 2: 20% per CP
|
||||
CleveRoids.lastRipCast = CleveRoids.lastRipCast or {
|
||||
duration = nil,
|
||||
targetGUID = nil,
|
||||
timestamp = 0
|
||||
}
|
||||
|
||||
CleveRoids.lastRakeCast = CleveRoids.lastRakeCast or {
|
||||
duration = nil,
|
||||
targetGUID = nil,
|
||||
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?"
|
||||
@@ -86,10 +69,8 @@ CleveRoids.ComboScalingSpellsByID = {
|
||||
[9896] = { base = 10, increment = 2, name = "Rip" }, -- Rank 6
|
||||
}
|
||||
|
||||
-- Ferocious Bite spell IDs (for Carnage talent mechanic)
|
||||
-- Carnage talent: When FB procs Carnage, refreshes Rip and Rake back to their original duration
|
||||
-- Proc detection: combo points stay at 1 after FB instead of dropping to 0
|
||||
-- Talent Position: Tab 2 (Feral Combat), Talent 17
|
||||
-- Ferocious Bite spell IDs. A combo-point finisher, so the cast-time combo
|
||||
-- snapshot has to know it.
|
||||
CleveRoids.FerociousBiteSpellIDs = {
|
||||
[22557] = true, -- Rank 1
|
||||
[22568] = true, -- Rank 2
|
||||
@@ -99,10 +80,6 @@ CleveRoids.FerociousBiteSpellIDs = {
|
||||
[31018] = true, -- Rank 6
|
||||
}
|
||||
|
||||
-- Rip / Rake families (for Carnage talent). Seeded by Rank 1; matches every rank.
|
||||
CleveRoids.RipSpellIDs = RankSet(1079)
|
||||
CleveRoids.RakeSpellIDs = RankSet(1822)
|
||||
|
||||
-- Combined table for all bleed spells that need immunity detection
|
||||
-- Used when checking if a cast bleed failed to apply (indicates bleed immunity)
|
||||
-- NOTE: These are the DEBUFF spell IDs (what appears on target), not cast spell IDs
|
||||
@@ -168,14 +145,6 @@ CleveRoids.ImmolateSpellIDs = {
|
||||
[25309] = true, -- Rank 8
|
||||
}
|
||||
|
||||
-- =============================================================================
|
||||
-- WARLOCK: Dark Harvest Duration Acceleration (TWoW Custom)
|
||||
-- Channeled spell that accelerates DoT tick rate by 30% while channeling
|
||||
-- Complex tracking: debuff expires 30% faster while Dark Harvest is active
|
||||
-- =============================================================================
|
||||
-- TWoW custom (see MoltenBlast note).
|
||||
CleveRoids.DarkHarvestSpellIDs = RankSet(52550)
|
||||
|
||||
-- =============================================================================
|
||||
-- DRUID: Rake Debuff Cap Boss Whitelist
|
||||
-- These bosses are likely to hit the 48 debuff cap, causing Rake to get pushed off
|
||||
@@ -773,55 +742,6 @@ end
|
||||
|
||||
function Extension.OnComboPointsChanged()
|
||||
CleveRoids.UpdateComboPoints()
|
||||
|
||||
-- CARNAGE PROC DETECTION (Cursive-style)
|
||||
-- When Ferocious Bite is used, combo points should drop to 0
|
||||
-- If Carnage procs, combo points will be 1 instead (the Carnage-granted combo point)
|
||||
-- Check: After Ferocious Bite (within 0.5s), if combo points > 0, Carnage procced
|
||||
if CleveRoids.lastFerociousBiteTime and CleveRoids.lastFerociousBiteTargetGUID then
|
||||
local timeSinceBite = GetTime() - CleveRoids.lastFerociousBiteTime
|
||||
if timeSinceBite < 0.5 then
|
||||
local currentCP = CleveRoids.GetComboPoints()
|
||||
if currentCP > 0 then
|
||||
-- Carnage procced! Combo points didn't drop to 0 (or rose back to 1)
|
||||
if CleveRoids.debug then
|
||||
DEFAULT_CHAT_FRAME:AddMessage(
|
||||
string.format("|cffff00ff[Carnage]|r PROC DETECTED! CP=%d after Ferocious Bite (%.2fs ago)",
|
||||
currentCP, timeSinceBite)
|
||||
)
|
||||
end
|
||||
|
||||
-- Apply the Carnage refresh to Rip and Rake
|
||||
local targetGUID = CleveRoids.lastFerociousBiteTargetGUID
|
||||
local targetName = CleveRoids.lastFerociousBiteTargetName or "Unknown"
|
||||
local biteSpellID = CleveRoids.lastFerociousBiteSpellID
|
||||
|
||||
-- Call the Carnage refresh function in Utility.lua
|
||||
if CleveRoids.libdebuff and CleveRoids.libdebuff.ApplyCarnageRefresh then
|
||||
CleveRoids.libdebuff.ApplyCarnageRefresh(targetGUID, targetName, biteSpellID)
|
||||
end
|
||||
|
||||
-- Clear the tracking to prevent multiple refreshes
|
||||
CleveRoids.lastFerociousBiteTime = nil
|
||||
CleveRoids.lastFerociousBiteTargetGUID = nil
|
||||
CleveRoids.lastFerociousBiteTargetName = nil
|
||||
CleveRoids.lastFerociousBiteSpellID = nil
|
||||
end
|
||||
else
|
||||
-- Time window expired, clear tracking
|
||||
if CleveRoids.lastFerociousBiteTime then
|
||||
if CleveRoids.debug then
|
||||
DEFAULT_CHAT_FRAME:AddMessage(
|
||||
string.format("|cffff00ff[Carnage]|r No proc - time window expired (%.2fs)", timeSinceBite)
|
||||
)
|
||||
end
|
||||
CleveRoids.lastFerociousBiteTime = nil
|
||||
CleveRoids.lastFerociousBiteTargetGUID = nil
|
||||
CleveRoids.lastFerociousBiteTargetName = nil
|
||||
CleveRoids.lastFerociousBiteSpellID = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Event handlers
|
||||
|
||||
+133
-47
@@ -897,43 +897,6 @@ local function OnAutoAttackOther(attackerGuid, targetGuid, totalDamage, hitInfo,
|
||||
CleveRoids.LastSwing.resistAmount = totalResist or 0
|
||||
CleveRoids.LastSwing.targetGuid = targetGuid
|
||||
|
||||
-- Paladin: refresh active Judgements on melee hit (Nampower fallback for UNIT_CASTEVENT)
|
||||
if CleveRoids.playerClass == "PALADIN" and targetGuid then
|
||||
local lib = type(CleveRoids.libdebuff) == "table" and CleveRoids.libdebuff or nil
|
||||
if lib and lib.objects then
|
||||
local normalizedTarget = CleveRoids.NormalizeGUID(targetGuid)
|
||||
if normalizedTarget and lib.objects[normalizedTarget] then
|
||||
for spellID, rec in pairs(lib.objects[normalizedTarget]) do
|
||||
if lib.judgementSpells and lib.judgementSpells[spellID] and rec.start and rec.duration then
|
||||
local remaining = rec.duration + rec.start - GetTime()
|
||||
if remaining > 0 and rec.caster == "player" then
|
||||
rec.start = GetTime()
|
||||
|
||||
if CleveRoids.debug then
|
||||
local spellName = C_Spell.GetSpellName(spellID) or "Unknown"
|
||||
local baseName = CleveRoids.StripRank(spellName) or "Unknown"
|
||||
DEFAULT_CHAT_FRAME:AddMessage(
|
||||
string.format("|cff00ffaa[Judgement Refresh]|r Refreshed %s (ID:%d) on melee hit - new duration: %ds",
|
||||
baseName, spellID, rec.duration)
|
||||
)
|
||||
end
|
||||
|
||||
-- Sync to pfUI if loaded (pre-7.6 only)
|
||||
if pfUI and pfUI.api and pfUI.api.libdebuff then
|
||||
local spellName = C_Spell.GetSpellName(spellID) or nil
|
||||
local baseName = CleveRoids.StripRank(spellName)
|
||||
local targetName = (lib.guidToName and lib.guidToName[normalizedTarget]) or UnitName("target")
|
||||
local targetLevel = UnitLevel("target") or 0
|
||||
if targetName and baseName then
|
||||
pfUI.api.libdebuff:AddEffect(targetName, targetLevel, baseName, rec.duration, "player")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Process AUTO_ATTACK_SELF event (player being attacked)
|
||||
@@ -5278,32 +5241,154 @@ local function ResolvePetHappinessState(value)
|
||||
return PET_HAPPINESS_STATES[GetLowercaseString(value)]
|
||||
end
|
||||
|
||||
-- ClassicAPI's totem bar is TBC-ordered: SUMMON_TOTEM_SLOT1..4 in Spell.dbc.
|
||||
local TOTEM_SLOTS = {
|
||||
fire = 1,
|
||||
earth = 2,
|
||||
water = 3,
|
||||
air = 4,
|
||||
["1"] = 1,
|
||||
["2"] = 2,
|
||||
["3"] = 3,
|
||||
["4"] = 4,
|
||||
}
|
||||
|
||||
-- Resolve a [totem:X] argument to a slot. An element name or slot number names
|
||||
-- the slot directly; anything else is matched against the name of the totem
|
||||
-- standing in each slot, so [totem:Searing_Totem] asks for that totem rather
|
||||
-- than "whatever is in the fire slot". nil when neither matches -- which
|
||||
-- includes naming a totem that is not currently out.
|
||||
local function ResolveTotemSlot(name)
|
||||
if type(name) ~= "string" or name == "" then return nil end
|
||||
|
||||
local searchName = GetLowercaseString(
|
||||
CleveRoids.Trim(string.gsub(CleveRoids.StripRank(name), "_", " ")))
|
||||
|
||||
local slot = TOTEM_SLOTS[searchName]
|
||||
if slot then return slot end
|
||||
|
||||
for i = 1, 4 do
|
||||
-- Second return, not the first: GetTotemInfo's haveTotem reports whether
|
||||
-- the player carries the slot's TOOL item, not whether a totem is out.
|
||||
local _, totemName = GetTotemInfo(i)
|
||||
if totemName and totemName ~= "" and GetLowercaseString(totemName) == searchName then
|
||||
return i
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- True while any totem slot has a timer running. Drives the OnUpdate re-test
|
||||
-- that keeps [totem:X<N] icons honest -- see the caller for why polling is the
|
||||
-- only option. GetTotemTimeLeft, not GetTotemInfo: this asks whether a countdown
|
||||
-- is in progress (a totem with no timer has nothing to go stale), and it reads
|
||||
-- the slot alone, where GetTotemInfo walks the bags for the tool item.
|
||||
function CleveRoids.AnyTotemTimerRunning()
|
||||
for i = 1, 4 do
|
||||
local timeLeft = GetTotemTimeLeft(i)
|
||||
if timeLeft and timeLeft > 0 then return true end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
-- [totem:X] / [nototem:X], shaped like the aura validators: X is a plain name or
|
||||
-- a parsed comparison entry, comparisons read seconds left, and `#N` stack
|
||||
-- comparisons read 1 for a standing totem -- a totem is either up or it isn't.
|
||||
--
|
||||
-- An empty slot reads -1 on both axes, the same "missing counts as least" the
|
||||
-- aura path uses, so [totem:Searing_Totem<5] passes while the totem is expiring
|
||||
-- AND while it is absent: one clause for the whole recast macro.
|
||||
local function ValidateTotem(args)
|
||||
if not args then return false end
|
||||
|
||||
if type(args) ~= "table" then
|
||||
args = { name = args }
|
||||
end
|
||||
|
||||
local remaining, stacks = -1, -1
|
||||
local slot = ResolveTotemSlot(args.name)
|
||||
if slot then
|
||||
-- Occupancy comes from the name, not from the time left: a totem whose
|
||||
-- summon spell carries no SpellDuration row reads 0 seconds, and that is
|
||||
-- "up with no timer", not "absent".
|
||||
local _, totemName = GetTotemInfo(slot)
|
||||
if totemName and totemName ~= "" then
|
||||
stacks = 1
|
||||
remaining = GetTotemTimeLeft(slot)
|
||||
end
|
||||
end
|
||||
|
||||
local ops = CleveRoids.operators
|
||||
local cmp = CleveRoids.comparators
|
||||
|
||||
-- Multi-comparison (e.g. >2&<8) - ALL must pass
|
||||
if args.comparisons and type(args.comparisons) == "table" then
|
||||
for _, comp in ipairs(args.comparisons) do
|
||||
if not ops[comp.operator] then return false end
|
||||
local value = comp.checkStacks and stacks or remaining
|
||||
if not cmp[comp.operator](value, comp.amount) then return false end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
if not args.amount and not args.operator and not args.checkStacks then
|
||||
return stacks == 1
|
||||
elseif args.amount and ops[args.operator] then
|
||||
return cmp[args.operator](args.checkStacks and stacks or remaining, args.amount)
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
-- A list of Conditionals and their functions to validate them
|
||||
CleveRoids.Keywords = {
|
||||
-- [button:N] — true while mouse button N is held (1=Left, 2=Right, 3=Middle,
|
||||
-- 4/5=extra). Routed through Multi so OR/AND lists and repeated groups behave
|
||||
-- like every other argument conditional ([button:1/2] = left or right).
|
||||
-- [button] with no argument — true if any mapped mouse button is held.
|
||||
-- [button:N] — N is the button that INVOKED this action (1=Left, 2=Right,
|
||||
-- 3=Middle, 4/5=extra), not one held down as a modifier. That is retail's
|
||||
-- meaning, and it is why a keybind press counts as button 1: retail activates
|
||||
-- through the left-click path, so [button:1] passes for a keybind and a
|
||||
-- left-click alike, while [button:2] passes only for an actual right-click.
|
||||
-- Routed through Multi so OR/AND lists behave like every other argument
|
||||
-- conditional ([button:1/2] = invoked by left or right).
|
||||
--
|
||||
-- Bare [button] / [nobutton] ask whether a click drove this at all -- the one
|
||||
-- thing the underlying data says that retail's conditional cannot express,
|
||||
-- and the practical "activated by a keybind, not a click" test.
|
||||
button = function(conditionals)
|
||||
if type(conditionals.button) ~= "table" then
|
||||
return CleveRoids.AnyMouseButtonDown()
|
||||
return CleveRoids.WasClickActivated()
|
||||
end
|
||||
local invoking = CleveRoids.GetActivatingButton()
|
||||
return Multi(conditionals.button, function(button)
|
||||
local name = CleveRoids.buttons[button]
|
||||
return name and IsMouseButtonDown(name) or false
|
||||
return CleveRoids.buttons[button] == invoking
|
||||
end, conditionals, "button")
|
||||
end,
|
||||
|
||||
nobutton = function(conditionals)
|
||||
if type(conditionals.nobutton) ~= "table" then
|
||||
return not CleveRoids.AnyMouseButtonDown()
|
||||
return not CleveRoids.WasClickActivated()
|
||||
end
|
||||
local invoking = CleveRoids.GetActivatingButton()
|
||||
return NegatedMulti(conditionals.nobutton, function(button)
|
||||
local name = CleveRoids.buttons[button]
|
||||
return not (name and IsMouseButtonDown(name))
|
||||
return CleveRoids.buttons[button] ~= invoking
|
||||
end, conditionals, "nobutton")
|
||||
end,
|
||||
|
||||
-- [totem:X] — X names a totem-bar slot (fire/earth/water/air, or 1-4) or the
|
||||
-- totem itself ([totem:Searing_Totem]). Bare [totem] takes the action as its
|
||||
-- argument, as [mybuff] does, so /cast [nototem] Searing Totem is the whole
|
||||
-- recast macro. Time and stack comparisons follow the aura precedent; see
|
||||
-- ValidateTotem.
|
||||
totem = function(conditionals)
|
||||
return Multi(conditionals.totem, function(v)
|
||||
return ValidateTotem(v)
|
||||
end, conditionals, "totem")
|
||||
end,
|
||||
|
||||
nototem = function(conditionals)
|
||||
return NegatedMulti(conditionals.nototem, function(v)
|
||||
return not ValidateTotem(v)
|
||||
end, conditionals, "nototem")
|
||||
end,
|
||||
|
||||
exists = function(conditionals)
|
||||
return UnitExists(conditionals.target)
|
||||
end,
|
||||
@@ -9237,6 +9322,7 @@ CleveRoids.STATIC_CONDITIONALS = {
|
||||
mod = true, nomod = true,
|
||||
keydown = true, nokeydown = true,
|
||||
button = true, nobutton = true,
|
||||
totem = true, nototem = true,
|
||||
swimming = true, noswimming = true, swim = true, noswim = true,
|
||||
indoors = true, noindoors = true, outdoors = true, nooutdoors = true,
|
||||
rooted = true, norooted = true,
|
||||
|
||||
@@ -201,9 +201,13 @@ requirementCheckFrame:SetScript("OnEvent", function()
|
||||
-- v1.15.0 added frame:RegisterUnitEvent, which Utility.lua calls at file scope.
|
||||
-- On an older build that call raises, aborting the rest of the chunk -- so this
|
||||
-- is not a degraded-features warning, it's "the addon did not finish loading".
|
||||
local hasClassicAPI1150 = hasClassicAPI and CleveRoids.ClassicAPI.HasMinimumVersion(1, 15, 0)
|
||||
-- v1.15.8 scoped GetMouseButtonClicked to the click dispatch. Below it the
|
||||
-- function exists but a held button keeps its value, so [button:N] reads the
|
||||
-- button you are turning the camera with -- wrong quietly, which is the worse
|
||||
-- failure and why the floor moved rather than the conditional being gated.
|
||||
local hasClassicAPI1158 = hasClassicAPI and CleveRoids.ClassicAPI.HasMinimumVersion(1, 15, 8)
|
||||
|
||||
if not hasNampower30 or not hasUnitXP or not hasClassicAPI or not hasClassicAPI1150 then
|
||||
if not hasNampower30 or not hasUnitXP or not hasClassicAPI or not hasClassicAPI1158 then
|
||||
-- Show warnings (don't disable — tearing down a partially-initialized addon causes hangs)
|
||||
if not hasNampower then
|
||||
CleveRoids.Print("|cFFFF9900WARNING:|r |cFF00FFFFAvitasia's Nampower v3.0.0+|r is required:")
|
||||
@@ -223,9 +227,9 @@ requirementCheckFrame:SetScript("OnEvent", function()
|
||||
CleveRoids.Print("|cFFFF9900WARNING:|r |cFF00FFFFClassicAPI|r is required:")
|
||||
CleveRoids.Print("https://github.com/brues-code/ClassicAPI")
|
||||
CleveRoids.Print("Dispel-type and movement conditionals will be unavailable without it.")
|
||||
elseif not hasClassicAPI1150 then
|
||||
elseif not hasClassicAPI1158 then
|
||||
local major, minor, patch = CleveRoids.ClassicAPI.GetVersion()
|
||||
CleveRoids.Print(format("|cFFFF9900WARNING:|r |cFF00FFFFClassicAPI v1.15.0+|r is required (you have v%d.%d.%d):", major, minor, patch))
|
||||
CleveRoids.Print(format("|cFFFF9900WARNING:|r |cFF00FFFFClassicAPI v1.15.8+|r is required (you have v%d.%d.%d):", major, minor, patch))
|
||||
CleveRoids.Print("https://github.com/brues-code/ClassicAPI")
|
||||
CleveRoids.Print("The addon cannot finish loading on this version -- update ClassicAPI.")
|
||||
end
|
||||
@@ -4190,6 +4194,21 @@ function CleveRoids.OnUpdate(self)
|
||||
end
|
||||
end
|
||||
|
||||
-- A totem's timer runs down with nothing behind it: PLAYER_TOTEM_UPDATE fires
|
||||
-- when a slot changes hands, never as one counts toward zero, so a
|
||||
-- [totem:X<N] icon would hold the answer from its last refresh until some
|
||||
-- unrelated event queued another. Unlike an aura, there is no ambient event
|
||||
-- stream to ride -- UNIT_AURA is what quietly keeps [mybuff:X<N] honest -- so
|
||||
-- a re-test once a second while any totem timer runs is the whole mechanism.
|
||||
-- Only the icon is at stake: the click path evaluates conditionals live.
|
||||
-- Realtime mode re-tests everything each tick and needs none of this.
|
||||
if CRM.realtime == 0 and time >= (CR.nextTotemPoll or 0) then
|
||||
CR.nextTotemPoll = time + 1
|
||||
if CR.AnyTotemTimerRunning() then
|
||||
CR.isActionUpdateQueued = true
|
||||
end
|
||||
end
|
||||
|
||||
-- Check the saved variable to decide which update mode to use.
|
||||
if CRM.realtime == 1 then
|
||||
-- Realtime Mode: Force an update on every throttled tick for maximum responsiveness.
|
||||
@@ -4260,20 +4279,6 @@ function CleveRoids.OnUpdate(self)
|
||||
if (time - CR.lastCleanupTime) >= CR.CLEANUP_INTERVAL then
|
||||
CR.lastCleanupTime = time
|
||||
|
||||
-- MEMORY: Clean up carnageDurationOverrides older than 30 seconds
|
||||
-- PERFORMANCE: Use next() directly instead of pairs() to avoid iterator allocation
|
||||
local carnageOverrides = CR.carnageDurationOverrides
|
||||
if carnageOverrides then
|
||||
local spellID, data = next(carnageOverrides)
|
||||
while spellID do
|
||||
local nextID = next(carnageOverrides, spellID)
|
||||
if data.timestamp and (time - data.timestamp) > 30 then
|
||||
carnageOverrides[spellID] = nil
|
||||
end
|
||||
spellID, data = nextID, nextID and carnageOverrides[nextID]
|
||||
end
|
||||
end
|
||||
|
||||
-- MEMORY: Clean up old ComboPointTracking entries (older than 60 seconds)
|
||||
-- PERFORMANCE: Use next() directly instead of pairs() to avoid iterator allocation
|
||||
local comboTracking = CR.ComboPointTracking
|
||||
@@ -4461,12 +4466,8 @@ CleveRoids.Frame:RegisterEvent("UPDATE_SHAPESHIFT_FORM")
|
||||
CleveRoids.Frame:RegisterEvent("SPELL_UPDATE_COOLDOWN")
|
||||
CleveRoids.Frame:RegisterEvent("PLAYER_STARTED_MOVING")
|
||||
-- ClassicAPI loss-of-control (school-interrupt lockout) for [locked]/[nolocked].
|
||||
-- Gated on the namespace so an older ClassicAPI without it doesn't error on an
|
||||
-- unknown event.
|
||||
if type(C_LossOfControl) == "table" then
|
||||
CleveRoids.Frame:RegisterEvent("LOSS_OF_CONTROL_ADDED")
|
||||
CleveRoids.Frame:RegisterEvent("LOSS_OF_CONTROL_UPDATE")
|
||||
end
|
||||
CleveRoids.Frame:RegisterEvent("LOSS_OF_CONTROL_ADDED")
|
||||
CleveRoids.Frame:RegisterEvent("LOSS_OF_CONTROL_UPDATE")
|
||||
-- The unit state streams that drive icon refresh: GUID events when Nampower
|
||||
-- provides them (v2.39+, one event per unit change rather than one per token),
|
||||
-- else the stock per-token events. These cannot become RegisterUnitEvent calls
|
||||
@@ -4530,6 +4531,11 @@ if CleveRoids.NampowerAPI.features.hasKeyEvents then
|
||||
CleveRoids.Frame:RegisterEvent("KEY_UP")
|
||||
end
|
||||
|
||||
-- ClassicAPI totem-bar tracking, for [totem:X] icon refresh. Ungated, unlike the
|
||||
-- Nampower features above: it long predates the ClassicAPI floor the addon
|
||||
-- already refuses to run below.
|
||||
CleveRoids.Frame:RegisterEvent("PLAYER_TOTEM_UPDATE")
|
||||
|
||||
-- NOTE: SuperMacro hook installation is handled by Compatibility/SuperMacro.lua
|
||||
-- which has the complete implementation including the INTERCEPT path for all commands
|
||||
|
||||
@@ -4630,58 +4636,11 @@ function CleveRoids.Frame:ADDON_LOADED(addon)
|
||||
end
|
||||
|
||||
function CleveRoids.Frame:UNIT_CASTEVENT(caster,target,action,spell_id,cast_time)
|
||||
-- Handle melee swings for judgement refresh
|
||||
-- Melee swings carry no spell to resolve. ClassicAPI mirrors the server's
|
||||
-- paladin judgement-refresh rule off SMSG_ATTACKERSTATEUPDATE in the DLL
|
||||
-- (src/aura/JudgementRefresh.cpp), so there is nothing to do here.
|
||||
if action == "MAINHAND" or action == "OFFHAND" then
|
||||
-- Only process if this is the player's melee swing
|
||||
if caster == CleveRoids.playerGuid and CleveRoids.playerClass == "PALADIN" then
|
||||
-- Refresh judgements on the target
|
||||
-- Defensive: verify libdebuff is a table before accessing properties
|
||||
local lib = type(CleveRoids.libdebuff) == "table" and CleveRoids.libdebuff or nil
|
||||
if target and lib and lib.objects then
|
||||
local normalizedTarget = CleveRoids.NormalizeGUID(target)
|
||||
if normalizedTarget and lib.objects[normalizedTarget] then
|
||||
-- Refresh all active Judgements on the target
|
||||
for spellID, rec in pairs(lib.objects[normalizedTarget]) do
|
||||
-- Check if this is a judgement by spell ID
|
||||
if lib.judgementSpells and lib.judgementSpells[spellID] and rec.start and rec.duration then
|
||||
-- Only refresh if the Judgement is still active and was cast by player
|
||||
local remaining = rec.duration + rec.start - GetTime()
|
||||
if remaining > 0 and rec.caster == "player" then
|
||||
-- Refresh the Judgement by updating the start time
|
||||
rec.start = GetTime()
|
||||
|
||||
local spellName = C_Spell.GetSpellName(spellID)
|
||||
local baseName = CleveRoids.StripRank(spellName) or "Unknown"
|
||||
|
||||
if CleveRoids.debug then
|
||||
DEFAULT_CHAT_FRAME:AddMessage(
|
||||
string.format("|cff00ffaa[Judgement Refresh]|r Refreshed %s (ID:%d) on %s hit - new duration: %ds",
|
||||
baseName, spellID, action, rec.duration)
|
||||
)
|
||||
end
|
||||
|
||||
-- Also sync to pfUI if it's loaded (pre-7.6 only)
|
||||
if pfUI and pfUI.api and pfUI.api.libdebuff then
|
||||
local targetName = (lib.guidToName and lib.guidToName[normalizedTarget]) or UnitName("target")
|
||||
local targetLevel = UnitLevel("target") or 0
|
||||
|
||||
if targetName then
|
||||
pfUI.api.libdebuff:AddEffect(targetName, targetLevel, baseName, rec.duration, "player")
|
||||
|
||||
if CleveRoids.debug then
|
||||
DEFAULT_CHAT_FRAME:AddMessage(
|
||||
string.format("|cff00ffaa[pfUI Judgement Refresh]|r Synced %s refresh to pfUI", baseName)
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return -- Still return early after processing melee
|
||||
return
|
||||
end
|
||||
|
||||
-- Debug channel tracking
|
||||
@@ -5075,26 +5034,6 @@ function CleveRoids.Frame:SPELLCAST_CHANNEL_STOP()
|
||||
CleveRoids.CurrentSpell.spellName = ""
|
||||
CleveRoids.CurrentSpell.castingSpellId = nil
|
||||
|
||||
-- WARLOCK DARK HARVEST: Mark channeling as ended
|
||||
-- Credits: Avitasia / Cursive addon
|
||||
if CleveRoids.darkHarvestData and CleveRoids.darkHarvestData.isActive then
|
||||
CleveRoids.darkHarvestData.isActive = false
|
||||
CleveRoids.darkHarvestData.endTime = GetTime()
|
||||
|
||||
-- Apply Dark Harvest end to all DoTs on target (finalizes reduction)
|
||||
if CleveRoids.libdebuff and CleveRoids.libdebuff.ApplyDarkHarvestEnd then
|
||||
CleveRoids.libdebuff.ApplyDarkHarvestEnd(CleveRoids.darkHarvestData.targetGUID)
|
||||
end
|
||||
|
||||
if CleveRoids.debug then
|
||||
local activeTime = CleveRoids.darkHarvestData.endTime - CleveRoids.darkHarvestData.startTime
|
||||
DEFAULT_CHAT_FRAME:AddMessage(
|
||||
string.format("|cff9482c9[Dark Harvest]|r Channel ended after %.1fs (DoT acceleration stopped)",
|
||||
activeTime)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
-- Force immediate action update
|
||||
CleveRoids.TestForAllActiveActions()
|
||||
end
|
||||
@@ -5648,6 +5587,14 @@ function CleveRoids.Frame:KEY_UP()
|
||||
CleveRoids.isActionUpdateQueued = true
|
||||
end
|
||||
|
||||
-- ClassicAPI PLAYER_TOTEM_UPDATE: arg1 = the slot (1 Fire .. 4 Air) whose totem
|
||||
-- was dropped, expired, killed or recalled. The slot goes unread and every macro
|
||||
-- is refreshed: a [totem:X] macro may name any slot, and [nototem:X] flips on any
|
||||
-- of them.
|
||||
function CleveRoids.Frame:PLAYER_TOTEM_UPDATE()
|
||||
CleveRoids.QueueActionUpdate()
|
||||
end
|
||||
|
||||
-- Base SendChatMessage captured at first hook; used to break a hook cycle.
|
||||
local baseSendChatMessage = SendChatMessage
|
||||
local sendingChatMessage = false
|
||||
|
||||
@@ -45,8 +45,9 @@ CleveRoids.supported = CleveRoids.hasTurtle
|
||||
-- "may we call C_Macro.SetMacroDisplay" guard, so the two can never disagree.
|
||||
--
|
||||
-- Feature-detect rather than version-check: SetMacroDisplay ships in ClassicAPI
|
||||
-- v1.15.0, this addon's minimum, so a nil here means the client mod is missing
|
||||
-- outright -- the case Core.lua's requirement check warns about but keeps running.
|
||||
-- v1.15.0, below this addon's minimum, so a nil here means the client mod is
|
||||
-- missing outright -- the case Core.lua's requirement check warns about but
|
||||
-- keeps running.
|
||||
CleveRoids.ClassicAPIMacroDisplay =
|
||||
(type(C_Macro) == "table" and C_Macro.SetMacroDisplay ~= nil) and true or false
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ Enhanced macro addon for World of Warcraft 1.12.1 (Vanilla/Turtle WoW) with dyna
|
||||
|-----|:--------:|---------|
|
||||
| [Nampower](https://github.com/brues-code/nampower/releases) (v3.0.0+) | ✅ | Spell queueing, DBC data, auto-attack events |
|
||||
| [UnitXP_SP3](https://codeberg.org/konaka/UnitXP_SP3/releases) | ✅ | Distance checks, `[multiscan]` enemy scanning |
|
||||
| [ClassicAPI](https://github.com/brues-code/ClassicAPI/releases) (v1.15.0+) | ✅ | Modern `C_*` API: dispel-type conditionals (`[magic]`, `[curse]`, …), `[moving]` speed, unit-filtered events |
|
||||
| [ClassicAPI](https://github.com/brues-code/ClassicAPI/releases) (v1.15.8+) | ✅ | Modern `C_*` API: dispel-type conditionals (`[magic]`, `[curse]`, …), `[moving]` speed, unit-filtered events |
|
||||
|
||||
## Installation
|
||||
|
||||
|
||||
+80
-1157
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user