Thread Tools Display Modes
08-09-15, 07:44 AM   #1
MunkDev
A Scalebane Royal Guard
 
MunkDev's Avatar
AddOn Author - Click to view addons
Join Date: Mar 2015
Posts: 431
Redirecting keybinds mid combat

I'm trying to figure out whether the following scenario is possible:
  • A key is bound to an in-game button, which inherits the SecureActionButtonTemplate.
  • When no extra frames are visible, the button casts an arbitrary spell or performs a protected action.
  • When a frame is visible (e.g. WorldMapFrame, QuestFrame) the keybinding is redirected to perform operations on that frame. Think of it as frame hotkeys.
  • When the player enters combat, the button is NOT immediately reverted to cast spells, but rather stick to handling whatever frame is overlaying the UI until the player decides to close that frame.
  • When in combat, and all frames are hidden, whether they are closed mid combat or not, the secure button goes back to casting spells.
  • The definition of an overlaying frame is set by the code author.

My current approach is just reverting instantly upon PLAYER_REGEN_DISABLED, since this will not taint or cause any issues. The problem is that if I'm trying to manage a frame using keyboard input, it will instantly stop working if I happen to grab aggro from a nearby mob. This is obviously not favorable, since there are more than a few scenarios in which you need to interact with the interface mid combat. Since action bar swapping is something I handle with a SecureStateHandler, maybe it's possible to also make decisions depending on which frames are visible?

Example of what I'm currently using:
Lua Code:
  1. local btn   = CreateFrame("Button", name..modifier, UIParent, "SecureActionButtonTemplate");
  2. --
  3. -- Bunch of irrelevant shit missing here
  4. --
  5. btn.reset   = function()
  6.     btn.default = {
  7.         type = "click",
  8.         attr = "clickbutton",
  9.         val  = btn.action
  10.     }
  11. end
  12. -- MainBarAction is a function that determines whether the action exists on the standard action bar
  13. btn.revert  = function()
  14.     if  MainBarAction(btn.default.val) then
  15.         btn.default.type = "action";
  16.         btn.default.attr = "action";
  17.         btn.default.val  = MainBarAction(btn.default.val);
  18.         btn:SetID(btn.default.val);
  19.     end
  20.     btn:SetAttribute("type", btn.default.type);
  21.     btn:SetAttribute(btn.default.attr, btn.default.val);
  22.     btn:SetAttribute("clickbutton", btn.action);
  23. end
  24.  
  25. -- This works because of the grace period between the event and secure state lockdown
  26. btn:RegisterEvent("PLAYER_REGEN_DISABLED");
  27. btn:SetScript("OnEvent", btn.revert);
  28.  
  29. -- Register to handler
  30. Handler:SetFrameRef("NewButton", btn);
  31. SecureHandlerExecute(Handler, [[
  32.     BUTTONS[self:GetFrameRef("NewButton")] = true
  33. ]]);

Handler code for bar and stance swapping:
Lua Code:
  1. local m = CreateFrame("Frame", "Handler", UIParent, "SecureHandlerStateTemplate");
  2. SecureHandlerExecute(m, [[
  3.     BUTTONS = newtable();
  4.     UpdateMainActionBar = [=[
  5.         local page = ...;
  6.         if page == "tempshapeshift" then
  7.             if HasTempShapeshiftActionBar() then
  8.                 page = GetTempShapeshiftBarIndex();
  9.             else
  10.                 page = 1;
  11.             end
  12.         elseif page == "possess" then
  13.             page = self:GetFrameRef("MainMenuBarArtFrame"):GetAttribute("actionpage");
  14.             if page <= 10 then
  15.                 page = self:GetFrameRef("OverrideActionBar"):GetAttribute("actionpage");
  16.             end
  17.             if page <= 10 then
  18.                 page = 12;
  19.             end
  20.         end
  21.         self:SetAttribute("actionpage", page);
  22.         for btn in pairs(BUTTONS) do
  23.             btn:SetAttribute("actionpage", page);
  24.         end
  25.     ]=]
  26. ]]);
  27. m:SetFrameRef("MainMenuBarArtFrame", MainMenuBarArtFrame)
  28. m:SetFrameRef("OverrideActionBar", OverrideActionBar)
  29. local state = {};
  30. tinsert(state, "[overridebar][possessbar]possess");
  31. for i = 2, 6 do
  32.     tinsert(state, ("[bar:%d]%d"):format(i, i));
  33. end
  34. local _, playerClass = UnitClass("player");
  35. if playerClass == "DRUID" then
  36.     tinsert(state, "[bonusbar:1,stealth]7");
  37. elseif playerClass == "WARRIOR" then
  38.     tinsert(state, "[stance:2]7");
  39.     tinsert(state, "[stance:3]8");
  40. end
  41. for i = 1, 4 do
  42.     tinsert(state, ("[bonusbar:%d]%d"):format(i, i+6));
  43. end
  44. tinsert(state, "[stance:1]tempshapeshift");
  45. tinsert(state, "1");
  46. state = table.concat(state, ";");
  47. local now = SecureCmdOptionParse(state);
  48. m:SetAttribute("actionpage", now);
  49. RegisterStateDriver(m, "page", state);
  50. m:SetAttribute("_onstate-page", [=[
  51.     self:Run(UpdateMainActionBar, newstate);
  52. ]=]);
