- Godot 4.7 project; fixed-tick FSM fighter, facing-relative input buffer with numpad motion parsing, box collision, training-mode hitbox overlay - characters/ = self-describing drop-in folders (spec in _spec/); two generated placeholder fighters (alpha, beta) - pipeline/: pack_character.py + autohitbox.py (pose/silhouette hurtboxes) - STUDY.md: reverse-engineering study of the BKB reference games Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
64 lines
1.5 KiB
GDScript
64 lines
1.5 KiB
GDScript
class_name InputBuffer
|
|
## Ring buffer of per-tick input snapshots for one fighter.
|
|
## Directions are stored FACING-RELATIVE (FWD = toward opponent) so
|
|
## motion inputs are side-agnostic, exactly like classic fighters.
|
|
|
|
const UP := 1
|
|
const DOWN := 2
|
|
const BACK := 4
|
|
const FWD := 8
|
|
const P := 16
|
|
const K := 32
|
|
|
|
const SIZE := 90
|
|
|
|
var _buf := PackedInt32Array()
|
|
var _head := -1
|
|
var _count := 0
|
|
|
|
func _init() -> void:
|
|
_buf.resize(SIZE)
|
|
_buf.fill(0)
|
|
|
|
func push(mask: int) -> void:
|
|
_head = (_head + 1) % SIZE
|
|
_buf[_head] = mask
|
|
_count = mini(_count + 1, SIZE)
|
|
|
|
## Snapshot from `ticks_ago` ticks in the past (0 = current tick).
|
|
func at(ticks_ago: int) -> int:
|
|
if ticks_ago >= _count:
|
|
return 0
|
|
return _buf[(_head - ticks_ago + SIZE) % SIZE]
|
|
|
|
## Edge detection: button went down this tick.
|
|
func pressed(btn: int) -> bool:
|
|
return (at(0) & btn) != 0 and (at(1) & btn) == 0
|
|
|
|
func held(btn: int) -> bool:
|
|
return (at(0) & btn) != 0
|
|
|
|
## Numpad-notation digit for the snapshot `ticks_ago` (5 = neutral).
|
|
static func numpad(mask: int) -> int:
|
|
var col := 5
|
|
if mask & FWD:
|
|
col = 6
|
|
elif mask & BACK:
|
|
col = 4
|
|
if mask & DOWN:
|
|
return col - 3 # 4->1 5->2 6->3
|
|
if mask & UP:
|
|
return col + 3 # 4->7 5->8 6->9
|
|
return col
|
|
|
|
## True if the numpad sequence `seq` (e.g. [2,3,6] = QCF) was entered
|
|
## within the last `window` ticks. Gaps between steps are allowed.
|
|
func motion(seq: Array, window: int = CFG.MOTION_WINDOW) -> bool:
|
|
var ptr := seq.size() - 1
|
|
for t in range(window):
|
|
if InputBuffer.numpad(at(t)) == seq[ptr]:
|
|
ptr -= 1
|
|
if ptr < 0:
|
|
return true
|
|
return false
|