nodes/controls.pyΒΆ

Part of GDQuest Open RPG.

 1"""What the player can do, declared once per mode and carried by a node.
 2
 3The port has two modes and one context each. ``EXPLORING`` rides on the player,
 4so walking arrives with the player and leaves with them. ``BATTLE`` rides on the
 5battle scene and hides everything below it, which is what a modal fight means:
 6while it is on screen the overworld's names are dark and only the battle's own
 7answer. Nothing is registered from ``main()``, where ``InputMap`` has no tree yet
 8and which a test that loads one scene never calls.
 9"""
10
11from __future__ import annotations
12
13from simvx.core import Axis2D, Controller, DeviceAxis, InputBinding, InputContext, Key, Mouse
14
15
16def _stick_half(axis: DeviceAxis, positive: bool) -> InputBinding:
17    """One direction of a stick as a button, for a cursor that steps."""
18    return InputBinding(axis.positive if positive else axis.negative, deadzone=0.5)
19
20
21#: Actions every screen needs. This is the root's own context, so it covers the
22#: whole tree while the root is mounted. ``advance`` takes a tap as well as a key
23#: and a face button, because the web runtime delivers touch as a left click and
24#: a pad player has to be able to leave the title card too.
25SHELL = {
26    "advance": [Key.ENTER, Key.SPACE, Key.E, Mouse.LEFT, Controller.A, Controller.START],
27    "quit": [Key.Q, Controller.BACK],
28}
29
30#: Walking the overworld. Movement is one typed action rather than four, so the
31#: keys, the arrows, a controller's D-pad and left stick and the drawn pad all
32#: feed it and the source furthest from rest wins. ``Input.value("move")`` is a
33#: ``Vec2``; the player rounds it to the one tile step it is asking for.
34EXPLORING = InputContext(
35    {
36        "move": [
37            Axis2D(up=Key.W, down=Key.S, left=Key.A, right=Key.D),
38            Axis2D(up=Key.UP, down=Key.DOWN, left=Key.LEFT, right=Key.RIGHT),
39            Axis2D(
40                up=Controller.DPAD_UP,
41                down=Controller.DPAD_DOWN,
42                left=Controller.DPAD_LEFT,
43                right=Controller.DPAD_RIGHT,
44            ),
45            Controller.LEFT_STICK,
46        ],
47        "interact": [Key.E, Controller.A],
48        "walk_to": [Mouse.LEFT],
49    },
50    name="exploring",
51)
52
53#: Fighting. The menu cursor steps, so its four directions are buttons rather
54#: than one vector: a stick half past its deadzone counts as a press, the same
55#: as an arrow key or a D-pad direction. ``confirm`` deliberately leaves the left
56#: button to ``select``, which carries a position: a tap picks the row or the foe
57#: it landed on rather than whatever the cursor was sitting over.
58BATTLE = InputContext(
59    {
60        "advance": [Key.ENTER, Key.SPACE, Mouse.LEFT, Controller.A],
61        "menu_up": [Key.W, Key.UP, Controller.DPAD_UP, _stick_half(Controller.LEFT_Y, False)],
62        "menu_down": [Key.S, Key.DOWN, Controller.DPAD_DOWN, _stick_half(Controller.LEFT_Y, True)],
63        "menu_left": [Key.A, Key.LEFT, Controller.DPAD_LEFT, _stick_half(Controller.LEFT_X, False)],
64        "menu_right": [Key.D, Key.RIGHT, Controller.DPAD_RIGHT, _stick_half(Controller.LEFT_X, True)],
65        "confirm": [Key.ENTER, Key.SPACE, Controller.A],
66        "cancel": [Key.ESCAPE, Key.X, Controller.B],
67        "select": [Mouse.LEFT],
68    },
69    name="battle",
70    hides=InputContext.ALL,
71)