"""What to print on screen for an input, on the device the player is holding.
A pad's controls are positional: ``Controller.A`` is the bottom face button on
every controller. What is *printed* on that button is not. It says A
on an Xbox pad, Cross on a PlayStation pad and B on a Nintendo one, because
Nintendo's face buttons are laid out the other way round, so a game that draws
"Press A" draws the wrong letter for two of the three. That swap is the whole
reason this module exists; the rest is spelling.
``Input.prompt("jump")`` is the ordinary entry point: it picks the binding that
matches whatever the player is using and labels it. The two functions below are
for a game drawing its own prompt for a button it names directly.
"""
from __future__ import annotations
from .controls import Controller, DeviceAxis, DeviceButton, DeviceType, key_to_name
from .enums import ControllerFamily
__all__ = ["axis_label", "button_label", "key_binding_label", "key_label", "pointer_button_label"]
#: What each family prints on a face, shoulder or system button. Only the
#: entries that differ from :data:`_GENERIC_BUTTONS` are listed; a family falls
#: back to it for the rest.
#:
#: Nintendo's swap is positional, not a rename: the engine's ``A`` is the
#: bottom button, which a Switch pad labels B.
_FAMILY_BUTTONS: dict[ControllerFamily, dict[DeviceButton, str]] = {
ControllerFamily.XBOX: {
Controller.LEFT_BUMPER: "LB",
Controller.RIGHT_BUMPER: "RB",
Controller.BACK: "View",
Controller.START: "Menu",
Controller.GUIDE: "Xbox",
Controller.LEFT_THUMB: "LS",
Controller.RIGHT_THUMB: "RS",
},
ControllerFamily.PLAYSTATION: {
Controller.A: "Cross",
Controller.B: "Circle",
Controller.X: "Square",
Controller.Y: "Triangle",
Controller.LEFT_BUMPER: "L1",
Controller.RIGHT_BUMPER: "R1",
Controller.BACK: "Share",
Controller.START: "Options",
Controller.GUIDE: "PS",
Controller.LEFT_THUMB: "L3",
Controller.RIGHT_THUMB: "R3",
},
ControllerFamily.NINTENDO: {
Controller.A: "B",
Controller.B: "A",
Controller.X: "Y",
Controller.Y: "X",
Controller.LEFT_BUMPER: "L",
Controller.RIGHT_BUMPER: "R",
Controller.BACK: "Minus",
Controller.START: "Plus",
Controller.GUIDE: "Home",
Controller.LEFT_THUMB: "LS",
Controller.RIGHT_THUMB: "RS",
},
}
_GENERIC_BUTTONS: dict[DeviceButton, str] = {
Controller.A: "A",
Controller.B: "B",
Controller.X: "X",
Controller.Y: "Y",
Controller.LEFT_BUMPER: "L1",
Controller.RIGHT_BUMPER: "R1",
Controller.BACK: "Select",
Controller.START: "Start",
Controller.GUIDE: "Guide",
Controller.LEFT_THUMB: "L3",
Controller.RIGHT_THUMB: "R3",
Controller.DPAD_UP: "D-Pad Up",
Controller.DPAD_DOWN: "D-Pad Down",
Controller.DPAD_LEFT: "D-Pad Left",
Controller.DPAD_RIGHT: "D-Pad Right",
}
_FAMILY_TRIGGERS: dict[ControllerFamily, tuple[str, str]] = {
ControllerFamily.XBOX: ("LT", "RT"),
ControllerFamily.PLAYSTATION: ("L2", "R2"),
ControllerFamily.NINTENDO: ("ZL", "ZR"),
}
_GENERIC_TRIGGERS = ("LT", "RT")
#: Which stick an axis belongs to, and what the two ends of it are called.
_STICK_AXES: dict[DeviceAxis, tuple[str, str, str]] = {
Controller.LEFT_X: ("Left Stick", "Left", "Right"),
Controller.LEFT_Y: ("Left Stick", "Up", "Down"),
Controller.RIGHT_X: ("Right Stick", "Left", "Right"),
Controller.RIGHT_Y: ("Right Stick", "Up", "Down"),
}
#: Keys whose engine name is not what a player would want to read.
_KEY_LABELS: dict[str, str] = {
"escape": "Esc",
"pageup": "Page Up",
"pagedown": "Page Down",
"space": "Space",
"enter": "Enter",
"tab": "Tab",
"backspace": "Backspace",
"delete": "Delete",
"insert": "Insert",
"ctrl": "Ctrl",
"shift": "Shift",
"alt": "Alt",
"super": "Super",
}
[docs]
def axis_label(
axis: DeviceAxis, family: ControllerFamily = ControllerFamily.GENERIC, *, positive: bool | None = None
) -> str:
"""What *family* prints on *axis*, optionally for one half of its travel.
``positive`` names a direction: a binding splits a stick axis in two, and
"Left Stick Right" is what that half of it is called. Left None the whole
axis is named. A trigger has one direction and ignores it.
"""
if positive is None and axis.half:
positive = axis.half > 0
axis = axis.whole
if axis in (Controller.LEFT_TRIGGER, Controller.RIGHT_TRIGGER):
left, right = _FAMILY_TRIGGERS.get(family, _GENERIC_TRIGGERS)
return left if axis is Controller.LEFT_TRIGGER else right
if axis not in _STICK_AXES:
return str(axis)
name, negative_end, positive_end = _STICK_AXES[axis]
if positive is None:
return name
return f"{name} {positive_end if positive else negative_end}"
[docs]
def key_binding_label(key_combo: str) -> str:
"""A combo the engine spells ``"shift+tab"``, as a player would read it."""
return "+".join(
_KEY_LABELS.get(part, part.upper() if len(part) == 1 else part.capitalize()) for part in key_combo.split("+")
)
[docs]
def key_label(key: DeviceButton) -> str:
"""What to print for one key, with no modifiers."""
return key_binding_label(key_to_name(key))