-
Notifications
You must be signed in to change notification settings - Fork 6
Keybinds
This system allows a mod to easily add a user-configurable function to be run when a user presses a specific key-combination, or to detect if a certain key-combination is pressed. It works closely with the Mod Settings system to make the keybinds modifiable.
A key combination is a group of (one or more) keys
which will run the keybind function when pressed.
Key combinations are handled as a string in the form:
"key1+key2+key3" where key1...keyX are the keys that need to be pressed
simultaneously for the function to be executed.
The number of keys is completely arbitrary, and keys are treated equally - ctrl or shift are treated the same as a or z.
Despite the name, two mouse keys can also be bound: leftclick and rightclick. Apart from the advanced Javascript keybinds, mouse and keyboard are treated equally.
Key combinations are usually handled as a group,
since any keybind can have any number of key combinations
that will call the function in the keybind.
These are then passed in the form "keycombination1/keycombination2",
where a keycombinationX is a combination of keys as described above.
Thus, a single keybind can have multiple key combinations, each consisting of one or more keys:
-
a: just the key a -
a+b: a and b simultaneously -
a+b/c+d: either a and b, or c and d.
Key combinations can be shared within and between mods. So, multiple mods could use the key combination a+b.
A squirrel keybind can be be called during three distinct points of a 'keypress': When it's first pressed down, while it's pressed down, or when it's released.
Different keybinds can fire for press, continuous, and release of the same key combination, and press is not automatically continuous.
Multiple states can be passed as | separate keys like game states : ::MSU.Key.KeyState.Press | ::MSU.Key.KeyState.Release.
::MSU.Key.KeyState.PressWill call the keybind when the appropriate key combination is first pressed.
::MSU.Key.KeyState.ReleaseWill call the keybind when the player releases any key after pressing the key combination. This is the default, and also used by vanilla keybinds.
::MSU.Key.KeyState.ContinuousWill call the keybind every frame the key combination is pressed.
The game has 3 distinct game states,
Squirrel keybinds can be set up to be call on any of the game states
by passing any combination of the below states separate by a |.
::MSU.Key.State.WorldThis is the state the game is in while the player is on the map.
::MSU.Key.State.TacticalThis is the state the game is in during player combat.
::MSU.Key.State.MainMenuThis is the state the game is in while no campaign has been loaded.
::MSU.Key.State.AllA convenience key to be used instead of
::MSU.Key.State.World | ::MSU.Key.State.Tactical | ::MSU.Key.State.MainMenu
As Squirrel and Javascript handle keys quite differently, MSU implements one type of keybind for each.
In the vast majority of circumstances,
a modder should use a Squirrel Keybind which directly calls squirrel code
when an appropriate key combination is pressed.
For a Javascript Keybind, while the keybind is defined in squirrel code, the modder mmust themselves implement a method in JS to check if the keybind is actuated. Usually, this takes the form of a keyboard or mouse event listener.
<Mod>.Keybinds.addSQKeybind( _id, _keyCombinations, _state, _function, _name = null, _keyState = null, _description = "" )
// returns the newly created keybind of class ::MSU.Class.KeybindSQ
// _id, _keyCombinations, _name, _description are strings
// _state is an entry in ::MSU.Key.State
// _function is a function
// _keyState is an entry in ::MSU.Key.KeyState
_id must be unique for all Keybinds and Settings Elements in the mod.
_keyCombinations is the default set of Key Combinations
that the keybind will be usable with
_state is a Game State
_name is an optional user facing name that defaults to _id
and will be used to generate a keybind setting.
_function is the function that will be called when the keybind is pressed.
In the function this will be whatever state the game is currently in.
Return true to interrupt any following keybinds from firing.
Note that the keybinds are executed in order of insertion. This means that, for example, vanilla keybinds will fire before custom keybinds.
_keyState is a Key State, defaults to ::MSU.Key.KeyState.Release
_description is the description for the mod setting that will be generated
Adds a Squirrel keybind that will call _function
when any key combination in _keyCombinations
is in the correct _keyState
and the game is in the game state _state.
<Mod>.Keybinds.addPassiveKeybind( _id, _keyCombinations, _name = null, _description = "")Refer to Squirrel Keybind for a description of the arguments.
This simple version of the squirrel keybind does not accept a function and thus does nothing on its own. Its purpose is to be used with isKeybindPressed to check if it is actuated.
<Mod>.Keybinds.addJSKeybind( _id, _keyCombinations, _name = null, _description = "" )
// returns the newly created keybind of class ::MSU.Class.KeybindJSRefer to Squirrel Keybind for a description of the arguments.
Adds a Javascript 'keybind' in that it allows javascript code to check if any of the key combinations are pressed by checking
// if the final key being pressed is a mouse button or
MSU.Keybinds.isMousebindPressed(_modID, _id, _event)
// if the final key being pressed is a key.
MSU.Keybinds.isKeybindPressed(_modID, _id, _event)
// _modID, _id are strings
// _event is the event passed to an eventlistener_modID is the id of your mod passed to MSU
_id is the id of the keybind
These function should be called inside an eventListener,
and therefore _event is the parameter that such a callback should have
as described on the MDN Web Docs.
JS keybinds/mousebinds are rare, but allow things like interactions with UI elements on button click.
See the second example.
For pure keybinds, you will need to add a keydown / up / press event handler. They only work on elements that are focus-able and currently in focus. A workaround can be a document - level keyhandler. See the third example
Once a keybind is added to a mod, a new Keybinds mod settings page will be added to the Mod Settings screen of that mod.
Each keybind gets an entry, where the user can customize the binding.
<Mod>.Keybinds.addDivider( _id )
// _id is a string_id must be unique for all Keybinds and Settings Elements in the mod.\
Adds a Settings Divider to the keybinds page in the mod's mod settings to allow for better organization of keybinds.
<Mod>.Keybinds.addTitle( _id, _name )
// _id and _name are strings_id must be unique for all Keybinds and Settings Elements in the mod.
_name will be displayed as the title.
Adds a Settings Title to the keybinds page in the mod's mod settings to allow for better organization of keybinds.
MSU overrides the default vanilla keyhandling system and re-implements all its original bindings as MSU keybinds. They can be found in the file vanilla_keybinds.
**Note **that the vanilla escape keybind will pop the MenuStack of the respective state (see world_state | tactical_state | main_menu_state :: m.MenuStack). It can thus be unnecessary or even pointless to add an escape keybind. Instead, a MenuStack event should be added in show(), for example:
// my_screen.nut inherits scripts/mods/msu/ui_screen
function show()
{
if (this.m.JSHandle != null && !this.isVisible())
{
this.Tooltip.hide();
this.World.State.m.MenuStack.push(function ()
{
::MyMod.MyScreen.hide();
});
this.m.JSHandle.asyncCall("show");
}
}<Mod>.Keybinds.getKeybind( _id )
// _id is a stringReturns the keybind object of your mod with the id _id.
<keybind>.getKeyCombinations()Returns the key combinations of this keybind as a string e.g. "h / k".
<keybind>.setBypassInputDenied( _bool )
By default, MSU SQ keybinds do not trigger when a JS input or textarea element is focused.
The point of this is to avoid the keybind triggering while the user is typing in something like a name setter text element.
This behavior can be enabled or disabled for individual SQ keys with the member setBypassInputDenied.
The corresponding property BypassInputDenied is initialized to false.
Key input denial can be entirely disabled with the MSU mod setting with the ID blockSQInput, found under the General page.
This should only be done in extreme cases and should be seen as a failsave or debugging tool.
<Mod>.Keybinds.update( _id, _keyCombinations )
// _id and _keycombinations are strings_id is the id of the keybind to update
_keyCombinations is the a set of key combinations
Changes the key combinations used for the Keybind to
_keyCombinations
<Mod>.Keybinds.isKeybindPressed(_id)
// _id is a string_id is the id of the keybind to check\
Returns true if the Keybind with _id is currently being held down, false otherwise.
Enable the keybindsLog setting in the MSU modsettings to log a variety of debug messages, such as the pressed key and the final key combination.
Basic squirrel keybind:
// Create a mod
local myMod = ::MSU.Class.Mod("myID", "1.0.0", "Pretty Name");
// add a keybind that prints "hi" when shift+a are 'released' in either main menu or world state:
myMod.Keybinds.addSQKeybind("printHi", "shift+a", ::MSU.Key.State.MainMenu | ::MSU.Key.State.World, function()
{
::logInfo("hi");
}, "Print Hi");
// make ctrl+x an available key combination for the keybind
myMod.Keybinds.update("printHi", "ctrl+x/shift+a");
// add a keybind that prints "bye" when escape is pressed (not released):
myMod.Keybinds.addSQKeybind("printBye", "escape", ::MSU.Key.State.MainMenu | ::MSU.Key.State.World, function()
{
::logInfo("bye");
}, "Print Bye", ::MSU.Key.KeyState.Press);Basic JS keybind:
myMod.Keybinds.addJSKeybind("rightclick", "shift+rightclick", "My Event", "Key combination will fire on shift.rightclick");myElement.mousedown(function (event)
{
var isPressed = MSU.Keybinds.isMousebindPressed(MyMod.ID, "rightclick", _event);
if (isPressed) console.error("Hello, world!");
});Keyboard JS keybind, using document - level event handler. Remember to remove those if you no longer need them, for example during your hide() function.
$(document).on('keydown.my-name-space', function(_event){
var isPressed = MSU.Keybinds.isKeybindPressed(MyMod.ID, "mykeybind", _event);
if (isPressed) console.error("Hello, world!");
})The values for the tables below are what a key combination can use
separated by + characters
MouseMapSQ = {
"1" : "leftclick",
"2" : "rightclick",
},
KeyMapSQ = {
"1" : "1",
"2" : "2",
"3" : "3",
"4" : "4",
"5" : "5",
"6" : "6",
"7" : "7",
"8" : "8",
"9" : "9",
"10" : "0",
"11" : "a",
"12" : "b",
"13" : "c",
"14" : "d",
"15" : "e",
"16" : "f",
"17" : "g",
"18" : "h",
"19" : "i",
"20" : "j",
"21" : "k",
"22" : "l",
"23" : "m",
"24" : "n",
"25" : "o",
"26" : "p",
"27" : "q",
"28" : "r",
"29" : "s",
"30" : "t",
"31" : "u",
"32" : "v",
"33" : "w",
"34" : "x",
"35" : "y",
"36" : "z",
"37" : "backspace",
"38" : "tab",
"39" : "enter",
"40" : "space",
"41" : "escape",
"44" : "end",
"45" : "home",
"46" : "pagedown",
"47" : "pageup",
"48" : "left",
"49" : "up",
"50" : "right",
"51" : "down",
"53" : "insert",
"54" : "delete",
"55" : "n0", // numpad keys
"56" : "n1",
"57" : "n2",
"58" : "n3",
"59" : "n4",
"60" : "n5",
"61" : "n6",
"62" : "n7",
"63" : "n8",
"64" : "n9",
/*
While technically present, these keys are unreliable
"66" : "*",
"67" : "+",
"68" : "-",
"70" : "/",
*/
"71" : "f1",
"72" : "f2",
"73" : "f3",
"74" : "f4",
"75" : "f5",
"76" : "f6",
"77" : "f7",
"78" : "f8",
"79" : "f9",
"80" : "f10",
"81" : "f11",
"82" : "f12",
"83" : "f13",
"84" : "f14",
"85" : "f15",
"86" : "f16",
"87" : "f17",
"88" : "f18",
"89" : "f19",
"90" : "f20",
"91" : "f21",
"92" : "f22",
"93" : "f23",
"94" : "f24",
"95" : "ctrl",
"96" : "shift",
"97" : "alt"
}- NOTE: MSU guarantees backwards compatibility for documented features and code only. Undocumented features/code of MSU may be changed at any time without notice, so we advise against using/referencing such code in your projects.
- For bug reports or feature requests, please create issues.
- If you would like to join the team, write to us at msu.team@protonmail.com.