Hands/POV - Cut a real first-person arms rig out of the GODVERSE modular character kit (tools/gen_fps_arms.py): ch01 hands + per-side sleeves on the full 65-bone mixamorig skeleton, so all 20 finger bones per hand are poseable at runtime. Textures shrunk to 1k; 4.2 MB. - ViewModel.gd instances that rig ONCE PER HAND and places each instance so its own hand bone lands on the grip — no IK. Grip orientation is measured off the rig at load (pinky->index knuckle = bore axis, elbow->hand = forearm dir), so it survives re-tuning grip_rest_rot instead of needing new euler angles. - Motion layers: look-sway with spring-back, walk bob scaled by speed and weapon heft, idle breathe, landing dip, weapon lower/raise on swap. - Sleeve material overridden (donor asset is a fantasy leather bracer); hand material forced non-metallic (its spec/gloss maps rendered skin as bronze). Weapons - Weapon.gd replaces MeleeAttack: 6 weapons, 4 swing archetypes, and the weapon-vs-material matrix from the founding chat. - Smashable moves from binary hits_to_break to hp/toughness, giving three outcomes: break, dent, or futile (dead clank, no score, HUD nudge). The box cutter genuinely shreds cardboard and genuinely cannot hurt a filing cabinet. - The hit lands at Weapon.contact THROUGH the swing, not on the click — that delay is most of why the sledge feels different from the cutter. - Slots on 1-6 / wheel / Q; game modes moved to M, HUD cycle to H. HUD - Hud.gd: Arcade, Minimal, Work Order (a corporate destruction docket that fills in line items) and Dev, over one shared data feed. Scoring + combo multipliers. Dev harness (not shipped) - macOS screen-recording perms aren't available to the CLI, so the game records itself: dev/demo.tscn + DemoDriver.gd drive a scripted tour for --write-movie, and dev/probe_*.gd print rig/scale/placement numbers. Fix: tools/gen_viewmodel.py box() scaled by size/2 on top of primitive_cube_add's already-unit side length, halving every box — which detached the bat's blade. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
110 lines
4.8 KiB
Markdown
110 lines
4.8 KiB
Markdown
# 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.
|