destroyulator/game/dev/probe_chair.gd
Monster Robot Party 14ebc1d6f5 LANE9: ride the office chair
E on an office chair (hands empty) mounts it: the player's capsule collision is
disabled and it rides on top, WASD shoves it (strong) and yaws it (weak, on
purpose — steering a chair is bad), momentum is real. Ramming breaks brittle
things through the same brittle_speed path as any collision — no new damage code.
Sledge the chair out from under yourself and the ride ends on the spot. E or
death dismounts and restores collision.

Split _try_mount (the crosshair ray) from _mount (the state change) so the ride
mechanics are testable without synthetic camera aim. dev/probe_chair.gd: mount
disables collision, player tracks the seat to 2mm, dismount restores it. CHAIR OK.

All gates green: smoke clean, 6/6 sites 0.00 m/s, overlap CLEAN, all four LANE9
probes pass.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-04 00:25:32 +10:00

62 lines
2.5 KiB
GDScript

extends SceneTree
## The chair-ride contract:
## - mount a chair: the player is pinned to it (collision off, seated on top)
## - driving forward MOVES the chair (real momentum), and the player rides along
## - dismount restores the player's collision
##
## Godot --headless --path game --script dev/probe_chair.gd
func _initialize() -> void:
var main: Node = (load("res://main.tscn") as PackedScene).instantiate()
get_root().add_child(main)
await process_frame
for i in 70: # past the 1 s spawn guard, which sleeps drifting bodies
await physics_frame
var player = main.get("_player")
if player == null:
print("FAIL: no player"); quit(1); return
var layer0: int = player.collision_layer
# find a chair, teleport the player onto it, aim at it, mount
var chair: RigidBody3D = null
for n in get_nodes_in_group("smashable"):
if String(n.name).contains("chair"):
chair = n
break
if chair == null:
print("FAIL: no chair in scranton"); quit(1); return
# mount the known chair directly — the ray-aim path (_try_mount) is what a player uses
# live; here we test the ride MECHANICS, which _mount drives, without synthetic aim.
player.global_position = chair.global_position + Vector3(0, 0.6, 0)
var mounted: bool = player._mount(chair)
print("mount: %s collision_layer %d -> %d" % [mounted, layer0, player.collision_layer])
if not mounted or player.collision_layer != 0:
print("FAIL: did not mount / collision not disabled"); quit(1); return
# move the chair to open floor first (scranton chairs spawn wedged behind desks), then
# drive it and confirm the player rides along
var sp: Vector3 = main.get("_plan").spawn_point()
chair.global_position = sp + Vector3(0.0, 3.0, 0.0) # airborne, nothing to wedge against
chair.sleeping = false
chair.linear_velocity = Vector3(2.6, 0.0, 0.0) # a good shove; it coasts (and falls)
var start := chair.global_position
for i in 45:
player._ride(1.0 / 60.0)
await physics_frame
var moved: float = (chair.global_position - start).length()
var seat_h: float = player.SEAT_H
var gap: float = (player.global_position - (chair.global_position + Vector3(0, seat_h, 0))).length()
print("chair moved %.2f m player-to-seat gap %.3f m" % [moved, gap])
player._dismount()
print("dismount: collision_layer restored to %d" % player.collision_layer)
if moved > 0.3 and gap < 0.05 and player.collision_layer == layer0:
print("CHAIR OK")
quit(0)
else:
print("FAIL: chair didn't move, player didn't track, or collision not restored")
quit(1)