Commit graph

1 commit

Author SHA1 Message Date
Admin
8be3e4c561 Arcade: third-person player rig, activity button, gamepad wiring
A game gets a walkable character that can get into a car and drive it
in three lines and never mentions a camera:

    let id = Character::new(...);
    player_rigs.insert(PlayerId(0), PlayerRig::new(id));

Everything crafted is engine-side with defaults nothing has to specify:
coyote time, jump buffering, variable jump height, asymmetric accel and
decel, air control, landing recovery, a boom that snaps in and eases
out, look-ahead, speed pullback, and delayed recentring.

Script surface grows by three verbs (115 -> 118): game.player_character,
game.interactable, game.interact_prompt. Cars and doors-with-interiors
are derived affordances, so a generated game with a car and a house
declares nothing; game.interactable is only for chests and switches.
The prompt and the press share one search so they cannot disagree, and
it picks the nearest candidate in front rather than merely the nearest.

Four bugs found by looking at what ran, not by reading:

- Arcade never polled the gamepad at all. No game_input_states() call
  existed anywhere in the app, so the pad's state never entered the
  process and every binding downstream read a struct nobody filled.
- LT drove both brake and negative throttle, and brake force opposes
  reverse motion. Measured: clean reverse covers 13.8m in 2s against
  22.25m forward; with brake held, 0.63m. car.rs is unchanged -- a foot
  on the brake winning is a car behaving like a car.
- Mount cleared `hidden`, which means "solid to everything, drawn by
  nothing" -- so boarding left the driver as an invisible collider at
  the kerb. Now uses attached_to, the sim's seat pin, and saves and
  restores hidden rather than asserting a value.
- GameWorld::new() never set gravity; only reset_content() did. All 70
  new() call sites floated, and four files had each independently grown
  their own `world.gravity = 30.0`. A floating character never reports
  on_floor, so the controller silently refused to jump.

The in-vehicle boom goes 9.0 -> 13.0. The boom is a time budget, not a
length: at the car's top speed 9m was 0.37s of road ahead, too little to
plan a turn. The test states it against CarConfig::top_speed, so raising
the car's speed fails the test instead of quietly making the view tight
again. The pivot deliberately does not rise with it -- eye.y is
pivot.y + sin(pitch)*boom, so the longer boom already buys the height,
and driving should sit lower and more planted than walking.

Player rigs now survive Blocks::clear(): where you are sitting and where
you are looking are the player's state, not the game's content, so a
script edit no longer ejects the driver mid-corner.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 19:38:05 +02:00