__________________

Last edited by MunkDev : 08-09-15 at 07:51 AM.
  Reply With Quote
08-09-15, 11:43 AM   #2
semlar
A Pyroguard Emberseer
 
semlar's Avatar
AddOn Author - Click to view addons
Join Date: Sep 2007
Posts: 1,060
If you're not planning on doing something restricted based on the visibility of the map, and I'm not sure why you would, you can just call :EnableKeyboard(true) on the WorldMapFrame and give it something to do OnKeyDown.

If you want their keys to continue doing other stuff, while still responding to whatever key presses you're interested in on the frame, you can call SetPropagateKeyboardInput on it.

Last edited by semlar : 08-09-15 at 11:45 AM.
  Reply With Quote
08-09-15, 11:49 AM   #3
MunkDev
A Scalebane Royal Guard
 
MunkDev's Avatar
AddOn Author - Click to view addons
Join Date: Mar 2015
Posts: 431
Originally Posted by semlar View Post
If you're not planning on doing something restricted based on the visibility of the map, and I'm not sure why you would, you can just call :EnableKeyboard(true) on the WorldMapFrame and give it something to do OnKeyDown.
Unless we're talking EditBox, I don't think you can keep moving around with keyboard enabled on a frame?

I've investigated the problem a bit, created a mock frame and tried to wrap its OnHide script with my current handler. This seems to work just fine, aslong as the frame in question is secure. In order to hide the frame in combat, I had to create a secure button for that aswell. The problem I face next is how (if its even possible) I can hide the frame by pressing Escape (ToggleGameMenu()), since it doesn't work simply by inserting the frame into the UISpecialFrames table.

This is the code I'm playing around with:
Lua Code:
  1. local Frame = CreateFrame("FRAME", "MockFrame", UIParent, "SecureHandlerBaseTemplate")
  2. Frame.Exit = CreateFrame("BUTTON", nil, Frame, "SecureActionButtonTemplate")
  3.  
  4. -- this works in and out of combat to hide the frame
  5. Frame:WrapScript(Frame.Exit, "OnClick", [[
  6.     self:GetParent():Hide();
  7. ]])
  8.  
  9. -- this doesn't work in combat
  10. -- tinsert(UISpecialFrames, "MockFrame");
  11.  
  12. -- Handler
  13. m:WrapScript(MockFrame, "OnHide", [[
  14.     -- run secure shit here
  15. ]])
__________________

Last edited by MunkDev : 08-09-15 at 05:22 PM.
  Reply With Quote
08-09-15, 11:57 AM   #4
semlar
A Pyroguard Emberseer
 
semlar's Avatar
AddOn Author - Click to view addons
Join Date: Sep 2007
Posts: 1,060
Originally Posted by MunkDev View Post
Unless we're talking EditBox, I don't think you can keep moving around with keyboard enabled on a frame?
You can do whatever you want as long as you allow all of the keys to pass through, or none of them to go through, depending on what your goal is.

If you could restrict arbitrary keys from going through in-combat while allowing the rest of your keys to function, that would be immensely abusable.
  Reply With Quote
08-09-15, 12:03 PM   #5
MunkDev
A Scalebane Royal Guard
 
MunkDev's Avatar
AddOn Author - Click to view addons
Join Date: Mar 2015
Posts: 431
Originally Posted by semlar View Post
You can do whatever you want as long as you allow all of the keys to pass through, or none of them to go through, depending on what your goal is.

If you could restrict arbitrary keys from going through in-combat while allowing the rest of your keys to function, that would be immensely abusable.
Yeah, that's not my goal either. I'm trying to change my SecureActionButtons behaviour depending on the visibility of a specific frame. I'm pretty much already doing this with the code I used to run, but the main issue is I can't hold off the reassignment until the overlaying frame is closed.

I would probably use an EditBox if I could, but it doesn't seem to work with OnKeyDown and OnKeyUp scripts. I don't mind removing all keybinds, except for movement.
__________________

Last edited by MunkDev : 08-09-15 at 12:09 PM.
  Reply With Quote
08-09-15, 12:49 PM   #6
semlar
A Pyroguard Emberseer
 
semlar's Avatar
AddOn Author - Click to view addons
Join Date: Sep 2007
Posts: 1,060
Originally Posted by MunkDev View Post
I'm trying to change my SecureActionButtons behaviour depending on the visibility of a specific frame.
If I understand you correctly, you want to change the behavior of a key (escape) based on the visibility of an unsecure frame (WorldMapFrame).

