Skip to content

Commit 895dbb0

Browse files
authored
Support Cast tags in a client with Secrets (#21)
We can't inspect all unit GUIDs and we can't operate on any values coming from Cast APIs, so instead of caching values we just build a new one every time a cast event update comes in. Tested with `[CastName]` `[CastTarget]` `[CastStartDuration]` `[CastEndDuration]` on Retail 12.0.0, Classic-Era 1.15.8, and TBC Anniversary 2.5.5 Fixes #20
1 parent bbe84ce commit 895dbb0

File tree

1 file changed

+140
-84
lines changed

1 file changed

+140
-84
lines changed

Categories/Cast.lua

Lines changed: 140 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ if MINOR_VERSION > _G.DogTag_Unit_MINOR_VERSION then
66
end
77

88
local _G, pairs, wipe, tonumber, GetTime = _G, pairs, wipe, tonumber, GetTime
9-
local UnitName, UnitGUID, UnitCastingInfo, UnitChannelInfo, CastingInfo, ChannelInfo =
10-
UnitName, UnitGUID, UnitCastingInfo, UnitChannelInfo, CastingInfo, ChannelInfo
9+
local UnitName, UnitGUID, UnitCastingInfo, UnitChannelInfo, CastingInfo, ChannelInfo, UnitCastingDuration, UnitChannelDuration, UnitSpellTargetName =
10+
UnitName, UnitGUID, UnitCastingInfo, UnitChannelInfo, CastingInfo, ChannelInfo, UnitCastingDuration, UnitChannelDuration, UnitSpellTargetName
1111

1212
DogTag_Unit_funcs[#DogTag_Unit_funcs+1] = function(DogTag_Unit, DogTag)
1313

@@ -19,110 +19,149 @@ local castData = {}
1919
local UnitGUID = UnitGUID
2020
local IsNormalUnit = DogTag_Unit.IsNormalUnit
2121
local issecretvalue = DogTag.issecretvalue
22+
local hasSecrets = C_Secrets and C_Secrets.HasSecretRestrictions()
2223

2324
local wow_ver = select(4, GetBuildInfo())
2425
local cast_api_has_ranks = wow_ver < 80000 and WOW_PROJECT_ID == WOW_PROJECT_MAINLINE
2526

2627
local playerGuid = nil
28+
local trackedUnits = {}
2729
DogTag:AddEventHandler("Unit", "PLAYER_LOGIN", function()
2830
playerGuid = UnitGUID("player")
2931
end)
3032

33+
local nextSpell, nextRank, nextTarget
34+
local function populateCastData(event, unit, data)
35+
local guid = UnitGUID(unit)
36+
if not guid then
37+
return false
38+
end
39+
40+
local spell, rank, displayName, icon, startTime, endTime, _, spellID
41+
local channeling = false
42+
if UnitCastingInfo then
43+
if cast_api_has_ranks then
44+
spell, rank, displayName, icon, startTime, endTime = UnitCastingInfo(unit)
45+
if not spell then
46+
spell, rank, displayName, icon, startTime, endTime = UnitChannelInfo(unit)
47+
channeling = true
48+
end
49+
else
50+
spell, displayName, icon, startTime, endTime, _, _, _, spellID = UnitCastingInfo(unit)
51+
rank = nil
52+
if not spell then
53+
spell, displayName, icon, startTime, endTime, _, _, spellID = UnitChannelInfo(unit)
54+
channeling = true
55+
end
56+
end
57+
elseif CastingInfo then
58+
-- Classic only has an API for player spellcasts. No API for arbitrary units.
59+
if unit == "player" then
60+
spell, displayName, icon, startTime, endTime, _, _, _, spellID = CastingInfo()
61+
rank = nil
62+
if not spell then
63+
spell, displayName, icon, startTime, endTime, _, _, spellID = ChannelInfo()
64+
channeling = true
65+
end
66+
end
67+
end
68+
69+
local durationFunc = channeling and UnitChannelDuration or UnitCastingDuration
70+
if durationFunc then
71+
data.duration = durationFunc(unit)
72+
end
73+
74+
if spell then
75+
data.spell = spell
76+
rank = rank and tonumber(rank:match("%d+"))
77+
data.rank = rank
78+
local oldStart = data.startTime
79+
if not issecretvalue(startTime) then
80+
startTime = startTime * 0.001
81+
end
82+
data.startTime = startTime
83+
data.endTime = issecretvalue(endTime) and endTime or endTime * 0.001
84+
if not issecretvalue(startTime) and (event == "UNIT_SPELLCAST_DELAYED" or event == "UNIT_SPELLCAST_CHANNEL_UPDATE") then
85+
data.delay = (data.delay or 0) + (startTime - (oldStart or startTime))
86+
else
87+
data.delay = 0
88+
end
89+
if UnitSpellTargetName then
90+
data.target = UnitSpellTargetName(unit)
91+
elseif not issecretvalue(guid) and guid == playerGuid
92+
and not issecretvalue(spellID) and spellID == nextSpell
93+
and rank == nextRank then
94+
data.target = nextTarget
95+
end
96+
data.casting = not channeling
97+
data.channeling = channeling
98+
data.fadeOut = false
99+
data.stopTime = nil
100+
data.stopMessage = nil
101+
return true
102+
end
103+
104+
if not data.spell then
105+
return true
106+
end
107+
108+
if event == "UNIT_SPELLCAST_FAILED" then
109+
data.stopMessage = _G.FAILED
110+
elseif event == "UNIT_SPELLCAST_INTERRUPTED" then
111+
data.stopMessage = _G.INTERRUPTED
112+
end
113+
114+
data.casting = false
115+
data.channeling = false
116+
data.fadeOut = true
117+
if not data.stopTime then
118+
data.stopTime = GetTime()
119+
end
120+
121+
return true
122+
end
123+
31124
local castEventIsSetup = false
32125
DogTag:AddEventHandler("Unit", "EventRequested", function(_, event)
33126
if event ~= "Cast" or castEventIsSetup then return end
34127
castEventIsSetup = true
35-
36-
local nextSpell, nextRank, nextTarget
128+
37129
local function updateInfo(event, unit)
38130
local guid = UnitGUID(unit)
39-
if not guid or issecretvalue(guid) then
131+
if not guid then
132+
return
133+
end
134+
if hasSecrets then
135+
trackedUnits[unit] = true
136+
DogTag:FireEvent("Cast", unit)
40137
return
41138
end
42139
local data = castData[guid]
43140
if not data then
44141
data = newList()
45142
castData[guid] = data
46143
end
47-
48-
local spell, rank, displayName, icon, startTime, endTime
49-
local channeling = false
50-
if UnitCastingInfo then
51-
if cast_api_has_ranks then
52-
spell, rank, displayName, icon, startTime, endTime = UnitCastingInfo(unit)
53-
if not spell then
54-
spell, rank, displayName, icon, startTime, endTime = UnitChannelInfo(unit)
55-
channeling = true
56-
end
57-
else
58-
spell, displayName, icon, startTime, endTime = UnitCastingInfo(unit)
59-
rank = nil
60-
if not spell then
61-
spell, displayName, icon, startTime, endTime = UnitChannelInfo(unit)
62-
channeling = true
63-
end
64-
end
65-
elseif CastingInfo then
66-
-- Classic only has an API for player spellcasts. No API for arbitrary units.
67-
if unit == "player" then
68-
spell, displayName, icon, startTime, endTime = CastingInfo()
69-
rank = nil
70-
if not spell then
71-
spell, displayName, icon, startTime, endTime = ChannelInfo()
72-
channeling = true
73-
end
74-
end
75-
end
76144

77-
if spell then
78-
data.spell = spell
79-
rank = rank and tonumber(rank:match("%d+"))
80-
data.rank = rank
81-
local oldStart = data.startTime
82-
startTime = startTime * 0.001
83-
data.startTime = startTime
84-
data.endTime = endTime * 0.001
85-
if event == "UNIT_SPELLCAST_DELAYED" or event == "UNIT_SPELLCAST_CHANNEL_UPDATE" then
86-
data.delay = (data.delay or 0) + (startTime - (oldStart or startTime))
87-
else
88-
data.delay = 0
89-
end
90-
if guid == playerGuid and spell == nextSpell and rank == nextRank then
91-
data.target = nextTarget
92-
end
93-
data.casting = not channeling
94-
data.channeling = channeling
95-
data.fadeOut = false
96-
data.stopTime = nil
97-
data.stopMessage = nil
98-
DogTag:FireEvent("Cast", unit)
145+
if not populateCastData(event, unit, data) then
99146
return
100147
end
101-
148+
102149
if not data.spell then
103150
castData[guid] = del(data)
104-
DogTag:FireEvent("Cast", unit)
105-
return
106-
end
107-
108-
if event == "UNIT_SPELLCAST_FAILED" then
109-
data.stopMessage = _G.FAILED
110-
elseif event == "UNIT_SPELLCAST_INTERRUPTED" then
111-
data.stopMessage = _G.INTERRUPTED
112-
end
113-
114-
data.casting = false
115-
data.channeling = false
116-
data.fadeOut = true
117-
if not data.stopTime then
118-
data.stopTime = GetTime()
119151
end
152+
120153
DogTag:FireEvent("Cast", unit)
121154
end
122155

123156
local guidsToFire, unitsToUpdate = {}, {}
124157
local function fixCastData()
125-
local frame
158+
if hasSecrets then
159+
for unit, _ in pairs(trackedUnits) do
160+
DogTag:FireEvent("Cast", unit)
161+
end
162+
return
163+
end
164+
126165
local currentTime = GetTime()
127166
for guid, data in pairs(castData) do
128167
if data.casting then
@@ -143,7 +182,7 @@ DogTag:AddEventHandler("Unit", "EventRequested", function(_, event)
143182
if stopTime then
144183
alpha = stopTime - currentTime + 1
145184
end
146-
185+
147186
if alpha <= 0 then
148187
castData[guid] = del(data)
149188
end
@@ -167,7 +206,7 @@ DogTag:AddEventHandler("Unit", "EventRequested", function(_, event)
167206
if not normal then
168207
unitsToUpdate[found] = true
169208
end
170-
209+
171210
guidsToFire[guid] = true
172211
end
173212
end
@@ -194,13 +233,11 @@ DogTag:AddEventHandler("Unit", "EventRequested", function(_, event)
194233
DogTag:AddEventHandler("Unit", "UNIT_SPELLCAST_CHANNEL_STOP", updateInfo)
195234
DogTag:AddEventHandler("Unit", "UnitChanged", updateInfo)
196235

197-
DogTag:AddEventHandler("Unit", "UNIT_SPELLCAST_SENT", function(event, unit, spell, rank, target)
198-
236+
DogTag:AddEventHandler("Unit", "UNIT_SPELLCAST_SENT", function(event, unit, target, castGUID, spellID)
237+
199238
-- The purpose of this event is to predict the next spell target.
200-
-- This seems to be removed in at least wow_800
201-
if unit == "player" and cast_api_has_ranks then
202-
nextSpell = spell
203-
nextRank = rank and tonumber(rank:match("%d+"))
239+
if unit == "player" then
240+
nextSpell = spellID
204241
nextTarget = target ~= "" and target or nil
205242
end
206243
end)
@@ -209,7 +246,18 @@ end)
209246

210247
local blank = {}
211248
local function getCastData(unit)
212-
return castData[UnitGUID(unit)] or blank
249+
local guid = UnitGUID(unit)
250+
if not guid then
251+
return blank
252+
end
253+
254+
if hasSecrets then
255+
local data = newList()
256+
populateCastData(nil, unit, data)
257+
return data
258+
end
259+
260+
return castData[guid] or blank
213261
end
214262

215263
DogTag:AddTag("Unit", "CastName", {
@@ -256,7 +304,11 @@ DogTag:AddTag("Unit", "CastRank", {
256304

257305
DogTag:AddTag("Unit", "CastStartDuration", {
258306
code = function(unit)
259-
local t = getCastData(unit).startTime
307+
local data = getCastData(unit)
308+
if data.duration then
309+
return data.duration:GetElapsedDuration()
310+
end
311+
local t = data.startTime
260312
if t then
261313
return GetTime() - t
262314
else
@@ -275,7 +327,11 @@ DogTag:AddTag("Unit", "CastStartDuration", {
275327

276328
DogTag:AddTag("Unit", "CastEndDuration", {
277329
code = function(unit)
278-
local t = getCastData(unit).endTime
330+
local data = getCastData(unit)
331+
if data.duration then
332+
return data.duration:GetRemainingDuration()
333+
end
334+
local t = data.endTime
279335
if t then
280336
return t - GetTime()
281337
else

0 commit comments

Comments
 (0)