diff --git a/docs/diagrams/architecture/audio-bus.d2 b/docs/diagrams/architecture/audio-bus.d2 new file mode 100644 index 000000000..bc655b1ed --- /dev/null +++ b/docs/diagrams/architecture/audio-bus.d2 @@ -0,0 +1,121 @@ +# Audio Architecture — D-068, D-073 +# 5-bus audio system with zone crossfade and AudioManager singleton. + +vars: { + color-bg: "#2a3040" + color-stroke: "#333340" + color-text: "#c8d0e0" + color-accent: "#c8d8f0" + color-dim: "#1a1e24" +} + +direction: down + +server_event: Rust Server\n(ObserverSnapshot) { + shape: hexagon + style.fill: ${color-bg} + style.stroke: ${color-accent} + style.font-color: ${color-text} + + zone_id: zone_id per tile { + style.fill: ${color-dim} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } + sound_events: sound events { + style.fill: ${color-dim} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } +} + +manager: AudioManager\n(GDScript autoload singleton) { + shape: hexagon + style.fill: ${color-bg} + style.stroke: ${color-accent} + style.font-color: ${color-text} + + registry: Directory-scan registry\n(res://audio/ on startup) { + style.fill: ${color-dim} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } + crossfade: Zone crossfade\n1.5-2s tween on zone_id change { + style.fill: ${color-dim} + style.stroke: ${color-accent} + style.font-color: ${color-text} + } +} + +buses: 5-Bus Mixer { + style.fill: ${color-dim} + style.stroke: ${color-accent} + style.font-color: ${color-text} + + music: Bus 1 — Music\n(reserved · empty v0.1) { + style.fill: ${color-bg} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + style.stroke-dash: 5 + } + ambient: Bus 2 — Ambient\n(station hum + zone overlays) { + style.fill: ${color-bg} + style.stroke: ${color-accent} + style.font-color: ${color-text} + } + world_sfx: Bus 3 — World SFX\n(NPC footsteps · doors · env events) { + style.fill: ${color-bg} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } + player_actions: Bus 4 — Player Actions\n(footsteps · future: combat · items) { + style.fill: ${color-bg} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } + ui_sounds: Bus 5 — UI Sounds\n(cursor hover · implant open · chimes) { + style.fill: ${color-bg} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } +} + +sliders: Player Volume Sliders\n(1 per bus · in Settings) { + shape: queue + style.fill: ${color-dim} + style.stroke: ${color-stroke} + style.font-color: ${color-text} +} + +dips: Audio Dip Profiles (D-069) { + style.fill: ${color-dim} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + + dialogue_dip: Dialogue\nAmbient -6 to -8dB · 300ms in { + style.fill: ${color-bg} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } + confrontation_dip: Confrontation\nAmbient -10 to -12dB + LPF · 500ms in { + style.fill: ${color-bg} + style.stroke: ${color-accent} + style.font-color: ${color-text} + } + listen_boost: ListeningFocus\nWorld SFX +2 to +3dB\n(stationary 30+ ticks) { + style.fill: ${color-bg} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } +} + +server_event.zone_id -> manager.crossfade: "zone change" { style.stroke: ${color-accent} } +server_event.sound_events -> manager: "typed events" { style.stroke: ${color-stroke} } +manager.registry -> manager.crossfade: "filename → AudioStream" { style.stroke: ${color-stroke} } +manager -> buses.ambient: "loop" { style.stroke: ${color-accent} } +manager -> buses.world_sfx: "event" { style.stroke: ${color-stroke} } +manager -> buses.player_actions: "event" { style.stroke: ${color-stroke} } +manager -> buses.ui_sounds: "event" { style.stroke: ${color-stroke} } +sliders -> buses: "proportional volume" { style.stroke: ${color-stroke} } +dips -> buses.ambient: "proportional dip" { style.stroke: ${color-stroke}; style.stroke-dash: 4 } +dips -> buses.world_sfx: "proportional dip" { style.stroke: ${color-stroke}; style.stroke-dash: 4 } diff --git a/docs/diagrams/architecture/audio-bus.png b/docs/diagrams/architecture/audio-bus.png new file mode 100644 index 000000000..e69c00baa Binary files /dev/null and b/docs/diagrams/architecture/audio-bus.png differ diff --git a/docs/diagrams/architecture/ipc-bridge.d2 b/docs/diagrams/architecture/ipc-bridge.d2 new file mode 100644 index 000000000..6e8453869 --- /dev/null +++ b/docs/diagrams/architecture/ipc-bridge.d2 @@ -0,0 +1,146 @@ +# IPC Bridge Architecture — D-020 +# Godot client + Rust simulation server via subprocess/IPC +# Single-player: subprocess + local socket. Multiplayer: TCP (same protocol). + +vars: { + color-bg: "#2a3040" + color-stroke: "#333340" + color-text: "#c8d0e0" + color-accent: "#c8d8f0" + color-dim: "#1a1e24" +} + +direction: right + +godot: Godot Client { + shape: hexagon + style.fill: ${color-bg} + style.stroke: ${color-accent} + style.font-color: ${color-text} + + renderer: Renderer { + style.fill: ${color-dim} + style.font-color: ${color-text} + style.stroke: ${color-stroke} + } + ui: UI + HUD { + style.fill: ${color-dim} + style.font-color: ${color-text} + style.stroke: ${color-stroke} + } + audio: AudioManager { + style.fill: ${color-dim} + style.font-color: ${color-text} + style.stroke: ${color-stroke} + } + input: Input Capture { + style.fill: ${color-dim} + style.font-color: ${color-text} + style.stroke: ${color-stroke} + } + bridge_client: SimBridge (LocalBridge / NetworkBridge) { + style.fill: ${color-dim} + style.font-color: ${color-text} + style.stroke: ${color-accent} + } +} + +transport: Transport Layer { + style.fill: ${color-dim} + style.stroke: ${color-accent} + style.font-color: ${color-text} + + sp: Single-player\nUnix socket / localhost TCP { + style.fill: ${color-bg} + style.font-color: ${color-text} + style.stroke: ${color-stroke} + style.stroke-dash: 4 + } + mp: Multiplayer\nRemote TCP { + style.fill: ${color-bg} + style.font-color: ${color-text} + style.stroke: ${color-stroke} + style.stroke-dash: 4 + } +} + +rust: Rust Simulation Server { + shape: hexagon + style.fill: ${color-bg} + style.stroke: ${color-accent} + style.font-color: ${color-text} + + bridge_server: SimBridge (recv end) { + style.fill: ${color-dim} + style.font-color: ${color-text} + style.stroke: ${color-accent} + } + ecs: bevy_ecs World { + style.fill: ${color-dim} + style.font-color: ${color-text} + style.stroke: ${color-stroke} + } + perception: Perception System { + style.fill: ${color-dim} + style.font-color: ${color-text} + style.stroke: ${color-stroke} + } + ai: AI + Storyteller { + style.fill: ${color-dim} + style.font-color: ${color-text} + style.stroke: ${color-stroke} + } + kg: Knowledge Graph { + style.fill: ${color-dim} + style.font-color: ${color-text} + style.stroke: ${color-stroke} + } +} + +msgpack_out: PlayerInput (MessagePack) { + shape: queue + style.fill: ${color-dim} + style.stroke: ${color-accent} + style.font-color: ${color-text} +} + +msgpack_in: ObserverSnapshot (MessagePack) { + shape: queue + style.fill: ${color-dim} + style.stroke: ${color-accent} + style.font-color: ${color-text} +} + +godot.input -> msgpack_out: "semantic actions\n(MoveNorth, Interact, UsePerceptionMode)" { + style.stroke: ${color-accent} +} +msgpack_out -> godot.bridge_client +godot.bridge_client -> transport.sp: "single-player" { + style.stroke: ${color-accent} + style.stroke-dash: 4 +} +godot.bridge_client -> transport.mp: "multiplayer" { + style.stroke: ${color-accent} + style.stroke-dash: 4 +} +transport.sp -> rust.bridge_server { + style.stroke: ${color-accent} + style.stroke-dash: 4 +} +transport.mp -> rust.bridge_server { + style.stroke: ${color-accent} + style.stroke-dash: 4 +} +rust.bridge_server -> rust.ecs: "dispatch input" { + style.stroke: ${color-stroke} +} +rust.ecs -> rust.perception +rust.ecs -> rust.ai +rust.ecs -> rust.kg +rust.ecs -> msgpack_in: "ObserverSnapshot\n(entities, fog, sound events,\nmonologue triggers, HUD widgets)" { + style.stroke: ${color-accent} +} +msgpack_in -> godot.bridge_client +godot.bridge_client -> godot.renderer +godot.bridge_client -> godot.ui +godot.bridge_client -> godot.audio diff --git a/docs/diagrams/architecture/ipc-bridge.png b/docs/diagrams/architecture/ipc-bridge.png new file mode 100644 index 000000000..a8887b1f0 Binary files /dev/null and b/docs/diagrams/architecture/ipc-bridge.png differ diff --git a/docs/diagrams/architecture/system-tiers.d2 b/docs/diagrams/architecture/system-tiers.d2 new file mode 100644 index 000000000..ba9286300 --- /dev/null +++ b/docs/diagrams/architecture/system-tiers.d2 @@ -0,0 +1,133 @@ +# Simulation Tiers + Multiplayer-Ready Principles — D-026, D-010, D-009 +# Four simulation tiers with LRU eviction. +# D-010: four non-negotiable architectural principles embedded as annotations. + +vars: { + color-bg: "#2a3040" + color-stroke: "#333340" + color-text: "#c8d0e0" + color-accent: "#c8d8f0" + color-dim: "#1a1e24" +} + +direction: down + +principles: Architectural Principles (D-010) { + style.fill: ${color-dim} + style.stroke: ${color-accent} + style.font-color: ${color-text} + style.border-radius: 4 + + p1: Client-Server Separation\neven in single-player { + style.fill: ${color-bg} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } + p2: Information Boundaries\nas first-class system { + style.fill: ${color-bg} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } + p3: No Baked Player Identity\n"characters, some player-controlled" { + style.fill: ${color-bg} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } + p4: Deterministic Simulation\ntimestamped input events { + style.fill: ${color-bg} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } +} + +tiers: Simulation Tiers (D-026) { + style.fill: ${color-dim} + style.stroke: ${color-accent} + style.font-color: ${color-text} + style.border-radius: 4 + + active: Tier 1 — Active { + style.fill: ${color-bg} + style.stroke: ${color-accent} + style.font-color: ${color-text} + style.bold: true + } + background: Tier 2 — Background { + style.fill: ${color-bg} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } + saved: Tier 3 — State-Saved { + style.fill: ${color-bg} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } + ungenerated: Tier 4 — Ungenerated { + style.fill: ${color-dim} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + style.stroke-dash: 5 + } +} + +active_detail: 30-80 NPCs\nFull sim · 10-20 ticks/sec\nScope tags keep NPCs here { + shape: page + style.fill: ${color-dim} + style.stroke: ${color-accent} + style.font-color: ${color-text} +} + +background_detail: 500-2,000 NPCs\nState machine · 1 tick/game-minute\n(schedule · mood · relationships · job) { + shape: page + style.fill: ${color-dim} + style.stroke: ${color-stroke} + style.font-color: ${color-text} +} + +saved_detail: 10,000+ NPCs\nFrozen serialized structs ~1-2KB\nReactivate on player return ~2-5ms { + shape: page + style.fill: ${color-dim} + style.stroke: ${color-stroke} + style.font-color: ${color-text} +} + +eviction: LRU Eviction\n(interaction-timestamp priority queue) { + shape: diamond + style.fill: ${color-bg} + style.stroke: ${color-accent} + style.font-color: ${color-text} +} + +tiers.active -> active_detail { + style.stroke: ${color-accent} +} +tiers.background -> background_detail { + style.stroke: ${color-stroke} +} +tiers.saved -> saved_detail { + style.stroke: ${color-stroke} +} + +tiers.active -> tiers.background: "evict (LRU)" { + style.stroke: ${color-accent} + style.stroke-dash: 4 +} +tiers.background -> tiers.active: "player proximity\nor scope tag" { + style.stroke: ${color-accent} +} +tiers.background -> tiers.saved: "evict (LRU)" { + style.stroke: ${color-stroke} + style.stroke-dash: 4 +} +tiers.saved -> tiers.background: "player return" { + style.stroke: ${color-stroke} +} +tiers.ungenerated -> tiers.saved: "player approach\n(generated ahead of arrival)" { + style.stroke: ${color-stroke} + style.stroke-dash: 4 +} + +eviction -> tiers { + style.stroke: ${color-accent} + style.stroke-dash: 4 +} diff --git a/docs/diagrams/architecture/system-tiers.png b/docs/diagrams/architecture/system-tiers.png new file mode 100644 index 000000000..74cfbf9fd Binary files /dev/null and b/docs/diagrams/architecture/system-tiers.png differ diff --git a/docs/diagrams/architecture/test-font-fixed.svg b/docs/diagrams/architecture/test-font-fixed.svg deleted file mode 100644 index d4f3018e2..000000000 --- a/docs/diagrams/architecture/test-font-fixed.svg +++ /dev/null @@ -1,97 +0,0 @@ -Godot ClientRust Server stdin IPCstdout IPC - - - - diff --git a/docs/diagrams/architecture/testability.d2 b/docs/diagrams/architecture/testability.d2 new file mode 100644 index 000000000..6d62dc81e --- /dev/null +++ b/docs/diagrams/architecture/testability.d2 @@ -0,0 +1,126 @@ +# Testability Architecture — D-030 +# Three-layer IPC testing, test runner tooling, and phase priorities. + +vars: { + color-bg: "#2a3040" + color-stroke: "#333340" + color-text: "#c8d0e0" + color-accent: "#c8d8f0" + color-dim: "#1a1e24" +} + +direction: down + +runners: Test Runners { + style.fill: ${color-dim} + style.stroke: ${color-accent} + style.font-color: ${color-text} + + rust_runner: cargo nextest\n(Rust) { + style.fill: ${color-bg} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } + godot_runner: gdUnit4\n(Godot) { + style.fill: ${color-bg} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } + wrapper: bash wrapper scripts\n(tests/ directory) { + style.fill: ${color-bg} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } +} + +output: Test Output { + shape: page + style.fill: ${color-dim} + style.stroke: ${color-stroke} + style.font-color: ${color-text} +} + +ipc_layers: IPC Test Layers { + style.fill: ${color-dim} + style.stroke: ${color-accent} + style.font-color: ${color-text} + + l1: Layer 1 — Fixture Serialization\nRoundtrip (fast · every edit) { + style.fill: ${color-bg} + style.stroke: ${color-accent} + style.font-color: ${color-text} + style.bold: true + } + l2: Layer 2 — Mock Subprocess Protocol\nState machine (medium · every PR) { + style.fill: ${color-bg} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } + l3: Layer 3 — Real Subprocess Integration\n(slow · daily / pre-merge) { + style.fill: ${color-bg} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + style.stroke-dash: 4 + } +} + +phases: Test Phases { + style.fill: ${color-dim} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + + ph1: Phase 1 (Sprint 1-2)\nInfra + collision / pathfinding / time { + style.fill: ${color-bg} + style.stroke: ${color-accent} + style.font-color: ${color-text} + } + ph2: Phase 2 (Sprint 3-4)\nMonologue pipeline + info boundary negatives { + style.fill: ${color-bg} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } + ph3: Phase 3 (Sprint 5+)\nCauseChain + divergent snapshots { + style.fill: ${color-bg} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + style.stroke-dash: 4 + } +} + +constraints: Production Code Constraints { + style.fill: ${color-dim} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + + c1: No cfg(test) in production code { + style.fill: ${color-bg} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } + c2: ECS World setup replaces mock injection { + style.fill: ${color-bg} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } + c3: CauseChain = production component\n(monologue provenance + debugging) { + style.fill: ${color-bg} + style.stroke: ${color-accent} + style.font-color: ${color-text} + } + c4: Deterministic replay (D-010 P4)\nSimulationTime · SimRng · InputQueue injectable { + style.fill: ${color-bg} + style.stroke: ${color-accent} + style.font-color: ${color-text} + } +} + +runners.rust_runner -> output: "JSON summary\n+ JUnit XML (CI)" { style.stroke: ${color-stroke} } +runners.godot_runner -> output: "JSON summary\n+ JUnit XML (CI)" { style.stroke: ${color-stroke} } +runners.wrapper -> runners.rust_runner +runners.wrapper -> runners.godot_runner + +ipc_layers.l1 -> ipc_layers.l2: "gates" { style.stroke: ${color-accent} } +ipc_layers.l2 -> ipc_layers.l3: "gates" { style.stroke: ${color-accent} } + +phases.ph1 -> phases.ph2: "unblocks" { style.stroke: ${color-stroke} } +phases.ph2 -> phases.ph3: "unblocks" { style.stroke: ${color-stroke} } diff --git a/docs/diagrams/architecture/testability.png b/docs/diagrams/architecture/testability.png new file mode 100644 index 000000000..055686c12 Binary files /dev/null and b/docs/diagrams/architecture/testability.png differ diff --git a/docs/diagrams/data-flow/dialogue-pipeline.d2 b/docs/diagrams/data-flow/dialogue-pipeline.d2 new file mode 100644 index 000000000..21af2ae3b --- /dev/null +++ b/docs/diagrams/data-flow/dialogue-pipeline.d2 @@ -0,0 +1,171 @@ +# Dialogue Pipeline — D-028, D-062, D-063, D-064, D-075 +# Four relational layers, two-phase verb computation, invisible locks, +# confrontation weight, walk-away consequences. + +vars: { + color-bg: "#2a3040" + color-stroke: "#333340" + color-text: "#c8d0e0" + color-accent: "#c8d8f0" + color-dim: "#1a1e24" +} + +direction: down + +player_action: Player Initiates Interaction { + shape: oval + style.fill: ${color-bg} + style.stroke: ${color-accent} + style.font-color: ${color-text} +} + +verb_phase1: Phase 1 — Simulation\n(no KnowledgeGraph read) { + style.fill: ${color-bg} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + style.border-radius: 4 + + object_type: ObjectType component\n→ max possible verb set { + style.fill: ${color-dim} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } +} + +verb_phase2: Phase 2 — Observer\n(reads KnowledgeGraph) { + style.fill: ${color-bg} + style.stroke: ${color-accent} + style.font-color: ${color-text} + style.border-radius: 4 + + kg_filter: Filter by KG confidence\n(Confront requires KnowsDetails+) { + style.fill: ${color-dim} + style.stroke: ${color-accent} + style.font-color: ${color-text} + } + poi_flip: POI priority flip\n+ contradiction markers { + style.fill: ${color-dim} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } + archetype_filter: Archetype filter rules\n(same crate: smuggler→Stash · detective→Flag) { + style.fill: ${color-dim} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } +} + +layer_filter: Four Relational Layers (D-028) { + style.fill: ${color-dim} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + + l1: Layer 1 — Access Tier\n(RelationshipState only) { + style.fill: ${color-bg} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } + l2: Layer 2 — Relationship History\n(interaction log: greeting + topic) { + style.fill: ${color-bg} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } + l3: Layer 3 — Trust-gated Gossip\n(RelationshipState AND KnowledgeConfidence — D-075) { + style.fill: ${color-bg} + style.stroke: ${color-accent} + style.font-color: ${color-text} + } + l4: Layer 4 — Unprompted Disclosure\n(weighted by trust + mood) { + style.fill: ${color-bg} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } +} + +invisible_locks: Invisible Locked Options (D-062) { + style.fill: ${color-bg} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + style.border-radius: 4 + + rule: Locked options = completely invisible\nNo grayed-out · no lock icons · no hints { + style.fill: ${color-dim} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } + reveal: New options appearing on repeat visits\n= the reward (dopamine) { + style.fill: ${color-dim} + style.stroke: ${color-accent} + style.font-color: ${color-text} + } +} + +confrontation: Confrontation Weight (D-063) { + style.fill: ${color-bg} + style.stroke: ${color-accent} + style.font-color: ${color-text} + style.border-radius: 4 + + voice: Written in character's internal voice\n(italicized first-person) { + style.fill: ${color-dim} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } + monologue_beat: Pre-delivery monologue beat\n1-2s: "No taking it back." { + style.fill: ${color-dim} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } + world_reacts: World reacts\n(NPC Tier 2 anim · D-033 fade · monologue spikes) { + style.fill: ${color-dim} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } +} + +walk_away: Walk-away (D-064)\nWASD during conversation { + shape: diamond + style.fill: ${color-bg} + style.stroke: ${color-accent} + style.font-color: ${color-text} +} + +walk_away_phases: Walk-away Consequences { + style.fill: ${color-dim} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + + immediate: 1 — Dialogue fades 300ms\n(no close button) { + style.fill: ${color-bg} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } + npc_reacts: 2 — NPC reacts\n(animation · may call after player) { + style.fill: ${color-bg} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } + kg_record: 3 — KG records incompleteness\n(per-seed tolerance: no universal safe-to-leave rules) { + style.fill: ${color-bg} + style.stroke: ${color-accent} + style.font-color: ${color-text} + } +} + +player_action -> verb_phase1 +verb_phase1 -> verb_phase2: "max verb set" { style.stroke: ${color-accent} } +verb_phase2 -> layer_filter: "filtered verb set" { style.stroke: ${color-accent} } +layer_filter.l1 -> layer_filter.l2 +layer_filter.l2 -> layer_filter.l3 +layer_filter.l3 -> layer_filter.l4 +layer_filter -> invisible_locks: "visible options only\n(max 3 shown)" { + style.stroke: ${color-accent} +} +invisible_locks -> confrontation: "if Confront option present" { + style.stroke: ${color-stroke} + style.stroke-dash: 4 +} +invisible_locks -> walk_away +walk_away -> walk_away_phases.immediate +walk_away_phases.immediate -> walk_away_phases.npc_reacts +walk_away_phases.npc_reacts -> walk_away_phases.kg_record diff --git a/docs/diagrams/data-flow/dialogue-pipeline.png b/docs/diagrams/data-flow/dialogue-pipeline.png new file mode 100644 index 000000000..e287b9279 Binary files /dev/null and b/docs/diagrams/data-flow/dialogue-pipeline.png differ diff --git a/docs/diagrams/data-flow/eavesdrop-pipeline.d2 b/docs/diagrams/data-flow/eavesdrop-pipeline.d2 new file mode 100644 index 000000000..b8f08b44b --- /dev/null +++ b/docs/diagrams/data-flow/eavesdrop-pipeline.d2 @@ -0,0 +1,162 @@ +# Eavesdrop Pipeline — D-071, D-072, D-078 +# Overheard NPC-to-NPC conversation: occlusion filter (server-authoritative), +# ListeningFocus boost, passive dialogue panel, dual-channel principle. + +vars: { + color-bg: "#2a3040" + color-stroke: "#333340" + color-text: "#c8d0e0" + color-accent: "#c8d8f0" + color-dim: "#1a1e24" +} + +direction: down + +npc_convo: NPC-to-NPC Conversation\n(authored at full fidelity by copy team) { + shape: oval + style.fill: ${color-bg} + style.stroke: ${color-accent} + style.font-color: ${color-text} +} + +murmur: Universal Murmur Asset (D-072)\nsingle asset plays everywhere { + shape: package + style.fill: ${color-bg} + style.stroke: ${color-stroke} + style.font-color: ${color-text} +} + +zone_ambient: Zone Ambient Level { + shape: diamond + style.fill: ${color-bg} + style.stroke: ${color-stroke} + style.font-color: ${color-text} +} + +conspicuousness: Conversation Conspicuousness { + style.fill: ${color-dim} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + + bar: Bar (high ambient)\nhidden in murmur, needs proximity { + style.fill: ${color-bg} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } + workplace: Workplace (moderate)\nsemi-conspicuous, distance-dependent { + style.fill: ${color-bg} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } + corridor: Corridor (low ambient)\nconspicuous, audible from distance { + style.fill: ${color-bg} + style.stroke: ${color-accent} + style.font-color: ${color-text} + } +} + +server_occlusion: Server-side Occlusion Filter (D-078)\nper-word probability roll { + style.fill: ${color-bg} + style.stroke: ${color-accent} + style.font-color: ${color-text} + style.border-radius: 4 + + factors: Factors: distance · ambient noise\n· ListeningFocus stance { + style.fill: ${color-dim} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } + stochastic: Stochastic per word\n(same scene can yield different fragments) { + style.fill: ${color-dim} + style.stroke: ${color-accent} + style.font-color: ${color-text} + } + seeded: Seedable RNG per tick\n→ deterministic replay (D-010) { + style.fill: ${color-dim} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } +} + +listen_focus: ListeningFocus Boost (D-071)\nWorld SFX +2 to +3dB\nstationary 30+ ticks { + style.fill: ${color-bg} + style.stroke: ${color-accent} + style.font-color: ${color-text} + style.border-radius: 4 +} + +no_ambient_dip: No Ambient Dip for Eavesdrop\n(opposite of direct conversation dip) { + style.fill: ${color-dim} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + style.stroke-dash: 4 +} + +occluded_text: Pre-occluded text\n(words → "..." per roll) { + shape: page + style.fill: ${color-dim} + style.stroke: ${color-stroke} + style.font-color: ${color-text} +} + +passive_panel: Passive Dialogue Panel (D-078)\n(D-061 box, read-only) { + style.fill: ${color-bg} + style.stroke: ${color-accent} + style.font-color: ${color-text} + style.border-radius: 4 + + speaker_label: Speaker format (e.g. Kael to Mira) { + style.fill: ${color-dim} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } + reduced_opacity: Reduced opacity (0.9)\nvs active dialogue { + style.fill: ${color-dim} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } + no_responses: No response options { + style.fill: ${color-dim} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } +} + +monologue_channel: Monologue Channel\n(D-016 — separate pipeline) { + style.fill: ${color-dim} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + + witness_trigger: witness_interaction trigger\n→ character's reaction { + style.fill: ${color-bg} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } + separate: Character reaction\n(thought · never quoted speech) { + style.fill: ${color-bg} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } +} + +kg_entry: KnowledgeGraph entry\n(KnowsOf confidence unless\nListeningFocus + proximity → KnowsDetails) { + shape: cylinder + style.fill: ${color-bg} + style.stroke: ${color-accent} + style.font-color: ${color-text} +} + +npc_convo -> murmur: "audio" { style.stroke: ${color-stroke} } +npc_convo -> zone_ambient: "text goes through" { style.stroke: ${color-accent} } +murmur -> zone_ambient: "conspicuousness shaped by" { style.stroke: ${color-stroke} } +zone_ambient -> conspicuousness +zone_ambient -> server_occlusion: "noise penalty" { style.stroke: ${color-stroke} } +listen_focus -> server_occlusion: "probability curve shift" { style.stroke: ${color-accent} } +no_ambient_dip -> listen_focus: "instead of dip" { style.stroke: ${color-stroke}; style.stroke-dash: 4 } +server_occlusion -> occluded_text: "pre-occluded text sent to client" { style.stroke: ${color-accent} } +occluded_text -> passive_panel: "client renders exactly what it receives" { style.stroke: ${color-accent} } +passive_panel -> monologue_channel: "triggers witness_interaction" { + style.stroke: ${color-stroke} + style.stroke-dash: 4 +} +passive_panel -> kg_entry: "overheard info enters KG" { style.stroke: ${color-accent} } diff --git a/docs/diagrams/data-flow/eavesdrop-pipeline.png b/docs/diagrams/data-flow/eavesdrop-pipeline.png new file mode 100644 index 000000000..cf622f00d Binary files /dev/null and b/docs/diagrams/data-flow/eavesdrop-pipeline.png differ diff --git a/docs/diagrams/data-flow/perception-pipeline.d2 b/docs/diagrams/data-flow/perception-pipeline.d2 new file mode 100644 index 000000000..7b55a8e84 --- /dev/null +++ b/docs/diagrams/data-flow/perception-pipeline.d2 @@ -0,0 +1,185 @@ +# Perception Pipeline — D-011, D-017, D-018, D-019, D-059, D-060, D-067 +# From simulation ECS through to rendered fog and monologue. +# Covers: fog of perception, LOS (D-035), perception modes, three-range sound, +# fog layers, cognitive delay, recognition chime. + +vars: { + color-bg: "#2a3040" + color-stroke: "#333340" + color-text: "#c8d0e0" + color-accent: "#c8d8f0" + color-dim: "#1a1e24" +} + +direction: down + +world: ECS World State { + shape: hexagon + style.fill: ${color-bg} + style.stroke: ${color-accent} + style.font-color: ${color-text} +} + +los: LOS System\n(Albert Ford symmetric shadowcasting — D-035) { + style.fill: ${color-bg} + style.stroke: ${color-accent} + style.font-color: ${color-text} + style.border-radius: 4 + + fov: compute_fov(is_opaque, origin, range, z)\n→ VisibilityMap { + style.fill: ${color-dim} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } + symmetry: Symmetry guarantee:\nif A sees B → B sees A { + style.fill: ${color-dim} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } +} + +perception_modes: Perception Modes (D-017) { + style.fill: ${color-dim} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + + natural: Natural Vision\n(walls block, cone model) { + style.fill: ${color-bg} + style.stroke: ${color-accent} + style.font-color: ${color-text} + } + thermal: Thermal\n(heat through walls, no identity) { + style.fill: ${color-bg} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + style.stroke-dash: 4 + } + camera: Camera Feeds\n(remote, spoofable) { + style.fill: ${color-bg} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + style.stroke-dash: 4 + } + unisphere: Unisphere Tracking\n(known inserts, maskable) { + style.fill: ${color-bg} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + style.stroke-dash: 4 + } + audio_analysis: Audio Analysis\n(signatures, no visual) { + style.fill: ${color-bg} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + style.stroke-dash: 4 + } +} + +sound: Sound System (D-018)\nThree-Range Model { + style.fill: ${color-dim} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + + close: Close (in/near LOS)\nHigh accuracy · stereo audio { + style.fill: ${color-bg} + style.stroke: ${color-accent} + style.font-color: ${color-text} + } + medium: Medium (outside LOS)\nVisual indicators at fog edge + monologue { + style.fill: ${color-bg} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } + long: Long (across map)\nInsert notifications · spoofable { + style.fill: ${color-bg} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + style.stroke-dash: 4 + } +} + +snapshot: ObserverSnapshot { + shape: queue + style.fill: ${color-dim} + style.stroke: ${color-accent} + style.font-color: ${color-text} +} + +kg_read: KnowledgeGraph read\n(per-character — what THIS observer knows) { + shape: cylinder + style.fill: ${color-bg} + style.stroke: ${color-accent} + style.font-color: ${color-text} +} + +fog: Fog Rendering (D-059)\n5-layer shader system { + style.fill: ${color-dim} + style.stroke: ${color-accent} + style.font-color: ${color-text} + + clear: Clear (vision cone)\nsoft gradient 6-8 sim tiles { + style.fill: ${color-bg} + style.stroke: ${color-accent} + style.font-color: ${color-text} + } + light_fog: Light Fog (peripheral)\ndesaturated 40-50%, Perlin noise { + style.fill: ${color-bg} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } + deep_fog: Deep Fog (explored)\nnear-monochrome + zone tint { + style.fill: ${color-bg} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } + unexplored_maps: Unexplored (has maps)\ngeometric wireframe outlines { + style.fill: ${color-bg} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } + unexplored_dark: Unexplored (no maps)\nsolid near-black { + style.fill: ${color-dim} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } +} + +recognition: Fog Entity Recognition { + style.fill: ${color-dim} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + + chime: Recognition chime fires (D-067)\nat ONSET of cognitive delay { + style.fill: ${color-bg} + style.stroke: ${color-accent} + style.font-color: ${color-text} + } + delay: Cognitive delay (D-060)\n0.6s base · 0.3s for anomaly { + style.fill: ${color-bg} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } + monologue_fire: Monologue fires DURING delay\n"Those footsteps... that's Kael's walk" { + style.fill: ${color-bg} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } + transition: Grey blob → D-033 color\n+ silhouette over 0.3s { + style.fill: ${color-bg} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } +} + +world -> los: "entity positions + opaque tiles" { style.stroke: ${color-accent} } +world -> sound: "sound events" { style.stroke: ${color-stroke} } +los -> perception_modes: "VisibilityMap" { style.stroke: ${color-accent} } +perception_modes -> snapshot: "observer query result\n(what this sensor reveals)" { + style.stroke: ${color-accent} +} +sound -> snapshot: "sound events" { style.stroke: ${color-stroke} } +kg_read -> snapshot: "per-entity recognition data" { style.stroke: ${color-accent} } +snapshot -> fog: "visible entities + fog state" { style.stroke: ${color-accent} } +snapshot -> recognition: "heard/sensed entity not in LOS" { style.stroke: ${color-stroke} } +recognition.chime -> recognition.delay: "concurrent" { style.stroke: ${color-accent} } +recognition.delay -> recognition.monologue_fire +recognition.delay -> recognition.transition diff --git a/docs/diagrams/data-flow/perception-pipeline.png b/docs/diagrams/data-flow/perception-pipeline.png new file mode 100644 index 000000000..3b8df192f Binary files /dev/null and b/docs/diagrams/data-flow/perception-pipeline.png differ diff --git a/docs/diagrams/entity/dialogue-tags.d2 b/docs/diagrams/entity/dialogue-tags.d2 new file mode 100644 index 000000000..451517766 --- /dev/null +++ b/docs/diagrams/entity/dialogue-tags.d2 @@ -0,0 +1,101 @@ +# Dialogue + Monologue Tag Taxonomy — D-035, D-028 +# Converged tag schema for tagged line pools. +# Structural tags (required), selection tags (recommended), monologue additions. +# Includes Sprint 8 + 14 + 15 amendments. + +vars: { + color-bg: "#2a3040" + color-stroke: "#333340" + color-text: "#c8d0e0" + color-accent: "#c8d8f0" + color-dim: "#1a1e24" +} + +dialogue_line: Dialogue Line { + shape: sql_table + id: "{npc-slug}_{d|m}_{###}" {constraint: primary_key} + text: String + role: enum (template-defined role) + access: list (public · insider · authority · peer · hostile) + trust: enum (surface · real · secret) + situation: list (14 situations) + topic: list (9 topics — optional) + mood: list (8 moods — optional) + tags: list (freeform — optional) +} + +monologue_line: Monologue Line { + shape: sql_table + id: "{npc-slug}_m_{s|d}_{###}" {constraint: primary_key} + character: enum (smuggler · detective) {constraint: not_null} + trigger: enum (9 triggers) {constraint: not_null} + prerequisite: map or null + dual_lens: map (authoring-only) + notes: String (authoring-only) +} + +access_values: AccessTier values { + shape: sql_table + public: Public — default, anyone + insider: Insider — social network member + authority: Authority — institutional role + peer: Peer — known colleague + hostile: Hostile — adversarial context +} + +trust_values: TrustTier values { + shape: sql_table + surface: Surface — any relationship + any confidence + real: Real — (Friendly|Known) + KnowsOf+ + secret: Secret — Friendly + KnowsDetails+ +} + +situation_values: Situation (14) { + shape: sql_table + s1: first_meeting + s2: regular_contact + s3: after_conflict + s4: work_context + s5: social_context + s6: private_context + s7: witnessed_something + s8: under_suspicion + s9: post_confrontation + s10: trust_broken + s11: urgent + s12: relaxed + s13: farewell + s14: greeting +} + +mood_values: Mood (8) { + shape: sql_table + m1: anxious + m2: frustrated + m3: content + m4: suspicious + m5: warm + m6: hostile + m7: relieved + m8: focused +} + +trigger_values: Monologue Triggers (9) { + shape: sql_table + t1: enter_location + t2: observe_npc + t3: hear_sound + t4: observe_anomaly + t5: post_conversation + t6: discover_evidence + t7: witness_interaction + t8: time_idle + t9: return_visit +} + +dialogue_line -> access_values: "access field" { style.stroke: ${color-accent} } +dialogue_line -> trust_values: "trust field" { style.stroke: ${color-accent} } +dialogue_line -> situation_values: "situation field" { style.stroke: ${color-stroke} } +dialogue_line -> mood_values: "mood field" { style.stroke: ${color-stroke} } +monologue_line -> dialogue_line: "extends" { style.stroke: ${color-accent} } +monologue_line -> trigger_values: "trigger field" { style.stroke: ${color-accent} } diff --git a/docs/diagrams/entity/dialogue-tags.png b/docs/diagrams/entity/dialogue-tags.png new file mode 100644 index 000000000..0ecde5204 Binary files /dev/null and b/docs/diagrams/entity/dialogue-tags.png differ diff --git a/docs/diagrams/entity/knowledge-graph.d2 b/docs/diagrams/entity/knowledge-graph.d2 new file mode 100644 index 000000000..baa84f5c9 --- /dev/null +++ b/docs/diagrams/entity/knowledge-graph.d2 @@ -0,0 +1,88 @@ +# Knowledge Graph Data Model — D-041 +# Per-entity bevy_ecs Component with BTreeMap storage. +# Confidence hierarchy, knowledge state, knowledge source, event-driven updates. + +vars: { + color-bg: "#2a3040" + color-stroke: "#333340" + color-text: "#c8d0e0" + color-accent: "#c8d8f0" + color-dim: "#1a1e24" +} + +KnowledgeGraph: KnowledgeGraph { + shape: sql_table + entity_knowledge: BTreeMap + fact_knowledge: BTreeMap +} + +EntityKnowledge: EntityKnowledge { + shape: sql_table + stable_id: StableId(u64) {constraint: foreign_key} + confidence: KnowledgeConfidence + state: KnowledgeState + source: KnowledgeSource + last_observed_tick: u64 +} + +FactKnowledge: FactKnowledge { + shape: sql_table + fact_id: FactId {constraint: primary_key} + confidence: KnowledgeConfidence + state: KnowledgeState + source: KnowledgeSource + last_observed_tick: u64 +} + +KnowledgeConfidence: KnowledgeConfidence { + shape: sql_table + direct: Direct — currently in LOS + knows_details: KnowsDetails — actionable (secret tier) + knows_of: KnowsOf — involved in Y (real tier) + suspects: Suspects — something off (surface tier) +} + +KnowledgeState: KnowledgeState { + shape: sql_table + active: Active — believed true + contradicted: Contradicted — conflict detected (Sprint 3+) + stale: Stale — aged beyond threshold (Sprint 3+) +} + +KnowledgeSource: KnowledgeSource { + shape: sql_table + direct_observation: DirectObservation + heard: Heard + told_by: ToldBy (Sprint 3+) + inferred: Inferred (Sprint 3+) + background: Background +} + +EntityRegistry: EntityRegistry { + shape: sql_table + stable_to_entity: BTreeMap + entity_to_stable: BTreeMap +} + +KnowledgeEventQueue: KnowledgeEventQueue\n(bevy_ecs Resource) { + shape: queue + style.fill: ${color-dim} + style.stroke: ${color-accent} + style.font-color: ${color-text} +} + +KnowledgeGraph -> EntityKnowledge: "contains many" +KnowledgeGraph -> FactKnowledge: "contains many" +EntityKnowledge -> KnowledgeConfidence: "uses" +EntityKnowledge -> KnowledgeState: "uses" +EntityKnowledge -> KnowledgeSource: "uses" +FactKnowledge -> KnowledgeConfidence: "uses" +FactKnowledge -> KnowledgeState: "uses" +FactKnowledge -> KnowledgeSource: "uses" +EntityKnowledge -> EntityRegistry: "StableId lookup" { + style.stroke: ${color-accent} + style.stroke-dash: 4 +} +KnowledgeEventQueue -> KnowledgeGraph: "drained each tick\n→ writes components" { + style.stroke: ${color-accent} +} diff --git a/docs/diagrams/entity/knowledge-graph.png b/docs/diagrams/entity/knowledge-graph.png new file mode 100644 index 000000000..2ed4c0789 Binary files /dev/null and b/docs/diagrams/entity/knowledge-graph.png differ diff --git a/docs/diagrams/entity/npc-generation.d2 b/docs/diagrams/entity/npc-generation.d2 new file mode 100644 index 000000000..ff62c21c5 --- /dev/null +++ b/docs/diagrams/entity/npc-generation.d2 @@ -0,0 +1,154 @@ +# NPC Generation Model — D-024, D-025 +# 10 axes + combat component. Social site / functional cluster as atomic template unit. + +vars: { + color-bg: "#2a3040" + color-stroke: "#333340" + color-text: "#c8d0e0" + color-accent: "#c8d8f0" + color-dim: "#1a1e24" +} + +direction: right + +npc: NPC (generated) { + shape: person + style.fill: ${color-bg} + style.stroke: ${color-accent} + style.font-color: ${color-text} +} + +essential: 7 Essential Axes { + style.fill: ${color-dim} + style.stroke: ${color-accent} + style.font-color: ${color-text} + + want: Want { + style.fill: ${color-bg} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } + secret: Secret / Vulnerability { + style.fill: ${color-bg} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } + relationships: Relationships (1-3) { + style.fill: ${color-bg} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } + tolerance: Tolerance Threshold { + style.fill: ${color-bg} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } + routine: Daily Routine { + style.fill: ${color-bg} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } + info_inv: Information Inventory { + style.fill: ${color-bg} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } + contentment: Contentment { + style.fill: ${color-bg} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } +} + +supporting: 3 Supporting Axes { + style.fill: ${color-dim} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + + personality: Personality Traits (2-3) { + style.fill: ${color-bg} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } + tell: Tell System { + style.fill: ${color-bg} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } + skills: Skill Set { + style.fill: ${color-bg} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } +} + +combat_comp: CombatCapability\n(optional ECS component) { + shape: package + style.fill: ${color-bg} + style.stroke: ${color-accent} + style.font-color: ${color-text} + style.stroke-dash: 4 +} + +key_interactions: Key Contradiction Interactions { + style.fill: ${color-dim} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + + i1: Want × Secret { + style.fill: ${color-bg} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } + i2: Routine × Secret { + style.fill: ${color-bg} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } + i3: Tolerance × Relationships { + style.fill: ${color-bg} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } + i4: Want × Relationships { + style.fill: ${color-bg} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } + i5: Personality × Tolerance { + style.fill: ${color-bg} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } +} + +social_site: Social Site\n(atomic Tier 2 template unit) { + style.fill: ${color-dim} + style.stroke: ${color-accent} + style.font-color: ${color-text} + + group: Social Group (4-8 NPCs)\n2+ triangles + 1 cross-template triangle { + style.fill: ${color-bg} + style.stroke: ${color-accent} + style.font-color: ${color-text} + } + cluster: Functional Cluster\n15-40 tiles, internal sightlines { + style.fill: ${color-bg} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } + ownership: NPC Ownership\nowned by 1 template, referenced by others { + style.fill: ${color-bg} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } +} + +npc -> essential: "generated with" { style.stroke: ${color-accent} } +npc -> supporting: "generated with" { style.stroke: ${color-stroke} } +supporting.skills -> combat_comp: "combat tag → component" { + style.stroke: ${color-accent} + style.stroke-dash: 4 +} +essential -> key_interactions: "produces" { style.stroke: ${color-stroke} } +npc -> social_site: "placed in" { style.stroke: ${color-accent} } diff --git a/docs/diagrams/entity/npc-generation.png b/docs/diagrams/entity/npc-generation.png new file mode 100644 index 000000000..27a7f786c Binary files /dev/null and b/docs/diagrams/entity/npc-generation.png differ diff --git a/docs/diagrams/state/knowledge-confidence.d2 b/docs/diagrams/state/knowledge-confidence.d2 new file mode 100644 index 000000000..26281433c --- /dev/null +++ b/docs/diagrams/state/knowledge-confidence.d2 @@ -0,0 +1,152 @@ +# Knowledge Confidence State Machine — D-041 +# 4-level confidence hierarchy with state tracking and decay. +# Maps to dialogue trust tiers (D-075) and fog recognition (D-059). + +vars: { + color-bg: "#2a3040" + color-stroke: "#333340" + color-text: "#c8d0e0" + color-accent: "#c8d8f0" + color-dim: "#1a1e24" +} + +none: No Knowledge\n(entity unknown) { + shape: oval + style.fill: ${color-dim} + style.stroke: ${color-stroke} + style.font-color: ${color-text} +} + +suspects: Suspects\n"something off" { + style.fill: ${color-bg} + style.stroke: ${color-stroke} + style.font-color: ${color-text} +} + +knows_of: KnowsOf\n"X is involved in Y" { + style.fill: ${color-bg} + style.stroke: ${color-stroke} + style.font-color: ${color-text} +} + +knows_details: KnowsDetails\n"actionable detail" { + style.fill: ${color-bg} + style.stroke: ${color-accent} + style.font-color: ${color-text} +} + +direct: Direct\n"currently in LOS" { + style.fill: ${color-bg} + style.stroke: ${color-accent} + style.font-color: ${color-text} + style.bold: true +} + +dialogue_gates: Dialogue Trust Gate (D-075) { + style.fill: ${color-dim} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + + surface: Surface tier\n(any confidence) { + style.fill: ${color-bg} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } + real: Real tier\n(KnowsOf+ AND Friendly/Known) { + style.fill: ${color-bg} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } + secret: Secret tier\n(KnowsDetails+ AND Friendly) { + style.fill: ${color-bg} + style.stroke: ${color-accent} + style.font-color: ${color-text} + } +} + +fog_gate: Fog Recognition (D-059) { + style.fill: ${color-dim} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + + recognized: Recognized entity\n(D-033 color + silhouette) { + style.fill: ${color-bg} + style.stroke: ${color-accent} + style.font-color: ${color-text} + } + unrecognized: Grey blob\n(no identity) { + style.fill: ${color-bg} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } +} + +knowledge_states: KnowledgeState { + shape: package + style.fill: ${color-dim} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + + active: Active (Sprint 2) { + style.fill: ${color-bg} + style.stroke: ${color-accent} + style.font-color: ${color-text} + } + contradicted: Contradicted (Sprint 3+)\nToldBy conflicts DirectObservation { + style.fill: ${color-bg} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + style.stroke-dash: 4 + } + stale: Stale (Sprint 3+)\naged past DecayThreshold { + style.fill: ${color-bg} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + style.stroke-dash: 4 + } +} + +none -> suspects: "observe anomaly\nor receive tip" { + style.stroke: ${color-stroke} +} +suspects -> knows_of: "further observation\nor testimony" { + style.stroke: ${color-stroke} +} +knows_of -> knows_details: "direct evidence\nor confrontation" { + style.stroke: ${color-accent} +} +knows_details -> direct: "enter LOS" { + style.stroke: ${color-accent} +} +direct -> knows_details: "exit LOS" { + style.stroke: ${color-stroke} + style.stroke-dash: 4 +} + +# Decay path (D-041: once per game-minute) +knows_details -> knows_of: "decay\n(last_observed_tick age)" { + style.stroke: ${color-stroke} + style.stroke-dash: 4 +} +knows_of -> suspects: "decay" { + style.stroke: ${color-stroke} + style.stroke-dash: 4 +} + +suspects -> dialogue_gates.surface +knows_of -> dialogue_gates.real +knows_details -> dialogue_gates.secret + +suspects -> fog_gate.unrecognized +knows_of -> fog_gate.recognized +knows_details -> fog_gate.recognized + +knows_details -> knowledge_states.contradicted: "ToldBy vs DirectObservation\n(Sprint 3+)" { + style.stroke: ${color-stroke} + style.stroke-dash: 4 +} +knows_of -> knowledge_states.stale: "decay (Sprint 3+)" { + style.stroke: ${color-stroke} + style.stroke-dash: 4 +} +none -> knowledge_states.active diff --git a/docs/diagrams/state/knowledge-confidence.png b/docs/diagrams/state/knowledge-confidence.png new file mode 100644 index 000000000..2a39389f5 Binary files /dev/null and b/docs/diagrams/state/knowledge-confidence.png differ diff --git a/docs/diagrams/state/movement-stances.d2 b/docs/diagrams/state/movement-stances.d2 new file mode 100644 index 000000000..41883dd4b --- /dev/null +++ b/docs/diagrams/state/movement-stances.d2 @@ -0,0 +1,111 @@ +# Movement Stance State Machine — D-053, D-054, D-055 +# Toggle ladder: Sprint / Walk / Careful / Crouch / (Prone future). +# Server-authoritative tile-based movement. Client Tween interpolation. + +vars: { + color-bg: "#2a3040" + color-stroke: "#333340" + color-text: "#c8d0e0" + color-accent: "#c8d8f0" + color-dim: "#1a1e24" +} + +sprint: Sprint { + style.fill: ${color-bg} + style.stroke: ${color-accent} + style.font-color: ${color-text} + style.bold: true +} + +sprint_detail: 1 tile/tick · loud footsteps\nMonologue 40% (urgent only)\nInteraction buffer CLEARED (D-055)\nSprint double-take: anomaly monologue fires retroactively { + shape: page + style.fill: ${color-dim} + style.stroke: ${color-accent} + style.font-color: ${color-text} +} + +walk: Walk\n(default stance) { + style.fill: ${color-bg} + style.stroke: ${color-stroke} + style.font-color: ${color-text} +} + +walk_detail: 1 tile/2 ticks · normal footsteps\nMonologue 100%\nDefault — MovementProfile component { + shape: page + style.fill: ${color-dim} + style.stroke: ${color-stroke} + style.font-color: ${color-text} +} + +careful: Careful { + style.fill: ${color-bg} + style.stroke: ${color-accent} + style.font-color: ${color-text} +} + +careful_detail: 1 tile/3 ticks · quiet footsteps\nMonologue 150% + tell notice bonus\nListeningFocus accumulates (stationary_ticks)\nEavesdrop bonus: ListeningFocus +2-3dB (D-071) { + shape: page + style.fill: ${color-dim} + style.stroke: ${color-accent} + style.font-color: ${color-text} +} + +crouch: Crouch { + style.fill: ${color-bg} + style.stroke: ${color-stroke} + style.font-color: ${color-text} +} + +crouch_detail: 1 tile/4 ticks · very quiet\nUses Prone/Seated occupancy layer (D-054) { + shape: page + style.fill: ${color-dim} + style.stroke: ${color-stroke} + style.font-color: ${color-text} +} + +prone: Prone\n(future) { + style.fill: ${color-dim} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + style.stroke-dash: 5 +} + +tile_occupancy: TilePresence (D-054)\nSame-tile occupancy layers { + shape: package + style.fill: ${color-dim} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + + standing: Standing { + style.fill: ${color-bg} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } + prone_layer: Prone / Seated { + style.fill: ${color-bg} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } + fixture_layer: Fixture { + style.fill: ${color-bg} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } +} + +sprint -> sprint_detail +walk -> walk_detail +careful -> careful_detail +crouch -> crouch_detail + +sprint -> walk: "toggle" { style.stroke: ${color-accent} } +walk -> sprint: "toggle" { style.stroke: ${color-accent} } +walk -> careful: "toggle" { style.stroke: ${color-stroke} } +careful -> walk: "toggle" { style.stroke: ${color-stroke} } +careful -> crouch: "toggle" { style.stroke: ${color-stroke} } +crouch -> careful: "toggle" { style.stroke: ${color-stroke} } +crouch -> prone: "toggle (future)" { style.stroke: ${color-stroke}; style.stroke-dash: 4 } +prone -> crouch: "toggle-out only" { style.stroke: ${color-stroke}; style.stroke-dash: 4 } + +crouch -> tile_occupancy.prone_layer: "occupies" { style.stroke: ${color-stroke} } +walk -> tile_occupancy.standing: "occupies" { style.stroke: ${color-stroke} } diff --git a/docs/diagrams/state/movement-stances.png b/docs/diagrams/state/movement-stances.png new file mode 100644 index 000000000..1c1280900 Binary files /dev/null and b/docs/diagrams/state/movement-stances.png differ diff --git a/docs/diagrams/state/npc-simulation-tiers.d2 b/docs/diagrams/state/npc-simulation-tiers.d2 new file mode 100644 index 000000000..9e4010174 --- /dev/null +++ b/docs/diagrams/state/npc-simulation-tiers.d2 @@ -0,0 +1,96 @@ +# NPC Simulation Tier State Machine — D-026 +# bevy_ecs dynamic component add/remove makes tier transitions seamless. +# Scope tags keep NPCs in Active tier. + +vars: { + color-bg: "#2a3040" + color-stroke: "#333340" + color-text: "#c8d0e0" + color-accent: "#c8d8f0" + color-dim: "#1a1e24" +} + +ungenerated: Tier 4\nUngenerated { + style.fill: ${color-dim} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + style.stroke-dash: 5 +} + +state_saved: Tier 3\nState-Saved\n~1-2KB frozen struct { + style.fill: ${color-bg} + style.stroke: ${color-stroke} + style.font-color: ${color-text} +} + +background: Tier 2\nBackground\n500-2,000 NPCs { + style.fill: ${color-bg} + style.stroke: ${color-stroke} + style.font-color: ${color-text} +} + +active: Tier 1\nActive\n30-80 NPCs · full sim { + style.fill: ${color-bg} + style.stroke: ${color-accent} + style.font-color: ${color-text} + style.bold: true +} + +scope_tags: Scope Tags (prevent eviction) { + shape: package + style.fill: ${color-dim} + style.stroke: ${color-accent} + style.font-color: ${color-text} + + neighborhood: neighborhood { + style.fill: ${color-bg} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } + active_quest: active-quest { + style.fill: ${color-bg} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } + colleague: colleague { + style.fill: ${color-bg} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } + known_contact: known-contact { + style.fill: ${color-bg} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } +} + +lru: LRU Priority Queue\n(interaction-timestamp) { + shape: diamond + style.fill: ${color-bg} + style.stroke: ${color-accent} + style.font-color: ${color-text} +} + +ungenerated -> state_saved: "player approach\n(generated ahead)" { + style.stroke: ${color-stroke} +} +state_saved -> background: "player return\n(~2-5ms reactivation)" { + style.stroke: ${color-stroke} +} +background -> state_saved: "LRU eviction" { + style.stroke: ${color-stroke} + style.stroke-dash: 4 +} +background -> active: "player proximity\nor scope tag match" { + style.stroke: ${color-accent} +} +active -> background: "LRU eviction\n(no scope tag)" { + style.stroke: ${color-stroke} + style.stroke-dash: 4 +} +scope_tags -> active: "prevents eviction" { + style.stroke: ${color-accent} + style.stroke-dash: 4 +} +lru -> active: "manages" { style.stroke: ${color-accent} } +lru -> background: "manages" { style.stroke: ${color-stroke} } diff --git a/docs/diagrams/state/npc-simulation-tiers.png b/docs/diagrams/state/npc-simulation-tiers.png new file mode 100644 index 000000000..403e849b0 Binary files /dev/null and b/docs/diagrams/state/npc-simulation-tiers.png differ diff --git a/docs/diagrams/ui/fog-layers.d2 b/docs/diagrams/ui/fog-layers.d2 new file mode 100644 index 000000000..64eb08d46 --- /dev/null +++ b/docs/diagrams/ui/fog-layers.d2 @@ -0,0 +1,108 @@ +# Fog System — Five Layers + Entity Rendering in Fog — D-059 +# Shader-based, knowledge-graph-driven. Covers all five fog states +# and the entity types rendered within fogged areas. + +vars: { + color-screen: "#1a1e24" + color-panel: "#2a3040" + color-text: "#c8d0e0" + color-nav: "#c8d8f0" + color-stroke: "#333340" +} + +direction: right + +kg_read: KnowledgeGraph\n(per character — D-041) { + shape: cylinder + style.fill: ${color-panel} + style.stroke: ${color-nav} + style.font-color: ${color-text} +} + +fog_layers: Five Fog Layers { + style.fill: ${color-screen} + style.stroke: ${color-nav} + style.font-color: ${color-text} + + l1: Layer 1 — Clear (vision cone)\nSoft gradient edge 6-8 sim tiles\n(= 3-4 visual tiles per D-066)\nNo hard line · Darkwood approach { + style.fill: ${color-panel} + style.stroke: ${color-nav} + style.font-color: ${color-text} + style.bold: true + } + l2: Layer 2 — Light Fog (peripheral)\nDesaturated 40-50% · brightness -30%\nAnimated Perlin noise (8-10s cycle)\nD-033 entity colors visible but reduced { + style.fill: ${color-panel} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } + l3: Layer 3 — Deep Fog (explored)\nNear-monochrome + ~10% zone tint\n(bar=warm dark · hub=cool dark)\nMore pronounced noise (15-20s cycle)\nzone_id from ObserverSnapshot (D-077) { + style.fill: ${color-panel} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } + l4: Layer 4 — Unexplored (has maps)\nGeometric wireframe outlines\nInsert data aesthetic { + style.fill: ${color-panel} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } + l5: Layer 5 — Unexplored (no maps)\nSolid near-black\nInformation zero { + style.fill: ${color-screen} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } +} + +fog_entities: Entities in Fog { + style.fill: ${color-screen} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + + sound_ping: Sound Ping\n2-3 concentric expanding rings\n(sonar-style · insert white-blue)\nLoud=3 rings bright · quiet=1 ring faint\nFade over 1.5s { + style.fill: ${color-panel} + style.stroke: ${color-nav} + style.font-color: ${color-text} + } + recognized: Recognized Entity\nD-033 color glow + silhouette feature\n(e.g. identifying clothing detail)\n0.8s breathing pulse\n+/-0.5 tile position drift { + style.fill: ${color-panel} + style.stroke: ${color-nav} + style.font-color: ${color-text} + } + unrecognized: Unrecognized Entity\nneutral grey blob\nno silhouette · no identity { + style.fill: ${color-panel} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } +} + +divergence: "Same fog, different information per character\nSmuggler: recognizes dock workers = green icons\nDetective: same entities = grey blobs\n\nFog is not darkness —\nit is the absence of your attention." { + shape: page + style.fill: ${color-screen} + style.stroke: ${color-nav} + style.font-color: ${color-text} + style.italic: true +} + +perf: Performance Budget\n<1ms/frame total\nVision cone = PointLight2D\nFog = CanvasGroup noise shader (Layer 5)\nSound pings = 0-5 sprites\nEntity ghosts = 0-10 sprites typical { + shape: package + style.fill: ${color-screen} + style.stroke: ${color-stroke} + style.font-color: ${color-text} +} + +kg_read -> fog_layers: "determines entity recognition\n(which observer sees which entities)" { + style.stroke: ${color-nav} +} +kg_read -> fog_entities.recognized: "KnowsOf+ → recognized" { + style.stroke: ${color-nav} +} +kg_read -> fog_entities.unrecognized: "Suspects / none → blob" { + style.stroke: ${color-stroke} +} +fog_layers -> fog_entities: "entities rendered within fog state" { + style.stroke: ${color-stroke} +} +fog_layers -> divergence: "same shader, per-character KG" { + style.stroke: ${color-nav} + style.stroke-dash: 4 +} +fog_layers -> perf diff --git a/docs/diagrams/ui/fog-layers.png b/docs/diagrams/ui/fog-layers.png new file mode 100644 index 000000000..f1e2a3aa3 Binary files /dev/null and b/docs/diagrams/ui/fog-layers.png differ diff --git a/docs/diagrams/ui/hud-layout.d2 b/docs/diagrams/ui/hud-layout.d2 new file mode 100644 index 000000000..f53d1ba86 --- /dev/null +++ b/docs/diagrams/ui/hud-layout.d2 @@ -0,0 +1,146 @@ +# HUD Layout and Z-Stack — D-049, D-048, D-061, D-076 +# 8-layer z-stack, insert overlay, dialogue box constraints. +# Dual-scale grid context: 64x64px visual tiles (D-066). + +vars: { + color-screen: "#1a1e24" + color-panel: "#2a3040" + color-text: "#c8d0e0" + color-nav: "#c8d8f0" + color-stroke: "#333340" +} + +direction: down + +zstack: Rendering Z-Stack (D-049)\n8 layers — bottom to top { + style.fill: ${color-screen} + style.stroke: ${color-nav} + style.font-color: ${color-text} + + l0: Layer 0 — Floor Tiles\n(zone identity · movement surface) { + style.fill: ${color-panel} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } + l1: Layer 1 — Floor Objects\n(cosmetic · walked over) { + style.fill: ${color-panel} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } + l2: Layer 2 — Furniture / Placed Objects\n(y-sorted with entities) { + style.fill: ${color-panel} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } + l3: Layer 3 — Entity Sprites\n(y-sorted · D-033 relationship color\n24x32px footprint in 64x64 tile) { + style.fill: ${color-panel} + style.stroke: ${color-nav} + style.font-color: ${color-text} + } + l4: Layer 4 — Overhead / Wall Tops\n(pipes · ducts · fixtures · signage\nsemi-transparent partial occlusion) { + style.fill: ${color-panel} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } + l5: Layer 5 — Fog of Perception\n(vision cone mask · fog shader\naffects layers 0-4) { + style.fill: ${color-panel} + style.stroke: ${color-nav} + style.font-color: ${color-text} + } + l6: Layer 6 — Insert Overlay\n(lattice HUD · bloom-rendered\nNOT affected by fog\nInteraction labels render here) { + style.fill: ${color-panel} + style.stroke: ${color-nav} + style.font-color: ${color-text} + } + l7: Layer 7 — Monologue / UI\n(always visible · top of stack\nDialogue box and response options) { + style.fill: ${color-panel} + style.stroke: ${color-nav} + style.font-color: ${color-text} + style.bold: true + } +} + +insert_overlay: Insert Overlay Design (D-048) { + style.fill: ${color-screen} + style.stroke: ${color-nav} + style.font-color: ${color-text} + + bloom: Soft bloom shader\n~2-3px gaussian · ~40% blend { + style.fill: ${color-panel} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } + smuggler_overlay: Smuggler\nthinner · sparser (baseline lattice) { + style.fill: ${color-panel} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } + detective_overlay: Detective\ndenser · crisper (augmented lattice) { + style.fill: ${color-panel} + style.stroke: ${color-nav} + style.font-color: ${color-text} + } + insert_off: Insert OFF behavior (OQ-07)\nCursor shape changes\nInteraction labels hidden { + style.fill: ${color-panel} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + style.stroke-dash: 4 + } +} + +dialogue_box: Dialogue Box (D-061, D-076) { + style.fill: ${color-screen} + style.stroke: ${color-nav} + style.font-color: ${color-text} + + position: Bottom of screen\nmax 20% height { + style.fill: ${color-panel} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } + max_width: Max-width 1200px\n(centered · ~62% of 1920px) { + style.fill: ${color-panel} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } + log: Unified conversation log\n(active + overheard — chronological) { + style.fill: ${color-panel} + style.stroke: ${color-nav} + style.font-color: ${color-text} + } + speaker_format: Speaker to Target format\nper-character name colours { + style.fill: ${color-panel} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } + responses: Response options below log\nmax 3 visible { + style.fill: ${color-panel} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } + no_portraits: No portraits\n(NPC is on screen) { + style.fill: ${color-panel} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } + timeout: Entry timeout\n15s + 3s fade (configurable) { + style.fill: ${color-panel} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } +} + +monologue_position: Monologue floats ABOVE dialogue box\n(z-layer 7 — spatial separation\ncharacter thinks while NPC speaks) { + style.fill: ${color-panel} + style.stroke: ${color-nav} + style.font-color: ${color-text} + style.border-radius: 4 +} + +zstack.l5 -> insert_overlay: "does NOT affect" { + style.stroke: ${color-nav} + style.stroke-dash: 4 +} +zstack.l6 -> insert_overlay: "rendered at" { style.stroke: ${color-nav} } +zstack.l7 -> dialogue_box: "rendered at" { style.stroke: ${color-nav} } +dialogue_box -> monologue_position: "monologue above" { style.stroke: ${color-nav} } diff --git a/docs/diagrams/ui/hud-layout.png b/docs/diagrams/ui/hud-layout.png new file mode 100644 index 000000000..e247a9221 Binary files /dev/null and b/docs/diagrams/ui/hud-layout.png differ diff --git a/docs/diagrams/ui/interaction-flow.d2 b/docs/diagrams/ui/interaction-flow.d2 new file mode 100644 index 000000000..67840c3d6 --- /dev/null +++ b/docs/diagrams/ui/interaction-flow.d2 @@ -0,0 +1,153 @@ +# Interaction UI Flow — D-056, D-057, D-058 +# Cursor states, entity interaction list, world radial menu. +# All insert-styled: geometric, bloom, diegetic. + +vars: { + color-screen: "#1a1e24" + color-panel: "#2a3040" + color-text: "#c8d0e0" + color-nav: "#c8d8f0" + color-stroke: "#333340" +} + +direction: down + +cursor: Cursor States (D-056)\n(insert's own interface element · z-layer 7) { + style.fill: ${color-screen} + style.stroke: ${color-nav} + style.font-color: ${color-text} + + default_cursor: Default\n4 thin inward ticks + bloom\nWhite-blue · barely visible { + style.fill: ${color-panel} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } + entity_hover: Entity Hover\nTicks expand outward (150ms)\nCorner brackets · D-033 relationship color\nVerb tooltip in insert styling { + style.fill: ${color-panel} + style.stroke: ${color-nav} + style.font-color: ${color-text} + } + object_hover: Object Hover\nTicks rotate 45° → X-shape\nMuted grey (amber if flagged) { + style.fill: ${color-panel} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } + weapon_aim: Weapon Aim\nTicks extend · center gap widens\nLines 1 to 2px · warm white\nNO bloom · D-033 tint on target { + style.fill: ${color-panel} + style.stroke: ${color-nav} + style.font-color: ${color-text} + } +} + +cursor_rules: Cursor Rules { + style.fill: ${color-screen} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + + los_trigger: Changes on LOS · not just proximity { + style.fill: ${color-panel} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } + range: Interaction range ~2 sim tiles (= 1m) { + style.fill: ${color-panel} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } + weapon_suppress: Weapon mode suppresses interaction prompts\n(unless Shift held) { + style.fill: ${color-panel} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } + insert_off: Insert OFF: cursor shape still changes\n(body reacts) · verb labels hidden\n(insert doesn't process targets) { + style.fill: ${color-panel} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + style.stroke-dash: 4 + } +} + +entity_list: Entity Interaction List (D-057)\n(left-click on entity) { + style.fill: ${color-screen} + style.stroke: ${color-nav} + style.font-color: ${color-text} + + format: Compact vertical list\nanchored to entity position\n2-4 options max { + style.fill: ${color-panel} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } + new_glow: New options: gradient glow background\n(knowledge change signal) { + style.fill: ${color-panel} + style.stroke: ${color-nav} + style.font-color: ${color-text} + } + z_layer: Labels on z-layer 6\n(insert data — hidden if insert off) { + style.fill: ${color-panel} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } + phase1: Phase 1 (simulation): max verb set\nfrom ObjectType component { + style.fill: ${color-panel} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } + phase2: Phase 2 (observer): filter by KG\narchetype variation · POI flip { + style.fill: ${color-panel} + style.stroke: ${color-nav} + style.font-color: ${color-text} + } +} + +world_menu: World Radial Menu (D-058)\n(right-click) { + style.fill: ${color-screen} + style.stroke: ${color-nav} + style.font-color: ${color-text} + + v01_spokes: v0.1: 2 spokes\nObserve (eye) · Insert (phone) { + style.fill: ${color-panel} + style.stroke: ${color-nav} + style.font-color: ${color-text} + style.bold: true + } + future_spokes: Future: up to 6 spokes\n+ Comms (signal) · Wait (clock) { + style.fill: ${color-panel} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + style.stroke-dash: 4 + } + input: Drag-release (power users)\nClick-click (newcomers) { + style.fill: ${color-panel} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } + why_radial: Fixed categories → spatial memory\n(unlike entity verbs which vary by knowledge) { + style.fill: ${color-panel} + style.stroke: ${color-stroke} + style.font-color: ${color-text} + } +} + +cursor.default_cursor -> cursor.entity_hover: "entity in LOS\n+ proximity" { + style.stroke: ${color-nav} +} +cursor.default_cursor -> cursor.object_hover: "object in LOS\n+ proximity" { + style.stroke: ${color-stroke} +} +cursor.entity_hover -> cursor.weapon_aim: "weapon selected" { + style.stroke: ${color-nav} +} +cursor.weapon_aim -> cursor.entity_hover: "weapon deselected" { + style.stroke: ${color-stroke} + style.stroke-dash: 4 +} + +cursor.entity_hover -> entity_list: "left-click\n→ shows interaction list" { + style.stroke: ${color-nav} +} +cursor.default_cursor -> world_menu: "right-click\n→ opens radial" { + style.stroke: ${color-nav} +} + +cursor -> cursor_rules +entity_list.phase1 -> entity_list.phase2: "filtered" { style.stroke: ${color-nav} } diff --git a/docs/diagrams/ui/interaction-flow.png b/docs/diagrams/ui/interaction-flow.png new file mode 100644 index 000000000..13e13a622 Binary files /dev/null and b/docs/diagrams/ui/interaction-flow.png differ