@@ -7,6 +7,30 @@ local function shouldPrintCastDebug()
77 return br .functions .misc and (br .functions .misc :isChecked (" Cast Debug" ) or br .functions .misc :isChecked (" Display Failcasts" ))
88end
99
10+ local function setCastIntentLock (spellID , duration )
11+ if spellID == nil then return end
12+ if duration == nil then duration = 0.15 end
13+ castIntentLocks [spellID ] = br ._G .GetTime () + duration
14+ end
15+
16+ local function isCastIntentLocked (spellID )
17+ if spellID == nil then return false end
18+ return castIntentLocks [spellID ] ~= nil and br ._G .GetTime () < castIntentLocks [spellID ]
19+ end
20+
21+ local function getSameSpellCastRemain (spellID )
22+ local now = br ._G .GetTime ()
23+ local _ , _ , _ , _ , castEndTime , _ , _ , _ , castingSpellID = br ._G .UnitCastingInfo (" player" )
24+ if castingSpellID == spellID and castEndTime and castEndTime > 0 then
25+ return math.max (0 , (castEndTime / 1000 ) - now )
26+ end
27+ local _ , _ , _ , _ , channelEndTime , _ , _ , channelingSpellID = br ._G .UnitChannelInfo (" player" )
28+ if channelingSpellID == spellID and channelEndTime and channelEndTime > 0 then
29+ return math.max (0 , (channelEndTime / 1000 ) - now )
30+ end
31+ return 0
32+ end
33+
1034local function takeCastStartSnapshot (spellID )
1135 local spellCdBefore = 0
1236 if br .functions .spell and br .functions .spell .getSpellCD then
@@ -1014,12 +1038,12 @@ function cast:createCastFunction(thisUnit, castType, minUnits, effectRng, spellI
10141038 -- Hard CC (stun/fear/silence/etc): don't spam cast attempts for spells that can't be used.
10151039 if br .functions .combat and br .functions .combat .cannotCast and br .functions .combat :cannotCast (spellID ) then
10161040 castTimers [spellID ] = br ._G .GetTime () + 0.50
1017- castIntentLocks [ spellID ] = br . _G . GetTime ( ) -- Lock intent during hard CC
1041+ setCastIntentLock ( spellID , 0.50 ) -- Lock intent during hard CC
10181042 return printReport (false , " No Control" )
10191043 end
10201044
10211045 -- Set cast intent lock to prevent rapid re-casting attempts
1022- castIntentLocks [ spellID ] = br . _G . GetTime ( )
1046+ setCastIntentLock ( spellID , 0.20 )
10231047
10241048 local snapshot = takeCastStartSnapshot (spellID )
10251049
@@ -1042,7 +1066,18 @@ function cast:createCastFunction(thisUnit, castType, minUnits, effectRng, spellI
10421066 if castSucceeded then
10431067 -- add to cast timer
10441068 castTimers [spellID ] = br ._G .GetTime () + 1
1045- castIntentLocks [spellID ] = br ._G .GetTime () -- Lock intent on successful cast
1069+ local sameSpellCastRemain = getSameSpellCastRemain (spellID )
1070+ if sameSpellCastRemain > 0 then
1071+ setCastIntentLock (spellID , sameSpellCastRemain + 0.12 )
1072+ else
1073+ local settleLock = 0.12
1074+ if spellType == " Helpful" then
1075+ settleLock = 0.30
1076+ elseif spellType == " Harmful" and not br ._G .UnitAffectingCombat (" player" ) then
1077+ settleLock = 0.30
1078+ end
1079+ setCastIntentLock (spellID , settleLock )
1080+ end
10461081 -- change main button icon
10471082 br .ui .toggles .mainButton :SetNormalTexture (icon )
10481083 -- Update Last Cast
@@ -1055,13 +1090,13 @@ function cast:createCastFunction(thisUnit, castType, minUnits, effectRng, spellI
10551090 -- Cast may have been queued/started but signals didn't update yet.
10561091 -- Throttle quick retries, but don't emit CAST_FAILED spam.
10571092 castTimers [spellID ] = br ._G .GetTime () + 0.10
1058- castIntentLocks [ spellID ] = br . _G . GetTime ( ) -- Lock intent on inconclusive result
1093+ setCastIntentLock ( spellID , 0.20 ) -- Lock intent on inconclusive result
10591094 return true
10601095 end
10611096
10621097 -- Failed to start casting: allow a quick retry, but don't lock out for 1s.
10631098 castTimers [spellID ] = br ._G .GetTime () + 0.10
1064- castIntentLocks [ spellID ] = br . _G . GetTime ( ) -- Lock intent on failed cast
1099+ setCastIntentLock ( spellID , 0.20 ) -- Lock intent on failed cast
10651100 -- if shouldPrintCastDebug() and not debug then
10661101 -- printCastFailedDetails("[CAST FAIL][CAST_FAILED] ", spellID, spellName, snapshot, details)
10671102 -- end
@@ -1093,9 +1128,13 @@ function cast:createCastFunction(thisUnit, castType, minUnits, effectRng, spellI
10931128 br .functions .lastCast .lastCastTable .castTime [spellID ] = br ._G .GetTime () -
10941129 (br .functions .spell :getGlobalCD (true ) + (select (3 , br ._G .GetNetStats ()) / 100 ))
10951130 end
1131+ -- Never allow same-spell recast while it's currently being cast/channelled.
1132+ if cast :isCastingSpell (spellID , " player" ) then
1133+ return false
1134+ end
10961135 -- Cast Intent Lock: Prevent multiple cast attempts in rapid succession
10971136 -- Only check when NOT in debug mode (actual casting, not ability checking)
1098- if not debug and castIntentLocks [ spellID ] and ( br . _G . GetTime () - castIntentLocks [ spellID ]) < 0.15 then
1137+ if not debug and isCastIntentLocked ( spellID ) then
10991138 return false
11001139 end
11011140 if (baseSpellID == spellID or overrideSpellID == spellID ) and (br .empowerID == nil or br .empowerID == 0 )
0 commit comments