chore(meta): plan Sprint 23: Terrain
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -193,7 +193,20 @@ tooling/db/ticket sprint assign <ticket_id> <sprint_id>
|
||||
The sprint stays in `planning` status until explicitly activated via
|
||||
`tooling/db/sprint start`. This prevents starting an unplanned sprint.
|
||||
|
||||
### 8. Present summary
|
||||
### 8. Commit and push
|
||||
|
||||
Stage the briefing files and any other changes (db backup, closed tickets),
|
||||
then commit and push so worktree branches can pull the planning artifacts:
|
||||
|
||||
```bash
|
||||
git add docs/sprints/sprint-N/
|
||||
make db-backup
|
||||
git add docs/backups/settledreach.db.backup
|
||||
git commit -m "chore(meta): plan Sprint N: Theme"
|
||||
git push
|
||||
```
|
||||
|
||||
### 9. Present summary
|
||||
|
||||
Output:
|
||||
- Sprint number, theme, and goal
|
||||
@@ -201,3 +214,4 @@ Output:
|
||||
- Carry-over count
|
||||
- Open questions that need early resolution
|
||||
- Files written
|
||||
- Commit pushed to main
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,116 @@
|
||||
# Sprint 23: Terrain — Client Tasks
|
||||
|
||||
**Goal:** Build the walkable Sova world under the authored content, complete the storyteller engagement layer, and add the debug console — clearing the path to v0.1 integration in Sprint 24.
|
||||
|
||||
**Branch:** `client`
|
||||
**Agents:** Stig (UI/rendering), Hoshe (QA)
|
||||
|
||||
---
|
||||
|
||||
## New Tickets
|
||||
|
||||
| # | Title | Blocked by |
|
||||
|---|-------|------------|
|
||||
| #573 | Dialogue speaker colors are position-based instead of speaker-bound | — |
|
||||
| #574 | Pre-existing test failures in test_entity_renderer (test_rendering.gd) | — |
|
||||
| #581 | Debug console client: tilde console UI, settings toggle, command dispatch | #580 (server) |
|
||||
| #585 | Walls at LOS boundary: client — render margin tiles correctly through fog | #584 (server) |
|
||||
|
||||
Use `tooling/db/ticket show <id>` for full descriptions.
|
||||
|
||||
---
|
||||
|
||||
## Key Decisions
|
||||
|
||||
- `decisions/architecture.md` — D-020 (ObserverSnapshot is the only data crossing IPC boundary), D-088 (3-state pause — Normal/Overlay/Paused)
|
||||
- `decisions/content.md` — D-033 (entity color = relationship to player; player color set at character creation), D-076 (dialogue box max-width 1200px)
|
||||
- `decisions/perception.md` — D-059 (fog system — 3-state: visible, explored, never-seen)
|
||||
|
||||
---
|
||||
|
||||
## Notes
|
||||
|
||||
### #573 — Dialogue speaker colors
|
||||
|
||||
**What exists:** `client/ui/dialogue_box.gd` renders the dialogue box (D-028, D-076). Speaker colors are currently assigned by position (left speaker = color A, right speaker = color B), which means all left-side speakers share one color and all right-side speakers share another regardless of identity.
|
||||
|
||||
**What to deliver:** Bind speaker color to entity identity, not screen position. The `ObserverSnapshot` carries entity IDs on dialogue lines (check `client/scripts/protocol/protocol.gd` `_decode_nearby_interaction` for the entity_id field on dialogue events). Maintain a `Dict[entity_id -> Color]` in `dialogue_box.gd`. On first encounter with an entity ID, assign a color from a palette and store it. Subsequent lines from the same entity reuse the stored color. The palette should be distinct from D-033 relationship colors (those are world-layer entity colors; dialogue colors are UI-layer only).
|
||||
|
||||
**Non-obvious gotcha:** The player character is always one speaker. Assign the player a fixed color (e.g. the character's D-033 color, or a designated white/near-white). NPCs get assigned from the palette on first appearance in the conversation.
|
||||
|
||||
### #574 — Entity renderer test failures
|
||||
|
||||
**What exists:** `client/tests/test_rendering.gd` contains `test_entity_renderer_*` tests that fail on clean main (5aea6e2). Affected: positions_centered, player_color_differs_from_npc, player_uses_d033_color, npc_uses_unknown_teal, object_uses_grey, facing_indicator_rotation_accuracy, regression_345_entity_position_set_from_entity_id_entity.
|
||||
|
||||
**What to deliver:** Fix the failing tests. `client/scripts/rendering/entity_renderer.gd` likely changed without the tests being updated. Read the renderer to understand the current implementation, then update tests to match (or fix the renderer if it regressed from D-033 spec). Do not change the passing visual golden tests — only fix the unit tests in `test_rendering.gd`. Run `make test-client` to verify.
|
||||
|
||||
**Gotcha:** D-033 specifies entity color = relationship to player. If entity_renderer.gd was updated correctly but tests use stale expected values, update the test assertions. If entity_renderer.gd was updated incorrectly and broke D-033 compliance, fix the renderer.
|
||||
|
||||
### #581 — Debug console client
|
||||
|
||||
**What exists:** `client/ui/settings_dialog.gd` — existing settings panel with 5-bus audio volume sliders. `client/ui/debug_overlay.gd` — existing debug overlay tied to `OS.is_debug_build()` and a `debug_overlay` input action. `client/scripts/main.gd` — input routing. `client/scripts/protocol/protocol.gd` — `encode_player_input(tick, action_name, action_data)` and `decode_snapshot()`.
|
||||
|
||||
**What to deliver:**
|
||||
|
||||
1. `client/ui/debug_console.gd` + `debug_console.tscn` — semi-transparent panel (40% screen height, anchored bottom). Input line at bottom, scrollable output log above (50-line buffer). Tilde key (`KEY_QUOTELEFT`) toggles open/closed. When open, swallows all keyboard input except tilde (no movement commands leak).
|
||||
|
||||
2. Settings integration — add a "Debug Console" checkbox to `settings_dialog.gd`. Persisted in `user://settings.cfg` alongside audio volumes. Default: enabled. When disabled, tilde key is inert.
|
||||
|
||||
3. Command parsing and dispatch — parse input as `<command> [args...]`. Map to `DebugCommandKind` variants. Serialize via `Protocol.encode_player_input('debug_command', { kind: ..., args: ... })`. Commands: `ticks <n>`, `contaminate`, `tp <x> <y> [z]`, `tp <name>`, `activate`, `triangle <id>`, `npc <id>`, `triangles`, `pop`, `status`, `help`.
|
||||
|
||||
4. Response display — read `debug_response` from the decoded snapshot dict (new optional field from server #580). Format and append to output log. Complex payloads (NPC state, triangle list) displayed as formatted key-value blocks.
|
||||
|
||||
5. Protocol extension — update `protocol.gd` to encode `debug_command` action and decode `debug_response` from snapshot. Bump `Protocol.PROTOCOL_VERSION` to 18 to match server.
|
||||
|
||||
**Coordination required:** Stig must coordinate with Dudley on the PROTOCOL_VERSION bump (#580 server) before either PR is merged. Client and server PRs must land together or within the same merge window — a version mismatch will crash the connection.
|
||||
|
||||
**Blocked by:** #580 (server must define `DebugCommandKind` wire format before client serialization is finalized).
|
||||
|
||||
### #585 — LOS boundary wall rendering
|
||||
|
||||
**What exists:** `client/scripts/rendering/tile_renderer.gd` — `update_tiles()` processes `VisibleTile` entries from the snapshot. The tile accumulation fix (removed `clear()` from `update_tiles()`) means explored tiles stay rendered. `client/scripts/autoloads/fog_state.gd` — manages exploration state per tile.
|
||||
|
||||
**What to deliver:** Handle the new `VisibilitySector::BoundaryWall` tiles from server #584. In `update_tiles()`, detect boundary wall sector tiles and:
|
||||
- Render them to the TileMapLayer as wall tiles (so fog has content to composite over)
|
||||
- Do NOT record them as explored in `fog_state.gd` — they are "seen right now" only, not explored
|
||||
- Do NOT update the fog exploration texture for these tiles
|
||||
|
||||
The visual result: walls at the LOS edge are distinct from fog (they render as wall tiles) but disappear from the map when not in the current LOS cone (they don't leave a memory trace).
|
||||
|
||||
**Blocked by:** #584 (server must send the boundary tiles before client can render them).
|
||||
|
||||
---
|
||||
|
||||
## Dependency Chain
|
||||
|
||||
```
|
||||
#573 (speaker color fix) — standalone
|
||||
#574 (renderer test fix) — standalone
|
||||
|
||||
#580 (server debug) → #581 (debug console)
|
||||
|
||||
#584 (server wall margin) → #585 (client wall margin)
|
||||
```
|
||||
|
||||
#573 and #574 are fully independent — start immediately. #581 is blocked on server work landing first. #585 is blocked on server #584 landing first.
|
||||
|
||||
---
|
||||
|
||||
## PR Workflow
|
||||
|
||||
```bash
|
||||
tea pr create --repo jpmschweitzer/settled-reach --login schweitz \
|
||||
--title "fix(client): bind dialogue speaker colors to entity identity" \
|
||||
--description "body" --base main --head client
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Sprint Completion (Client Criteria)
|
||||
|
||||
1. Dialogue conversations show consistent per-speaker colors regardless of screen position.
|
||||
2. All `test_entity_renderer_*` tests pass in `test_rendering.gd`.
|
||||
3. Tilde key opens debug console in-game; `status` command returns contamination state; `triangles` lists all active triangles.
|
||||
4. Debug console toggle visible in Settings menu; persists across restarts.
|
||||
5. Walls at LOS edge render as distinct tile content rather than blending into fog.
|
||||
6. `make test-client` green on client branch.
|
||||
@@ -0,0 +1,102 @@
|
||||
# Sprint 23: Terrain — Joint / Integration
|
||||
|
||||
**Goal:** Build the walkable Sova world under the authored content, complete the storyteller engagement layer, and add the debug console — clearing the path to v0.1 integration in Sprint 24.
|
||||
|
||||
**Agents:** All implementation agents (Dudley, Tyre, Hoshe, Stig, Araminta)
|
||||
|
||||
---
|
||||
|
||||
## Pre-Sprint Actions
|
||||
|
||||
These must happen at sprint start, before implementation tickets begin:
|
||||
|
||||
| Action | Owner | Blocks |
|
||||
|--------|-------|--------|
|
||||
| Confirm tile format (character codes, row encoding, multi-z approach) | Dudley + Araminta sync (day 1–2) | #582, #583 — Araminta cannot start until format is published in types.rs |
|
||||
| Confirm DebugCommandKind wire format (exact JSON/msgpack structure) | Dudley + Stig sync (day 1–2) | #581 — client dispatch serialization depends on this |
|
||||
| Confirm PROTOCOL_VERSION bump: 17 → 18 | Dudley + Stig | #580 server + #581 client must land together |
|
||||
|
||||
---
|
||||
|
||||
## Cross-Team Dependencies
|
||||
|
||||
| Dependency | Direction | Notes |
|
||||
|------------|-----------|-------|
|
||||
| Tile format spec | Server (#577) → Visual (#582, #583) | Dudley publishes format in types.rs; Araminta starts authoring same day |
|
||||
| DebugCommandKind wire format | Server (#580) → Client (#581) | Stig cannot finalize protocol.gd encoding until Dudley publishes the enum |
|
||||
| PROTOCOL_VERSION 17→18 | Server (#580) ↔ Client (#581) | Both must land in the same merge window; version mismatch crashes the connection |
|
||||
| LOS wall margin | Server (#584) → Client (#585) | Client rendering change depends on server sending boundary tiles |
|
||||
|
||||
---
|
||||
|
||||
## Housekeeping Done This Sprint
|
||||
|
||||
The following stale tickets were closed during sprint planning:
|
||||
|
||||
| # | Reason |
|
||||
|---|--------|
|
||||
| #184 | Logistics hub template YAML was already complete in `templates/logistics-hub.yaml` |
|
||||
| #339 | Protocol version handshake already implemented in Sprint 19 via #555/#556 |
|
||||
| #143 | Superseded by richer #576 (tile-type layer with full spec) |
|
||||
| #144 | Superseded by #577 (location loader is the v0.1 equivalent) |
|
||||
| #145 | Superseded by richer #578 (chunk streaming with v0.1 full-preload mode) |
|
||||
| #154 | Functional cluster spatial specs live in D-025 and D-093 — already done |
|
||||
| #155 | Hand-crafted authoring is now #582/#583 with correct format and blocker |
|
||||
| #157 | Gate corridor is covered by #583 (Last Shift + maintenance + gate) |
|
||||
|
||||
---
|
||||
|
||||
## Sprint Completion Proof
|
||||
|
||||
The sprint is done when all of the following are observable:
|
||||
|
||||
1. **Walkable world boots.** Start the server in production mode (no gauntlet feature). It loads tile data from all four Sova locations (The Terminal, The Last Shift, maintenance corridors, gate corridor). No panic. WalkabilityMap populated from YAML — walls block movement, floors allow it.
|
||||
|
||||
2. **Storyteller activates from player behavior.** Run a test: spawn at The Terminal, observe Kael for 3000 ticks. EngagementRecord increments. After CONTAMINATION_DELAY_TICKS, activation_pass() fires and emits TriangleActivated for the hub-power or worried-partner triangle.
|
||||
|
||||
3. **Debug console works end-to-end.** Boot production game. Tilde key opens console. Type `status` — returns contamination state and current tick. Type `contaminate` — skips to contamination. Type `triangles` — lists all TriangleState entities with their IDs and classifications.
|
||||
|
||||
4. **Debug console in settings.** Open Settings menu. Debug Console toggle is visible. Disable it. Tilde key no longer opens console.
|
||||
|
||||
5. **Speaker colors are identity-bound.** Open a conversation with Kael, then end it. Open another conversation with Voss. Kael's color is consistent; Voss has a different color; neither color is position-based.
|
||||
|
||||
6. **LOS boundary walls render.** Walk up to a wall at the edge of vision. The wall tile is visible and distinct from fog rather than bleeding into the fog overlay.
|
||||
|
||||
7. **CI green.** `make ci` passes on all branches. No regressions.
|
||||
|
||||
---
|
||||
|
||||
## Test Plan Alignment (D-030)
|
||||
|
||||
Sprint 23 is Phase 2 integration territory (D-030 Phase 2: integration tests + information boundary tests):
|
||||
|
||||
- **#576:** Unit tests — TileKind round-trip in WalkabilityMap. Phase 1 (data structure).
|
||||
- **#577:** Integration test — production boot loads tile data, WalkabilityMap is populated correctly. Phase 2.
|
||||
- **#578:** Unit test — chunk load/unload at player boundary. Phase 1.
|
||||
- **#570–#572–#579:** Integration test — 3000-tick simulation asserts EngagementRecord increments + TriangleActivated fires. Phase 2.
|
||||
- **#580/#581:** Manual acceptance — debug console smoke test (status, contaminate, triangles). Phase 2.
|
||||
- **#584/#585:** Visual regression test — add golden test for LOS boundary wall rendering. Phase 2.
|
||||
|
||||
---
|
||||
|
||||
## Open Questions
|
||||
|
||||
| ID | Question | Blocks | Action |
|
||||
|----|----------|--------|--------|
|
||||
| Q-052 | Storyteller hint delivery channels | #579 downstream consumers | Read for context but does not block Sprint 23. #579 emits TriangleActivated; consumers wired in Sprint 24. |
|
||||
|
||||
---
|
||||
|
||||
## PR Merge Order
|
||||
|
||||
To avoid conflicts on shared files (`bridge/types.rs`, `protocol.gd`, `PROTOCOL_VERSION`):
|
||||
|
||||
1. Server PRs (#576, #570, #571, #572) — no client/visual dependencies; merge first.
|
||||
2. Server PR (#577 tile format) — merge and notify Araminta immediately so visual authoring starts.
|
||||
3. Server PRs (#578, #579, #584) — merge in any order after their respective blockers.
|
||||
4. **Server PR #580 (debug server) + Client PR #581 (debug client)** — must land together. Coordinate merge timing. Both bump PROTOCOL_VERSION to 18.
|
||||
5. Visual PRs (#582, #583) — merge after server #577 is in main.
|
||||
6. Client PRs (#573, #574) — no dependencies; merge anytime.
|
||||
7. Client PR #585 (LOS wall margin) — merge after server #584 is in main.
|
||||
|
||||
**Critical:** #580 and #581 share the PROTOCOL_VERSION bump. Do not merge one without the other.
|
||||
@@ -0,0 +1,144 @@
|
||||
# Sprint 23: Terrain — Server Tasks
|
||||
|
||||
**Goal:** Build the walkable Sova world under the authored content, complete the storyteller engagement layer, and add the debug console — clearing the path to v0.1 integration in Sprint 24.
|
||||
|
||||
**Branch:** `server`
|
||||
**Agents:** Dudley (simulation), Tyre (architecture), Hoshe (QA)
|
||||
|
||||
---
|
||||
|
||||
## New Tickets
|
||||
|
||||
| # | Title | Blocked by |
|
||||
|---|-------|------------|
|
||||
| #576 | Chunk data structure: tile-type layer and ChunkStore resource | — |
|
||||
| #577 | Location YAML tile format and content loader integration | #576 |
|
||||
| #578 | Chunk loading/unloading: streaming around player position | #576 |
|
||||
| #570 | EngagementRecord component — track per-NPC engagement metrics | — |
|
||||
| #571 | MovementHistoryBuffer resource — player path history | — |
|
||||
| #572 | Storyteller lifecycle rules — multi-activation, cooldown, resolution | — |
|
||||
| #579 | Storyteller activation pass — implement activation_pass() system | #570, #571, #572 |
|
||||
| #580 | Debug console server: command handler and state manipulation API | — |
|
||||
| #584 | Walls at LOS boundary: server — extend visible_tiles with wall margin | — |
|
||||
|
||||
Use `tooling/db/ticket show <id>` for full descriptions.
|
||||
|
||||
---
|
||||
|
||||
## Key Decisions
|
||||
|
||||
- `decisions/architecture.md` — D-012 (tile/chunk spec), D-094 (chunk 32×32, block 128×128, district 256×256), D-110 (signed z-levels: base_z i8, z_levels u8)
|
||||
- `decisions/content.md` — D-023 (three-tier content model, storyteller activation), D-025 (functional cluster, 15-40 tile social sites), D-093 (Sova Transit District — full spatial spec with tile dimensions), D-029 (population entanglement ratio 30/50/20)
|
||||
- `decisions/architecture.md` — D-010 (determinism — BTreeMap, no float non-determinism), D-031 (10 ticks/game-minute, TICKS_PER_GAME_MINUTE)
|
||||
|
||||
---
|
||||
|
||||
## Notes
|
||||
|
||||
### #576 — Chunk data structure: tile-type layer
|
||||
|
||||
**What exists:** `WalkabilityMap` in `server/src/simulation/movement.rs` stores per-tile walkability as `Vec<bool>` inside private `ChunkData` structs, indexed by `ChunkCoord { cx, cy, z }`. `CHUNK_SIZE = 32`. The public API is `is_walkable(pos)` / `set_walkable(pos, bool)`. Generator types (`GeneratorChunkData`, `TileId`, etc.) are stubs in `server/src/simulation/generator.rs`.
|
||||
|
||||
**What to deliver:** Extend `ChunkData` to carry a `TileKind` per tile. Define `TileKind` enum: `Floor | Wall | Void | Restricted`. Add `tile_kind(pos) -> TileKind` and `set_tile_kind(pos, kind)` to `WalkabilityMap`. Keep `is_walkable` / `set_walkable` API intact — many existing tests use it directly. All existing tests must pass.
|
||||
|
||||
**Non-obvious gotcha:** `ChunkData` is private. Only `WalkabilityMap` methods are the extension surface. Do not make `ChunkData` or `TileKind` pub unnecessarily — encapsulate through the map API.
|
||||
|
||||
### #577 — Location YAML tile format and loader
|
||||
|
||||
**What exists:** `Location` struct in `server/src/content/types.rs` has `tile_bounds: Option<TileBounds>` (bounding box only) and `sightlines`. The three location YAMLs (`the-terminal.yaml`, `the-last-shift.yaml`, `maintenance-corridors.yaml`) are 2-line stubs — header comments only. `load_district()` in `server/src/content/loader.rs` already calls `load_yaml_dir::<Location>()` on the locations directory; stubs are silently skipped.
|
||||
|
||||
**What to deliver:** Two things. First, agree the tile format with Araminta (visual team) on day 1–2 of the sprint — she cannot start authoring until you publish the format. Suggested format: `tiles: ["FFFFF", "FWWWF", "FFFFF"]` — array of strings, one char per tile, left-to-right = +x, top-to-bottom = +y. Characters: `F`=floor, `W`=wall, `V`=void, `R`=restricted. Post the agreed format as a comment block in `types.rs` before Araminta starts.
|
||||
|
||||
Second, implement `load_location_tiles(location: &Location, origin: TilePosition, map: &mut WalkabilityMap)` and call it from the production startup path in `server/src/main.rs` after NPCs/triangles are spawned. The Gauntlet startup path (`server/src/test_world/`) must be untouched.
|
||||
|
||||
**Dependency:** #576 must be merged first (needs `set_tile_kind` API). Unblocks #582 and #583 (Araminta's tile authoring).
|
||||
|
||||
### #578 — Chunk loading/unloading
|
||||
|
||||
**What exists:** `WalkabilityMap` is pre-allocated at startup via `WalkabilityMap::new(width, height, z_levels)` which eagerly creates all chunks. `ZLevelLoadState` enum is defined in `server/src/simulation/generator.rs` with `Loaded(GeneratorChunkData) | Skeleton(FloorZone) | Ungenerated` variants but not yet used at runtime.
|
||||
|
||||
**What to deliver:** A chunk streaming system. For v0.1 the entire hand-authored district fits in memory (256×256 visual = 8×8 chunks of 32 tiles). Pre-load all chunks at startup; the streaming system exists but covers the full map. Architecture must support future per-demand loading (v0.3+ generator). Add `ChunkLoadRadius` resource. Add a system that tracks player position → loaded chunk set → unloads distant chunks. Test: verify chunks at boundary are unloaded when player moves away.
|
||||
|
||||
**Blocked by:** #576.
|
||||
|
||||
### #570 — EngagementRecord component
|
||||
|
||||
**What exists:** Nothing. The KnowledgeGraph (`server/src/knowledge/`) tracks NPC information inventory but not engagement metrics. The storyteller activation spec in #162 requires three fields per observed entity: `observation_time_ticks`, `conversation_count`, `monologue_trigger_count`.
|
||||
|
||||
**What to deliver:** `EngagementRecord` as a bevy ECS `Component` on NPC entities. Three fields. The perception system (`server/src/perception/observation.rs`) should increment `observation_time_ticks` per tick the NPC is in the player's LOS. The dialogue system (`server/src/simulation/dialogue.rs`) should increment `conversation_count` on conversation start. The monologue system (`server/src/simulation/monologue.rs`) should increment `monologue_trigger_count` when a monologue fires for a specific NPC context. All three writes must be additive — no overwrite.
|
||||
|
||||
### #571 — MovementHistoryBuffer resource
|
||||
|
||||
**What exists:** Nothing. `TilePosition` tracks current position. No history buffer exists.
|
||||
|
||||
**What to deliver:** `MovementHistoryBuffer` as a bevy `Resource`. Ring buffer of the player's `TilePosition` over the last `ENGAGEMENT_WINDOW_TICKS = 3000` ticks. The movement system (`server/src/simulation/movement.rs`) appends to it each tick when the player moves. Expose a query method: `npcs_copresent_in_window(threshold: i32) -> Vec<Entity>` — returns NPC entities whose `TilePosition` was within `threshold` tiles of any player position in the buffer. Used by the activation pass (#579).
|
||||
|
||||
### #572 — Storyteller lifecycle rules
|
||||
|
||||
**What exists:** The contamination layer fires once and sets `ContaminationActive`. No activation lifecycle spec exists.
|
||||
|
||||
**What to deliver:** A decision/spec (can be a code comment block or a D-record filed with Qatux, not necessarily full implementation). For v0.1: single activation per session only. No concurrent activations. No cooldown needed (one and done). Triangle resolution: when a triangle reaches a terminal phase, mark it resolved; do not re-activate. Document these rules as constants and a comment in `server/src/storyteller/mod.rs`. These rules gate #579.
|
||||
|
||||
### #579 — Storyteller activation pass
|
||||
|
||||
**What exists:** `server/src/storyteller/mod.rs` has `ContaminationActive`, `ContaminationEventQueue`, and `tick_contamination_activation()`. No activation pass, no engagement scoring, no `TriangleActivated` event.
|
||||
|
||||
**What to deliver:** `activation_pass()` system implementing the 6 steps from #162. Gate → proximity query → engagement scoring → routing → module selection → emit `TriangleActivated { triangle_id }`. Add `TriangleActivated` event type and `TriangleActivatedQueue` resource. Register `activation_pass` in `StorytellerPlugin.build()` — runs after contamination is active, on 10-tick cadence.
|
||||
|
||||
**Blocked by:** #570, #571, #572.
|
||||
|
||||
### #580 — Debug console server
|
||||
|
||||
**What exists:** `PlayerAction` enum in `server/src/bridge/types.rs` has movement, interaction, save/load, pause, `TeleportToHub`. `PROTOCOL_VERSION = 17`.
|
||||
|
||||
**What to deliver:** New `PlayerAction::DebugCommand(DebugCommandKind)` variant. `DebugCommandKind` enum with 9 variants (see ticket description). `DebugResponsePayload` added as `Option<>` to `ObserverSnapshot`. Bump `PROTOCOL_VERSION` to 18 when adding the new variant. A `handle_debug_commands` system that only executes when a `DebugEnabled` resource is true (set at startup; default true for v0.1). The system routes each `DebugCommandKind` to the appropriate ECS query or mutation.
|
||||
|
||||
**Gotcha:** `PROTOCOL_VERSION` is checked by the client on every snapshot. Dudley must coordinate with Stig on the version bump — client #581 must update `protocol.gd` simultaneously to avoid mismatch errors.
|
||||
|
||||
### #584 — LOS boundary wall margin
|
||||
|
||||
**What exists:** Observer snapshot generation is in `server/src/perception/observer/mod.rs`. `VisibilitySector` enum in `bridge/types.rs` has existing variants. `WalkabilityMap` is available via `Res<WalkabilityMap>`.
|
||||
|
||||
**What to deliver:** After the standard visible tiles are computed, walk the LOS boundary and add wall tiles 1 tile beyond the cone. Use a new `VisibilitySector::BoundaryWall` variant (or reuse `LosObstructed` if that field already serves this purpose — check #514). Wall margin tiles must not affect the exploration/memory state — they are "seen now" only.
|
||||
|
||||
---
|
||||
|
||||
## Dependency Chain
|
||||
|
||||
```
|
||||
#576 (tile-type layer)
|
||||
→ #577 (tile format + loader) → #582 (Terminal tile map, visual)
|
||||
| → #583 (Last Shift + corridor maps, visual)
|
||||
→ #578 (chunk streaming)
|
||||
|
||||
#570 (EngagementRecord) ─┐
|
||||
#571 (MovementHistory) ─┤→ #579 (activation pass)
|
||||
#572 (lifecycle rules) ─┘
|
||||
|
||||
#580 (debug server) → #581 (debug client)
|
||||
|
||||
#584 (LOS wall margin server) → #585 (LOS wall margin client)
|
||||
```
|
||||
|
||||
Parallel tracks: spatial chain (#576→#577→#578), storyteller chain (#570-572→#579), debug chain (#580), LOS fix (#584). All four tracks are independent and can run in parallel.
|
||||
|
||||
---
|
||||
|
||||
## PR Workflow
|
||||
|
||||
```bash
|
||||
tea pr create --repo jpmschweitzer/settled-reach --login schweitz \
|
||||
--title "feat(server): chunk tile-type layer and location loader" \
|
||||
--description "body" --base main --head server
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Sprint Completion (Server Criteria)
|
||||
|
||||
1. `cargo test` green on server branch — all existing tests pass, new tile-kind tests pass.
|
||||
2. Production startup (no gauntlet feature) boots with tile data loaded — WalkabilityMap populated from location YAML for The Terminal, The Last Shift, maintenance corridors.
|
||||
3. `EngagementRecord` increments correctly — perception/dialogue/monologue all write to it.
|
||||
4. `activation_pass()` fires after contamination and emits `TriangleActivated` for the highest-engagement NPC's triangle.
|
||||
5. `DebugCommand::SkipToContamination` advances simulation to `CONTAMINATION_DELAY_TICKS` and the response is returned in the snapshot.
|
||||
6. LOS wall margin — snapshot includes 1-tile wall data beyond the LOS cone boundary.
|
||||
@@ -0,0 +1,144 @@
|
||||
# Sprint 23: Terrain — Visual Tasks
|
||||
|
||||
**Goal:** Build the walkable Sova world under the authored content, complete the storyteller engagement layer, and add the debug console — clearing the path to v0.1 integration in Sprint 24.
|
||||
|
||||
**Branch:** `visual`
|
||||
**Agents:** Araminta (art direction, tile authoring)
|
||||
|
||||
---
|
||||
|
||||
## New Tickets
|
||||
|
||||
| # | Title | Blocked by |
|
||||
|---|-------|------------|
|
||||
| #582 | Author tile map: The Terminal (logistics hub) | #577 (server tile format) |
|
||||
| #583 | Author tile maps: The Last Shift, maintenance corridors, and gate corridor | #577 (server tile format) |
|
||||
|
||||
Use `tooling/db/ticket show <id>` for full descriptions.
|
||||
|
||||
---
|
||||
|
||||
## Key Decisions
|
||||
|
||||
- `decisions/content.md` — D-093 (Sova Transit District — **full spatial spec with tile dimensions, zone palette, corridor widths, zone-by-zone breakdown**)
|
||||
- `decisions/content.md` — D-025 (functional cluster: 15-40 tile connected spaces, internal sightlines, public/private zones)
|
||||
- `decisions/architecture.md` — D-094 (spatial hierarchy: chunk 32×32 sim tiles = 16×16 visual tiles at 2x retina, D-066), D-066 (dual-scale grid: 0.5m sim, 1m visual)
|
||||
- `decisions/architecture.md` — D-110 (z-levels: z=0 maintenance corridors, z=1 main structures, z=2 observation gallery)
|
||||
|
||||
---
|
||||
|
||||
## Open Questions to Resolve Early
|
||||
|
||||
- **Tile format (day 1–2):** Server ticket #577 must publish the tile format before authoring begins. Coordinate with Dudley. The format is expected to be: `tiles:` array of strings, one char per tile. Do not author tile data until the format is confirmed and documented in `server/src/content/types.rs`.
|
||||
|
||||
---
|
||||
|
||||
## Notes
|
||||
|
||||
### Tile format primer
|
||||
|
||||
The server will confirm a format like:
|
||||
|
||||
```yaml
|
||||
tiles:
|
||||
- "WWWWWWWWWW"
|
||||
- "WFFFFFFFFFFW"
|
||||
- "WFFFFFFFFFFW"
|
||||
- "WWWWWWWWWW"
|
||||
```
|
||||
|
||||
Characters: `F`=floor (walkable), `W`=wall (blocks movement/LOS), `V`=void (empty space outside the location boundary), `R`=restricted (walkable but access-tier restricted). Left-to-right = +x, top-to-bottom = +y. Origin (0,0) = top-left corner of the tile_bounds bounding box.
|
||||
|
||||
Do not use any other characters until the server documents them.
|
||||
|
||||
### #582 — The Terminal tile map
|
||||
|
||||
**Source materials:**
|
||||
- `#311` (done): Spatial layout — The Terminal. Tile-level floor plan with sightline analysis. Sprint 12.
|
||||
- `D-093`: Terminal dimensions = **44×28 visual tiles**. Zone palette surface hex: `#7a8490`, fog tint: `#0d1520`.
|
||||
- Access tiers: main corridor = public, work floor = credentialed, restricted storage = restricted.
|
||||
- Key spatial features:
|
||||
- Scanner bays (entry — public)
|
||||
- Main corridor (chokepoint — high sightline value)
|
||||
- Manifest processing (work floor)
|
||||
- Break room (social site — NPC off-hours presence)
|
||||
- Supervisor office (window faces main corridor — LOS design requirement)
|
||||
- Restricted storage (smuggling ops — `R` tiles for access restriction)
|
||||
|
||||
**Output:** Complete `content/campaigns/main/systems/krenn/stations/sova/districts/transit/locations/the-terminal.yaml` with:
|
||||
- `canonical_id: krenn.sova.transit.location.the-terminal`
|
||||
- `display_name: "The Terminal"`
|
||||
- `tile_bounds: { x_min: 0, y_min: 0, x_max: 43, y_max: 27, z: 1 }` (z=1 per D-093)
|
||||
- `tiles: [...]` — the 44×28 grid
|
||||
- `sightlines: { open: false, notes: "supervisor office window faces main corridor; LOS critical for investigation" }`
|
||||
- `social_site: "logistics-hub"` (links to the template)
|
||||
|
||||
**Sightline requirement:** The supervisor office window must be placed so that the player standing in the main corridor has LOS to the office interior. This is investigatively critical — it is how the player first suspects the supervisor. Do not place it behind a wall that would block shadowcasting.
|
||||
|
||||
### #583 — The Last Shift, maintenance corridors, gate corridor
|
||||
|
||||
Three YAML files. All blocked on #577 tile format; can author all three in parallel once format is confirmed.
|
||||
|
||||
**The Last Shift (`the-last-shift.yaml`)**
|
||||
- Source: #312 (done), Sprint 12
|
||||
- D-093: **28×22 visual tiles + 6m east extension** (= ~6 visual tiles). Total footprint: ~34×22.
|
||||
- Zone palette: `#6b4018` (warm dark brown — converted maintenance area), fog tint: `#200c04`
|
||||
- Key features: long bar counter (high visibility — staff can see all tables), corner booth (observation premium — sees bar + card table + entrance + back room), scattered tables, news ticker display (`F` tiles), back room with alley exit (`R` or `F` tiles — navigable, but the back exit is the key traversal route)
|
||||
- z=1 (main structure per D-093)
|
||||
|
||||
**Maintenance corridors (`maintenance-corridors.yaml`)**
|
||||
- Source: #313 (done), Sprint 12
|
||||
- D-093: Corridor width = 2 visual tiles (maintenance standard). z=0 (Era 1, below main structures).
|
||||
- Zone palette: `#4e5054` (dark utilitarian grey), fog tint: `#101214`
|
||||
- Key features: restricted storage (shared with Terminal — use `R` tiles), maintenance hatch (connects z=0 to z=1 — note: cross-z connection is a future movement feature; for now just tile the spaces), transition corridor 40m between hub and bar district (~40 visual tiles length at 1m/tile)
|
||||
- Design principle (D-093 G-08): the ring operates here; all spaces must read as mundane maintenance unless you know what to look for. No obviously suspicious tile arrangements.
|
||||
|
||||
**Gate corridor (`gate-corridor.yaml`) — new file**
|
||||
- Source: D-093 gate cluster zone spec
|
||||
- D-093: Gate cluster **40×32 visual tiles**, z=1 main + z=2 observation gallery
|
||||
- Zone palette: `#b8bec4` (neutral cool — institutional grey), fog tint: `#0a1222`
|
||||
- Seven zones (author these as continuous tile grid, walls between zones):
|
||||
- Aperture chamber: 8×4 (restricted — `R` tiles)
|
||||
- Freight staging: 24×8 (private — use `F` tiles, access enforced by NPCs not tile kind)
|
||||
- Passenger arrival: 12×8 (semi-public — `F`)
|
||||
- Freight customs: 20×10 (semi-private, 3–5 lanes of 2-visual-tile width separated by wall stubs)
|
||||
- Pedestrian customs: 12×10 (semi-public, 3 lanes of 1-visual-tile width)
|
||||
- Gate concourse: 40×8 (public — wide open, 8vt corridor width per D-093)
|
||||
- Observation gallery: z=2, 32×10 (Commission-only — `R` tiles; separate tile_bounds entry with z:2)
|
||||
- D-093 cross-z LOS: gallery rail = transparent low wall. Model this as a separate z=2 tile grid with `R` floor tiles (navigable but restricted access). The LOS cross-z behavior is handled by the server; just provide the tile data.
|
||||
- Add `gate-corridor` to `content/campaigns/main/systems/krenn/stations/sova/districts/transit/district.yaml` locations list.
|
||||
|
||||
**Cross-z note:** Author two sections in the gate-corridor YAML if the format supports multiple `tile_bounds` (one at z=1, one at z=2). If the format only supports a single z-level per Location, author two separate files: `gate-ground.yaml` and `gate-gallery.yaml`. Confirm with Dudley when the format is published.
|
||||
|
||||
---
|
||||
|
||||
## Dependency Chain
|
||||
|
||||
```
|
||||
#577 (server: tile format published, day 1-2)
|
||||
→ #582 (Terminal) — can start same day format is confirmed
|
||||
→ #583 (Last Shift + maintenance + gate) — parallel with #582
|
||||
```
|
||||
|
||||
Both tile authoring tickets can run in parallel once #577 publishes the format. Expect 2–3 days of authoring work per ticket given the complexity of the gate corridor.
|
||||
|
||||
---
|
||||
|
||||
## PR Workflow
|
||||
|
||||
```bash
|
||||
tea pr create --repo jpmschweitzer/settled-reach --login schweitz \
|
||||
--title "feat(visual): author tile maps for Sova Transit District" \
|
||||
--description "body" --base main --head visual
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Sprint Completion (Visual Criteria)
|
||||
|
||||
1. `the-terminal.yaml` has complete tile data — 44×28 grid, all zones represented, sightlines documented.
|
||||
2. `the-last-shift.yaml` has complete tile data — corner booth placement satisfies observation affordance.
|
||||
3. `maintenance-corridors.yaml` has complete tile data — z=0, maintenance aesthetic, restricted storage marked.
|
||||
4. `gate-corridor.yaml` (or equivalent) has complete tile data for all 7 zones including z=2 observation gallery.
|
||||
5. `district.yaml` locations list updated to include gate-corridor.
|
||||
6. Server can boot production startup and load tile data from all four locations without error.
|
||||
Reference in New Issue
Block a user