Add multi-key binding API
Pathoschild opened this issue ยท 4 comments
Add a multi-key binding utility to SMAPI, which mods could optionally use in their config files. That would let players set both keyboard and controller keys for split-screen mode.
Design goals
- Keybind functionality:
- An action can have multiple bindings (e.g. both
P
andControllerA
). - A binding can consist of multiple keys (e.g. press both
Ctrl
andS
at once). - Multi-key bindings are commutative (i.e.
Ctrl + S
andS + Ctrl
have the same effect).
- An action can have multiple bindings (e.g. both
- Config support:
- Key bindings can be used directly in
config.json
models. - An
SButton
value in a pre-existingconfig.json
file can be mapped to aKeybind
field (i.e.SButton
fields are forward-compatible withKeybind
).
- Key bindings can be used directly in
- Limitations:
- Overlapping bindings aren't resolved automatically (similar to existing single-key bindings). For example, if you register both
CTRL + S
andS
as separate bindings, both would be triggered when you press bothCTRL
andS
. It's up to the mod(s) to determine the priority by which they check bindings.
- Overlapping bindings aren't resolved automatically (similar to existing single-key bindings). For example, if you register both
See #630 for a proposed larger keybind registration API.
Proposed design
Let's say a mod's menu should open when you press P
, or press both LeftShoulder
and ControllerA
.
SMAPI mods could use a KeybindList
class in their config model and specify a default value directly:
public class ModConfig
{
public KeybindList OpenMenuKey { get; set; } = new KeybindList("P, LeftShoulder + ControllerA");
}
This would be (de)serialized to a string value automatically for user readability:
{
"OpenMenuKey": "P, LeftShoulder + ControllerA"
}
Usually mods would call a JustPressed
method to check whether it was activated this tick (i.e. not held from a previous tick):
if (config.OpenMenuKey.JustPressed())
this.OpenMenu();
But the class would provide other methods to use as needed:
bool isDown = keybind.IsDown();
SButtonState state = keybind.GetState(); // e.g. Pressed, Held, Released, or None
string readableForm = keybind.ToString();
KeybindList
would support multiple keybindings, but mods could access the individual keybinds if needed:
if (openKey.JustPressed())
{
this.OpenMenu();
this.Monitor.Log($"Press {openKey.Bindings.First(p => p.JustPressed())} again to disable the menu.", LogLevel.Info);
}
Would it be possible to make it backwards compatible with SButton (so changing the ModConfig.OpenMenuKey type from SButton to KeybindList would read the old value correctly)?
I wonder how I'm supposed to support this in GMCM. :P
Yep, SButton
(de)serializes to string too so your existing config.json
files would get parsed as a keybind with one button. I'll add that as an explicit design goal though.
Done in develop
for the upcoming SMAPI 3.9. See documentation on the wiki for details.