While SendChatMessage does require a hardware event now, an interesting thing with hardware events is that you can do stuff while a hardware event is happening and get “credit” for it. (In Vanilla I wrote an addon that would cast the instant-cast Inner Fire self buff when you needed it while you were moving around by piggy backing CastSpellByName onto the movement keys.)
And while it’s been almost 14 years since we could CastSpellByName for class buffs, we can still use the same principle in events that happen during hardware events.
This is how my warriors heroic leap:
local key = "SHIFT-C"
if select(2,UnitClass("player"))=="WARRIOR" then
local button = CreateFrame("Button","HeroicLeapOnUp",nil,"SecureActionButtonTemplate")
button:RegisterForClicks("AnyDown","AnyUp")
button:SetAttribute("type","macro")
SecureHandlerWrapScript(button,"OnClick",button,[[
if down then
self:SetAttribute("macrotext","/cast heroic leap")
else
self:SetAttribute("macrotext","/stopspelltarget\n/cast [@cursor] heroic leap")
end
]])
button:RegisterEvent("PLAYER_LOGIN")
button:SetScript("OnEvent",function(self,event,...)
SetOverrideBindingClick(self,true,key,"HeroicLeapOnUp")
end)
end
Rather than hitting a key, moving the reticle where you want to leap and then clicking the mouse; with this you press the key down, move the reticle where you want to leap and then release the key. You’ll leap to wherever the mouse was when you let go. (The @cursor does the same thing but without the ability to use the reticle to target a spot.)
In this scenario you can choose an OnKeyUp or OnAttributeChanged or probably a few other options that are valid during the hardware events that caused them. The following uses OnAttributeChanged because it’s one you wouldn’t intuitively expect to work:
local key = "SHIFT-C"
if select(2,UnitClass("player"))=="WARRIOR" then
local button = CreateFrame("Button","HeroicLeapOnUp",nil,"SecureActionButtonTemplate")
button:RegisterForClicks("AnyDown","AnyUp")
button:SetAttribute("type","macro")
SecureHandlerWrapScript(button,"OnClick",button,[[
if down then
self:SetAttribute("macrotext","/cast heroic leap")
else
self:SetAttribute("macrotext","/stopspelltarget\n/cast [@cursor] heroic leap")
end
]])
button:RegisterEvent("PLAYER_LOGIN")
button:SetScript("OnEvent",function(self,event,...)
SetOverrideBindingClick(self,true,key,"HeroicLeapOnUp")
end)
button:SetScript("OnAttributeChanged",function(self,attribType,attribValue)
if (attribValue or ""):match("^/stopspell.+heroic leap$") then
if GetSpellCooldown(6544)==0 then
SendChatMessage("Hi there","say")
end
end
end)
button:SetScript("PostClick",function(self) self:SetAttribute("macrotext",nil) end)
end
The PostClick is there so that if the attribute changes by some other means it won’t trigger a chat when you’re not leaping. For a more practical use I recommend OnKeyUp.