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