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>
62 lines
2.5 KiB
GDScript
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)
|