HexDecimal / python-tcod-engine-2023

MIT License
4 stars 1 forks source link

Keybindings. #5

Open HexDecimal opened 1 year ago

HexDecimal commented 1 year ago

I often end up hardcoding keys to actions, but this time I should try making a keybinding database.

Key combinations will become linked to string identifiers with Dict[Keybind, str], these can be loaded from a file, probably keys.toml. An in-game key editor would be ideal, but I'd need a GUI for that.

This system is important since I can package it as a public library afterwards.

I'll hardcode the following to the arrow-keys/escape/enter so that one can't lock themselves out of a key editor:

ESCAPE
CONFIRM
UI_UP
UI_DOWN
UI_LEFT
UI_RIGHT

Additional keys can be added but the hardcoded keys can not be removed.

Then these will be bound to multiple keys the way I normally do them:

MOVE_N
MOVE_NE
MOVE_E
MOVE_SE
MOVE_S
MOVE_SW
MOVE_W
MOVE_NW
MOVE_N
WAIT

This will likely assign the same keys to UI_UP and MOVE_N. I'll need to figure out the best way to handle this.

Events from tcod will be used to lookup which key event is bound to which keybinding.

HexDecimal commented 1 year ago

Maybe something that integrates with Python enums:


@Keybindings.register(category="UI")
class UIKeys(Enum):
    ESCAPE = auto()
    CONFIRM = auto()
    UP = auto()
    DOWN = auto()
    LEFT = auto()
    RIGHT = auto()

@Keybindings.register(category="INGAME")
class InGameKeys(Enum):
    MOVE_N = auto()
    MOVE_NE = auto()
    MOVE_E = auto()
    MOVE_SE = auto()
    MOVE_S = auto()
    MOVE_SW = auto()
    MOVE_W = auto()
    MOVE_NW = auto()
    MOVE_N = auto()
    WAIT = auto()

keybinds = Keybindings(file="keys.toml")
keybinds.save_as("keys.toml")

event: tcod.event.Event
ui_command: Optional[UIKeys] = bindings.parse(event, UIKeys)
game_command: Optional[InGameKeys] = bindings.parse(event, InGameKeys)
HexDecimal commented 1 year ago

Possible binding type:

@attrs.define(frozen=True)
class Bind:
    sym: Optional[tcod.event.KeySym] = None
    scancode: Optional[tcod.event.Scancode] = None
    shift: Optional[bool | tuple[bool, bool]] = None
    alt: Optional[bool | tuple[bool, bool]] = None
    ctrl: Optional[bool | tuple[bool, bool]] = None
    gui: Optional[bool | tuple[bool, bool]] = None
    mode: Optional[bool] = None
    num_lock: Optional[bool] = None
    caps_lock: Optional[bool] = None
    scroll_lock: Optional[bool] = None

    def match(self, other: tcod.event.Event, toggle_shift: bool) -> bool:
        ... # True if all non-None values match Event.

any_s = Bind(tcod.event.KeySym.s)  # S key ignore case.
shift_s = Bind(tcod.event.KeySym.s, shift=True)  # S key upper-case or shift held depending on toggle_shift.
lower_s = Bind(tcod.event.KeySym.s, shift=False)  # S key lower-case or shift not held depending on toggle_shift.
left_shift_s = Bind(tcod.event.KeySym.s, shift=(True, False))  # S key left shift held and right shift not held.
alt_ctrl_s = Bind(tcod.event.KeySym.s, alt=True, ctrl=True)  # S key with either alt and either control key.
HexDecimal commented 1 year ago

Enum-based keybinding is in, but I might need to modify it to work better to work with a theoretical GUI.