input
The fighting-game input layer: a controller sampled once per fixed fight frame that answers what direction is held **relative to the fighter's facing**, which attack buttons were pressed recently enough to still count, and whether a motion has just been completed.
emberline.input
The fighting-game input layer: a controller sampled once per fixed fight frame that answers what direction is held relative to the fighter's facing, which attack buttons were pressed recently enough to still count, and whether a motion has just been completed.
local Input = require("emberline.input")
Input.beginFrame(frameNumber) -- one keyboard snapshot per fight frame
local ctl = Input.keyboard(1) -- or Input.virtual() for a driven fighter
ctl:sample(fighter.facing)
ctl.dir -- numpad direction, 6 is toward the opponent
ctl.back -- holding away: this is what blocking reads
ctl:buffered("medium")
ctl:motion("qcf") -- "qcf" | "qcb" | "dp" | "doubleQcf" | "charge"
ctl.dash -- "F" | "B" on the frame a double tap completes
Input.virtual() takes its state from a table (ctl:hold { forward = true, heavy = true }) and runs through exactly the same buffer, history and motion
parser as a hand on a keyboard — so an AI dragon punch is a real dragon punch,
buffered and parsed the same way, and can be stuffed the same way.
Two rules that are easy to get wrong and are handled here:
- A dash tap must come out of neutral. A quarter circle also ends on forward and a double quarter circle ends on it twice; counting every arrival at 6 turns every super into a dash.
- Direction is read through facing, written out rather than with
and/or, because the idiom collapses when the middle term isfalse.
Keys are matched against a list of aliases per control, so a hardware key
("KeyA") and an emulated one ("A") land on the same control.
Scoped to this part · feeds back into the world's score.