fix reactive for overpower and revenge to see as active in any stance.

This commit is contained in:
Jrc13245
2025-11-25 16:58:44 -05:00
parent 139c95ba22
commit 21119c8bd5
4 changed files with 312 additions and 6 deletions
+7 -1
View File
@@ -1185,7 +1185,13 @@ function CleveRoids.GetActionButtonInfo(slot)
end
function CleveRoids.IsReactiveUsable(spellName)
-- First check combat log-based proc tracking (works across stances)
if CleveRoids.HasReactiveProc and CleveRoids.HasReactiveProc(spellName) then
return 1
end
-- Use Nampower's IsSpellUsable if available (more accurate)
-- Note: IsSpellUsable doesn't actually exist in Nampower, but keeping for future compatibility
if IsSpellUsable then
local usable, oom = IsSpellUsable(spellName)
if usable == 1 and oom ~= 1 then
@@ -1195,7 +1201,7 @@ function CleveRoids.IsReactiveUsable(spellName)
end
end
-- Fallback to original method
-- Fallback to action bar slot checking (requires correct stance)
if not CleveRoids.reactiveSlots[spellName] then return false end
local actionSlot = CleveRoids.reactiveSlots[spellName]
local isUsable, oom = CleveRoids.Hooks.OriginalIsUsableAction(actionSlot)
+94 -2
View File
@@ -594,8 +594,18 @@ function CleveRoids.TestForActiveAction(actions)
local onCooldown = (start > 0 and duration > 0)
if actions.active.isReactive then
-- Use Nampower's IsSpellUsable if available for better detection
if IsSpellUsable then
-- Check combat log-based proc tracking first (stance-independent)
if CleveRoids.HasReactiveProc and CleveRoids.HasReactiveProc(actions.active.action) then
-- Proc is active, show as usable if in range and have enough rage/mana
if actions.active.inRange ~= 0 and not actions.active.oom then
actions.active.usable = 1
elseif pfUI and pfUI.bars and actions.active.oom then
actions.active.usable = 2 -- pfUI: out of mana/rage
else
actions.active.usable = nil
end
-- Use Nampower's IsSpellUsable if available (stance-aware fallback)
elseif IsSpellUsable then
local usable, oom = IsSpellUsable(actions.active.action)
if usable == 1 and oom ~= 1 then
actions.active.usable = (pfUI and pfUI.bars) and nil or 1
@@ -3349,6 +3359,10 @@ SlashCmdList["CLEVEROID"] = function(msg)
DEFAULT_CHAT_FRAME:AddMessage('/cleveroid diagnosetalent <spellID> - Diagnose talent modifier issues')
DEFAULT_CHAT_FRAME:AddMessage("|cffffaa00Equipment Modifiers:|r")
DEFAULT_CHAT_FRAME:AddMessage('/cleveroid testequip <spellID> - Test equipment modifier for a spell')
DEFAULT_CHAT_FRAME:AddMessage("|cffffaa00Reactive Proc Tracking:|r")
DEFAULT_CHAT_FRAME:AddMessage('/cleveroid listprocs - Show active reactive ability procs')
DEFAULT_CHAT_FRAME:AddMessage('/cleveroid setproc <spell> [duration] - Manually set proc (testing)')
DEFAULT_CHAT_FRAME:AddMessage('/cleveroid clearproc [spell|all] - Clear reactive proc(s)')
DEFAULT_CHAT_FRAME:AddMessage("|cffffaa00Casting Detection:|r")
DEFAULT_CHAT_FRAME:AddMessage('/cleveroid testcasting - Test [selfcasting]/[noselfcasting] conditionals')
return
@@ -3811,6 +3825,84 @@ SlashCmdList["CLEVEROID"] = function(msg)
return
end
-- listprocs (show active reactive procs)
if cmd == "listprocs" or cmd == "procs" or cmd == "reactive" then
CleveRoids.Print("=== Active Reactive Procs ===")
local found = false
local now = GetTime()
local _, currentTargetGUID = UnitExists("target")
if CleveRoids.reactiveProcs then
for spellName, procData in pairs(CleveRoids.reactiveProcs) do
if procData and procData.expiry and procData.expiry > now then
local remaining = procData.expiry - now
local guidInfo = ""
if procData.targetGUID then
local matches = currentTargetGUID and (currentTargetGUID == procData.targetGUID)
if matches then
guidInfo = " |cff00ff00[Current Target]|r"
else
local targetName = UnitName("target")
if targetName then
guidInfo = " |cffff0000[Wrong Target: " .. targetName .. "]|r"
else
guidInfo = " |cffff0000[No Target]|r"
end
end
end
CleveRoids.Print(spellName .. ": " .. string.format("%.1fs", remaining) .. " remaining" .. guidInfo)
found = true
end
end
end
if not found then
CleveRoids.Print("|cffffaa00No active reactive procs|r")
end
return
end
-- setproc (manually set a reactive proc for testing)
if cmd == "setproc" or cmd == "procset" then
if val == "" then
CleveRoids.Print("Usage: /cleveroid setproc <spell> [duration]")
CleveRoids.Print("Example: /cleveroid setproc Overpower 5")
CleveRoids.Print("Note: Uses current target's GUID for target-specific procs")
return
end
local duration = tonumber(val2) or 5.0
local _, targetGUID = UnitExists("target")
if CleveRoids.SetReactiveProc then
CleveRoids.SetReactiveProc(val, duration, targetGUID)
local guidMsg = targetGUID and (" for target [" .. (UnitName("target") or "Unknown") .. "]") or ""
CleveRoids.Print("Set " .. val .. " proc for " .. duration .. " seconds" .. guidMsg)
else
CleveRoids.Print("|cffff0000Reactive proc system not loaded!|r")
end
return
end
-- clearproc (clear a reactive proc)
if cmd == "clearproc" or cmd == "procclear" then
if val == "" or val == "all" then
CleveRoids.reactiveProcs = {}
CleveRoids.Print("Cleared all reactive procs")
else
if CleveRoids.ClearReactiveProc then
CleveRoids.ClearReactiveProc(val)
CleveRoids.Print("Cleared " .. val .. " proc")
else
CleveRoids.Print("|cffff0000Reactive proc system not loaded!|r")
end
end
CleveRoids.QueueActionUpdate()
return
end
-- Unknown command fallback
CleveRoids.Print("Usage:")
DEFAULT_CHAT_FRAME:AddMessage("/cleveroid - Show current settings")
+23 -3
View File
@@ -231,18 +231,38 @@ end
function CleveRoids.IndexActionSlot(slot)
if not HasAction(slot) then
CleveRoids.Actions[slot] = nil
-- When clearing a reactive slot, check if we need to find it elsewhere
local clearedReactiveName = CleveRoids.reactiveSlots[slot]
CleveRoids.ClearSlot(CleveRoids.reactiveSlots, slot)
CleveRoids.ClearSlot(CleveRoids.actionSlots, slot)
-- If we cleared a reactive spell, rescan to find it in another slot
if clearedReactiveName then
for i = 1, 120 do
if i ~= slot and HasAction(i) then
local _, _, scanName = CleveRoids.GetActionButtonInfo(i)
if scanName == clearedReactiveName then
CleveRoids.reactiveSlots[clearedReactiveName] = i
CleveRoids.reactiveSlots[i] = clearedReactiveName
break
end
end
end
end
else
local actionType, _, name, rank = CleveRoids.GetActionButtonInfo(slot)
if name then
local reactiveName = CleveRoids.reactiveSpells[name] and name
local actionSlotName = name..(rank and ("("..rank..")") or "")
if reactiveName then
if not CleveRoids.reactiveSlots[reactiveName] then
CleveRoids.reactiveSlots[reactiveName] = slot
CleveRoids.reactiveSlots[slot] = reactiveName
-- Always update the mapping to ensure we track the correct slot
-- Clear old mapping for this spell name first
local oldSlot = CleveRoids.reactiveSlots[reactiveName]
if oldSlot and oldSlot ~= slot then
CleveRoids.reactiveSlots[oldSlot] = nil
end
CleveRoids.reactiveSlots[reactiveName] = slot
CleveRoids.reactiveSlots[slot] = reactiveName
elseif not reactiveName then
CleveRoids.ClearSlot(CleveRoids.reactiveSlots, slot)
end
+188
View File
@@ -2535,3 +2535,191 @@ immunityFrame:SetScript("OnEvent", function()
ParseImmunityCombatLog()
end
end)
-- ============================================================================
-- REACTIVE ABILITY PROC TRACKING SYSTEM
-- ============================================================================
-- Tracks reactive ability procs independently of stance/usability
-- Allows detection of Overpower/Revenge/Riposte procs even when not in correct stance
-- Table to store reactive proc states with expiry times and target GUIDs
-- Structure: { spellName = { expiry = time, targetGUID = guid } }
CleveRoids.reactiveProcs = CleveRoids.reactiveProcs or {}
-- Proc durations (in seconds)
-- Overpower and Revenge: 4 seconds
-- Riposte: 5 seconds (keeping at 5 for safety, can be adjusted)
local REACTIVE_PROC_DURATION = 4.0
-- Reactive ability trigger patterns for combat log
local reactivePatterns = {
Overpower = {
-- Procs when ENEMY dodges YOUR attack (auto or ability)
patterns = {
-- Auto attack dodges
"(.+) dodges", -- English: "Target dodges"
"(.+) weicht aus", -- German
"(.+) esquive", -- French
"(.+)이%(가%) 회피", -- Korean
"躲闪了(.+)", -- Chinese Simplified
"躲閃了(.+)", -- Chinese Traditional
-- Ability dodges
"was dodged by", -- English: "Your Mortal Strike was dodged by Target"
"wurde von (.+) ausgewichen", -- German
"a été esquivé par", -- French
"을%(를%) (.+)이%(가%) 회피", -- Korean
"被(.+)躲闪", -- Chinese Simplified
"被(.+)躲閃", -- Chinese Traditional
},
type = "enemy_dodge",
requiresTargetGUID = true,
duration = 4.0 -- 4 second proc window
},
Riposte = {
-- Procs when YOU parry an enemy attack
patterns = {
"You parry", -- English: "You parry X's Y"
"Ihr pariert", -- German
"Vous parez", -- French
"막아냈습니다", -- Korean
"你招架了", -- Chinese Simplified
"你招架了", -- Chinese Traditional
},
type = "player_parry",
requiresTargetGUID = true, -- Track which enemy you parried
duration = 5.0 -- 5 second proc window (estimated, may be 4s)
},
Revenge = {
-- Procs when YOU block, dodge, or parry an enemy attack (any stance)
patterns = {
"You block", -- English: "You block X's Y"
"You dodge", -- English: "You dodge X's Y"
"You parry", -- English: "You parry X's Y"
"Ihr blockt", -- German block
"Ihr weicht aus", -- German dodge
"Ihr pariert", -- German parry
"Vous bloquez", -- French block
"Vous esquivez", -- French dodge
"Vous parez", -- French parry
"막았습니다", -- Korean block
"회피했습니다", -- Korean dodge
"막아냈습니다", -- Korean parry
"你格挡了", -- Chinese Simplified block
"你躲闪了", -- Chinese Simplified dodge
"你招架了", -- Chinese Simplified parry
"你格擋了", -- Chinese Traditional block
"你躲閃了", -- Chinese Traditional dodge
"你招架了", -- Chinese Traditional parry
},
type = "player_avoid",
requiresTargetGUID = true, -- Track which enemy triggered it
duration = 4.0 -- 4 second proc window
}
}
-- Set a reactive proc state with optional target GUID
function CleveRoids.SetReactiveProc(spellName, duration, targetGUID)
duration = duration or REACTIVE_PROC_DURATION
CleveRoids.reactiveProcs[spellName] = {
expiry = GetTime() + duration,
targetGUID = targetGUID
}
end
-- Check if a reactive proc is active (with optional GUID check)
function CleveRoids.HasReactiveProc(spellName)
local procData = CleveRoids.reactiveProcs[spellName]
if not procData or not procData.expiry then return false end
local now = GetTime()
if now >= procData.expiry then
-- Expired, clear it
CleveRoids.reactiveProcs[spellName] = nil
return false
end
-- If proc has a target GUID requirement, check if current target matches
if procData.targetGUID then
local _, targetGUID = UnitExists("target")
if not targetGUID or targetGUID ~= procData.targetGUID then
return false
end
end
return true
end
-- Clear a reactive proc
function CleveRoids.ClearReactiveProc(spellName)
CleveRoids.reactiveProcs[spellName] = nil
end
-- Parse combat log for reactive ability triggers
function CleveRoids.ParseReactiveCombatLog()
if not arg1 then return end
local message = arg1
local _, targetGUID = UnitExists("target")
-- Check each reactive ability's trigger patterns
for spellName, config in pairs(reactivePatterns) do
-- Skip if player doesn't know this spell
if CleveRoids.GetSpell(spellName) then
for _, pattern in ipairs(config.patterns) do
if strfind(message, pattern) then
-- Found a trigger event (works in any stance)
local guid = config.requiresTargetGUID and targetGUID or nil
local duration = config.duration or REACTIVE_PROC_DURATION
CleveRoids.SetReactiveProc(spellName, duration, guid)
-- Update action buttons to reflect new state
CleveRoids.QueueActionUpdate()
break
end
end
end
end
end
-- Clear reactive proc when spell is cast
function CleveRoids.ClearReactiveProcOnCast(spellName)
if not spellName then return end
-- Check if this is a reactive spell
if CleveRoids.reactiveSpells and CleveRoids.reactiveSpells[spellName] then
CleveRoids.ClearReactiveProc(spellName)
CleveRoids.QueueActionUpdate()
end
end
-- Hook UNIT_CASTEVENT to clear reactive procs
local originalUnitCastEvent = CleveRoids.Frame and CleveRoids.Frame.UNIT_CASTEVENT
if originalUnitCastEvent then
CleveRoids.Frame.UNIT_CASTEVENT = function(...)
-- Call original handler first
if type(originalUnitCastEvent) == "function" then
originalUnitCastEvent(unpack(arg))
end
-- Clear reactive proc on spell cast start
if arg1 == "player" and arg2 == "START" and arg4 then
CleveRoids.ClearReactiveProcOnCast(arg4)
end
end
end
-- Register combat log event for reactive proc tracking
local reactiveFrame = CreateFrame("Frame", "CleveRoidsReactiveFrame")
reactiveFrame:RegisterEvent("RAW_COMBATLOG")
reactiveFrame:RegisterEvent("CHAT_MSG_COMBAT_SELF_HITS")
reactiveFrame:RegisterEvent("CHAT_MSG_COMBAT_SELF_MISSES")
reactiveFrame:RegisterEvent("CHAT_MSG_SPELL_SELF_DAMAGE")
reactiveFrame:SetScript("OnEvent", function()
if event == "RAW_COMBATLOG" or
event == "CHAT_MSG_COMBAT_SELF_HITS" or
event == "CHAT_MSG_COMBAT_SELF_MISSES" or
event == "CHAT_MSG_SPELL_SELF_DAMAGE" then
CleveRoids.ParseReactiveCombatLog()
end
end)