Builtin Library
README.md
chords
Chord and sequence state machines. Two kinds:
sequence— ordered steps with a per-step window and an overall window (Konami-style codes).simultaneous— N bindings pressed inside a tolerance window, order-insensitive (piano-chord / fighter-combo style).
Chords are edge-triggered — fired(name) is true for exactly one
frame when the full pattern completes. A continuous combo like "Ctrl+S
held" is a button control whose kbm class is B.modKey("Ctrl+S"): a
chord recognizes a pattern over time, which is a different question from
whether a combination is down right now.
Exports
Lifecycle:
M.define(spec: ChordSpec)— define / replace chords.M.remove(name) / M.clear()M.has(name) -> boolean/M.names() -> { string }/M.get(name) -> ChordDef?
Queries:
M.fired(name) -> booleanM.progress(name) -> number(0..1)M.cancel(name)— reset state.
Subscriptions:
M.onFire(name, callback, opts: OnFireOpts?) -> OnFireHandleM.off(handle: OnFireHandle)
Tick hooks (called by Zin.tick):
M._beginTick()— clears per-frame fire flags.M._observeEvent(ev)— advance state machines on an input event.M._advanceTime(dt)— age out stale recognizers.M._dispatchFires()— invoke onFire callbacks for chords that fired this tick.
Types:
ChordDef = { kind, context, steps?, stepWindowSec?, windowSec?, bindings?, toleranceSec? }ChordSpec = { [string]: any }OnFireOpts = { context: string?, once: boolean? }OnFireHandle = { id: number, name: string, off: () -> () }
Usage
local Zin = require("@builtin::modules.zinput")
local B = Zin.bindings
Zin.chords.define({
konami = {
kind = "sequence",
steps = { B.key("ArrowUp"), B.key("ArrowUp"), B.key("ArrowDown"),
B.key("ArrowDown"), B.key("ArrowLeft"), B.key("ArrowRight"),
B.key("ArrowLeft"), B.key("ArrowRight"),
B.key("KeyB"), B.key("KeyA") },
stepWindowSec = 0.5,
windowSec = 4.0,
},
})
Zin.chords.onFire("konami", function() unlockBonus() end)
Notes
- Without a tick host (or
Zin.autoTick.start()) chords never fire — every state machine is dormant untilZin.tickdrives it. - A wrong key in a sequence resets to step 0. If the wrong key happens to match step 1, the recogniser restarts from step 1 on the same event (so a long input run can self-recover).
- For simultaneous chords, the tolerance window starts on the first
matched press. If all bindings haven't matched before
toleranceSecelapses the state resets —firstPressAtis reset too so the next press starts a fresh window. - Subscriber errors are caught and logged via
log.warn.