You're going to have to think about a less directly exploitable way to do what you're trying to do, because if you could modify the behavior of a key based on the state of a frame that is not secure, you could do something like, say, prevent yourself from casting an interrupt if the target isn't casting an interruptible spell, or reduce your entire rotation down to spamming one key.

You can rebind M to toggle your frame, then subsequently toggle the map, but you can't change what M does based on the visibility of the map, at least not without making the map frame secure which would prevent anything else from moving it or toggling its visibility.
  Reply With Quote
08-09-15, 01:16 PM   #7
SDPhantom
A Pyroguard Emberseer
 
SDPhantom's Avatar
AddOn Author - Click to view addons
Join Date: Jul 2006
Posts: 2,323
I've found this section of Blizzard code that apparently captures only one key and causes all others to pass through. This is located in Blizzard_GarrisonUI\Blizzard_GarrisonMissionTemplates.xml line 758.
Code:
if ( key == "SPACE" ) then
	self:SetPropagateKeyboardInput(false);
	self.ViewButton:Click();
else
	self:SetPropagateKeyboardInput(true);
end
__________________
WoWInterface AddOns
"All I want is a pretty girl, a decent meal, and the right to shoot lightning at fools."
-Anders (Dragon Age: Origins - Awakening)

Last edited by SDPhantom : 08-09-15 at 01:18 PM.
  Reply With Quote
08-09-15, 01:23 PM   #8
MunkDev
A Scalebane Royal Guard
 
MunkDev's Avatar
AddOn Author - Click to view addons
Join Date: Mar 2015
Posts: 431
Forget about the WorldMapFrame, it was just an example. In simple terms, I want my frame to capture all keyboard input, but let the player still move around. The code I've provided can do that, but then I can't hide the frame in combat using Escape. I can only hide it by pressing the secure button attached to the frame. So my code works, but I have to click the cross in the corner to get rid of the frame, which kinda sucks.

It also sucks to be dabbling in secure functionality just to make this possible. It feels extremely unnecessary.

Originally Posted by SDPhantom View Post
I've found this section of Blizzard code that apparently captures only one key and causes all others to pass through. This is located in Blizzard_GarrisonMissionTemplates.xml line 758 in Blizzard_GarrisonUI.
Code:
if ( key == "SPACE" ) then
	self:SetPropagateKeyboardInput(false);
	self.ViewButton:Click();
else
	self:SetPropagateKeyboardInput(true);
end
Worth looking into. Does this mean even that input will propagate? Not just the next one?

Edit: Doesn't seem to work the way I anticipated.
__________________

Last edited by MunkDev : 08-09-15 at 01:50 PM.
  Reply With Quote
08-09-15, 01:50 PM   #9
Fizzlemizz
I did that?
 
Fizzlemizz's Avatar
Premium Member
AddOn Author - Click to view addons
Join Date: Dec 2011
Posts: 1,877
If it's just the esc type functionality you're looking for
Code:
tinsert(UISpecialFrames, "Your_Frame_Name")
Will hide it automaticaly when entering combat.
Edit: and using esc. when not.
__________________
Fizzlemizz
Maintainer of Discord Unit Frames and Discord Art.
Author of FauxMazzle, FauxMazzleHUD and Move Pad Plus.

Last edited by Fizzlemizz : 08-09-15 at 01:58 PM.
  Reply With Quote
08-09-15, 02:16 PM   #10
MunkDev
A Scalebane Royal Guard
 
MunkDev's Avatar
AddOn Author - Click to view addons
Join Date: Mar 2015
Posts: 431
Originally Posted by Fizzlemizz View Post
If it's just the esc type functionality you're looking for
Code:
tinsert(UISpecialFrames, "Your_Frame_Name")
Will hide it automaticaly when entering combat.
Edit: and using esc. when not.
Only works for non-secure frames. If the frame is not secure then it can't change attributes on SecureActionButtons in combat.
__________________
  Reply With Quote
08-09-15, 02:30 PM   #11
Fizzlemizz
I did that?
 
Fizzlemizz's Avatar
Premium Member
AddOn Author - Click to view addons
Join Date: Dec 2011
Posts: 1,877
Originally Posted by MunkDev View Post
Only works for non-secure frames.
Missed that bit, sorry.
__________________
Fizzlemizz
Maintainer of Discord Unit Frames and Discord Art.
Author of FauxMazzle, FauxMazzleHUD and Move Pad Plus.
  Reply With Quote
08-09-15, 03:28 PM   #12
semlar
A Pyroguard Emberseer
 
semlar's Avatar
AddOn Author - Click to view addons
Join Date: Sep 2007
Posts: 1,060
You can override whatever keys you're interested in while your frame is visible within the restricted environment using frame:SetBindingClick(priority, "key", "buttonName", ["mouseButton"]) and frame:ClearBindings()
  Reply With Quote

WoWInterface » Developer Discussions » Lua/XML Help » Redirecting keybinds mid combat

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are On
[IMG] code is On
HTML code is Off