docs(sprints): add sprint 16 "Converse" briefings
Plan 8 tickets across server (4), client (2), copy (1), visual (1). Focus: two-way dialogue exchange, trust-gated gossip, sprite pipeline, and housekeeping fixes. No carry-overs from sprint 15. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
# Sprint 16: Converse — Client Tasks
|
||||
|
||||
**Goal:** Dialogue becomes a two-way exchange — players pick options and the server responds; NPC information-giving deepens through trust and trait-shaped delivery; the sprite pipeline gets its correct art-direction angle; and housekeeping fixes land across all three active teams.
|
||||
|
||||
**Branch:** `client`
|
||||
**Agents:** Stig (dev), Tyre (arch), Hoshe (QA)
|
||||
|
||||
## Carry-over from Sprint 15
|
||||
|
||||
None. Sprint 15 was 100% complete (16/16 done).
|
||||
|
||||
## New Tickets
|
||||
|
||||
| # | Title | Blocked by |
|
||||
|---|-------|------------|
|
||||
| #543 | Optimize zone_id extraction O(N) → O(1) dictionary lookup | — |
|
||||
| #540 | Adjust sprite camera to D-019 angle (~15-20° from vertical) | #541 (visual, must land first) |
|
||||
|
||||
Use `db/connectors/ticket show <id>` for full details.
|
||||
|
||||
## Key Decisions
|
||||
|
||||
- `decisions/perception.md` — D-019 (top-down camera, angle amendment: ~15-20° from vertical, "the angle"), D-043 (visual style, functional warmth), D-049 (z-level rendering stack)
|
||||
- `decisions/architecture.md` — D-066 (dual-scale grid: 0.5m sim, 1m visual, 2x retina), D-077 (zone temperature memory, server-tracked zone_id)
|
||||
|
||||
## Open Questions to Resolve Early
|
||||
|
||||
None blocking either ticket.
|
||||
|
||||
## Notes
|
||||
|
||||
### #543 — Optimize zone_id extraction O(N) → O(1) dictionary lookup
|
||||
|
||||
**What exists:** `client/scripts/autoloads/game_state.gd` `apply_snapshot()` function. The current zone_id extraction (lines 243-252) does a linear scan of `visible_tiles` on every snapshot to find the tile whose coordinates match the player position:
|
||||
|
||||
```gdscript
|
||||
current_zone_id = ""
|
||||
var _px := int(player_position.x)
|
||||
var _py := int(player_position.y)
|
||||
for _ztile in visible_tiles:
|
||||
if _ztile is Dictionary and _ztile.get("x") == _px and _ztile.get("y") == _py:
|
||||
current_zone_id = _ztile.get("zone_id", "")
|
||||
break
|
||||
```
|
||||
|
||||
Within the same `apply_snapshot()` call (lines 256-268), `visible_tiles` is already iterated to build `visible_positions` (a `Dictionary` of `Vector2i → true`). The optimization is to build a second dictionary `_tile_by_coord: Dictionary` (mapping `Vector2i → tile Dictionary`) in that same existing loop, then replace the O(N) zone_id scan with an O(1) lookup.
|
||||
|
||||
**What to deliver:**
|
||||
|
||||
1. Declare `_tile_by_coord` as a local variable in `apply_snapshot()` (or as a member var if AudioManager or others need tile data by coord)
|
||||
2. In the existing `visible_tiles` iteration loop (lines 256-268), add: `_tile_by_coord[pos] = vtile`
|
||||
3. Replace the O(N) zone_id scan (lines 243-252) with:
|
||||
```gdscript
|
||||
var player_pos_key := Vector2i(int(player_position.x), int(player_position.y))
|
||||
var player_tile = _tile_by_coord.get(player_pos_key, null)
|
||||
current_zone_id = player_tile.get("zone_id", "") if player_tile else ""
|
||||
```
|
||||
4. Remove the old loop block (lines 249-252)
|
||||
|
||||
**Gotcha:** The zone_id scan (lines 243-252) currently runs BEFORE the `visible_tiles` iteration that builds `visible_positions` (lines 256-268). You need to reorder: build `_tile_by_coord` in the main tile loop first, then do the zone_id lookup. The comment on line 244 ("O(1) via visible_positions dict would be ideal") confirms this was known and flagged by Tyre in a PR review — this ticket resolves it.
|
||||
|
||||
**Net-zero complexity:** The tile loop is already there. This adds one dict-set per tile in the existing loop, then removes the separate zone_id scan loop entirely. No behavioral change; the same `current_zone_id` value is produced.
|
||||
|
||||
**Test:** Verify `current_zone_id` is populated correctly after the refactor by checking the existing client P-tests or adding a unit test in gdUnit4.
|
||||
|
||||
### #540 — Adjust sprite camera to D-019 angle (~15-20° from vertical)
|
||||
|
||||
**Blocked by #541 (visual).** Do not start until Araminta's 3D sprite render pipeline is set up and has produced at least a test sprite at the new angle.
|
||||
|
||||
**What exists:** The Godot client renders entity, object, and wall sprites. Current sprites are rendered at 90° (pure top-down). D-019 amendment specifies "the angle": Camera3D at -72.5° from horizontal (= ~17.5° from vertical, the midpoint of the 15-20° range). This matches Rimworld's orthographic-with-art-tilt approach. The Godot gameplay camera stays orthographic — the tilt is entirely an art convention baked into sprites.
|
||||
|
||||
**What to deliver:**
|
||||
|
||||
Once #541 delivers the offline render pipeline and a set of test sprites at -72.5°:
|
||||
1. Update `client/scripts/rendering/entity_renderer.gd` to reference the new sprite assets (path may change per Araminta's pipeline output structure)
|
||||
2. Verify `client/scripts/rendering/tile_renderer.gd` and `world_renderer.gd` — wall/object sprites from #541 should slot in at the same visual tile size (64×64px runtime per D-043 resolution chain)
|
||||
3. Confirm the z-sorting logic in `entity_renderer.gd` still produces correct results: entities south-facing should overlap objects on the same row correctly with the tilted perspective
|
||||
4. Check that the fog shader in `fog_shader.gd` is unaffected — fog is screen-space and driven by the LOS mask, not by sprite perspective (D-019: "tile grid remains square/orthogonal, vision cone math remains pure 2D")
|
||||
|
||||
**Key constraint from D-066:** Entity sprites render across a 2×2 sim tile footprint. With the tilt, the south-facing face of entities/objects is now visible — confirm the sprite footprint is still contained within the 2×2 sim tile bounding box so interaction range (2 sim tiles) remains accurate.
|
||||
|
||||
**What NOT to change:** The `client/scripts/rendering/fog_shader.gd`, fog vision cone shape, or any LOS computation. Those are 2D sim-space and are unaffected by the art-direction angle.
|
||||
|
||||
## Dependency Chain
|
||||
|
||||
```
|
||||
#543 (zone_id O(1)) → standalone, no dependencies
|
||||
|
||||
#541 (visual: 3D render pipeline) → #540 (sprite camera angle)
|
||||
```
|
||||
|
||||
## PR Workflow
|
||||
|
||||
When ready to submit, create a PR with `tea` CLI. **All flags are required** to avoid TTY prompts (see CLAUDE.md "Gitea access" section):
|
||||
|
||||
```bash
|
||||
tea pr create --repo jpmschweitzer/settled-reach --login schweitz --title "feat(client): sprint 16 zone lookup + sprite angle" --description "body" --base main --head client
|
||||
```
|
||||
@@ -0,0 +1,90 @@
|
||||
# Sprint 16: Converse — Copy Tasks
|
||||
|
||||
**Goal:** Dialogue becomes a two-way exchange — players pick options and the server responds; NPC information-giving deepens through trust and trait-shaped delivery; the sprite pipeline gets its correct art-direction angle; and housekeeping fixes land across all three active teams.
|
||||
|
||||
**Branch:** `copy`
|
||||
**Agents:** Mellanie (author), Paula (narrative), Gestalt (systems)
|
||||
|
||||
## Carry-over from Sprint 15
|
||||
|
||||
None. Sprint 15 was 100% complete (16/16 done).
|
||||
|
||||
## New Tickets
|
||||
|
||||
| # | Title | Blocked by |
|
||||
|---|-------|------------|
|
||||
| #542 | Migrate line IDs from location-scoped to NPC-scoped namespace | — |
|
||||
|
||||
Use `db/connectors/ticket show 542` for full details.
|
||||
|
||||
## Key Decisions
|
||||
|
||||
- `decisions/content.md` — D-035 (tag taxonomy; Sprint 15 amendment: line ID namespace change from location-scoped to NPC-scoped), D-028 (dialogue architecture, line pool format)
|
||||
- `decisions/process.md` — D-022 (process workflow)
|
||||
|
||||
## Notes
|
||||
|
||||
### #542 — Migrate line IDs from location-scoped to NPC-scoped namespace
|
||||
|
||||
**What exists:** All dialogue and monologue YAML files under `content/campaigns/main/systems/krenn/stations/sova/districts/transit/`. There are approximately 20 dialogue pool files and a set of monologue pool files. The current line ID scheme uses the location as prefix:
|
||||
|
||||
- Old: `the-terminal_d_039` (all NPCs at The Terminal share one ID sequence)
|
||||
- New: `kael-davan_d_001`, `dock-worker_d_001` (each NPC has an independent sequence)
|
||||
|
||||
The D-035 Sprint 15 amendment confirmed this change. The collision that prompted it (`the-terminal_d_039` appeared in multiple NPC files) demonstrates the old scheme does not scale to procedurally generated populations.
|
||||
|
||||
**Dialogue pool files to update (location + NPC name):**
|
||||
- `dialogue/maintenance-corridors/kael-davan.yaml`
|
||||
- `dialogue/maintenance-corridors/ring-operative.yaml`
|
||||
- `dialogue/maintenance-corridors/transit-worker.yaml`
|
||||
- `dialogue/the-last-shift/bar-owner.yaml`
|
||||
- `dialogue/the-last-shift/bar-regular.yaml`
|
||||
- `dialogue/the-last-shift/bartender.yaml`
|
||||
- `dialogue/the-last-shift/day-worker.yaml`
|
||||
- `dialogue/the-last-shift/kael-davan.yaml`
|
||||
- `dialogue/the-last-shift/pc-detective.yaml`
|
||||
- `dialogue/the-last-shift/pc-smuggler.yaml`
|
||||
- `dialogue/the-last-shift/sera-venn.yaml`
|
||||
- `dialogue/the-terminal/courier.yaml`
|
||||
- `dialogue/the-terminal/dock-worker.yaml`
|
||||
- `dialogue/the-terminal/kael-davan.yaml`
|
||||
- `dialogue/the-terminal/maintenance-tech.yaml`
|
||||
- `dialogue/the-terminal/new-hire.yaml`
|
||||
- `dialogue/the-terminal/pc-detective.yaml`
|
||||
- `dialogue/the-terminal/pc-smuggler.yaml`
|
||||
- `dialogue/the-terminal/scheduler.yaml`
|
||||
- `dialogue/the-terminal/shift-supervisor.yaml`
|
||||
|
||||
**Monologue files:** Check all `monologue/detective/*.yaml` and `monologue/smuggler/*.yaml` (if present) for any `m_` prefixed line IDs that use the old location-slug convention.
|
||||
|
||||
**ID scheme rules:**
|
||||
|
||||
For each NPC file, assign a fresh sequence starting at `_001`:
|
||||
- Dialogue lines: `{npc-slug}_d_{###}` — e.g., `kael-davan_d_001`
|
||||
- Monologue lines: `{npc-slug}_m_s_{###}` (smuggler) or `{npc-slug}_m_d_{###}` (detective) per D-032
|
||||
|
||||
The npc-slug is the NPC's name in kebab-case: `kael-davan`, `dock-worker`, `shift-supervisor`, `pc-smuggler`, `pc-detective`, etc.
|
||||
|
||||
Each file's IDs restart at `_001` — do not carry a running counter across files. The 999-line ceiling is per-NPC, not per-location.
|
||||
|
||||
**Schema:** The `dialogue-pool.schema.json` regex pattern already accepts the new format (`^[a-z][a-z0-9-]*`). Only the `description` field in the schema needs updating to reference the new convention. No code changes required — line IDs are opaque strings to the server.
|
||||
|
||||
**Authoring docs:** Update `docs/workshops/content-gap-analysis_v0_1/` or any voice guide that references the old `{location_slug}` convention. Check `docs/briefings/mellanie.md` and related copy briefings for any hardcoded examples.
|
||||
|
||||
**Golden tests:** Run `make pre-pr` after all renames to confirm no golden test snapshots reference old IDs. The content cross-reference validation (CI ticket #464, done) will catch any mismatches between schema and file content.
|
||||
|
||||
**This is a mechanical rename with zero content changes.** Do not rewrite lines, adjust trust tiers, or add new content in this ticket. Pure ID substitution only.
|
||||
|
||||
## Dependency Chain
|
||||
|
||||
```
|
||||
#542 (line ID namespace migration) → standalone, no dependencies
|
||||
```
|
||||
|
||||
## PR Workflow
|
||||
|
||||
When ready to submit, create a PR with `tea` CLI. **All flags are required** to avoid TTY prompts (see CLAUDE.md "Gitea access" section):
|
||||
|
||||
```bash
|
||||
tea pr create --repo jpmschweitzer/settled-reach --login schweitz --title "feat(content): migrate line IDs to NPC-scoped namespace (D-035)" --description "body" --base main --head copy
|
||||
```
|
||||
@@ -0,0 +1,73 @@
|
||||
# Sprint 16: Converse — Joint Coordination
|
||||
|
||||
**Goal:** Dialogue becomes a two-way exchange — players pick options and the server responds; NPC information-giving deepens through trust and trait-shaped delivery; the sprite pipeline gets its correct art-direction angle; and housekeeping fixes land across all three active teams.
|
||||
|
||||
## Pre-Sprint
|
||||
|
||||
No blocking decisions or schema work required before implementation starts. All relevant decisions are confirmed (D-019, D-028, D-035, D-075, D-077). Sprint 16 can begin immediately.
|
||||
|
||||
| Item | Owner | Status |
|
||||
|------|-------|--------|
|
||||
| D-035 line ID scheme (NPC-scoped) | confirmed | done |
|
||||
| D-075 layered confidence gate on trust | confirmed | done |
|
||||
| D-019 camera angle amendment | confirmed | done |
|
||||
| D-077 zone_id on VisibleTile | confirmed, implemented | done |
|
||||
|
||||
## Ticket Overview
|
||||
|
||||
| # | Title | Team | Blocked by |
|
||||
|---|-------|------|------------|
|
||||
| #538 | Arch: Move dialogue systems BridgePlugin → NpcPlugin | server | — |
|
||||
| #539 | Server: implement DialogueResponse verb handler | server | #538 |
|
||||
| #171 | Layer 3: Trust-gated gossip | server | — |
|
||||
| #338 | Line variety tracker — prevent repeat dialogue | server | — |
|
||||
| #543 | Optimize zone_id extraction O(N) → O(1) | client | — |
|
||||
| #540 | Adjust sprite camera to D-019 angle | client | #541 |
|
||||
| #542 | Migrate line IDs to NPC-scoped namespace | copy | — |
|
||||
| #541 | 3D sprite render pipeline setup | visual | — |
|
||||
|
||||
## Cross-team Dependencies
|
||||
|
||||
**#541 (visual) → #540 (client):** Araminta must deliver at minimum a test entity sprite and a wall sprite at the -72.5° angle before Stig can validate the import path and z-sorting behavior. Early delivery of test sprites (even before the full pipeline doc is written) unblocks client work. Coordinate via the `visual` → `client` handoff — Araminta commits test sprites to `client/assets/sprites/` on the visual branch; Stig pulls and verifies fit in `entity_renderer.gd`.
|
||||
|
||||
**#538 (server) → #539 (server):** Sequential within the server team. #538 refactors registration; #539 adds a new system in the correct location. Dudley should complete and merge #538 before starting #539.
|
||||
|
||||
**#542 (copy) → server golden tests:** After #542 renames all line IDs, the server golden test fixtures in `server/tests/` may reference old line IDs (e.g., `the-terminal_d_039`). Run `make pre-pr` (which includes the content cross-reference validation and fixture staleness check) after #542 merges. If golden tests fail, the server team updates fixtures with `make golden-update`.
|
||||
|
||||
## Sprint Completion Proof
|
||||
|
||||
The sprint is complete when all of the following are observable:
|
||||
|
||||
1. **DialogueResponse closes the loop:** Player opens dialogue with any NPC, selects response option 1 or 2 (non-confrontation), and the NPC delivers a contextually appropriate follow-up line. No silent no-op on option selection. Observable in-game or via the test client replay.
|
||||
|
||||
2. **Trust-gated gossip unlocks progressively:** A player with `KnowsOf` confidence on a target NPC receives different (more forthcoming) dialogue lines than a player with `Suspects` confidence. Observable via the line previewer CLI or a gauntlet test room. The same NPC gives Surface-tier lines on first contact and Real-tier lines after investigation.
|
||||
|
||||
3. **No line repeats within 1 game-hour:** The player can initiate dialogue with the same NPC 5+ times in quick succession and observe that no dialogue line_id repeats. Observable via the WRONG button capture or server logs.
|
||||
|
||||
4. **Zone_id lookup is O(1):** `game_state.gd` `apply_snapshot()` no longer contains the linear tile scan for zone_id (lines 249-252 removed). `current_zone_id` populates correctly. Confirmed by code review + existing client tests passing.
|
||||
|
||||
5. **Sprite camera angle:** At least one entity sprite and one wall sprite are rendered at -72.5° from horizontal and display correctly in the Godot client without z-sorting artifacts. Observable in the rendering scene.
|
||||
|
||||
6. **Line ID migration complete:** `make pre-pr` passes with no content validation errors. No YAML file under `content/` contains line IDs with the old `{location_slug}_` prefix. Observable via CI.
|
||||
|
||||
## Test Plan Alignment (D-030)
|
||||
|
||||
Sprint 16 is in the Sprint 3-4 integration phase per D-030 test priorities.
|
||||
|
||||
- **#539 (DialogueResponse handler):** New system requires an integration test: player sends `DialogueResponse { response_id }`, assert `DialogueResponseBuffer` is populated with a follow-up line. Use existing test harness pattern from `dialogue.rs` game_loop tests.
|
||||
- **#171 (Trust-gated gossip):** Requires a unit test: initialize player KG with `KnowsDetails` on target, run `process_talk_interaction`, assert a `Secret`-tier line is selected. Mirror with `Suspects` confidence, assert only `Surface`-tier lines are selected.
|
||||
- **#338 (Line variety tracker):** Requires a regression test: send `Talk` 10 times to same NPC within 600 ticks, assert no line_id appears twice.
|
||||
- **#543 (O(1) zone lookup):** Code-change-only; existing client tests cover `apply_snapshot()` behavior. Add assertion that `current_zone_id` is non-empty when the player is on a zone-tagged tile.
|
||||
- **#542 (line ID rename):** Covered by `make pre-pr` content cross-reference validation (CI #464).
|
||||
|
||||
## PR Merge Order Recommendation
|
||||
|
||||
To minimize merge conflicts and test failures:
|
||||
|
||||
1. `copy` #542 — no code dependencies, merge first to unblock golden test validation
|
||||
2. `server` #538 — plugin refactor, isolated change
|
||||
3. `server` #171 and #338 — parallel, both isolated from #539
|
||||
4. `server` #539 — depends on #538 being merged
|
||||
5. `visual` #541 — can merge any time, gates #540
|
||||
6. `client` #543 — standalone, merge any time
|
||||
7. `client` #540 — merge after #541 lands
|
||||
@@ -0,0 +1,100 @@
|
||||
# Sprint 16: Converse — Server Tasks
|
||||
|
||||
**Goal:** Dialogue becomes a two-way exchange — players pick options and the server responds; NPC information-giving deepens through trust and trait-shaped delivery; the sprite pipeline gets its correct art-direction angle; and housekeeping fixes land across all three active teams.
|
||||
|
||||
**Branch:** `server`
|
||||
**Agents:** Dudley (simulation), Tyre (arch), Hoshe (QA)
|
||||
|
||||
## Carry-over from Sprint 15
|
||||
|
||||
None. Sprint 15 was 100% complete (16/16 done).
|
||||
|
||||
## New Tickets
|
||||
|
||||
| # | Title | Blocked by |
|
||||
|---|-------|------------|
|
||||
| #538 | Arch: Move dialogue systems from BridgePlugin to NpcPlugin | — |
|
||||
| #539 | Server: implement DialogueResponse verb handler | #538 |
|
||||
| #171 | Layer 3: Trust-gated gossip | — |
|
||||
| #338 | Line variety tracker — prevent repeat dialogue | — (unblocked: #305 done) |
|
||||
|
||||
Use `db/connectors/ticket show <id>` for full details.
|
||||
|
||||
## Key Decisions
|
||||
|
||||
- `decisions/content.md` — D-028 (dialogue architecture, four relational layers), D-035 (tag taxonomy, trust/access tiers), D-075 (layered confidence gate on trust tier)
|
||||
- `decisions/architecture.md` — D-041 (knowledge graph data model, KnowledgeConfidence hierarchy), D-010 (no baking player identity into game loop)
|
||||
|
||||
## Notes
|
||||
|
||||
### #538 — Arch: Move dialogue systems from BridgePlugin to NpcPlugin
|
||||
|
||||
**What exists:** `server/src/bridge/mod.rs` (lines 181-187) registers `process_talk_interaction`, `process_walk_away`, and `process_confrontation_response` inside `BridgePlugin::build()`. These systems depend on NPC-layer resources (`TrustEventQueue`, `KnowledgeGraph`, `KnowledgeEventQueue`) but live in the bridge module, which is supposed to own only wire protocol concerns.
|
||||
|
||||
**What to deliver:** Move the three `.add_systems()` calls from `BridgePlugin::build()` to `NpcPlugin::build()` in `server/src/npc/mod.rs`. The function implementations in `server/src/simulation/dialogue.rs` do not move — only their registration site changes. The scheduling constraints (`.after(process_player_input)`, `.before(compute_observer_snapshot)`) must be preserved exactly. `NpcPlugin` already has access to `TrustEventQueue` and other NPC resources (it `init_resource`s them), so no new resource registration is needed.
|
||||
|
||||
**Gotcha:** The `game_loop` integration tests in `dialogue.rs` that manually set up `TrustEventQueue` were the signal that this was misplaced. After the move, those tests should no longer need the manual init. Verify no test breaks.
|
||||
|
||||
**Integration point for #539:** This must merge before #539 starts, since #539 adds a new dialogue system that should register in `NpcPlugin` from the start.
|
||||
|
||||
### #539 — Server: implement DialogueResponse verb handler
|
||||
|
||||
**What exists:** `process_talk_interaction` in `server/src/simulation/dialogue.rs` handles the initial `Talk` verb. It selects a line via the 4-layer pipeline and writes to `DialogueResponseBuffer`. The client sends `DialogueResponse { response_id }` when the player picks an option, but no server system consumes it. `PlayerAction::DialogueResponse` is already defined in the bridge types.
|
||||
|
||||
**What to deliver:** A new system `process_dialogue_response` in `server/src/simulation/dialogue.rs` that:
|
||||
1. Reads `DialogueResponse` actions from the player input queue
|
||||
2. Looks up the NPC's `DialogueProfile` (location, role) and active `KnowledgeGraph` state
|
||||
3. Runs the same 4-layer filtering pipeline (`query_dialogue` on `LinePoolIndexResource`) to select a follow-up line based on the response_id context
|
||||
4. Writes the result to `DialogueResponseBuffer` for snapshot inclusion
|
||||
5. For conversations that have ended (no follow-up lines), clears `ActiveDialogue`
|
||||
|
||||
**Key design constraints from D-028 + D-062:** Dialogue options the player hasn't unlocked are invisible — so `response_id` will only arrive for options that were legitimately sent. No validation needed beyond confirming the response_id maps to a known dialogue context. The server does not need to validate "was this a valid choice?" — if the client sent it, it was shown.
|
||||
|
||||
**Integration with #171:** If trust-gated gossip (#171) is complete in the same sprint, this system should also route `Real`/`Secret` tier responses through the trust-gated pipeline. If #171 lands after, stub with `Surface`-only for now.
|
||||
|
||||
**Register in NpcPlugin** (after #538 lands).
|
||||
|
||||
### #171 — Layer 3: Trust-gated gossip
|
||||
|
||||
**What exists:** The trust tier gate is already fully implemented. `relationship_to_trust()` in `server/src/simulation/dialogue.rs` (lines 199-216) takes `RelationshipState` and `KnowledgeConfidence` and returns `TrustTier` per D-075. The `LinePoolIndexResource` query pipeline in `content/mod.rs` accepts a `TrustTier` and filters lines accordingly. The content YAML files already carry `trust: real` and `trust: secret` tags on authored lines.
|
||||
|
||||
**What to deliver:** Three disclosure tiers per NPC role (surface / real / secret). The engine is ready — the gap is that `process_talk_interaction` may not be passing the correct `KnowledgeConfidence` from the observer's `KnowledgeGraph` to `relationship_to_trust()`. Verify in `process_talk_interaction` that:
|
||||
1. The observer's `KnowledgeGraph` is queried for the NPC's `StableId`
|
||||
2. `confidence_of(&target_sid)` is passed as the second arg to `relationship_to_trust()`
|
||||
3. The resulting `TrustTier` filters lines in `query_dialogue`
|
||||
|
||||
If this path is already wired (check near line 345 in `dialogue.rs`), the ticket may be a content validation: confirm that authored lines with `trust: real` and `trust: secret` actually surface for a player with `KnowsOf+` / `KnowsDetails+` knowledge. Write a test that initializes a player KG with `KnowsDetails` on a target NPC, runs `process_talk_interaction`, and asserts a `Secret`-tier line was selected.
|
||||
|
||||
**Progressive revelation:** The same NPC gives different answers on first meeting (Surface only) vs. after investigation (Real/Secret unlocked). This is not a UI change — it is already handled by D-062's invisible locks. The engine must simply populate `DialogueResponseBuffer` with the tier-appropriate line.
|
||||
|
||||
### #338 — Line variety tracker — prevent repeat dialogue
|
||||
|
||||
**What exists:** `DialogueCooldownTracker` is already implemented as a `Component` in `server/src/simulation/dialogue.rs` (lines 78-105). It tracks `line_id → tick_used` in a `BTreeMap<String, u64>` with a 600-tick (1 game-hour) cooldown. `record()`, `is_on_cooldown()`, and `prune()` methods exist.
|
||||
|
||||
**What to deliver:** Wire the tracker into `process_talk_interaction`:
|
||||
1. Query `DialogueCooldownTracker` on the player entity (it may already be queried — check the system signature near line 345)
|
||||
2. After the 4-layer pipeline returns candidates, filter out lines where `tracker.is_on_cooldown(line_id, current_tick)` returns true
|
||||
3. Call `tracker.record(selected_line_id, current_tick)` after selection
|
||||
4. Call `tracker.prune(current_tick)` each time to prevent unbounded growth
|
||||
|
||||
If the tracker is already being queried but not used to filter, this is a 10-line change. If it is not queried at all, add it to the system signature and wire it in. Write a test that sends `Talk` repeatedly to the same NPC and asserts no line_id appears twice within `LINE_COOLDOWN_TICKS`.
|
||||
|
||||
## Dependency Chain
|
||||
|
||||
```
|
||||
#538 (BridgePlugin → NpcPlugin refactor)
|
||||
→ #539 (DialogueResponse verb handler)
|
||||
|
||||
#171 (Layer 3: Trust-gated gossip) → standalone, parallel track
|
||||
[may inform #539 if both land same sprint]
|
||||
|
||||
#338 (Line variety tracker wiring) → standalone, parallel track
|
||||
```
|
||||
|
||||
## PR Workflow
|
||||
|
||||
When ready to submit, create a PR with `tea` CLI. **All flags are required** to avoid TTY prompts (see CLAUDE.md "Gitea access" section):
|
||||
|
||||
```bash
|
||||
tea pr create --repo jpmschweitzer/settled-reach --login schweitz --title "feat(simulation): sprint 16 dialogue server" --description "body" --base main --head server
|
||||
```
|
||||
@@ -0,0 +1,69 @@
|
||||
# Sprint 16: Converse — Visual Tasks
|
||||
|
||||
**Goal:** Dialogue becomes a two-way exchange — players pick options and the server responds; NPC information-giving deepens through trust and trait-shaped delivery; the sprite pipeline gets its correct art-direction angle; and housekeeping fixes land across all three active teams.
|
||||
|
||||
**Branch:** `visual`
|
||||
**Agents:** Araminta (art direction)
|
||||
|
||||
## Carry-over from Sprint 15
|
||||
|
||||
None. Sprint 15 was 100% complete (16/16 done).
|
||||
|
||||
## New Tickets
|
||||
|
||||
| # | Title | Blocked by |
|
||||
|---|-------|------------|
|
||||
| #541 | 3D sprite render pipeline setup | — |
|
||||
|
||||
Use `db/connectors/ticket show 541` for full details.
|
||||
|
||||
## Key Decisions
|
||||
|
||||
- `decisions/perception.md` — D-019 (camera angle amendment: -72.5° from horizontal = ~17.5° from vertical, "the angle"; Camera3D in the offline 3D pipeline produces sprites at this angle), D-043 (visual style: functional warmth, clean 2D with bold silhouettes), D-044 (visual hierarchy: entity > object > structure, resolution chain 1024→256→64), D-049 (z-level rendering stack, 8 layers)
|
||||
- `decisions/architecture.md` — D-066 (dual-scale grid: 64×64px visual tiles on 1m grid; entity sprites: 24×32px footprint within 64×64px visual tile)
|
||||
|
||||
## Notes
|
||||
|
||||
### #541 — 3D sprite render pipeline setup
|
||||
|
||||
**What to deliver:** Establish the offline 3D sprite render pipeline used to produce all entity, object, and wall sprites for the game. This pipeline is a Blender (or equivalent) render setup, not a runtime system.
|
||||
|
||||
**Camera spec (D-019 amendment):**
|
||||
- Camera3D positioned at **-72.5° from horizontal** (= 17.5° from vertical, midpoint of 15-20° range)
|
||||
- Orthographic projection (not perspective) to match the Godot gameplay camera model
|
||||
- This is "the angle" — all sprites for v0.1 are rendered at this angle
|
||||
|
||||
**Output spec (D-043, D-044):**
|
||||
- Source resolution: 1024×1024px (highest fidelity, for outline processing)
|
||||
- Working resolution: 256×256px (outlines applied at 4-8px, dark blue-grey `#333340`)
|
||||
- Runtime resolution: 64×64px (bilinear interpolation from 256×256)
|
||||
- Entity sprites: 24×32px footprint within the 64×64px visual tile (D-044)
|
||||
- Sprites are shape templates — no baked shadows, no baked lighting, no baked mood (lighting is applied at runtime by Godot's PointLight2D pipeline)
|
||||
|
||||
**Lighting rig:**
|
||||
- Three-point studio rig (key, fill, rim) to produce clean silhouettes without baked directional shadows
|
||||
- Do NOT bake lighting direction into sprites — the runtime Godot lighting system (PointLight2D per fixture) provides all scene lighting
|
||||
- Keep surface colors flat and readable; the 3D render should produce crisp outlines and readable silhouettes, not mood-lit final frames
|
||||
|
||||
**Export process:**
|
||||
- Document the render settings (resolution, camera angle, lighting rig) so any team member can re-render consistent sprites
|
||||
- Export at least one entity test sprite (generic NPC silhouette) and one structural test sprite (wall segment) to validate the pipeline before full production
|
||||
- Store render source files in the `visual` branch; export the runtime 64×64 sprites to `client/assets/sprites/` (or the established asset path) for the client team
|
||||
|
||||
**Blocks #540 (client):** The client team cannot implement the camera angle adjustment (#540) until this pipeline produces correctly-angled sprites. Deliver at minimum a test entity sprite and one wall sprite so the client team can begin #540 integration work.
|
||||
|
||||
**Key constraint from D-066:** Entity sprites render across a 2×2 sim tile footprint. The 24×32px entity footprint within a 64×64px visual tile must be respected — test sprites should fit this bounding box so the client's z-sorting logic (y-based sort) remains accurate.
|
||||
|
||||
## Dependency Chain
|
||||
|
||||
```
|
||||
#541 (3D render pipeline setup) → #540 (client: sprite camera angle)
|
||||
```
|
||||
|
||||
## PR Workflow
|
||||
|
||||
When ready to submit, create a PR with `tea` CLI. **All flags are required** to avoid TTY prompts (see CLAUDE.md "Gitea access" section):
|
||||
|
||||
```bash
|
||||
tea pr create --repo jpmschweitzer/settled-reach --login schweitz --title "feat(assets): 3D sprite render pipeline setup (D-019 angle)" --description "body" --base main --head visual
|
||||
```
|
||||
Reference in New Issue
Block a user