destroyulator/game
Monster Robot Party ad0ca62f3e LANE1: record-destruction ritual + Find-the-Misfiled-Disc mode
Turn records from one-hit props into the store's signature dexterity ritual,
and add the first game mode on top of it.

Record.gd (class_name Record extends Smashable): a cardboard SLEEVE that hides a
vinyl DISC. Splits record.glb's Sleeve_Mesh / Vinyl_Disc nodes at runtime — the
sleeve stays on the body, the disc becomes its own frozen-kinematic vinyl Smashable
that follows the sleeve until freed. State machine IN_CRATE -> HELD -> EXTRACTING
-> DISC_FREE. set_extract() slides the disc up and out of the mouth; release_disc()
arms it as a live brittle throwable; shatter() spills the disc first so brute-force
smashing still spills it. Genre tints on disc label / sleeve spine make a misfiled
disc read at a glance.

GrabController.gd (under Player): hold point on the camera, E grabs the Record under
the crosshair (fat-sphere query, Record-filtered), horizontal mouse drag pulls the
disc (~600px = full), LMB throws the freed disc then trashes the empty sleeve, G
drops. On-screen pull meter.

GameMode.gd (class_name GameMode, owned by Main): FreePlay (default) + FindMisfiled —
plants exactly one disc_genre != sleeve_genre, 60s timer, win by extracting it and
pressing F while holding it, scores the mess (bodies smashed) + records disturbed.
Prints the plant on start.

Wiring: Player creates the GrabController and routes E/G/LMB/drag (yaw suspended
mid-pull, Esc untouched); Main._build_rack spawns Record instances on the crates
(keeps supports cascade + sleeve-art skinning + per-record genres), forwards smashes
to the mode, and adds keys 1/2 (modes) and F (report).

Headless smoke test clean (0 script errors / 0 warnings, 15 records); a runtime
harness exercised grab/extract/throw/drop/shatter-spill/mode-switch/reset — all green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 19:40:52 +10:00
..
assets Restructure into monorepo: game/ is the Godot lane 2026-07-14 15:33:13 +10:00
scripts LANE1: record-destruction ritual + Find-the-Misfiled-Disc mode 2026-07-14 19:40:52 +10:00
.gitignore Restructure into monorepo: game/ is the Godot lane 2026-07-14 15:33:13 +10:00
main.tscn Restructure into monorepo: game/ is the Godot lane 2026-07-14 15:33:13 +10:00
project.godot Restructure into monorepo: game/ is the Godot lane 2026-07-14 15:33:13 +10:00
README.md Restructure into monorepo: game/ is the Godot lane 2026-07-14 15:33:13 +10:00

Destroyulator — Cascade Prototype

Mac-first (Apple Silicon / Metal), Godot 4.7, Jolt physics. Built to prove two things on the M3 before committing: the destruction feels good, and the frame rate holds.

Run it

Editor (easiest): open /Applications/Godot.app, import this folder (game/), press F5 / ▶.

CLI:

/Applications/Godot.app/Contents/MacOS/Godot --path /Users/m3ultra/Documents/Destroyulater/game

Controls

Input Does
Left-click Swing the (nerf) hammer at whatever's under the cursor
S Rain 500 rigid bodies — the Day-0 stress gate. Watch the FPS counter hold.
R Reset the rack

Top-left HUD shows FPS · live body count · things smashed.

What it's proving — the cascade

The rack is nested the same way your store's DB is (rack → shelf → crate → jacket → record):

  • Frame + shelves start frozen (act static, hold everything up).
  • Smash a shelf → it bursts into shards and vanishes → the crates on it fall → the cardboard jackets spill → the vinyl records tumble out →
  • records are brittle (brittle_speed in Smashable.gd) → they crack a beat later when they smack the floor. A record that lands flat survives — there's your comedy/score beat.

No scripted "release" logic — gravity reads the hierarchy. That's the whole point.

Files

  • scripts/Smashable.gd — one component for every breakable piece. The PROFILES table is where material identity is authored (color · shard count · brittleness · bounce). Swap wood/cardboard/vinyl/glass/steel behaviours here.
  • scripts/Main.gd — builds the world + rack from primitives, hammer raycast, stress test, HUD.

Next step: real art (one function)

Everything is grey primitives so it runs with zero import deps. To use your actual assets, change _piece() in Main.gd to load a GLB instead of building a BoxMesh, e.g. load("res://assets/wooden-rack-type-long.glb").instantiate() for the visual, keep the convex collider. Your 3D-STORE/clean_glbs/*.glb are ready to copy into game/assets/.