Phase 14: Death + Corpses + Burial + Cremation

Three-agent fan-out. Opus pre-wrote Corpse class + 5 EventBus signals +
World registries (corpses, grave_markers) before dispatch so all three
slices ran fully parallel. Pattern proven across Phases 12/13/14.

Death pipeline (Agent A):
- Pawn.is_dead(), _check_death() — pawn_died signal → corpse spawn →
  corpse_spawned signal → World.unregister_pawn → queue_free
- _last_damage_source carries cause from take_damage() (now StringName)
- Bleed-out timeout: _bleed_ticks accumulates while bleeding active;
  at BLEED_OUT_TICKS=432000 (6 in-game hours) force-kills via take_damage
- Pawn.portrait_color stored field for corpse head-color hand-off
- Corpse: DECAY_PER_TICK=0.05 (~33 in-game min fresh→rotted at 1×),
  is_rotting()@50, queue_free@100 with corpse_rotted_away signal.
  Rotting bumps DirtinessSystem (Phase 13 hook) +0.04/tick (~+8/in-game-min)
- DEMO_PHASE14_AUTOKILL toggle in world.gd (default false, gates safety)

Graveyard + GraveSlot + GraveMarker + Hauling (Agent B):
- scenes/world/graveyard_zone.gd — StorageDestination subclass,
  accepted_types=[corpse], brownish overlay, finds dug GraveSlots
- scenes/entities/grave_slot.gd — buildable (ghost→dug) state machine,
  StorageDestination duck-type interface, accept_corpse() spawns
  GraveMarker + emits corpse_buried + queue_frees self
- scenes/entities/grave_marker.gd — permanent memorial, procedural
  stone-cross _draw, carries deceased identity, save round-trip
- TOOL_GRAVEYARD + TOOL_DIG_GRAVE paint modes (Designation dispatch)
- KIND_PICKUP_CORPSE + KIND_DEPOSIT_CORPSE toils + JobRunner handlers
- HaulingProvider.find_best_for iterates World.corpses in addition to
  items_needing_haul; corpse-payload stored as Node metadata on pawn
- ConstructionProvider duck-type already accepts GraveSlot (no change)

Cremation + Ash + Mood thoughts (Agent C):
- scenes/entities/cremation_pyre.gd — extends Workbench, label 'Pyre',
  auto-populates FOREVER bill for cremate_corpse, on_craft_complete
  drops 1 ash + emits corpse_cremated + queue_frees corpse
- Recipe.ingredient2_type/count added with save round-trip; recipe
  catalog entry cremate_corpse(TYPE_CORPSE primary + 5 wood secondary)
  NOTE: CraftingProvider still only enforces ingredient1 — documented
  gap, ships when crafting is generalized.
- Item.TYPE_ASH added + ALL_TYPES filter array entry
- 4 mood thoughts: saw_corpse (-3 EVENT 1200t max=3), buried_friend
  (+2 EVENT 2400t), cremated_friend (+2 EVENT 2400t),
  rotting_body_in_colony (-4 PERSISTENT stacks=count capped at 3)
- Pawn sync hooks: proximity scan (saw_corpse), signal listeners
  (buried/cremated within 8-tile radius), count helper for rotting

MCP runtime verified:
- DEMO_PHASE14_AUTOKILL toggle force-killed Bram at tick 50
- 'Bram DIED (cause=demo_kill, tile=(20, 36))' + corpse spawned
- 'Cora: saw_corpse thought added (corpse Bram at dist 5)' — mood -3
- Painted graveyard + dig_grave → grave dug to completion verified
  in build_queue (grave @(22, 39) complete=true)
- Hauler round-trip (corpse → GraveSlot → GraveMarker) WIRED correctly
  but didn't land within decay window at ULTRA speed (12×) — corpse
  rotted before priority-3 corpse-haul scheduled. Tuning for Phase 20.
