# Destroyulator — the game Mac-first (Apple Silicon / Metal), **Godot 4.7**, **Jolt** physics. ## Run it **Editor:** open `/Applications/Godot.app`, import this folder (`game/`), press **F5** / ▶. **CLI:** ```sh /Applications/Godot.app/Contents/MacOS/Godot --path /Users/m3ultra/Documents/Destroyulater/game ``` ## Controls | Input | Does | |---|---| | **WASD / Space** | move / jump | | **mouse** | look (Esc releases the cursor, click recaptures) | | **LMB** | swing the equipped weapon | | **1–6 · wheel · Q** | pick a weapon · cycle · swap to previous | | **E · drag ←→ · LMB · G** | grab a record · slide the disc out · throw it · drop | | **H** | cycle HUD style (Arcade / Minimal / Work Order / Dev) | | **M** | toggle game mode (FreePlay ⟷ Find-the-Misfiled-Disc) | | **F** | report the disc in hand (Find-the-Misfiled-Disc) | | **B · R** | rain 500 bodies (stress gate) · reset the store | ## The weapon-vs-material matrix This is the decision the game is built on. A swing deals `Weapon.power × Weapon.vs[material]` against that material's `hp`, and there are three outcomes: it **breaks**, it **dents** (knockback + a chip of damage), or it's **futile** — a dead clank, no score, no combo, and a HUD nudge to switch tools. | | cardboard | paper | vinyl | glass | wood | steel | |---|---|---|---|---|---|---| | **Bare Hands** | ok | good | ok | ok | poor | useless | | **Box Cutter** | **shreds** | **shreds** | good | poor | useless | useless | | **Cricket Bat** | good | poor | **great** | **great** | ok | poor | | **Crowbar** | ok | poor | ok | good | good | **great** | | **Sledgehammer** | ok | poor | ok | **great** | **great** | **great** | | **Fire Extinguisher** | ok | poor | ok | **great** | good | good | Tuned in one place: `scripts/Weapon.gd` (`vs` tables) and `scripts/Smashable.gd` (`PROFILES[kind].hp`). The office printer is the level boss — steel at `toughness_scale = 5.0`, so it's a real fight with anything but the sledge. Weapons also differ in **when** they connect, not just how hard: the hit lands at `Weapon.contact` through the swing animation, so the sledgehammer commits late and heavy while the box cutter is near-instant. ## The viewmodel `scripts/ViewModel.gd`. Real rigged hands (mixamorig, all 20 finger bones per hand) holding real weapon meshes. The arms rig is **instanced once per hand** and each instance is *placed* so its own hand bone lands on the grip — no IK solver. That works because in first person you only ever see forearm and hand. Placement solves two aims at once: the fist's bore lines up with the weapon shaft, and the forearm runs back toward where that shoulder would be. Both directions are **measured off the rig** at load (pinky-knuckle → index-knuckle for the bore, elbow → hand for the forearm), so the grip survives re-tuning `grip_rest_rot` instead of needing a fresh set of hand-authored euler angles. Motion is layered on one node: look-sway with spring-back, walk bob scaled by speed and weapon heft, idle breathing, landing dip, and a keyframed swing arc per archetype (`overhead` / `horizontal` / `jab` / `thrust`). ## Assets | Path | Source | |---|---| | `assets/viewmodel/fps_arms.glb` | `tools/gen_fps_arms.py` — cut from the GODVERSE modular character kit | | `assets/viewmodel/{bat,sledge,crowbar,cutter,extinguisher}.glb` | `tools/gen_viewmodel.py` — procedural, grip at origin, shaft +Y | | `assets/store/*.glb` + `*.fractured.glb` | Lane 2 prop batch; the fractured sibling drives real chunk destruction | | `assets/art/sleeves_*.jpg` | 3×3 sheets of generated cover art, windowed per record via uv1 offset | Regenerate either set with: ```sh /Applications/Blender.app/Contents/MacOS/Blender --background --python tools/gen_viewmodel.py -- --render ``` ## Dev harness `dev/` is not shipped. Because macOS screen-recording permission isn't available to the CLI, the game records **itself**: ```sh Godot --path game --resolution 1280x720 --write-movie /tmp/cap.avi --quit-after 2250 dev/demo.tscn ``` `dev/DemoDriver.gd` drives the player through a scripted tour (every weapon, the material matrix, the boss, all four HUDs) so a change can be eyeballed frame by frame without a human at the keyboard. `dev/probe_*.gd` print rig/scale/placement numbers — renders are too coarse to tell a 5 cm error from a 50 cm one. ## Smoke test before committing ```sh /Applications/Godot.app/Contents/MacOS/Godot --headless --path game --quit-after 300 ``` Zero script errors required. After adding any `class_name` script, run `Godot --path game --editor --quit` first — headless Godot won't rescan a stale class cache. ## What's still missing The store has no **room**: props sit on a grey plane in a black void, and several topple on spawn before you touch them. That's the biggest remaining gap — see the top-level README.