This commit is contained in:
bee
2026-07-10 17:26:23 +02:00
parent db201081f5
commit 90bae5f3e5
14 changed files with 308 additions and 9 deletions
+121
View File
@@ -0,0 +1,121 @@
# Adding content — quick checklist
Rough working notes. Verify against the scripts if anything looks off; file
references are the source of truth.
## 1. Adding a new weapon
The pipeline is def-driven: `AmmoDef` (what it fires) → `WeaponDef` (the gun)
→ weapon scene (the visible mount). Scenes are shared — `mark12.tscn` is used
by both `mark12.tres` and `bofors.tres`. Only build a new scene for a new look.
1. **AmmoDef** — new `.tres` in `resources/ammo/` (script:
`scripts/resource_definitions/ammo_def.gd`). Fields: `projectile_scene`
(usually `shell.tscn`), `damage`, `muzzle_speed`, `gravity_scale`,
`lifetime`, `display_name`.
2. **WeaponDef** — new `.tres` in `resources/weapons/` (script:
`scripts/resource_definitions/weapon_def.gd`). Fields: `weapon_scene`
(required — `WeaponSlot.can_mount()` rejects a def without one; reuse
`mark12.tscn` unless you made a new scene), `ammo`, `fire_interval`,
`muzzle_speed_scale` (barrel-length multiplier on ammo speed), `size` /
`type` (slot gating), traverse/elevation speeds and elevation limits.
3. **Weapon scene** (only if it needs a new look) — structure per
`mark12.tscn` / `scripts/weapon.gd`:
- Root `Node3D` with `scripts/weapon.gd`.
- `%Yaw` (Node3D, unique name) — rotates horizontally. **Optional**: omit
it and the weapon is a fixed mount (no aiming, fires along the muzzle).
- `%Pitch` (Node3D, unique name) under Yaw — barrel elevation. Also
optional (yaw-only mount if omitted).
- `%Muzzle` (Marker3D, unique name) at the barrel tip, **-Z pointing out
of the barrel**. **Required**`weapon.gd` hard-references it.
- `%Hitbox` (AnimatableBody3D, unique name) under Yaw, with a
CollisionShape3D. **Sync To Physics must be UNTICKED** — with it on the
collider does not follow the moving boat. Collision layer = subsystems
(7) only, collision mask = empty. Projectiles get a collision exception
with the shooter's hitboxes at fire time.
- **Leave the root's `def` export EMPTY.** The mounting WeaponSlot assigns
it (`weapon_slot.gd` `mount()`). A scene referencing its own def is
infinite recursion (def → scene → def …).
4. **Put it on a boat** — add an entry to the Boat's `loadout` dictionary
(`scripts/boat.gd`): key = the WeaponSlot node's **exact name**
(StringName), value = the WeaponDef. `Boat._ready()` mounts everything.
The slot's `allowed_sizes` / `allowed_types` must contain the def's
`size` / `type` or `mount()` warns and refuses.
New projectile scenes: root must use `scripts/projectile.gd` (or replicate
it) — it self-registers into `shift_with_origin` and calls
`reset_physics_interpolation()` on launch. Copy `shell.tscn`'s physics setup:
layer 4 (projectiles), mask = terrain+player+enemies, `continuous_cd` on,
`contact_monitor` on with `max_contacts_reported` ≥ 1.
## 2. Setting up a boat scene
Template: `boat.tscn`.
1. Root: `RigidBody3D` with `scripts/boat.gd` (extends
`scripts/floating_body.gd`).
- Group: `shift_with_origin` (set in the scene). The `boats` group is
joined automatically in `Boat._ready()` — don't add it by hand.
- Collision layer: player (2) or enemies (3).
- Set `mass` — all handling forces scale with it, so handling stays the
same across masses; mass mostly matters for collisions.
2. Children: `MeshInstance3D` + `CollisionShape3D`. **Never scale physics
nodes** — set sizes on the mesh/shape resources themselves.
3. Buoyancy tuning (exports from `floating_body.gd`):
- `buoyancy` (multiple of gravity when fully submerged; >1 floats),
`full_force_depth`, `water_drag`, `water_angular_drag`, `probe_damping`.
- Probe placement: `probe_extents` (half-width, height, half-length) +
`probe_grid` (columns × rows). A grid axis of 1 is valid (centerline).
- **Any Marker3D direct children override the generated grid** and become
the probes — also means: don't park unrelated Marker3Ds directly under
the boat root.
4. Handling exports on `boat.gd`: `engine_power`, `reverse_ratio`,
`throttle_response`, `rudder_strength`, `keel_grip`.
5. Weapon mounts: child `Node3D`s with `scripts/weapon_slot.gd`, positioned
and rotated as the mount points (e.g. stern mount rotated 180°). Set
`allowed_sizes` / `allowed_types` per slot (defaults: MEDIUM, BALLISTIC).
6. `loadout` dict on the root: slot node name → WeaponDef (see section 1).
Keys must match the node names exactly.
7. `weapon_groups`: group id → array of slot names. Group 0 is auto-built in
`_ready()` (all slots) — any editor value for it is ignored.
## 3. Other things to keep in mind
- **Tabs, never spaces**, for all GDScript.
- `@export` = static configuration only. Runtime-controlled references are
plain `var` with a `# Controlled by PlayerController` style comment;
internals use `_prefix`.
- Collision layers: 1 terrain, 2 player, 3 enemies, 4 projectiles,
5 pickups, 6 water, 7 subsystems (see `project.godot` `[layer_names]`).
Water is query-only — surface/contact logic goes through
`Ocean.get_wave_height()`, never a collider (that's how `floating_body.gd`
does buoyancy).
- **Wave math is dual-implemented**: `shaders/ocean.gdshader` (rendering) and
`scripts/ocean.gd` (physics). Any wave change goes in BOTH. Never
`set_shader_parameter` on the water material from gameplay code — use
Ocean's properties.
- **Origin shifting is active.** Free-moving objects (projectiles, debris,
anything not parented to a shifted node) must join `shift_with_origin`
prefer `add_to_group()` in `_ready()`. Never cache world positions across
frames; for true world-space positions use `WorldManager.true_position()`.
- **Physics interpolation is ON.** Every deliberate teleport needs a
`reset_physics_interpolation()` chaser or it renders as a smear.
- **Boats never read `Input`.** Controllers write intents:
`desired_throttle`, `steer_input`, `fire_intents`, `aim_point`.
- **Inspector-stored-value trap**: once a value is edited in a scene it's
stored in the `.tscn` and overrides any later change to the script's
default. Changed a default and it "doesn't work"? Check the scene file.
- **WeaponSlots are static scene structure** — never added/removed at
runtime. The build-once caches in `Boat._ready()` depend on this.
- Scripts aren't `@tool`, so export setters don't run in the editor — never
trust an exported value to have been validated at edit time.
---
*Notes / discrepancies found while verifying (2026-07):* `%Muzzle` is a hard
requirement of `weapon.gd` (plain `@onready %Muzzle`), unlike `%Yaw` /
`%Pitch` / `%Hitbox` which use `get_node_or_null`. A weapon scene isn't truly
optional for a def — `can_mount()` requires `weapon_scene != null`, so "make
a new scene" is what's optional; the def always points at one. CLAUDE.md's
layer table lists only 15; `project.godot` also names 6 water and
7 subsystems (the Hitbox layer value 64 in `mark12.tscn` = layer 7).