Cauldron2D.Input (Cauldron2D v0.1.3)

Copy Markdown View Source

Turns terminal events into the set of actions a player is holding.

A game declares its actions and a keymap from action to keys:

keymap = %{left: [:left, :a], right: [:right, :d], jump: [:up, {:w, []}, :left_shift], act: [:" ", {:f, [:ctrl]}]}
input = Cauldron2D.Input.new(keymap, releases?: true, toggles: [:jump])

Feed it every event with handle/3 and call expire/2 once a tick; state/1 is what the player is doing now: %{held: MapSet.t(), aim: {column, row} | nil}. A state from an analog control may add strength: %{action => 0.0..1.0}, how hard each such action is held; an action without an entry is held in full. Keys give none.

Two ways to hold a key

With releases?: true the terminal reports key releases, and an action is held from {:key_down, ...} to {:key_up, ...}. Legacy {:key, ...} events are ignored.

With releases?: false a {:key, ...} press holds the action for :tap_ms milliseconds; the terminal's key repeat keeps it held while the key is down, and a tap holds it for one window. An action listed in :holds is held for its own, longer, window on a first press — long enough to cover the terminal's delay before it starts repeating — and each repeat then extends it by :repeat_ms, so it stays held while the key is down and lets go soon after. Actions listed in :toggles flip on each press instead and stay until pressed again. A {:key_release_support, true} event switches to the first way and drops every hold.

Keys are matched by their unshifted name: :A and {:a, [:shift]} both press what :a is bound to. A key given with modifiers in the keymap, {:f, [:ctrl]}, matches only with them; a bare key matches with any.

Keys against the pointer

Actions listed in :steering are the ones the pointer also drives. Pressing a key bound to one drops the aim, so the keys take over, until the pointer moves again.

Mouse buttons

A key spec {:mouse, :left | :middle | :right} binds a mouse button. A button is held from its :mouse_down to its :mouse_up, through any drag, whichever way keys are being held; the pointer position of every mouse event becomes the aim.

merge/2 adds actions held by another source, such as a controller.

Summary

Functions

The actions a key event presses or releases, whichever way keys are held.

Drop every tapped hold whose window has passed by millisecond time now.

Apply one terminal event at millisecond time now.

Replace the actions held by an outside source.

An input reading keymap.

Whether releases are being reported.

What the player is doing now.

Whether pressing a key for any of actions drops the aim.

Types

action()

@type action() :: atom()

key_spec()

@type key_spec() :: atom() | {atom(), [atom()]} | {:mouse, :left | :middle | :right}

keymap()

@type keymap() :: %{required(action()) => [key_spec()]}

state()

@type state() :: %{
  :held => MapSet.t(action()),
  :aim => {number(), number()} | nil,
  optional(:strength) => %{required(action()) => float()}
}

t()

@type t() :: %Cauldron2D.Input{
  aim: {number(), number()} | nil,
  bindings: [{key_spec(), action()}],
  expiries: %{required(action()) => integer()},
  external: MapSet.t(action()),
  held: MapSet.t(action()),
  holds: %{required(action()) => pos_integer()},
  releases?: boolean(),
  repeat_ms: pos_integer(),
  steering: MapSet.t(action()),
  tap_ms: pos_integer(),
  toggled: MapSet.t(action()),
  toggles: MapSet.t(action())
}

Functions

actions(input, arg2)

@spec actions(t(), term()) :: [action()]

The actions a key event presses or releases, whichever way keys are held.

expire(input, now)

@spec expire(t(), integer()) :: t()

Drop every tapped hold whose window has passed by millisecond time now.

handle(input, arg2, now)

@spec handle(t(), term(), integer()) :: t()

Apply one terminal event at millisecond time now.

merge(input, external)

@spec merge(t(), MapSet.t(action())) :: t()

Replace the actions held by an outside source.

new(keymap, opts \\ [])

@spec new(keymap(), keyword()) :: t()

An input reading keymap.

Options

  • :releases? — whether the terminal reports key releases. Default false
  • :toggles — actions that flip on each press when releases are not reported. Default []
  • :tap_ms — how long a press holds an action when releases are not reported. Default 120
  • :holds[action: milliseconds], actions a first press holds for that long instead, when releases are not reported. Default []
  • :repeat_ms — how long a repeat of a held action extends it, when releases are not reported. Default 150
  • :steering — actions whose key press drops the pointer's aim. Default []

releases?(input)

@spec releases?(t()) :: boolean()

Whether releases are being reported.

state(input)

@spec state(t()) :: state()

What the player is doing now.

steers?(input, actions)

@spec steers?(t(), [action()]) :: boolean()

Whether pressing a key for any of actions drops the aim.