mirror of
https://github.com/imjasonh/nescript
synced 2026-07-09 09:18:01 +00:00
118 lines
3.9 KiB
Text
118 lines
3.9 KiB
Text
|
|
// pong/input.ne — paddle update (human + CPU).
|
||
|
|
//
|
||
|
|
// `step_paddles()` is called every frame by the Playing state
|
||
|
|
// phase machine. Humans move their paddle up/down based on the
|
||
|
|
// D-pad on their controller; CPU paddles (is_cpu[side] == 1)
|
||
|
|
// are driven by `step_cpu_paddle(side)` which tracks the nearest
|
||
|
|
// active ball's y position with a one-frame lag and a small miss
|
||
|
|
// zone so the AI feels beatable.
|
||
|
|
//
|
||
|
|
// Clamping: paddles can never leave the playfield vertically.
|
||
|
|
// `move_paddle_up` / `move_paddle_down` handle the clamp so the
|
||
|
|
// caller just has to ask for motion in one direction at a time.
|
||
|
|
|
||
|
|
// Move a paddle upward by PADDLE_SPEED, clamped to PLAYFIELD_TOP.
|
||
|
|
fun move_paddle_up(side: u8) {
|
||
|
|
if paddle_y[side] >= PLAYFIELD_TOP + PADDLE_SPEED {
|
||
|
|
paddle_y[side] -= PADDLE_SPEED
|
||
|
|
} else {
|
||
|
|
paddle_y[side] = PLAYFIELD_TOP
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Move a paddle downward by PADDLE_SPEED, clamped to
|
||
|
|
// (PLAYFIELD_BOTTOM - paddle_height). The paddle height depends
|
||
|
|
// on whether this side is currently in long-paddle mode.
|
||
|
|
fun move_paddle_down(side: u8) {
|
||
|
|
var h: u8 = PADDLE_H
|
||
|
|
if paddle_long[side] > 0 {
|
||
|
|
h = PADDLE_H_LONG
|
||
|
|
}
|
||
|
|
var max_y: u8 = PLAYFIELD_BOTTOM - h
|
||
|
|
if paddle_y[side] + PADDLE_SPEED <= max_y {
|
||
|
|
paddle_y[side] += PADDLE_SPEED
|
||
|
|
} else {
|
||
|
|
paddle_y[side] = max_y
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// ── CPU AI ────────────────────────────────────────────────
|
||
|
|
//
|
||
|
|
// The CPU tracks the y-centre of the nearest active ball that
|
||
|
|
// is heading toward its side. It moves CPU_SPEED px per frame
|
||
|
|
// toward the ball's y, but only if the ball is more than 4 px
|
||
|
|
// away from the paddle centre (the "dead zone" that makes the
|
||
|
|
// AI imperfect and lets fast balls score occasionally).
|
||
|
|
//
|
||
|
|
// If no ball is heading toward this side, the CPU drifts toward
|
||
|
|
// the playfield center so it isn't caught flat-footed on the
|
||
|
|
// next serve.
|
||
|
|
fun step_cpu_paddle(side: u8) {
|
||
|
|
// Find the nearest ball heading toward this side.
|
||
|
|
var target_y: u8 = 112 // default: field centre
|
||
|
|
var found: u8 = 0
|
||
|
|
var j: u8 = 0
|
||
|
|
while j < MAX_BALLS {
|
||
|
|
if ball_active[j] == 1 {
|
||
|
|
// Ball heading left → of interest to the left paddle
|
||
|
|
// Ball heading right → of interest to the right paddle
|
||
|
|
if side == SIDE_LEFT and ball_dx_sign[j] == 1 {
|
||
|
|
target_y = ball_y[j] + (BALL_SIZE >> 1)
|
||
|
|
found = 1
|
||
|
|
}
|
||
|
|
if side == SIDE_RIGHT and ball_dx_sign[j] == 0 {
|
||
|
|
target_y = ball_y[j] + (BALL_SIZE >> 1)
|
||
|
|
found = 1
|
||
|
|
}
|
||
|
|
}
|
||
|
|
j += 1
|
||
|
|
}
|
||
|
|
|
||
|
|
// Aim the paddle centre at target_y.
|
||
|
|
var ph: u8 = PADDLE_H
|
||
|
|
if paddle_long[side] > 0 {
|
||
|
|
ph = PADDLE_H_LONG
|
||
|
|
}
|
||
|
|
var centre: u8 = paddle_y[side] + (ph >> 1)
|
||
|
|
|
||
|
|
// Dead zone: don't jitter if we're already close.
|
||
|
|
if centre + 4 < target_y {
|
||
|
|
// Need to move down.
|
||
|
|
if paddle_y[side] + CPU_SPEED + ph <= PLAYFIELD_BOTTOM {
|
||
|
|
paddle_y[side] += CPU_SPEED
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if centre > target_y + 4 {
|
||
|
|
// Need to move up.
|
||
|
|
if paddle_y[side] >= PLAYFIELD_TOP + CPU_SPEED {
|
||
|
|
paddle_y[side] -= CPU_SPEED
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Drive both paddles from controller input (humans) or the CPU
|
||
|
|
// AI (bots) this frame. The per-player button reads are hand-
|
||
|
|
// rolled because `p1` / `p2` are compile-time syntactic prefixes.
|
||
|
|
fun step_paddles() {
|
||
|
|
if is_cpu[SIDE_LEFT] == 0 {
|
||
|
|
if button.up {
|
||
|
|
move_paddle_up(SIDE_LEFT)
|
||
|
|
}
|
||
|
|
if button.down {
|
||
|
|
move_paddle_down(SIDE_LEFT)
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
step_cpu_paddle(SIDE_LEFT)
|
||
|
|
}
|
||
|
|
if is_cpu[SIDE_RIGHT] == 0 {
|
||
|
|
if p2.button.up {
|
||
|
|
move_paddle_up(SIDE_RIGHT)
|
||
|
|
}
|
||
|
|
if p2.button.down {
|
||
|
|
move_paddle_down(SIDE_RIGHT)
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
step_cpu_paddle(SIDE_RIGHT)
|
||
|
|
}
|
||
|
|
}
|