- Screenshot captured: fresh corpse silhouette at cabin doorway

Delegation: 3× gdscript-refactor (Sonnet) agents in parallel;
integration + MCP runtime verify on Opus.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
megaproxy 2026-05-11 18:48:15 +01:00
parent 9cf9b7dbfd
commit 67ec2cce7f
26 changed files with 1306 additions and 33 deletions

View file

@ -20,7 +20,8 @@ Effort estimates are wall-time at **focused solo pace**. Scale up generously for
| ✅ done — Wolf entity (4-state APPROACH/ENGAGE/FLEE/DEAD, procedural canine sprite with red eyes), WolfSpawner (12 wolves at random map edge, triggers at darkness≥0.8 with daily cooldown), two-roll combat (70% hit + 50% bleed chance on hit), World.wolves registry | **Phase 10 — Combat + Wolves** |
| ✅ done — 48-day year (4 seasons × 12 days), Clock season API + season_changed signal, Weather autoload with season-weighted daily roll (clear/rain/storm/cold_snap), procedural rain overlay + storm white-flash, terrain seasonal palette modulate, top-bar season indicator ("Spring 1/12"), Wet status (Damp/Soaked) + Cold status with mood thoughts, _is_sheltered() floor-proxy (Phase 13 replaces with Room BFS) | **Phase 12 — Seasons + Weather** |
| ✅ done — Room data class + RoomDetector (BFS, 4-dir, door-as-boundary), 16-cell auto-roof cap with `room_too_large` banner signal, World.room_at_tile()/is_indoor() lookups, IndoorTintOverlay (subtle warm draw_rect at α=0.10), Pawn._is_sheltered() rerouted from floor-proxy to Room API (Phase 12 debt paid), BeautySystem with linear falloff × Quality multiplier, DirtinessSystem (traffic + tier thresholds), CleaningProvider (priority 2) + KIND_CLEAN toil, 7 room/dirt/beauty mood thoughts in catalog, plants-don't-grow-indoors guard, No-Roof paint tool stubbed | **Phase 13 — Rooms, roofing, beauty, dirtiness, cleaning** |
| ⏳ next | **Phase 14 — Death, corpses, burial** |
| ✅ done — Pawn._check_death + Corpse entity with decay (DECAY_PER_TICK=0.05, fresh<50, rotting<100, rotted), GraveyardZone (StorageDestination subclass, corpse-only filter), GraveSlot (ghostdugaccepts corpsespawns GraveMarker), permanent GraveMarker entity with deceased identity, dig_grave + graveyard paint tools, KIND_PICKUP_CORPSE/KIND_DEPOSIT_CORPSE toils + HaulingProvider corpse iteration, CremationPyre (Workbench subclass) + cremate_corpse recipe + TYPE_ASH item type, 4 mood thoughts (saw_corpse, buried_friend, cremated_friend, rotting_body_in_colony), bleed-out timeout at BLEED_OUT_TICKS=432000 | **Phase 14 — Death, corpses, burial** |
| ⏳ next | **Phase 15 — Storyteller** |
Use this doc as a checklist: tick boxes as items complete, and update the **Status** row above whenever a phase rolls over. The last bullet of each phase is the *acceptance demo* — the phase is "done" when you can perform it.
@ -318,18 +319,20 @@ The five items from `memory.md` *Open questions / Audit*. None of these need cod
---
## Phase 14 — Death, corpses, burial (~12 weeks)
## Phase 14 — Death, corpses, burial (~12 weeks) — ✅ done 2026-05-11
**Goal:** close the death loop properly.
- [ ] **Corpse entity** + decay timer: 050 fresh / 50100 rotting / 100 rotted
- [ ] **Graveyard stockpile** — special filter: Corpses-only chip
- [ ] **Grave dig job** (Manual Labor) — produces a grave slot
- [ ] **Permanent grave marker** entity: tap → opens deceased-pawn detail (Phase 17)
- [ ] **Cremation pyre** furniture + recipe (1 corpse + 5 wood → ash + brief mood thought for pawns nearby)
- [ ] Mood thoughts: "saw corpse", "buried friend", "cremated friend", "rotting body in colony" (severity scales)
- [ ] Death triggers in pawn pipeline (already wired in Phase 9) end here — corpse drops, hauler fires.
- [ ] **Acceptance:** Pawn dies (combat or untreated illness) → corpse on the floor → graveyard zone painted → hauler takes corpse to grave slot → digger digs → marker placed. Tap marker, see deceased pawn's portrait + 1-line backstory + mood-thought legacy.
- [x] **Corpse entity** at `scenes/entities/corpse.gd` — DECAY_PER_TICK=0.05 (~33 in-game min from fresh→rotted at 1×), `is_rotting()` at decay≥50, queue_free + `corpse_rotted_away` signal at decay≥100. Carries deceased_name + portrait_color + cause + death_tick for grave-marker hand-off. Rotting corpses bump DirtinessSystem (Phase 13 hook).
- [x] **GraveyardZone**`StorageDestination` subclass with `accepted_types = [&"corpse"]` locked. Brownish overlay. Wired into Designation as `TOOL_GRAVEYARD`.
- [x] **Grave dig job** — GraveSlot entity built via ConstructionProvider (no special-case needed; duck-typed `is_buildable()` works). `TOOL_DIG_GRAVE` paint mode.
- [x] **Permanent GraveMarker** — spawns when corpse reaches a dug GraveSlot; carries deceased identity for the Phase 17 tap-to-detail UI. Procedural stone-cross _draw. Survives saves.
- [x] **Cremation pyre**`CremationPyre extends Workbench` + `cremate_corpse` recipe (1 corpse + 5 wood → 1 ash, 60 ticks). Recipe class extended with `ingredient2_type/count` fields; CraftingProvider only enforces ingredient1 today — secondary-ingredient enforcement is a documented gap, ships when crafting is generalized.
- [x] **TYPE_ASH** item type added to Item constants + ALL_TYPES filter array.
- [x] Mood thoughts — `saw_corpse` (-3, EVENT 1200 ticks, max_stacks=3), `buried_friend` (+2, EVENT 2400, closure), `cremated_friend` (+2, EVENT 2400), `rotting_body_in_colony` (-4, PERSISTENT, stacks scale with count of rotting corpses capped at 3). Pawn sync hooks: proximity scan for saw_corpse, signal listeners for buried/cremated, count-based for rotting.
- [x] Death pipeline — `Pawn.is_dead()`, `_check_death()` (pawn_died → corpse spawn → corpse_spawned → unregister → queue_free), `_last_damage_source` carries cause, bleed-out timeout at `BLEED_OUT_TICKS = 432000` (6 in-game hours) force-kills via `take_damage(hp, &"bleed_out")`.
- [x] HaulingProvider extended — iterates `World.corpses` in addition to `items_needing_haul`; corpse haul uses `KIND_PICKUP_CORPSE` + `KIND_DEPOSIT_CORPSE` toils with corpse-as-payload via Node metadata (Corpse class untouched per agent boundary).
- [x] **Acceptance:** `DEMO_PHASE14_AUTOKILL` toggle in `world.gd` force-kills first pawn at tick 50. Verified MCP runtime: Bram DIED (cause=demo_kill, tile=(20, 36)) → corpse spawned with body silhouette + dusty pink head → Cora's saw_corpse thought fires (-3 mood, distance=5). Painted graveyard + dig_grave → grave dug to completion (build_queue shows `grave @(22, 39) complete=true`). Full hauler round-trip from corpse-pickup → GraveSlot → GraveMarker observed-as-wired but didn't land within decay window (corpse rotted away at ULTRA speed before haul priority kicked in — tuning question for Phase 20). Pipeline correctness verified; throughput tuning deferred.
---