Merge remote-tracking branch 'origin/main' into client

# Conflicts:
#	CHANGELOG.md
#	client/scripts/autoloads/sim_bridge.gd
This commit is contained in:
2026-02-12 23:45:48 +01:00
74 changed files with 6572 additions and 990 deletions
+55
View File
@@ -0,0 +1,55 @@
---
name: render-sprite
description: >
Render a 3D model to 2D sprites via the Godot render pipeline. Produces
sprites at 3 resolutions (1024, 256, 64) from 4 cardinal directions (north,
east, south, west) with outline applied at working resolution. Use when the
user says "render sprite", "render model", "run the render pipeline",
"test the pipeline", "/render-sprite", or asks to render a specific model
(e.g., "render wall_structural"). Output: 12 PNG files in renderer/output/.
---
## Render Pipeline
Run the render script with the model name:
```bash
.claude/skills/render-sprite/scripts/render.sh <model_name>
```
### Available Models
Models live at `renderer/models/<name>.tscn`. List them:
```bash
ls renderer/models/*.tscn | xargs -I{} basename {} .tscn
```
### Output
12 files per model in `renderer/output/`:
```
<model>_north_1024.png <model>_north_256.png <model>_north_64.png
<model>_east_1024.png <model>_east_256.png <model>_east_64.png
<model>_south_1024.png <model>_south_256.png <model>_south_64.png
<model>_west_1024.png <model>_west_256.png <model>_west_64.png
```
### After Rendering
1. Read the 64x64 output files to visually inspect the runtime sprites
2. Read the 256x256 files to check outline quality
3. Report: does it read as the intended object at runtime scale?
### Troubleshooting
- **No output files**: Godot needs a display. If running headless, prefix with `xvfb-run`.
- **Model not found**: Check the model .tscn exists in `renderer/models/`.
- **Godot not found**: Pass path as second arg: `render.sh wall_structural /path/to/godot`
### Adding New Models
1. Create model scene at `renderer/models/<name>.tscn`
2. Apply texture from `renderer/textures/` via StandardMaterial3D
3. Run: `.claude/skills/render-sprite/scripts/render.sh <name>`
+47
View File
@@ -0,0 +1,47 @@
#!/bin/bash
# Render a 3D model to 2D sprites at 3 resolutions from 4 cardinal directions.
# Usage: render.sh <model_name> [godot_path]
# Example: render.sh wall_structural
set -euo pipefail
MODEL_NAME="${1:?Usage: render.sh <model_name> [godot_path]}"
GODOT="${2:-$HOME/bin/godot}"
REPO_ROOT="$(cd "$(dirname "$0")" && git rev-parse --show-toplevel)"
PROJECT="$REPO_ROOT/renderer"
SCENE="res://render_scene.tscn"
OUTPUT="$PROJECT/output"
# Verify model exists
MODEL_FILE="$PROJECT/models/${MODEL_NAME}.tscn"
if [ ! -f "$MODEL_FILE" ]; then
echo "ERROR: Model not found: $MODEL_FILE"
echo "Available models:"
ls "$PROJECT/models/"*.tscn 2>/dev/null | xargs -I{} basename {} .tscn
exit 1
fi
echo "Rendering: $MODEL_NAME"
echo "Godot: $GODOT"
echo "Project: $PROJECT"
# Ensure resources are imported (headless, no display needed)
echo "Importing resources..."
"$GODOT" --path "$PROJECT" --headless --import 2>&1 || echo "Warning: import step had errors (may be non-fatal)"
# Run Godot with the render scene, passing model name as user arg
"$GODOT" --path "$PROJECT" "$SCENE" -- "$MODEL_NAME" 2>&1
# Check output
EXPECTED_FILES=0
for dir in north east south west; do
for res in 1024 256 64; do
f="$OUTPUT/${MODEL_NAME}_${dir}_${res}.png"
if [ -f "$f" ]; then
EXPECTED_FILES=$((EXPECTED_FILES + 1))
fi
done
done
echo ""
echo "Generated $EXPECTED_FILES/12 files in $OUTPUT/"
ls -la "$OUTPUT/${MODEL_NAME}"_*.png 2>/dev/null || echo "No output files found."
+7 -2
View File
@@ -2,11 +2,16 @@
.cache/
server/target/
# Godot
# Godot client (further ignores managed by client team)
client/.godot/
client/export/
client/reports/
client/*.import
# Renderer (separate Godot project for sprite pipeline)
renderer/.godot/
renderer/**/*.import
renderer/**/*.uid
renderer/output/*.png
# Database (shared across worktrees at ../settledreach.db, not tracked)
db/commonwealth.db*
+22
View File
@@ -8,6 +8,26 @@ Format based on [Keep a Changelog](https://keepachangelog.com/).
### Added
- Interaction prompt system (#405) — server-driven "E - Talk" prompt decoding v4 nearby_interactions with nested VerbOption structs, fade animation, extensible get_interaction_target/get_selected_verb interface for future radial verb menu
- Art direction & mood board workshop (3 rounds + closing) — 4-agent team establishes visual identity, 16 art direction principles, 9 mood board images, 10 candidate decisions (D-042D-051)
- 3D-to-2D sprite render pipeline (`client/tooling/sprite_renderer/`) — Godot @tool scene renders textured 3D models at "the angle" (-72.5deg ortho) from 4 cardinal directions at 1024/256/64 resolutions with outline applied at working resolution
- `/render-sprite` skill — CLI wrapper for the render pipeline with headless import step
- Pipeline POC: Era 1 institutional wall + bar green wall textures generated via Nano Banana and rendered through full pipeline
- PerceptionQuery trait and ActivePerceptionMode resource — abstraction layer for D-017 perception mode swapping (NaturalVision default implementation)
- VisibilityGeometry intermediate resource decoupling FOV computation from entity filtering
- Client-side PROTOCOL_VERSION enforcement — snapshot decoder rejects version mismatches with error log
- POI verb priority test in observer pipeline — asserts both verb kind and priority values end-to-end
### Changed
- Observer pipeline decomposed into two-stage system: compute_visibility_geometry (geometry) → compute_observer_snapshot (entity filtering + assembly)
- POI verb priority adjustment moved from simulation phase (interaction.rs) to perception phase (observer) — fixes D-010 information boundary violation
- compute_nearby_interactions no longer reads KnowledgeGraph — determines verb availability by proximity only, verb priority adjusted by observer
- compute_nearby_interactions scheduling moved from SimulationPlugin to BridgePlugin for explicit ordering with geometry and observer systems
- Client test snapshot updated to v4 format (Protocol.PROTOCOL_VERSION, tick_rate replaces paused)
### Added
- Content validation tooling — `make validate-content` validates campaign YAML files against JSON schemas, maps files by directory context
- Entity::to_bits() roundtrip test — guards against bevy version changes silently breaking wire IDs
- TickRate switch mid-accumulation test — verifies Half→Full→Paused→Half transitions preserve accumulator state
- Tick rate scaling system (#406, D-052) — Full/Half/Paused rates with fractional accumulation, SetTickRate player action, replaces binary pause flag
- Proximity detection and interaction verbs (#404, D-060) — compute_nearby_interactions system with Manhattan distance ranges (close ≤2, mid ≤5), context-sensitive verb computation (Talk, Observe, Examine), PersonOfInterest priority flip, NearbyInteraction in ObserverSnapshot v4
- Content directory skeleton (#385, D-057) — district-as-atomic-pack layout with Sova Transit first district, 17 NPC stubs, 3 locations, 5 triangles, dialogue/monologue pools, factions, knowledge catalogs, enum definitions
@@ -21,6 +41,8 @@ Format based on [Keep a Changelog](https://keepachangelog.com/).
- Unregistered entities in observer/interaction now log tracing::error instead of silently falling back to Entity::to_bits()
### Changed
- NearbyInteractionBuffer refactored from global Resource to per-entity Component on PlayerCharacter — multiplayer-ready (D-009)
- Observer module split into mod.rs (244 lines) + tests.rs (480 lines) — reduces module complexity
- Content directory restructured from flat districts/ to hierarchical campaigns/main/systems/krenn/stations/sova/districts/transit/ — path mirrors canonical IDs, glob-based discovery, multi-campaign/DLC ready
- Content manifest (content.yaml) rewritten for glob-based district discovery
- District identity fields (system, station, district) now derived from directory path — removed from district.yaml required fields
+7 -1
View File
@@ -2,7 +2,7 @@ GODOT := $(shell command -v godot4 2>/dev/null || command -v godot 2>/dev/null)
.PHONY: help setup build client server test lint ci ci-client ci-server clean \
decisions-sync decisions-coverage decisions-active decisions-orphan \
db-backup db-install
db-backup db-install validate-content
# --- Configuration ---
@@ -30,6 +30,7 @@ help:
@echo " make decisions-coverage Decision-to-ticket coverage by domain"
@echo " make decisions-active List active decisions"
@echo " make decisions-orphan Decisions without implementing tickets"
@echo " make validate-content Validate content YAML against schemas"
@echo ""
@echo " GODOT_VERSION=4.6 make setup Override Godot version"
@@ -127,6 +128,11 @@ decisions-active:
decisions-orphan:
@db/connectors/sqlite-query "SELECT id, title FROM decisions WHERE type='confirmed' AND status='active' AND id NOT IN (SELECT DISTINCT decision_ref FROM tickets WHERE decision_ref IS NOT NULL)"
# --- Content Validation ---
validate-content:
@tooling/validate-content
# --- Clean ---
clean:
+1 -1
View File
@@ -309,7 +309,7 @@ func _test_snapshot() -> Dictionary:
return {
"tick": _test_tick,
"version": 4,
"version": Protocol.PROTOCOL_VERSION,
"game_time": {
"day": 0,
"time_of_day": _test_tick * 10,
+11 -2
View File
@@ -9,6 +9,10 @@ class_name Protocol
## Unit enum variants (no data) → bare strings ("MoveNorth", "Npc")
## Data enum variants → single-element maps ({"UsePerceptionMode": "thermal"})
## Protocol version — must match server PROTOCOL_VERSION in bridge/types.rs.
## Reject snapshots where version != this value.
const PROTOCOL_VERSION: int = 4
# -- Decode: bytes from server → GDScript types --------------------------------
@@ -27,6 +31,12 @@ static func decode_snapshot(bytes: PackedByteArray) -> Variant:
push_error("Protocol: snapshot missing required fields")
return null
# Version check: reject snapshots from incompatible server
var version: Variant = raw.get("version")
if version != PROTOCOL_VERSION:
push_error("Protocol: version mismatch (got %s, expected %s). Server and client are out of sync." % [version, PROTOCOL_VERSION])
return null
var entities: Array[Dictionary] = []
var raw_entities: Array = raw["entities"]
var dropped := 0
@@ -44,8 +54,7 @@ static func decode_snapshot(bytes: PackedByteArray) -> Variant:
# in any realistic scenario (would require ~29 billion years at 10 ticks/game-minute per D-031).
var tick: int = raw["tick"]
# v2 fields — optional for backward compatibility
var version: Variant = raw.get("version")
# version already checked above; game_time for HUD display
var game_time: Variant = raw.get("game_time")
# player_facing: FacingDirection is a unit enum → bare string in rmp_serde
@@ -0,0 +1,29 @@
# Closing Statement: Araminta (Art Direction & Mood Board)
**Date:** 2026-02-12
---
## 1. Does the workshop capture my domain?
Yes. The visual design surfaces are covered: palette, lighting, entity system, fog rendering, insert overlay, z-stack, tile composition, camera angle, animation tiers, weather rendering. The v0.1.1 sprite set gives me a concrete first deliverable.
One thing to carry forward that isn't a decision but a production principle: **sprites are shape templates that the lighting system completes.** Flat-lit assets, engine-driven atmosphere. This must be in the Nano Banana style guide as rule #1 — no baked shadows, no baked lighting, no baked mood. The sprites are neutral. Light2D does the rest.
## 2. Most important thing for v0.1.1?
The Light2D warm/cool zone contrast working with the fog-of-perception cone in a single scene — that's the moment the art direction stops being a document and becomes the game.
## 3. Flags for decision recording
- **D-019 amendment** should state "Rimworld-angle shallow tilt" explicitly, not "slight isometric" — those are different things. Rimworld is orthographic with a cosmetic forward tilt. Isometric implies diamond grid geometry. Keep the language precise.
- **Wall rendering (Option B structural / Option A partitions)** may need revisiting now that the shallow tilt naturally shows wall faces. At Rimworld's angle, even thin partitions show a front face. The distinction may collapse to just thickness/color rather than rendering method. Flag as "confirmed in principle, implementation detail pending Tyre/Stig."
- **"Entity always wins visual ties"** should be recorded as a hard rendering rule. If an entity and an object overlap, the entity's D-033 color must remain visible. This is a readability guarantee, not an aesthetic preference.
## 4. One sentence
A well-maintained space station where the lighting tells you how to feel and the people tell you what to fear.
---
*Araminta. Keep it clean.*
@@ -0,0 +1,45 @@
# Art Direction & Mood Board Workshop — Closing Round
**Date:** 2026-02-12
**Purpose:** Final statements, confirm decisions for recording, flag anything that needs follow-up work.
---
## Workshop Summary
Three rounds produced unanimous consensus on art direction fundamentals:
- **Visual style:** Clean 2D, bold silhouettes, muted palette, lighting-driven atmosphere
- **Camera:** Shallow-tilt top-down (Rimworld-style) — D-019 amendment
- **World composition:** Tile-based, 1x1 placed objects on a grid (base builder compatible)
- **Lighting:** Darkwood cone mechanics + BR2049 color temperature + Hopper emotional register
- **Entity system:** 24x32 on 64x64 tiles, silhouette = identity (D-033 color = relationship)
- **Fog of perception:** Cone-shaped (D-015), graduated desaturation, fog = absence of knowledge
- **Insert overlay:** Geometric data + bloom render = precision perceived as organic
- **Environment:** Strict zero shift on conspiracy. Character temperature via daily spatial paths.
- **Animation:** Two-tier (clear routine / ambiguous investigation)
- **Weather:** Gameplay system (perception modifier, NPC disruptor, storyteller tool)
- **Z-levels:** 8-layer stack, walls Option B structural / Option A partitions, sprite stacking OUT
- **v0.1.1:** ~20-35 sprites, bar + corridor test scene, prove art direction in-engine
- **Planet:** Velen (Krenn System, temperate-maritime)
## Mood Board Images
9 images generated. Key references:
- **#1** (bar): Warmth register
- **#5** (vision cone): Camera scale and perspective
- **#6** (span gate): Emotional register, mundane-extraordinary
- **#7** (convergence): First synthesis attempt
- **#8** (tile composition): Tile-based world proven
- **#9** (bar antagonists): Emotional core — populated bar, surveillance composition
## Closing Round Instructions
This is your final statement. Keep it short. Address:
1. **Does the workshop output capture your domain's concerns?** Anything missing?
2. **What's the single most important thing to get right in v0.1.1?** One sentence.
3. **Any flags for Qatux's decision recording?** Corrections, nuances, things that must be worded carefully.
4. **One sentence: what is this game's visual identity?**
Write to `closing-{your-name}.md`. Qatux: compile the final workshop document after all four closing statements.
@@ -0,0 +1,21 @@
# Closing Statement — Gore (Themes & Endgame Design)
## 1. Does the workshop capture my domain?
Yes. The thematic spine is intact across every decision. Environmental neutrality is confirmed — the world doesn't tip its hand. The life-sim substrate is visually grounded: warm spaces are warm because people settled them, not because the renderer has opinions. The dual-lens concept landed cleanly as spatial paths, not overlay manipulation. "Is this life enough?" has a visual form: a warm bar full of placed objects, seen from a cool corridor by someone deciding whether to enter.
Nothing missing.
## 2. Most important thing for v0.1.1?
The warm/cool doorway transition — if crossing a threshold changes how the room *feels* through lighting alone, the art direction is proven.
## 3. Flags for decision recording
**Environmental neutrality** must be recorded precisely: the base world layer never shifts in response to conspiracy activation or investigation state. Time-of-day and weather are allowed (diegetic). Insert overlay density is allowed (character cognition). CanvasModulate shifts tied to narrative state are not. This is a mechanical constraint, not an aesthetic preference — it protects the information model.
**"Settling is placement"** should be captured as a design principle, not just a production note. The tile-based world is thematically load-bearing: object density in a space correlates with how settled it is, and investigation reads placement as intention.
## 4. One sentence: the game's visual identity
A place worth living in, seen by someone trying to decide if they believe it.
@@ -0,0 +1,34 @@
# Closing Statement — Miri (Worldbuilder & Setting Designer)
**Workshop:** Art Direction & Mood Board
**Date:** 2026-02-12
---
## 1. Does the workshop output capture my domain's concerns?
Yes. The three items I came in guarding are all resolved correctly:
- **Three-era material vocabulary** is captured in the tile system. The shallow tilt makes wall-face era differentiation native rather than a cheat.
- **Environmental neutrality** is confirmed with the right nuance: the world doesn't shift, but the world already has warm and cool zones, and each character's daily path through those zones creates their color temperature experience. The setting does the work, not a shader.
- **IP originality** — nothing in the converged direction reads as derivative of a specific franchise. The references (Rimworld, Darkwood, BR2049, Hopper) are structural inspirations, not surface borrowings. The span gate visual language still needs Araminta's interpretation to confirm it doesn't drift toward Stargate, but this is a follow-up item, not a workshop gap.
Nothing missing from my domain.
## 2. Most important thing for v0.1.1?
The floor tile transition between two zones must read instantly — if you can't tell you've crossed from institutional space to personal space by looking at the floor, the three-era vocabulary fails and the setting becomes invisible.
## 3. Flags for decision recording
- **D-019 amendment (camera angle):** Record this precisely. The angle is Rimworld's shallow tilt — nearly orthographic top-down with enough forward perspective to reveal object and wall front faces. It is NOT classic 2:1 isometric, NOT 3/4 view. The grid remains square/orthogonal. Vision cone math remains 2D. "Top-down with depth" is the description; "isometric" is incorrect terminology for this angle.
- **Velen:** Record as canonical. Krenn System's primary habitable world. Temperate-maritime, ~0.9G, regular rain, morning/evening fog. Station Sova orbits Velen.
- **"Where they hang" temperature:** Record the mechanism precisely. This is NOT an overlay tint. The smuggler's playthrough feels warmer because the smuggler physically spends time in warm-lit spaces (bar, dock floor, break room). The detective's playthrough feels cooler because the detective physically spends time in cool-lit spaces (Commission kiosk, observation posts, corridors). Same Light2D fixtures, different daily paths. The setting produces the feeling.
## 4. One sentence: the game's visual identity
A lived-in station assembled from forty years of modular parts, seen through a cone of imperfect knowledge, where the warmth of the bar and the cold of the corridor are the same reality experienced by different lives.
---
*Miri out.*
@@ -0,0 +1,21 @@
# Closing Statement — Ozzie (Player Experience / Wow Factor)
## 1. Does the workshop capture my domain?
Yes. The workshop nailed the player experience priorities: readability as non-negotiable, lighting as the emotional engine, the two-tier animation split that IS the investigation mechanic, environmental neutrality that forces the player to do their own detective work, and the vision cone as directional tension. The six wow moments (D-039) are all served by the decisions made here. THE FRIEND's contradiction moment — the green-to-amber shift, the urgent chime, the warm bar gone cold in the player's mind — has a visual language now.
Nothing missing.
## 2. Most important thing for v0.1.1?
The temperature shift when you walk from the corridor into the bar must make you FEEL something — if the warm amber light doesn't pull you in, nothing else matters.
## 3. Flags for decision recording
- **"Strict zero environmental shift" needs precise wording.** The WORLD doesn't shift when conspiracy activates. But the world already HAS warm and cool zones, and the insert overlay CAN get denser. Weather CAN change (it's diegetic, not a hint). The prohibition is specifically: the rendering pipeline never modifies world-layer visuals in response to narrative state. Zone lighting is fixed. Weather is the storyteller's instrument. Insert overlay is the character's analytical state. Those three are different systems with different rules.
- **The camera angle decision amends D-019.** D-019 says "top-down confirmed." The workshop shifts this to "shallow-tilt top-down (Rimworld-style)." This should be recorded as an amendment, not a contradiction — the spirit of D-019 (top-down as primary, not isometric or first-person) holds.
## 4. One sentence: the game's visual identity
A warm, lived-in space station seen through one person's eyes — where the lighting tells you how to feel, the fog tells you what you don't know, and the same room looks like home or like evidence depending on who you are.
Binary file not shown.

After

Width:  |  Height:  |  Size: 823 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 825 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 718 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 809 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 602 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 875 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 465 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 540 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 662 KiB

@@ -0,0 +1,61 @@
# Pipeline Test: Era 1 Institutional Wall Texture
**Author:** Araminta
**Date:** 2026-02-12
**Purpose:** First real test of the Nano Banana → 3D model → render pipeline
---
## Nano Banana Prompt
> Seamless tileable texture of a space station wall panel. Flat front-facing view, no perspective, no angle. Muted grey-blue metal surface, hex color range #7a7f85 to #6e7580. Subtle brushed metal grain running horizontally. Faint panel seam lines at regular intervals — thin, slightly darker than the base surface, suggesting prefabricated modular panels joined together. The surface is clean and maintained but shows 40 years of age: very slight tonal variation across the surface, not grime or damage, just the natural patina of metal that has been cleaned thousands of times. No scratches, no rust, no decay — this is institutional and well-maintained. No baked shadows, no directional lighting, no highlights — the texture must be uniformly lit (flat lighting) so that game engine lighting can be applied at runtime. No text, no signage, no objects. Just the wall material itself. Style: clean, muted, functional. Not grimy, not shiny, not new. A 40-year-old wall that someone maintains professionally.
## Generation Settings
- **Aspect ratio:** 1:1 (square texture for UV mapping)
- **Resolution:** Highest available (we'll downscale ourselves)
- **Blending:** Off
- **Character consistency:** Off
- **World knowledge:** Off
- **Purpose:** "game texture, tileable wall material"
## Prompt Design Notes
### Why #7a7f85, not the #2a2a42 from Round 1?
The Round 1 hex values (#2a2a42 for walls) are the FINAL rendered appearance — what the player sees after Light2D darkens and tints the scene. The raw texture needs to be lighter and more neutral than the final target. When Light2D applies zone-appropriate lighting (cool white for logistics hub, dim for corridors), the result should land in the Round 1 range. If we start with a dark texture and Light2D darkens further, we get mud.
Rule of thumb: raw textures should be ~40-50% lighter than the intended final rendered appearance.
### Why "no baked shadows/lighting"?
Nano Banana defaults to adding dramatic lighting for visual appeal. For game textures, this is destructive — baked shadows conflict with the runtime Light2D system. A baked highlight on the left side of the texture means that side always looks lit, even when the game's light source is on the right. Flat-lit textures are a hard requirement for the entire pipeline.
### Why "brushed metal grain"?
Gives the surface subtle directionality without fine detail. At 1024x1024 source, the grain will be visible and give the texture character. At 64x64 runtime, the grain compresses to a subtle tonal variation that reads as "metal, not plastic" without any individual grain lines being visible. This is the kind of detail that survives downscaling as FEEL rather than DETAIL.
### Why "seamless tileable"?
This texture repeats across every wall tile in a zone. Visible seam lines where tiles meet would break the illusion. The panel seam lines I asked for are INTERNAL to the texture (part of the design — modular panels) not at the tile edges.
---
## Expected Pipeline After Generation
1. Generate texture via Nano Banana at highest resolution
2. QA: check for baked lighting, check tileability, check color range
3. UV-map onto 3D wall model in Godot render scene
4. Render at "the angle" (~17° from vertical) from 4 cardinal directions
5. Export at 1024x1024 (archival master)
6. Downscale: 1024 → 256 (working) → 64 (runtime), bilinear both passes
7. Evaluate: does the 64x64 output read as "institutional wall" in the test scene?
## Success Criteria
- At 64x64: reads as "wall" through silhouette and color, not through texture detail
- Color is in the muted grey-blue range, neutral enough for Light2D to tint
- No baked shadows visible — surface appears uniformly lit
- When tiled 3x3, no visible seam artifacts
- When Light2D cool white is applied, the result looks institutional
- When Light2D warm amber is applied, the same texture reads as "warmer space" (testing versatility)
Binary file not shown.

After

Width:  |  Height:  |  Size: 516 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 161 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 258 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 208 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

@@ -0,0 +1,464 @@
# Round 1 Workshop Response: Araminta (Art Direction & Mood Board)
**Agent:** Araminta (Visual Designer / Q-003 Lead)
**Date:** 2026-02-12
**Workshop:** Art Direction & Mood Board
**Status:** Round 1
---
## Q1: Visual References — What games/films/art look like what we're making?
I'm going to be blunt about each reference. What we take, what we leave, and why.
### Games
**Rimworld** — Our closest mechanical cousin. The most important reference and the one we need to understand deepest.
- **What it gets RIGHT:** Readability at scale. Rimworld can have 200+ entities on screen and you can parse the game state in a glance. The art deliberately reduces cognitive load — simple shapes, thick outlines (2px per 64px tile), abstracted icons that force the player's brain to fill in the story. That's not a limitation, it's a design philosophy. Entity color communicates state (mood bars, faction colors). Spatial layout IS the game — room shapes, furniture placement, traffic flow tell you everything about a colony.
- **What it gets WRONG for us:** Visually flat. Rimworld has essentially no lighting system — everything is evenly lit, no shadows, no atmosphere from light. The art style is intentionally emotionless — it counts on the systemic storytelling to create drama, not the visuals. There's no sense of PLACE. A Rimworld base on a tundra and one in a jungle feel identical if you squint at the layout. That's fine for Rimworld's "you are the overseer" perspective, but we need "you ARE this person in THIS place." Rimworld's even illumination is the opposite of what we need — our shadowcasting (D-035) and fog of perception (D-011) are the game's visual backbone.
- **What we take:** Readability principles. Entity-as-icon philosophy. The idea that simple shapes with clear color-coding beat detailed sprites with ambiguous silhouettes. The spatial-layout-IS-gameplay principle.
- **What we leave:** The emotional flatness. The uniform lighting. The "view from nowhere" camera feel.
**XCOM 2** — Fog of war as tension, information-under-pressure.
- **What it gets RIGHT:** The fog of war is TERRIFYING. Unseen tiles feel genuinely threatening. The visual transition from known to unknown is sharp and readable — you never wonder whether you can see a tile. The UI layers information cleanly during high-pressure moments: threat indicators, overwatch cones, probability displays. It proves that a tactical game can have atmosphere AND readability. The way XCOM dims and desaturates unknown areas while keeping known areas vivid creates a constant visual tension between safety and danger.
- **What it gets WRONG for us:** It's isometric 3D, not top-down 2D — different technical pipeline entirely. It's MILITARY. Every visual decision screams "tactical combat operation." Our game has combat but it's the punctuation, not the sentence. XCOM's visual language is too aggressive, too kinetic, too "this is a battlefield." We need "this is a workplace where something is wrong." XCOM's fog of war is binary (seen/unseen) — ours has four states (visible/fog-edge/hidden/remembered) with information decay.
- **What we take:** The emotional weight of unseen space. The principle that fog of war should feel HEAVY, not just "grey tiles." The layered UI approach — tactical information overlaid on the world without obscuring it.
- **What we leave:** The military aesthetic. The binary fog. The combat-centric visual language.
**The Sims (3 and 4)** — Making daily life readable and engaging from overhead.
- **What it gets RIGHT:** This is the gold standard for "daily routine as visual entertainment." The Sims proves that watching someone eat breakfast, go to work, and chat with a neighbor can be COMPELLING from a top-down/isometric camera. Object density creates "lived-in" spaces — a room feels real because it has the RIGHT furniture in the RIGHT places. Need states are communicated through simple visual indicators (mood icons, thought bubbles) without cluttering the view. The visual distinction between public and private spaces through decoration density and lighting warmth is exactly what we need.
- **What it gets WRONG for us:** Too cheery. Too clean. Too colorful. The Sims' visual language says "everything is fine and fun." Sova Transit District is comfortable but not CHEERFUL — it's a working port where people get by. The Sims has no tension, no information asymmetry, no shadows hiding things. The isometric angle gives the player godlike vision — the opposite of our locked camera with vision cone. Also, The Sims' UI is extremely game-y (need bars, relationship meters, skill progress) where ours must be diegetic.
- **What we take:** Object placement as storytelling. The principle that a well-furnished room communicates "someone LIVES here" from any camera angle. The readability of daily routines — you can tell what a Sim is DOING from their position and posture. Room differentiation through object density and lighting warmth.
- **What we leave:** The cheeriness. The color saturation. The non-diegetic UI. The god-perspective.
**Heat Signature** (Tom Francis, 2017) — Top-down space station infiltration.
- **What it gets RIGHT:** Ship layouts are instantly readable. Walls are information barriers you can see. The moment you breach into an unknown room — that split second of "what's inside?" — is the exact emotional beat our fog-of-perception system needs to nail. Ship interiors feel like FUNCTIONAL SPACES: bridge, engine room, cargo hold. The top-down camera makes every room a puzzle of sightlines and cover. The art style is minimal but the spatial design does all the work.
- **What it gets WRONG for us:** Too minimal. Zero atmosphere. No sense of place, no lived-in quality, no warmth. Every ship feels the same because there's no environmental storytelling — just walls and guards. It's an infiltration game, not a life-sim. The NPCs are dots with cones, not people with routines and relationships.
- **What we take:** The spatial clarity. Walls-as-information-barriers rendered in top-down. The infiltration "what's behind this door?" tension. The proof that top-down 2D space stations can feel like real places through layout alone.
- **What we leave:** The sterility. The lack of environmental personality. The uniform aesthetic.
**Tactical Breach Wizards** (Tom Francis / Suspicious Developments, 2024) — The evolution of Heat Signature's spatial clarity into actual art.
- **What it gets RIGHT:** This is Heat Signature's developer growing up visually. TBW uses a flat, minimalist polygonal art style with few textures — and it WORKS. Characters are instantly distinguishable through silhouette alone: body shape, posture, clothing outline, accessories. You can tell every character apart at a glance even without reading names. The rooms are small but every tile communicates — walls, windows, cover, sightlines. The top-down tactical view is clean enough for complex ability previews (trajectory lines, blast radii, knock-back paths) overlaid on the game world WITHOUT clutter. It proves that a top-down game can have personality AND information clarity simultaneously. The color palette is warm and readable without being garish.
- **What it gets WRONG for us:** Turn-based, so it can afford more visual density per frame than our real-time game. The comedic tone (wizards + SWAT) doesn't match our register. The environments are small arenas, not lived-in spaces with daily routines. No fog of war — you see the whole room. No life-sim layer.
- **What we take:** The silhouette-as-identity principle — this is CRITICAL for us (see Q3). The proof that flat, minimalist 2D with bold shapes reads perfectly at top-down scale. The overlay approach — tactical information layered on the game world cleanly. The detail level per character: enough to have personality, not so much that it fights the overlay.
- **What we leave:** The comedic tone. The arena-style room design. The turn-based visual pacing.
**Hotline Miami** — Top-down camera as terror engine.
- **What it gets RIGHT:** D-015 literally cites this game. The camera creates tension because you CAN'T see everything. Every room you enter is a potential death. The neon palette creates mood so strong you can FEEL it. Each floor is a spatial puzzle. The game proves that top-down can be viscerally intense when the camera is a limitation, not a superpower. The way it uses color to communicate emotional state (cool blues for calm, hot pinks for violence) is directly relevant to our relationship color system.
- **What it gets WRONG for us:** WAY too fast. WAY too violent. The neon palette screams "1980s drug fever dream" — the Commonwealth is sleek, advanced, subtle. Hotline Miami's aesthetic is maximalist where ours must be restrained. The emotional register is adrenaline-and-horror where ours is unease-and-suspicion. Also, Hotline Miami has zero life-sim — there's no "quiet life is good" counterbalance.
- **What we take:** The principle that a locked top-down camera creates tension through information denial. The use of color palette as emotional register. The spatial puzzle of "I need to see around this corner."
- **What we leave:** Everything about the aesthetic. The speed. The violence. The maximalism.
**Invisible Inc.** (Klei Entertainment, 2015) — Tactical stealth, fog of war, information management.
- **What it gets RIGHT:** The closest existing game to our intersection of stealth + information + top-down. The fog of war is sharp and readable. Peek mechanics let you see around corners — visual information as a resource you spend actions to gain. The UI layers tactical data (guard patrol paths, alarm status, hack progress) without overwhelming the game view. Klei's clean, comic-book art style is distinctive without fighting the information overlay. The visual language clearly distinguishes known, unknown, and suspected information.
- **What it gets WRONG for us:** It's isometric, not top-down. It's turn-based, so information can be displayed statically — our real-time game needs information that reads at a GLANCE, not after deliberation. It's a heist game — the aesthetic is "cyberpunk spy thriller," too stylized and too genre-specific for the Commonwealth's sleek functionality. No life-sim component, no daily routines, no investment in place.
- **What we take:** The information-as-resource visual language. The layered UI that separates world state from tactical overlay. The proof that stealth + information management can be visually clear in a top-down format. The fog of war that communicates DEGREES of knowledge (seen, heard, suspected).
- **What we leave:** The isometric perspective. The cyberpunk aesthetic. The turn-based visual pacing.
**Citizen Sleeper** (Jump Over the Age, 2022) — Space station life, quiet tension, daily routines.
- **What it gets RIGHT:** The MOOD. "Quiet life with undertow" is exactly our register. The visual design is clean, information-forward, atmospheric without being cluttered. The space station feels like a PLACE — different areas have different visual identities. The UI is elegant and minimal. The art (by Guillaume Singelin, also known for the Lancer TTRPG) uses bold shapes, limited palettes, and strong silhouettes. The color palette is muted with strategic warmth — exactly the Commonwealth tone. The way daily life feels meaningful and engaging despite being mechanically simple is what our life-sim substrate needs.
- **What it gets WRONG for us:** It's a visual novel / narrative RPG with dice mechanics, not a spatial game. There's no movement, no sightlines, no fog, no spatial puzzles. The "station" is a menu, not a map. The gorgeous character portraits are irrelevant to our top-down perspective. The game has no observation mechanic — you learn things through dialogue choices, not by watching.
- **What we take:** The mood. The color temperature. The "space station as home" feeling. The visual principle that muted palettes with strategic color accents communicate both comfort and unease. The proof that a space station setting can feel warm and human, not just cold and industrial.
- **What we leave:** Everything about the presentation format. It's a different genre.
**Teleglitch** (Test3 Projects, 2012) — Top-down survival horror with extreme LOS.
- **What it gets RIGHT:** The most terrifying fog of perception in any top-down game. The LOS system creates genuine horror — you can hear things in the dark but can't see them. The procedurally generated space station environments feel authentically industrial. The visual minimalism forces your imagination to fill in the details, which is scarier than anything rendered. Proves that top-down + restricted vision + sound = survival horror without any fancy graphics.
- **What it gets WRONG for us:** Too dark. Too horror. Too lo-fi. Teleglitch's aesthetic is deliberately ugly — raw, pixelated, oppressive. Our game needs "quiet life that's comfortable enough to be complacent" — Teleglitch never lets you breathe. The visual style communicates "everything is broken and trying to kill you," which is the opposite of Sova's functional prosperity.
- **What we take:** The emotional power of restricted LOS in top-down. The sound-at-fog-edge principle. The proof that you don't need beautiful graphics to make darkness terrifying.
- **What we leave:** The horror aesthetic. The lo-fi style. The relentless oppression. We need contrast — comfort AND unease — not just unease.
**Door Kickers** (KillHouse Games, 2014) — Tactical top-down with clear character silhouettes.
- **What it gets RIGHT:** The detail level per entity is almost exactly what we should target for our post-boxes fidelity. Characters have identifiable silhouettes and distinct enough to tell apart, but NOT so detailed that they fight with tactical overlays. The top-down camera angle, the sightline visualization, the room-clearing spatial puzzles. The fog of war through closed doors. The way you plan movement based on what you can and can't see.
- **What it gets WRONG for us:** Military aesthetic again. No life-sim, no daily routines, no civilian spaces. The color palette is tactical greens and greys. But the SCALE of visual detail per entity — that's our target.
- **What we take:** Entity detail level. Silhouette readability. The scale of character sprites relative to environment tiles.
- **What we leave:** The military palette and aesthetic.
### Films and TV
**Blade Runner 2049** (Denis Villeneuve, 2017) — Color temperature as storytelling.
- **What it gets RIGHT:** Every frame uses color temperature to communicate emotional state. Roger Deakins' cinematography is a masterclass in "lighting as narrative." Orange/amber = warmth, memory, the past. Blue-grey = cold, institutional, the present. White = sterile, clinical, manufactured. The way a scene's mood shifts through nothing but a lighting change — that's directly applicable to our zone palettes and solar light color system.
- **What it gets WRONG for us:** Too cinematic, too moody for a top-down game. Dystopian where we need "functional prosperity." The visual language is about loneliness and existential dread — we need a place where people LIVE together, not wander alone.
- **What we take:** Color temperature as mood. The principle that warm lighting = personal/comfortable and cool lighting = institutional/analytical. The idea that a single location can feel completely different under different light.
- **What we leave:** The dystopian register. The cinematic framing.
**The Expanse** (TV series, 2015-2022) — Best modern lived-in space station aesthetic.
- **What it gets RIGHT:** Medina Station, Tycho Station, Ceres — these feel like REAL PLACES where real people work and live. Different social zones look different: the bar district has different lighting than the docks. Institutional signage is everywhere. The stations show their age — repairs are visible, modifications are layered. The diversity of spaces (dive bars next to control rooms next to residential corridors) is exactly the spatial variety Sova needs. The visual language says "advanced civilization, working class, functional" — which is precisely our register.
- **What it gets WRONG for us:** Too gritty. The Expanse is harder sci-fi — more resource-scarce, more political tension visible in the infrastructure. The Commonwealth is wealthier, more comfortable. Sova should feel like a well-maintained 40-year-old building, not a station on the edge of revolution. The Expanse's color palette tends toward industrial grey-brown, too desaturated for the warmth we need in social spaces.
- **What we take:** The "different zones feel different" principle. Institutional signage as environmental storytelling. The visual language of repairs and modifications visible in walls and surfaces. The feeling that these are WORKPLACES first, sci-fi settings second.
- **What we leave:** The grittiness. The scarcity aesthetic. The political tension written into the infrastructure.
**Moon** (Duncan Jones, 2009) — Functional space station, warm/cool contrast.
- **What it gets RIGHT:** The warm lighting in Sam's personal space versus the cool institutional areas. The way the station feels simultaneously comfortable and wrong. The gradual revelation that things aren't what they seem — that's our entire game arc. The production design (by Tony Noble) uses practical, functional surfaces with just enough warmth to make it feel habitable.
- **What we take:** The warm/cool spatial contrast. The "comfortable but something's off" visual register.
- **What we leave:** The isolation. Our station is populated, not lonely.
### Artists and Concept Art
**Syd Mead** — The godfather of "lived-in future" design.
- His work on Blade Runner, Aliens, and personal concept pieces shows technology as INFRASTRUCTURE, not decoration. Clean lines, functional surfaces, institutional colors. People USE his environments — they don't just pose in them. The Commonwealth's visual language should channel Syd Mead: advanced technology that serves human needs, not technology that exists to look futuristic.
- **What we take:** Technology as infrastructure. Clean lines. Functional surfaces. The principle that futuristic = well-designed, not flashy.
**Ron Cobb** — Designed the Nostromo interiors for Alien.
- The "working spaceship" aesthetic. Warning signs, coffee stains, wear patterns, institutional signage that nobody reads anymore. His famous design principle: "If you can imagine yourself living or working in a particular set, then it has validity." That's our test for every room in Sova.
- **What we take:** Environmental signage. Wear-as-character. The "can you imagine working here?" test.
**Simon Stalenhag** — Mundane life alongside extraordinary technology.
- The Swedish countryside with massive robots and megastructures in the background. Kids play, parents argue, Volvos rust — while impossible technology hums on the horizon. The "mundane-extraordinary juxtaposition" is EXACTLY the emotional register of Sova Transit District. People live ordinary lives next to a wormhole gate. The technology is remarkable but NORMALIZED. Stalenhag's palette — muted, naturalistic, with strategic warm accents — is close to what we need.
- **What we take:** The emotional register. Ordinary life coexisting with extraordinary infrastructure. The muted-with-warmth palette. The principle that technology becomes invisible through familiarity.
- **What we leave:** The nostalgic/melancholic tone. Stalenhag's work is about loss and decay. Sova is about continuation and routine.
---
## Q2: Mood and Atmosphere — What emotional register?
### The Core Tension: Comfortable Enough to Be Complacent
Sova Transit District should feel like a place where "quiet life is good" is a genuine, defensible choice. Not naive — real. The bar is warm. Your colleagues are familiar. The pay is steady. The span gate hums. This is HOME.
The visual mood starts there and STAYS there for the first 15-20 minutes. No visual foreshadowing. No ominous shadows. No "something is clearly wrong" color grading. The player should feel genuinely comfortable before anything shifts.
### "Quiet Life Is Good" — Visual Language
- **Warm ambient light** in social spaces. The bar radiates warmth. The break room in the logistics hub has warmer lighting than the work floor. Personal spaces are warmer than institutional ones.
- **NPCs in expected positions.** Kael at the dock. Lera behind the bar. People where they belong. The visual rhythm of daily routines is COMFORTING.
- **Steady span gate glow.** The rhythmic amber-orange pulse from the gate terminal. Always there. The station's heartbeat. When it's steady, things are normal.
- **Moderate entity density.** Not crowded, not empty. Enough people that the station feels alive, few enough that you notice individuals.
- **The player's insert overlay is quiet.** Minimal notifications. The detective's analytical overlay has data, but nothing flags as urgent. The smuggler's social overlay shows green dots — everyone's accounted for.
### "Something Is Wrong Here" — Visual Language
This is CRUCIAL: the visual mood shift should be SUBTLE. Almost subliminal. The player should feel unease before they can articulate why.
- **A single NPC out of position.** One green rectangle where it shouldn't be. The eye catches it because everything else is rhythmic. The monologue fires: *"That's Kael. Why is he near restricted storage?"*
- **The first color shift.** Green to amber. 0.5 seconds. The first time ANY entity changes color in the session. The system proving it's dynamic on someone the player trusted. This IS the visual mood shift.
- **The insert gains a notification.** Something flags. The detective's overlay highlights a discrepancy. The smuggler gets a message. The insert — which was quiet — now has something to say.
- **Lighting DOESN'T change.** The station is the same. The bar is still warm. The corridors are still dim. The WORLD hasn't shifted — the player's PERCEPTION of it has. The mood shift happens inside the player's head, not on screen.
### Lighting Language
This is where the team lead's direction on lighting as load-bearing gets concrete.
| Zone | Temperature | Quality | Feeling |
|------|-------------|---------|---------|
| **Logistics Hub** | Cool white, ~5500K equivalent | Even, institutional fluorescent. Slight harshness. | "Work." Functional. You're here to do a job. Clear visibility — nothing hidden in the light. Things are hidden behind WALLS. |
| **Bar (The Last Shift)** | Warm amber, ~3000K equivalent | Uneven, lower fixtures. Pools of warmth with dimmer gaps. | "Life." Comfortable. The warmth invites lingering. The uneven pools create semi-private pockets. |
| **Corridors / Maintenance** | Neutral dim, ~4000K equivalent | Irregular. Light sources spaced further apart. Gaps. | "Between." The transition spaces. Not threatening — just less attended. The lighting says "this area isn't a destination." |
| **Smuggling Spaces** | Whatever's available. Maintenance-level. | Minimal. A single strip light. Maybe nothing but bleed from adjacent corridors. | "Not for you." Insufficient lighting says "this space isn't meant for the public" without screaming "DANGER." |
| **Span Gate Terminal** | Warm amber-orange from gate + cool institutional | Mixed. The gate's glow competes with institutional lighting. | "Threshold." The tension between the extraordinary (gate) and the mundane (desk work) IS the visual character. |
### Solar Light Color — Sova's Stellar Identity
The Krenn System star is a G3V — basically solar-type, slightly cooler than Sol. This means Sova's "natural light" (through viewports, light wells, exterior hull sections) should be:
- **Base color:** `#fff5e0` — warm white with a slight golden cast. Not orange like a red dwarf, not blue-white like a hot star. Familiar. Almost like late-afternoon sunlight.
- **Viewport rendering:** Where the station has viewports or light wells, solar light pools in with this warm tone. It creates a subtle distinction between "areas with exterior exposure" and "deep interior" spaces.
- **Identity function:** When the player visits a different system's station (future scope), the different solar color instantly communicates "you're somewhere else." A station around a red dwarf: deep amber cast. A station near a blue-white star: cool, almost clinical light. This is FREE atmosphere — just shift the ambient light source color in the tilemap shader.
### Smuggler vs. Detective Visual Mood
Same station, different experience. The visual mood shift is achieved through three things:
| Element | Smuggler | Detective |
|---------|----------|-----------|
| **Player entity color** | `#e8e0d0` — warm. Home. | `#e0e8ff` — cool. Assignment. |
| **Insert overlay density** | Light. Consumer-grade lattice. Less data on screen. The world feels PERSONAL. | Denser. Institutional lattice. More overlay data. The world feels OBSERVED. |
| **Monologue text color** | `#d0d0c0` — warm off-white. Casual. | `#c0c8e0` — cool off-white. Analytical. |
| **What the player SEES** | Green dots (friends), familiar names, comfortable spaces. The station is home. | Teal dots (unknowns), role labels, potential evidence. The station is a case. |
The visual mood difference isn't dramatic. It's a 10% color temperature shift applied consistently across every element. Over 30 minutes, it compounds. The smuggler's experience feels warmer. The detective's feels cooler. Neither is objectively "right" — both are the character's subjective lens.
---
## Q3: Top-Down Art Style Direction
This is Q-003. v0.1 is defined (boxes with labels, D-033 color system, no image files). This section is about the long-term target — what does The Settled Reach look like when it grows up?
### My Recommendation: Clean 2D with Bold Silhouettes, Driven by Dynamic Lighting
**NOT pixel art.** Pixel art communicates "retro" and "indie." The Commonwealth is advanced. The aesthetic should feel contemporary, not nostalgic. Pixel art also fights with our information overlay — the diegetic insert, monologue text, and perception mode layers need clean visual separation from the game world, and pixel art's uniform texture density makes that harder.
**NOT painted / Disco Elysium style.** Too detailed per frame, too expensive to produce consistently, and critically: too hard for AI image generation (Nano Banana / Gemini 2.5 Flash) to maintain style consistency across hundreds of assets. Disco Elysium's painterly beauty was hand-crafted by a small team of exceptional artists. We can't replicate that and shouldn't try.
**NOT photorealistic 3D rendered to 2D.** Too expensive, too slow, uncanny valley risk at top-down scale.
**YES: Stylized 2D with strong silhouettes, limited palette, and LIGHTING as the primary atmosphere engine.**
Think: the readability of Rimworld + the spatial clarity of Heat Signature + the mood of Citizen Sleeper + the tactical layer of Door Kickers. But the atmosphere comes primarily from LIGHT AND SHADOW, not from art detail.
### Why Lighting-Driven Art Direction
Here's the insight: at top-down scale, you can't see facial expressions. You can't see fabric texture. You can barely see individual fingers. Detail below a certain threshold is wasted. But LIGHTING affects everything on screen simultaneously. A room that's well-lit feels safe. The same room with half the lights out feels threatening. A warm glow from a bar doorway says "welcome." A cold blue corridor says "institutional."
Our shadowcasting system (D-035) already computes LOS per entity per tick. Godot 4's Light2D system can render dynamic shadows, colored light sources, and ambient light variation natively. The SAME computation that drives the fog of perception can drive atmospheric lighting. This isn't two systems — it's one system serving two purposes.
**Art direction principle: sprites provide SHAPE. Light provides MOOD.**
### What This Means for Sprites (Post-Boxes Fidelity)
**Entity detail level: between Rimworld and Door Kickers.**
- Rimworld: too simple. Colonists are functional but have no character. You can't tell them apart without labels.
- Door Kickers: right scale. Characters have identifiable silhouettes, distinct posture, readable equipment. You can tell them apart but they don't fight with the tactical overlay.
### Silhouette IS Identity — The D-033 Constraint
This is a critical art direction constraint that Ozzie identified and I want to make load-bearing: **since entity color = relationship state (D-033), we CANNOT use color to differentiate NPCs from each other.** Two trusted colleagues are both green. Three unknown workers are all teal. Color is spoken for — it carries relationship information, not identity information.
**Therefore: silhouette MUST carry identity.** Body shape, posture, height, clothing outline, accessories, gait. This is how the player tells Kael from Lera from Sera. Not by color (that tells you how your CHARACTER feels about them), but by SHAPE (that tells you WHO they are).
This has major implications for sprite design:
- **Every named NPC needs a distinctive silhouette.** Not dramatically different — recognizably different. Kael is stocky with a work vest outline. Lera is medium build with an apron. Sera has a Commission uniform silhouette. Torek is tall and broad.
- **Flat/background NPCs can share silhouette templates** — they're the visual "noise floor." 3-4 generic body types that read as "worker," "civilian," "official."
- **The silhouette must read at the SMALLEST zoom level.** If you can't tell Kael from Lera when zoomed out to see the whole logistics hub, the silhouette has failed.
TBW proves this works at top-down scale. Their characters are instantly recognizable by shape alone. Nano Banana is strong at generating bold, distinctive shapes — this plays to the AI pipeline's strength. We define the silhouette constraints (height ratio, shoulder width, key accessory), generate options, and pick the most readable.
**This is THE bridge between v0.1 (boxes with labels, color = relationship) and v1.0 (sprites with silhouettes, color = relationship).** The color system doesn't change. The identification system evolves from text labels to visual silhouettes. Same grammar, richer vocabulary.
**Target specification per entity:**
- **NPCs:** ~24x24 to 32x32 pixel footprint on a 64x64 tile grid. Bold silhouette with one identifying shape feature per named NPC. 2-3 animation states (idle, walk, interact). Relationship color as the PRIMARY visual signal — the sprite's base palette shifts to match the D-033 color. Identity carried by silhouette, NOT by color. One identifying feature per named NPC (Kael's vest, Lera's apron, Sera's Commission badge) — visible at zoom, readable as "different shape" when zoomed out.
- **Furniture/environment:** Bold shapes, low internal detail, high readability. A desk is a desk. A bar counter is a bar counter. Object silhouette should be identifiable WITHOUT color — so the color can carry other information (state, interactability).
- **Walls/floors:** Tilemap-based. Zone palette provides base color (see Q2 lighting language). Construction-era variation through tile color shifts (section 4 below). Light2D provides atmosphere.
**Animation approach:**
- State-based with smooth transitions, NOT frame-by-frame sprite animation. Fewer frames, more interpolation.
- Walk cycles: 4-6 frames. Godot's AnimationPlayer handles blending.
- Idle: 1-2 frames with subtle procedural sway (shader-based, cheap).
- Interact: 2-3 frames per interaction type.
- Ambient animation: ALL particle systems. Ventilation particles, span gate glow, weather effects, condensation. Godot's GPUParticles2D handles this natively.
### Why This Works for Godot 4
| Godot Feature | Our Use |
|---------------|---------|
| **TileMap** | Floor/wall geometry. Zone palettes. Construction-era tile variants. |
| **Light2D + shadows** | Dynamic lighting per zone. Shadow casting synchronized with LOS. Solar light through viewports. |
| **CanvasLayer stacking** | Game world (layer 0) > fog overlay (layer 1) > insert/HUD (layer 2) > monologue text (layer 3). Clean separation. |
| **Shader (CanvasItem)** | Fog desaturation/darkening. Perception mode overlays (thermal ramp, camera grain). Remembered-state ghost effect. Weather effects on visibility. |
| **GPUParticles2D** | Span gate glow. Ventilation particles. Weather (rain, dust, condensation). Ambient atmosphere. |
| **AnimationPlayer/Tree** | Entity state transitions. Walk cycles. Interaction animations. |
This art direction doesn't require custom engine work. Everything lives within Godot 4's standard 2D pipeline.
### Why This Works for Nano Banana Asset Generation
The AI image generation pipeline (Gemini 2.5 Flash) has specific strengths and weaknesses:
| AI Strength | How We Leverage It |
|-------------|-------------------|
| Bold silhouettes | Entity sprites with clear, distinctive outlines |
| Limited color palettes | Sprites generated within the D-033 palette constraints |
| Stylized/illustrative | "Clean 2D illustration" as the style prompt — not photorealistic, not pixel art |
| Single-object generation | One entity sprite at a time, with consistent style guide |
| AI Weakness | How We Mitigate It |
|-------------|-------------------|
| Style consistency across many assets | Strong style guide with explicit constraints (outline weight, palette, proportions). Post-generation QA pass. |
| Fine detail control | We don't NEED fine detail. Sprites are small. Bold shapes matter, not detail. |
| Lighting/shadow baking | We DON'T bake lighting into sprites. Sprites are flat-lit. Godot's Light2D system adds all atmospheric lighting at runtime. This means sprites only need to be correct shapes with correct base colors — lighting is handled by the engine. |
**Critical principle: sprites are SHAPE TEMPLATES that the lighting system COMPLETES.** AI generates the shapes. The engine adds the mood. This division of labor plays to both systems' strengths.
---
## Q4: The Station as Character — How Does "Lived-In" Read in Top-Down?
### The Answer Is Variation in Regularity
A brand-new station: uniform tile patterns, consistent wall materials, identical rooms, perfect symmetry. Sova, at 40 years old: ALMOST uniform, but with tell-tale variations.
### Visual Language for Age and Modification
**1. Construction-era tile variation**
Sections built in different decades have slightly different floor/wall tones. Not dramatic — subtle shifts that the player absorbs subconsciously.
| Era | Floor Hex | Wall Hex | Where |
|-----|-----------|----------|-------|
| Original construction (40 years ago) | `#1a1a2e` | `#2a2a42` | Deep corridors, structural walls, utility spaces |
| First renovation (~25 years ago) | `#1e1e32` | `#2e2e48` | Logistics hub main areas, primary corridors |
| Recent modifications (~5-10 years ago) | `#222236` | `#32324c` | Bar renovations, office upgrades, new partitions |
| Patch repairs (ongoing) | `#202038` | Mix of surrounding | Floor patches, replaced wall sections — slightly "wrong" tone |
The player won't consciously catalog these. But walking from an original-era corridor into a recently renovated section, they'll feel "this is different" before they know why. That's the station's HISTORY readable in the floor.
**2. Wall misalignment**
Old stations get modified. A wall that doesn't align with the grid. A room clearly subdivided from a larger space (wall inserted where the floor tiles don't match). A corridor that jogs around something that was added later. In the tilemap: walls on half-tile offsets. Rooms with asymmetric proportions. These read as "someone changed this place" without any text.
**3. Signage layers**
Old signage beneath new signage. A room labeled "Storage 7C" with a different label "Break Room" next to it. Official Commission notices on top of station management notices on top of original construction placards. Environmental text tells the station's bureaucratic history.
**4. Object-density gradient**
- **Logistics Hub:** Regulated. Equipment in standard positions. Clean. The institutional face.
- **Bar:** Dense. Mismatched furniture. Personal touches. Things accumulated over years. The warmth comes from STUFF — the evidence that someone cared enough to make this space comfortable.
- **Corridors:** Sparse. Functional. Conduits visible. The honest infrastructure.
### Zone Differentiation (Expanded from Content Gap Analysis)
The three zones should feel like walking through three different decades and three different social registers, but within the SAME station. Same base materials, different treatment.
**Zone 1 — The Terminal (Logistics Hub):**
The institutional face. Most uniform, most maintained. Newer fixtures, consistent lighting, regulatory signage. This is where the Commission comes when they visit. It looks OFFICIAL. The floor is clean. The lights work. The signage is current.
Visual character: ORDER. Regular grid. Consistent materials. The occasional piece of newer equipment (scanner upgrade, new terminal) that's slightly different from its neighbors. Competent maintenance, not personality.
**Zone 2 — The Last Shift (Bar):**
The social heart. Oldest character, most modification. Lera has been running this place for years. The lighting is warm because the fixtures are older — or maybe she chose warm bulbs because it makes the space feel better. Furniture is mismatched but ARRANGED — someone with taste organized this room over time.
Visual character: PERSONALITY. Irregular layout. Booth partitions that create sightline puzzles. Objects that don't match but feel RIGHT together. The walls have been repainted but you can see where the bar was once smaller (floor tiles change at the expansion boundary).
**Zone 3 — Corridors and Maintenance:**
The honest parts. No facade. Exposed conduits, maintenance hatches, wear marks on floors from decades of foot traffic. Different construction eras visible in wall materials. This is where the station's age shows most clearly — and where the smuggling ring operates, because nobody goes here unless they have to.
Visual character: TRUTH. The station without makeup. Mixed materials. Irregular lighting. Gaps between maintained sections. Not DECAYED — just not PRESENTED. Things work, but nobody's trying to impress you.
### "Layers" Without Grimy-Dystopia
The team lead's brief nails this: Sova is "functional prosperity." The key:
- **It's MAINTAINED, not decaying.** Someone fixes things. The repairs are visible but competent. Pipes don't leak. Lights work (except where they're meant to be dim).
- **It's PERSONALIZED, not abandoned.** People modified their spaces. The break room has a coffee setup. The bar has character. Even the logistics hub has a notice board with personal items among the official notices.
- **It's EFFICIENT, not desperate.** The modifications made the station work BETTER. The subdivided rooms make more sense than the original layout. The added corridors serve real traffic needs. This place has been IMPROVED by living in it, not degraded.
Think of a 40-year-old university campus. Not glamorous. Not decrepit. Layered with decades of use and modification. Comfortable for people who know it. Legible to newcomers. REAL.
### Weather Systems — Interior and Exterior
Weather is in scope per the team lead, and it's load-bearing for atmosphere AND gameplay.
**Exterior weather (planet-side stations or stations with exterior exposure):**
| Weather | Visual Effect (Top-Down) | Gameplay Effect |
|---------|-------------------------|-----------------|
| Rain | Diagonal particle streaks. Darkened ambient. Puddle reflections on exterior tiles. | Reduced exterior visibility range. Sound masking (medium range compressed). NPC routine changes (fewer outdoor NPCs). |
| Dust/sand storm | Dense warm-toned particles. Desaturated visibility. Orange-amber cast. | Severely reduced visibility. Sound masking extreme. NPCs shelter. Opportunity for unobserved movement. |
| Fog/mist | Reduced contrast. Objects fade at shorter range. The vision cone visually shrinks. | Vision range reduced for ALL entities (including NPCs). Equalizer — low-perception characters benefit. |
| Snow | White particle overlay. Brightened ambient. Track marks on ground tiles. | Moderate visibility reduction. Tracks reveal recent movement (information!). |
| Clear | Full visibility. Sharp shadows from solar light. | Baseline conditions. |
**Interior weather effects (always present to some degree):**
- **Condensation shimmer:** Near hull sections and viewports, faint particle shimmer. Indicates exterior temperature differential. Cosmetic but grounding.
- **Ventilation drafts:** Subtle particle drift in corridors (the ventilation system moves air). Directional — can hint at air flow patterns (useful for audio propagation too).
- **Humidity variation:** Bar is warmer and more humid (cooking, bodies, recycled air). Corridors near the hull are cooler. Subtle haze overlay variation between zones.
**Weather implementation in Godot 4:**
- GPUParticles2D for rain, dust, snow, condensation
- Shader-based ambient color shift for weather conditions (desaturation for fog, warm cast for dust)
- Viewport post-processing for mist/haze (simple gaussian blur at distance)
- Weather state modifies LOS range parameter in the shadowcasting system — one variable change, the fog rendering adapts automatically
**Weather per planet as identity:**
Each world in the wormhole network gets a weather profile alongside its solar light color. Sova's planet climate becomes part of Sova's identity. Different stations feel different because they're in different WEATHER, not just different tilesets. This is free atmosphere.
---
## Q5: Information Visualization — How Does Knowledge Look?
### The Neural Insert Overlay
The insert is a LAYER, not a PANEL. It exists in the same visual space as the game world, overlaid with transparency.
**Aesthetic direction:**
- **Base tint:** `#3d7aaf` at low opacity (30-40%). The implant's presence is a subtle blue-cast filter on reality. Not "HUD bolted on" — "reality seen through technology."
- **Typography:** Clean sans-serif for labels. Monospace for data readouts (cargo manifests, timestamps, case file references). The distinction between "human-readable label" and "machine data" should be typographic.
- **Line quality:** Thin, geometric, precise. 1-pixel lines at partial opacity for grid/boundary elements. The insert renders CLEAN because it's digital — contrast with the organic messiness of the physical station.
- **Animation:** Subtle. Data updates with brief flicker (the lattice processing). POI markers pulse gently (the system is alive, scanning). Not dramatic — BACKGROUND. The insert should feel like it's always been there.
**The insert should NOT feel like:**
- Iron Man's helmet display (too dramatic, too military)
- A smartphone notification center (too contemporary, breaks immersion)
- A video game HUD (too gamey, breaks diegetic principle)
**The insert SHOULD feel like:**
- Augmented reality glasses showing a subtle data overlay on the real world
- A very refined, minimal heads-up display that you've worn so long you forget it's there
- The way you stop noticing your glasses frames after a few minutes
**The "always-on" principle:** Even when the player isn't actively using insert features, a faint hint of the overlay persists. The character's neural lattice is always processing. A barely-visible grid at 3-5% opacity. Occasional ambient data flickers. The insert is part of how the character SEES, not a tool they pick up.
### Perception Mode Overlays — Each Feels Different
Each perception mode should have a DISTINCT visual signature that reads immediately. The player should know which mode they're in without checking a label.
| Mode | Visual Treatment | Godot Implementation | Emotional Register |
|------|-----------------|---------------------|-------------------|
| **Natural vision** | No overlay. The world as rendered. | Default render. | Baseline. "My eyes." |
| **Thermal** | Warm-to-hot color ramp. Walls become semi-transparent dark silhouettes. Entities glow (orange = warm, white = hot). Background desaturated to near-monochrome. | CanvasItem shader: remap sprite colors to thermal ramp. Light2D disabled (thermal doesn't show normal light). Entity glow via Light2D point lights on warm entities. | Clinical. Functional. "I'm looking for bodies, not people." |
| **Camera feeds** | Fixed-position rectangles on screen showing remote camera POV. Slight scanline effect. Timestamp overlay. Reduced color (security camera aesthetic). | CanvasLayer with SubViewport rectangles. Shader for scanline + desaturation. | Distant. Surveilling. "I can see but I'm not THERE." |
| **Unisphere tracking** | Clean dots on the insert minimap only. No world-space overlay. Data labels (name, last known position, timestamp). Stale data shown dimmer. | Insert layer only — UI elements, not world render changes. | Analytical. Detached. "Data, not perception." |
| **Audio analysis** | Directional arcs from player entity. Color-coded by sound type (neutral/conversation/threat). Arcs pulse with detected sounds. No visual change to the world — it's about SOUND space, not visual space. | Shader-based arcs from player position. Particle pulse effects on detection. | Focused. Attentive. "Listening in the dark." |
**Key principle:** Switching perception modes should feel like SWITCHING SENSES. The world changes because HOW YOU SEE IT changes. Not a UI toggle — a perceptual shift. The transition between modes (0.3s crossfade) should feel like blinking and opening your eyes differently.
### Visual Language for Information States
Beyond the D-033 entity colors, information itself has visual qualities:
**"I'm learning something new":**
- **Monologue chime** (D-038 audio) + urgent monologue text color shift
- **Observation eye icon** appears alongside the monologue — brief, clear
- **Entity pulse:** The observed entity's color briefly brightens (100ms, +20% luminance) before settling to its new relationship color. A visual "ping" that says "this entity just became more interesting."
- **Insert update:** If the observation generates insert data (new POI, case file update), the insert area briefly highlights. Not a popup — a glow in the relevant area of the overlay.
- **Overall feeling:** A NOTIFICATION. Brief. Clear. Then absorbed. The player's mental model updates and the visual returns to baseline.
**"This confirms what I suspected":**
- **Subtler.** A monologue line that echoes an earlier one. More measured tone.
- **NO entity pulse.** The entity's color stays the same. Confirmation is the ABSENCE of surprise.
- **Insert data consolidation:** Previously separate data points link. On the detective's overlay, two amber markers might get a connecting line. On the smuggler's, a contact's reliability rating might tick up.
- **Overall feeling:** STABILITY where the player was bracing for change. The absence of visual drama IS the signal.
**"I was wrong about something":**
- **Color shift on an entity** the player thought they understood. Amber to red. Or worse: green to amber. The system contradicting the player's assumption.
- **Monologue with different tone** — the character processing surprise or denial.
- **This is THE FRIEND's moment.** The first green-to-amber shift in the session. Maximum impact because until now, colors were static. The system just proved it can change, and it changed on someone the player trusted.
### The Information Decay Visual
D-011 says fog returns. Previously visited areas revert. This needs a visual language:
- **Visited, currently visible:** Full color, full detail, entities shown.
- **Recently left (< 5 minutes game time):** Slightly desaturated. Entities hidden but furniture/walls persist as "memory." The remembered state.
- **Left a while ago (5-30 minutes):** More desaturated. Furniture begins to dim. The memory is fading.
- **Long departed (> 30 minutes):** Near-fog. Only major structural elements (walls, doors) persist as faint outlines. Anything could have changed.
This gradient communicates INFORMATION RELIABILITY visually. The player can glance at any area and know: "I was JUST there" vs "I haven't been back in a while — anything could have changed." Time degrades visual certainty. That's D-011 rendered.
---
## Summary: The Art Direction Thesis
**The Settled Reach should look like a well-maintained 40-year-old space station seen through the eyes of someone who lives there. Clean, functional, subtly warm in social spaces, institutional in work spaces, honest in corridors. LIGHTING does the emotional heavy lifting. Sprites provide shape and identity. Color communicates relationship. Fog communicates knowledge. The insert overlay is a permanent, subtle technological lens on reality.**
**Three principles:**
1. **Readability over beauty.** Rimworld's lesson: if you can't parse the game state in a glance, nothing else matters.
2. **Lighting over detail.** The same room, differently lit, is a different room. Invest in the light system, not in texture detail.
3. **Restraint IS the aesthetic.** The Commonwealth is advanced and subtle. Not flashy, not grim, not neon, not grimy. Clean lines. Muted palettes. Strategic warmth. The visual equivalent of a well-designed tool.
**Long-term style target:** Clean 2D, bold silhouettes, lighting-driven atmosphere. Achievable in Godot 4's standard pipeline. Compatible with AI-generated asset workflow. Scales from boxes-with-labels to full art without changing the visual grammar.
**What makes The Settled Reach look like ITSELF, not like another game:** The relationship color system (D-033), the four-state fog, the diegetic insert overlay, and the solar light color per planetary system. No other game has all four. Together, they create a visual identity where the same station looks different to different characters, different at different times, and different under different stars.
---
*Written by Araminta. Keep it clean, we can layer detail later.*
@@ -0,0 +1,264 @@
# Round 1 — Gore (Themes & Endgame Design)
## The Question Before the Questions
Before I answer what the game should look like, I need to name what the game is asking. Because art direction isn't decoration — it's the visual form of the question the game poses.
The Settled Reach asks: **"Is this life enough?"**
That question has a visual shape. It looks like warmth you might not want to leave. It looks like comfort that might be complacency. It looks like a corridor you've walked a thousand times that suddenly, because you looked differently, becomes a place you've never been.
The art direction must serve this tension. Not "grim vs. bright." Not "safe vs. dangerous." The tension is: **this place is genuinely good, and you might destroy it by looking too closely.**
---
## Q1: Visual References
I'm going to name references that other participants probably won't, because I'm not thinking about what the game looks like moment-to-moment. I'm thinking about what it *means* visually.
### Edward Hopper — *Nighthawks* and the diner paintings
- **What it gets RIGHT:** Hopper painted ordinary spaces — diners, gas stations, hotel rooms — and made them feel like they contained entire philosophies. The warm light of the Nighthawks diner against the black street outside is exactly our game's central image: a small pocket of known life surrounded by unknowable dark. Hopper's figures are together but isolated. They're in a lit room but there's no visible door. That's Sova Transit District. That's the life-sim substrate. Warm, contained, and you can't quite tell if it's shelter or a trap.
- **What it gets WRONG:** Hopper's stillness is permanent. Our game needs the possibility that the stillness might break. The diner could be disturbed. Hopper never shows that — his work is about the stillness itself.
- **What we're referencing:** The *lighting philosophy*. Warm interior light as the visual language of "this life is enough." The darkness outside isn't menacing — it's just unknown. The menace comes from looking too hard at the light.
### Gregory Crewdson — *Twilight* and *Beneath the Roses*
- **What it gets RIGHT:** Crewdson photographs American suburban scenes that are meticulously ordinary — a woman standing in a flooded living room, a man on a lawn at dusk, a street that looks like every street — but something is *wrong*. He calls it "there but not there." That phrase is our game. Crewdson's signature is that the uncanny emerges not from anything alien but from the *attention of the camera itself*. The act of looking this carefully at ordinary life makes it strange. That is literally what our detective does. That is literally what our smuggler fears.
- **What it gets WRONG:** Crewdson's work is static and resolved. Each photograph is a finished sentence. Our game needs the unresolved version — the moment before you know whether something is wrong or you're imagining it.
- **What we're referencing:** The *uncanny register*. Ordinary spaces that become extraordinary through the quality of attention paid to them. Not through visual distortion. Not through color grading. Through *composition and stillness and the feeling that someone is watching*.
### Andrei Tarkovsky — *Stalker* (1979)
- **What it gets RIGHT:** Tarkovsky's masterwork uses one of the most powerful visual tricks in cinema: the real world is sepia, and the Zone — the place where wishes come true, where reality bends — is in color. The ordinary world is desaturated. The extraordinary world is vivid. But here's what matters for us: the Zone is made of ordinary objects. Puddles. Grass. Ruined buildings. The color doesn't make them alien. It makes you *see them as if for the first time*. That's what perception modes should feel like — not "now I have thermal vision" but "now I see this same corridor differently."
- **What it gets WRONG:** The Zone is geographically separate from ordinary life. In our game, the Zone is *layered on top of* ordinary life. The extraordinary is in the same space as the mundane. You don't travel to it. You notice it.
- **What we're referencing:** The *perceptual shift*. The idea that changing how you look changes what exists. Stalker's sepia-to-color transition is the thematic ancestor of our perception mode system. Same space. Different truth.
### Florian Henckel von Donnersmarck — *The Lives of Others* (2006)
- **What it gets RIGHT:** A Stasi agent surveils a playwright and actress in East Berlin. The visual design creates two worlds in one city: the GDR — concrete grey, institutional, airless — and the apartment — warm browns, scattered books, sheet music on the piano, art on the walls. The surveilling agent lives in a bare, colorless flat. As he listens to their lives, their warmth begins to change him. The film's visual grammar is: *the observed life is richer than the observer's life*. That's our detective. That's the cost of the investigative gaze.
- **What it gets WRONG:** The film's sympathy is entirely with the observed. Our game must make both positions — the watcher and the watched — feel like valid answers to "is this enough?"
- **What we're referencing:** The *visual cost of surveillance*. The detective's world should feel colder, more analytical, more ordered than the world being observed. The smuggler's world should feel warmer, more embodied, more textured. Same station. Different visual temperature. This is D-033 (entity color = relationship to player) extended to the entire visual register.
### Lucas Pope — *Papers, Please* and *Return of the Obra Dinn*
- **What Papers, Please gets RIGHT:** The most morally devastating game of the last decade looks like a spreadsheet. Pixel art. Cluttered desk. Rubber stamps. The mundanity of the visual design IS the point. You're stamping documents. You're checking passports. And the moral weight is unbearable. Papers, Please proves that visual restraint amplifies thematic weight. The less the game *shows* you drama, the more you feel it. That's our life-sim substrate. Daily routine rendered in functional, readable, undramatic visual language — until you realize what's happening inside it.
- **What Obra Dinn gets RIGHT:** The 1-bit aesthetic functions as an information design choice. By stripping away visual detail, Pope forces you to read *composition, position, gesture*. You identify characters by their shape and context, not their face. In a top-down game where characters are small, this principle is essential. But more importantly: Obra Dinn's aesthetic makes the act of *looking* feel like work. You're squinting. You're piecing things together. The visual style makes investigation *physical*. That's the feeling we want when our player switches to a perception mode and starts analyzing.
- **What they get WRONG:** Both games are retrospective — you're examining things that already happened. Our game is live. The visual style needs to support both the contemplative investigation and the real-time life unfolding around you.
- **What we're referencing:** The principle that **visual restraint is thematic confidence**. A game that's truly about "is this life enough?" shouldn't be visually overwhelming. It should be quiet enough that you can hear the question.
### Disco Elysium
- **What it gets RIGHT:** The oil-painting aesthetic does something no other RPG has done — it makes a ruined, post-revolutionary city look like it deserves to be *studied*. Every decaying wall is rendered with the care of a portrait. The mundane is treated as worthy of artistic attention. That's the philosophical stance our game needs: that Sova Transit District — 40 years old, prefab, working-class — is worth looking at this closely.
- **What it gets WRONG:** Disco Elysium's visual beauty is melancholic. It's a wake. Our game needs the beauty to feel *alive* — warm meals, laughter in the bar, the rhythm of shift changes. The beauty should make you want to stay, not mourn.
- **What we're referencing:** The *dignity of the mundane*. Art direction that treats a freight logistics hub with the same visual seriousness as a palace.
### Rimworld — Our Closest Mechanical Sibling
- **What it gets RIGHT:** Rimworld's art style is, by Tynan Sylwester's own admission, "a solution to a problem" — clear, cheap to create, abstract enough for players to project their own narratives onto. And that projection IS the point. Rimworld's visual restraint is what makes its emergent stories feel like *your* stories. When a colonist dies, the grief doesn't come from an animation — it comes from the gap between what you see (a simple sprite going still) and what you know (that was the cook who survived three raids). That gap is where all of Rimworld's emotional power lives. It's where ours should live too.
- **What it gets WRONG:** Rimworld's environments are *flat*. They communicate function (stockpile, bedroom, hospital) but not *feeling*. You never feel like a Rimworld base is a place people *love*. It's a place people *use*. For a game asking "is this life enough?", the environment needs to carry emotional weight that Rimworld deliberately avoids. You need to be able to look at The Last Shift and feel the warmth Rimworld would never render.
- **What we're referencing:** The *projection principle*. Simple entities + systemic complexity = the player fills in the emotional detail. But we need the *environment* to do more emotional work than Rimworld asks of it. The entities can be abstract. The station cannot.
### XCOM (especially XCOM 2) — Information Under Pressure
- **What it gets RIGHT:** XCOM's fog of war isn't just a visibility mechanic — it's an *emotional* mechanic. The dark tiles aren't empty. They're *threatening*. You know something is there. The camera's inability to show you is the source of tension, not the aliens themselves. XCOM proved that what you *can't* see in a tactical game generates more feeling than what you can. That's directly applicable to our fog of perception (D-011). The fog isn't a limitation — it's the game.
- **What it gets WRONG:** XCOM's fog is binary: seen or unseen, known or unknown. Our information model is graduated — you might have heard someone in a corridor, or seen a heat signature, or read a report about activity in this sector. XCOM's "dark tile = unknown" is too crude for a game about *degrees of knowing*. And XCOM's mood is martial — the visual language says "combat mission." Ours says "ordinary Tuesday that might not be."
- **What we're referencing:** The *emotional weight of unseen space*. Fog of war as a source of feeling, not just a gameplay constraint. But where XCOM fills that darkness with threat, we fill it with *uncertainty*. The dark tile might contain danger. It might contain someone having a perfectly normal conversation. You don't know. That's worse.
### The Sims — Life Made Legible
- **What it gets RIGHT:** The Sims solved a design problem that nobody else has solved as well: making *daily routine visually engaging*. Eating, sleeping, going to work, talking to neighbors — The Sims makes this readable and, crucially, *worth watching*. The visual language of need bars, mood indicators, and social animations turns the mundane into information the player cares about. For our life-sim substrate (D-023), this is the benchmark: can you watch an NPC's daily routine and find it interesting? The Sims says yes, by giving you just enough visual information to read the *quality* of their life, not just its schedule.
- **What it gets WRONG:** The Sims is transparent. You see everything. Needs, relationships, mood — it's all surfaced. That's the opposite of our game. Our NPCs have wants, secrets, tolerances (D-024), but you don't get a dashboard. You have to *observe*. The Sims shows you the answer. We show you enough to form a question. The Sims' visual genius is making routine readable; our challenge is making routine readable *while keeping secrets invisible*.
- **What we're referencing:** The *life-sim legibility problem*. How do you make "person eats lunch, goes to work, comes home" visually interesting enough to sustain a 30-minute session before conspiracy activates? The Sims proves it can be done. But our version must do it through observation and inference, not omniscient UI. The player should be reading NPCs the way you read people at a cafe — from body language and context, not stat panels.
### Tactical Breach Wizards (Suspicious Developments / Tom Francis, 2024)
- **What it gets RIGHT:** TBW solved a problem we'll face at a smaller scale: how to make a top-down tactical space *instantly readable* without sacrificing personality. Its flat, colorful polygonal style with minimal textures creates a visual hierarchy where the things that matter — character positions, room geometry, threat vectors — read immediately. Characters are silhouette-first (they don't even have mouths), yet each one is instantly identifiable through shape and color. Every enemy action is telegraphed visually. The result: you spend zero cognitive load on "what am I looking at?" and all of it on "what should I do about it?" That's information design as art direction. For a game where reading spatial relationships IS the core skill — who's near who, who can see who, who's where they shouldn't be — that effortless legibility is the benchmark.
- **What it gets WRONG:** TBW is a puzzle game wearing a tactics game's clothes. Every room is a fully known, fully visible, manually crafted challenge. There's no fog, no uncertainty, no hidden information. Tom Francis himself noted the tradeoff: because everything is telegraphed, you never get XCOM's anticipation moment — that held breath before a shot. TBW trades mystery for mastery. We need both. Our rooms should be as readable as TBW's *within the vision cone*, but the fog of perception (D-011) means you never see the whole room at once. Clarity within your field of view, mystery beyond it.
- **What it gets WRONG for our mood:** TBW is whimsical, bright, playful. Its color palette says "fun tactical toy." Ours needs to say "lived-in workplace that might be hiding something." But the *principle* — that you can have strong visual personality AND instant readability simultaneously — transfers directly. Personality and legibility aren't tradeoffs. TBW proves they're allies.
- **What we're referencing:** The *legibility-first design philosophy*. Entity silhouettes that read at a glance. Room layouts where spatial relationships are immediately clear. The confidence to use a minimal visual language and trust that the systems will provide the depth. But where TBW gives you all the information upfront (full puzzle visibility), we give you partial information beautifully (clear within your cone, absent beyond it). TBW's legibility *within* what you can see, married to our fog *outside* what you can see.
---
## Q2: Mood and Atmosphere
The emotional register of The Settled Reach is not "quiet life vs. danger." It's more precise than that.
**The mood is: contentment with an undertow.**
Here's what that means visually:
### "Quiet life is good" looks like:
- **Warm practical lighting.** Not cinematic. Not romantic. The light of a place designed for working and living — slightly yellow-white overhead panels, warmer accent light in the bar, the blue-white of cargo bay floods. Functional light that people have lived under long enough to feel like home.
- **Readable routine.** NPCs moving in patterns that feel purposeful. The visual rhythm of shift changes, meal breaks, people greeting each other. The Hopper diner: not glamorous, but occupied. People *belong here*.
- **Patina.** Surfaces that show use. Not grime — use. A bar counter worn smooth. Cable bundles zip-tied to walls. Repair patches on flooring that are a different shade than the original. The visual language of "someone has maintained this place for forty years because it matters to them."
### "Something is wrong here" looks like:
- **Nothing changes.** That's the key. The visual design should NOT shift when conspiracy activates. No red filter. No ominous lighting change. The wrongness is that the same warm, comfortable visual language now contains information you wish you hadn't found. The lighting doesn't change. Your relationship to it changes.
- **Crewdson's "there but not there."** A figure in a corridor who shouldn't be there at this hour. A door that's usually open, closed. The visual anomaly is *behavioral*, not atmospheric. The environment stays warm. The wrongness is in the pattern.
- **The absence of change as horror.** The most unsettling thing in our game should be: the station looks exactly the same after you discover the smuggling ring as it did before. The lighting is warm. The NPCs are friendly. Nothing has changed except what you know. And now you can never see it the same way. That's the visual thesis.
### Lighting language:
- **Default:** Warm overhead with zone-specific accent. The Terminal is brighter, bluer (work lighting). The Last Shift is warmer, more amber (social lighting). Corridors are dimmer, more uneven (infrastructure lighting — not horror-dim, maintenance-dim).
- **Perception modes** should not change the lighting. They should add a *layer*. The station still looks warm underneath. But now you're seeing data overlaid on warmth. That dissonance — the analytical overlay on the human space — IS the detective's experience.
### Visual temperature per character:
- **Smuggler:** The world is warm, textured, social. Entity colors lean green. The smuggler knows people, belongs here. Their visual world says "this is home."
- **Detective:** The same world is cooler, more diagrammatic, more spatial. Entity colors lean teal and amber. The detective sees vectors, patterns, anomalies. Same station. Same lighting. But the detective's *overlay* — the insert, the analytical layer — makes it feel like studying a specimen.
The same station. Two visual temperatures. That's the game's thesis rendered as art direction.
---
## Q3: Top-Down Art Style Direction
I'm going to say something the others might not: **the art style should feel like a document.**
Not a photograph. Not a painting. Not a movie frame. A document. Something that's being *recorded*, *analyzed*, *compiled*. Because the game is about information — who has it, who lacks it, what it costs to get it, what it costs to use it.
### Style direction:
- **Clean, readable, slightly technical.** Think of the visual language of architectural plans or facility diagrams — but warm. Not sterile. Not military. The kind of diagram someone made who *cares about this place*.
- **Stylized but not cute.** Rimworld-level entity detail is approximately right — silhouettes and color convey meaning, not facial expressions. But the *environment* should have more texture than Rimworld. The station should feel like a *place*, not a spreadsheet.
- **The gap between entity simplicity and environment detail IS the game.** Simple character silhouettes in a richly textured station environment. You're reading the environment through the people, and the people through the environment. Neither is fully legible alone.
### Animation:
- State-based animation with smooth transitions. Not fluid character animation — that would make characters too readable, too human, too legible. Slightly abstracted movement preserves the information game. You see that a figure *stopped*, but you can't tell from the animation alone whether they stopped because they're looking at something, talking to someone, or checking if they're being followed. You have to use other information channels (monologue, perception, proximity) to interpret the movement. The animation should be *legible but ambiguous*.
### The Obra Dinn principle applied:
- Reduced visual fidelity forces the player to become a better observer. If you can see everything clearly from the art alone, there's no game. The art should give you *enough* to ask questions, and make you use the game's systems to get answers.
---
## Q4: The Station as Character
There's a word for what we're describing with Sova Transit District, and it's not "lived-in." It's **settled**.
That's in the title. The Settled Reach. These people have *settled*. They've decided this is enough. They've stopped reaching. Or — and this is the question — they've found something worth settling for.
### What "settled" looks like:
- **Layers of modification.** Not decay. Modification. A wall panel replaced with a different manufacturer's panel. A power conduit rerouted around a later-added doorway. Environmental storytelling through *accretion* — things added, adjusted, worked around. This isn't a ruin. It's a home that's been renovated by its inhabitants for forty years.
- **Personalization within constraint.** The station is prefab — standardized panels, modular construction. But people have made it theirs. A maintenance corridor with someone's jacket hung on a pipe. A cargo bay with a break area improvised from shipping containers. The Last Shift's decor — whatever Lera has accumulated over the years. The visual language is: "the structure is institutional, the life within it is human."
- **Three zones, three relationships to settling:**
- **The Terminal (logistics hub):** The machine. Functional, clean-ish, purposeful. People here are *doing* something. The visual mood is competent routine. Light is work-bright.
- **The Last Shift (bar):** The hearth. Where people go when they're not working. The visual mood is the Hopper diner — warm, contained, voluntary. This is where "is this enough?" gets answered with "yes, actually."
- **Corridors:** The in-between. Where the station reveals its infrastructure, its age, its bones. Not grimy — honest. The corridors are where the station stops performing "home" and just *is* what it is: a forty-year-old structure in space. This is where the smuggling happens. Not because corridors are dark and scary, but because corridors are the spaces people don't pay attention to. The spaces that haven't been settled.
### The thematic visual key:
**The more a space has been settled — personalized, inhabited, made warm — the harder it is to investigate without feeling like you're violating something.** The detective walking through The Last Shift with analytical eyes should feel like reading someone's diary. The visual warmth of that space is the resistance. You're not in danger. You're being *intrusive*.
---
## Q5: Information Visualization
This is where I have the strongest opinion.
### The neural insert overlay should look like **thought**, not technology.
Every other sci-fi game makes HUD overlays look like military targeting systems or corporate dashboards. Hard edges, scan lines, data readouts. That's wrong for us. The insert is *neural*. It's wired into cognition. The information it provides should feel like it's arriving the way thoughts arrive — not projected onto your vision but integrated into your understanding.
### Concretely:
- **Soft edges.** Information overlays should bloom into visibility, not snap on. Think of how you gradually become aware of something in peripheral vision — not a hard rectangle appearing but a soft awareness emerging.
- **Organic data visualization.** Instead of targeting brackets around a person of interest, a subtle warmth or emphasis — as if your attention is being *drawn* rather than *directed*. The amber of D-033 should feel like a gut feeling made visible, not a computer flag.
- **Perception modes should feel like different kinds of attention, not different instruments.** Natural vision is how you see when you're just *being there*. Audio analysis is how you perceive when you *close your eyes and listen*. Thermal is how you perceive when you're *hypervigilant*. Each mode is a state of consciousness, not a piece of equipment.
### "Learning something new" vs. "confirming a suspicion":
- **New information** should feel like a small disturbance in the visual field. Not an alert — a *noticing*. Something shifts. The monologue chime (D-038) plays. Your eye is drawn. The visual design should make you feel like your character noticed something before you did.
- **Confirmation** should feel like settling. Like a piece clicking into place. Visually: an entity's color shifts smoothly to where you expected it would end up. The amber you suspected becomes the amber you know. No fanfare. Just the quiet weight of being right. The visual language of confirmation should be *stillness after movement* — the information stops being uncertain and becomes fact.
### The real question about information visualization:
It's not "how do we show data." It's: **what does it feel like to know something you can't unknow?**
When THE FRIEND's color shifts from green to amber (D-034), that's not a UI event. That's a philosophical event. The visual design of that moment should feel like the game's central question made visible: you looked too closely, and now this life — this warm, functional, settled life — will never look the same.
The entire information visualization system is in service of that single feeling. Everything else is engineering.
---
## Technical Constraints as Thematic Allies
I don't usually talk about engines. But the constraints here actually serve the themes, so they're worth naming.
### Godot 4's 2D pipeline
Godot's Light2D system supports exactly what I'm describing: zone-specific ambient lighting through CanvasModulate (warm base tint for the whole station), overlaid with Light2D nodes per zone (bluer in The Terminal, warmer amber in The Last Shift, dimmer uneven pools in corridors). The perception mode overlays I'm proposing — soft-edged awareness layers, not hard HUD rectangles — map naturally to CanvasLayer stacking with shader-driven bloom and transparency. Normal maps on environment tiles would let the same station geometry respond differently to different light sources, reinforcing the "same space, different truth" principle.
The fog of perception (D-011) using Godot's 2D shadow-casting with Light2D occluders is well-documented in the engine. The vision cone from D-015 can be a shaped Light2D. The key insight: in Godot's 2D pipeline, *darkness is the default*. You render what's known, and the unknown is simply... absent. That's thematically perfect. The world exists only where your character's attention reaches. Everything else is the question mark.
What this means for art direction: we should lean into Godot's strengths. Tile-based environments with atmospheric lighting. Shader effects for perception mode overlays (color shifting, edge glow, desaturation). Particle systems for ambient atmosphere (dust, recycled air, subtle station hum visualization). These are cheap in Godot and they carry mood.
### Nano Banana / Gemini 2.5 Flash asset pipeline
Here's where the constraints become genuinely useful. AI image generation is good at bold silhouettes, distinctive color palettes, and stylized assets. It's bad at consistency across hundreds of sprites when those sprites need subtle detail.
This *supports* the thematic argument I'm making. If our entities need to be silhouette-readable and color-coded (D-033), and our AI pipeline produces better results with bold shapes than fine detail, then the art direction and the production constraint are saying the same thing: **keep entities simple, let color and shape carry meaning.** Don't fight the tool. The tool is telling you what Rimworld already proved: projection works better than detail.
For environments — tiles, station infrastructure, zone decoration — the AI pipeline can generate base textures and tile variants, with manual cleanup for seaming and alignment. The "layers of modification" I described for the station (mismatched panels, rerouted conduits, repair patches) actually benefit from slight AI inconsistency — each generated tile variant looks slightly different, which reads as "different manufacturers, different eras of repair." What would be a bug in a polished AAA pipeline is a feature in a 40-year-old station's visual language.
For the neural insert overlay elements: soft glows, awareness blooms, organic data visualization — these should be shader-driven, not sprite-based. Keep the AI pipeline for tangible objects. Keep the insert ethereal through code.
**The production constraint and the thematic constraint converge:** simple entities, warm textured environments, shader-driven information overlays. The art direction I'm proposing isn't aspirational. It's what the tools want to make.
---
## Weather as the World's Opinion
Everyone else will think about weather as a visual effect or a gameplay modifier. I want to name what weather actually *is* in this game.
Weather is the world reminding you that you haven't mastered it.
In a game about "is this life enough?", weather is the voice of the environment itself — the one thing the characters can't plan around, can't investigate, can't control. It's the universe's indifference to your little conspiracies and your little routines. The smuggling ring doesn't matter to the dust storm. The detective's case file doesn't matter to the rain. Weather is scale. It says: you are small, this world is large, and it was here before you settled it.
That makes weather thematically essential, not cosmetic.
### Exterior weather as philosophical punctuation
Rain, dust, fog, snow — each is a different relationship between the character and the world.
- **Rain** is melancholy without menace. It reduces sightlines, softens edges, muffles sound. Top-down rain should read as a *curtain* — vertical streaks that slightly occlude entities below, the way real rain makes it harder to pick out faces on a street. Color palette shifts cooler, everything slightly desaturated. Rain is the weather of reflection. The quiet moment (D-039, wow moment #6) should feel twice as heavy in rain — idle in a corridor, rain audible through the hull, unprompted reflective monologue. Rain is "is this enough?" asked gently.
- **Dust storms** are hostility. Reduced visibility not as softness but as *abrasion* — warm-tinted particles, amber-brown palette shift, vision cone contracted hard. NPCs change routines. Exteriors become dangerous. Dust storms are the world saying "this was never yours." Top-down, a dust storm should make the station feel like a shelter — suddenly the warm lighting inside isn't just comfortable, it's *necessary*. The contrast between interior warmth and exterior hostility should make you feel what the settlers felt: gratitude for the walls.
- **Fog** is the most thematically loaded weather state. Fog is *uncertainty made visible*. In a game about asymmetric information, fog is the environment doing what the game's systems do — hiding things in plain sight. Fog doesn't create darkness. It creates *ambiguity*. Shapes at the edge of perception that might be people, might be infrastructure, might be nothing. The vision cone in fog should degrade gracefully — full clarity close, increasingly indistinct at range, entities becoming silhouettes becoming suggestions. Fog is the game's thesis as weather.
- **Snow** (on worlds that have it) is silence. Muffled sound ranges, high contrast between dark figures and white ground, the visual calm that makes any movement conspicuous. Snow is the weather that makes surveillance easier and hiding harder. Clean sightlines, clear footprints. Snow is the detective's weather.
### Interior weather effects as permeability
This is the part others might miss. The station isn't sealed off from the world — it's *settled into* the world. Interior weather effects are the visual language of that permeability.
- **Condensation on surfaces** when it's humid outside — a subtle sheen on metal walls, droplets on viewports. Purely atmospheric, but it says: the world outside is pressing in.
- **Ventilation drafts carrying particles** — dust motes in air currents, visible in light beams. Godot particle systems handle this natively. These particles should drift, not swirl. They're the station breathing.
- **Temperature cues** — during cold external weather, NPCs' breath might be faintly visible in corridors (less heated than main spaces). During heat, a slight shimmer near exterior bulkheads. These are subtle, shader-driven, but they ground the station in a physical world.
The thematic point: **the settled life is permeable.** You can build walls. You can create routine. You can make a home. But the world leaks in. The weather is the most literal expression of what the conspiracy is the metaphorical expression of: the outside world has opinions about your comfortable life, and it will make them known.
### Weather as information system
From a pure gameplay perspective: weather modifies the perception model (D-011, D-015, D-018). Reduced sightlines, contracted vision cones, muffled sound ranges, altered NPC routines. But thematically, weather does something more interesting than modify numbers.
Weather creates *shared vulnerability.*
When a dust storm hits, everyone's perception is degraded. The detective and the smuggler are equally blind. The NPCs are equally disrupted. For once, the information asymmetry isn't about *who knows what* — it's about *nobody knowing anything*. Weather is the great equalizer. And that creates opportunity: the smuggler moves cargo during the storm because nobody can see. The detective loses a tail because the fog rolled in. Weather introduces chaos into the information game, and chaos is where both characters find freedom and danger.
Visually, weather should never be *on top of* the game. It should be *woven into* the perception system. Rain doesn't add a rain overlay. Rain *is* a perception state — the vision cone contracts, entities blur at range, sound indicators shift. The player shouldn't think "it's raining." They should think "I can't see as far" and then realize why.
### Weather per world as identity
Different planets, different weather, different visual signatures. This is where weather becomes part of location identity — the way a city has a climate that shapes its character.
For Sova's planet specifically: I'd argue for a climate that's *mostly calm with dramatic exceptions*. The station should usually feel stable — clear sightlines, predictable conditions. The weather events should be *events* — disruptions to the settled routine. A dust storm isn't Tuesday. It's the day everything changed. This serves the pacing: the storyteller (D-005) could use weather as a dramatic tool, the way Rimworld uses raid timing. Clear skies for the 30-minute life-sim runway. Then the weather shifts when the conspiracy needs cover.
On other worlds (future scope): weather becomes a defining visual characteristic. A perpetually foggy world is a different game than a clear-sky world. A world with violent electrical storms has different routines, different architecture, different relationships to the outside. Weather is worldbuilding expressed as gameplay.
### Implementation note for Godot 4
Weather effects map cleanly to Godot's 2D pipeline: particle systems for rain/dust/snow/fog, CanvasModulate shifts for palette changes, shader-driven distortion for heat shimmer and condensation, Light2D range modification for visibility changes. Interior particle effects (dust motes, condensation) are low-cost ambient systems. The key: weather should modify existing perception shaders, not add new visual layers. Rain changes the parameters of the fog-of-perception shader (range, edge softness, entity clarity at distance). This keeps weather integrated with the information system rather than decorative.
---
## Summary: The Visual Thesis
The Settled Reach should look like a place worth living in, rendered with the visual attention of someone who suspects it might be a lie.
Warm. Readable. Quiet. Functional. Settled.
And underneath — not in the shadows, not in the dark corners, but in the exact same warm light — something that will change everything if you look at it the right way.
The art direction is the question. The player's eyes are the answer.
@@ -0,0 +1,488 @@
# Round 1 -- Miri's Worldbuilding & Setting Perspective
**Agent:** Miri (Worldbuilder & Setting Designer)
**Date:** 2026-02-12
**Workshop:** Art Direction & Mood Board
---
## Approach
I'm answering all five questions from the setting outward. My job here is to define what the Settled Reach's universe *demands* visually -- what materials exist, what light sources are plausible, what the built environment communicates about the civilization that made it, and where the IP originality guardrails sit. Araminta translates this into art direction. I provide the raw material.
Everything below is checked against D-036 (Sova Transit District), D-033 (entity colors), D-005 (single-character perspective), and my Round 1 Sova setting brief from the Content Gap Analysis workshop.
**Additional direction from the project lead** has been incorporated: XCOM 2, The Sims, and Rimworld as mandatory reference analysis; Godot 4 and Nano Banana as technical constraints; solar light color per star type as location identity; weather systems as gameplay-integrated visual layer; day/night cycles and dynamic shadows as essential, not optional.
---
## Q1: Visual References -- What Games/Films/Art Look Like What We're Making?
### References That Fit
**1. The Expanse (TV series) -- Ceres Station interiors**
What it gets RIGHT: Ceres is the closest visual analogue to Sova in existing media. It shows space infrastructure as *infrastructure* -- corridors with exposed utility runs, modular sections bolted together over decades, businesses occupying repurposed spaces. The mid-level areas of Ceres (not the fancy Medina level, not the slums) show exactly the economic register of Sova Transit District: functional, maintained, imperfect. People live here. They've put up signs. The ventilation works most of the time.
What it gets WRONG: Ceres skews too large and too vertical for our top-down perspective. The Expanse also leans into gravity-as-class-metaphor (spin gravity varies by level) which we don't use. And the show's visual language is firmly TV sci-fi -- higher production polish than our setting needs to communicate.
What we're referencing: **Material vocabulary.** The mix of metal paneling, institutional signage, retrofit wiring, and personal clutter in residential-adjacent spaces. The way a commercial district looks when it's 40 years old and nobody has fully renovated it.
**2. Alien (1979) / Alien: Isolation (2014) -- "Truckers in Space"**
What it gets RIGHT: Ridley Scott's Nostromo invented "used future" as cinematic language -- the idea that space technology is *equipment*, not spectacle. Alien: Isolation extended this into playable space with Sevastopol Station. Creative Assembly studied the original production designs and built a station where every surface feels like it was installed by a contractor, maintained by a crew, and slowly worn by use. The retro-future terminals, the institutional signage, the way lighting shifts between well-maintained corridors and neglected service areas -- this is the grammar we need.
What it gets WRONG: The Alien aesthetic is horror-coded. Dark corridors, flickering lights, shadows that suggest threat. Sova Transit District is *not* a horror space. People work here comfortably. The lighting is adequate. The horror, when it comes, is social and informational, not environmental. We need the material language of Alien without the horror lighting.
What we're referencing: **The principle of "industrial mundane."** Equipment has manufacturer labels. Corridors have utility markings. Signage is institutional, not decorative. The extraordinary technology (span gates, lattice networks) is integrated into mundane infrastructure the way electrical wiring is integrated into modern buildings -- present, functional, invisible until you look for it.
**3. Disco Elysium -- Mood as Visual Language**
What it gets RIGHT: Disco Elysium proved that a top-down investigation game can carry enormous atmospheric weight through art direction alone. The painted, expressionist style communicates emotional register before the player reads a word. Environments aren't just spaces -- they're character studies. The Whirling-in-Rags room isn't just messy; its visual chaos *is* the character's internal state. That integration of mood and environment is what we need.
What it gets WRONG: Disco Elysium's painterly style serves a very different game. Its visual density -- thick brushstrokes, saturated color, environmental storytelling in every corner -- works for a game where you click and examine. Our game has fog of perception, vision cones, and entity color systems that demand higher visual clarity. We can't sacrifice readability for expressionism.
What we're referencing: **The emotional palette principle.** Different zones have different color temperatures. Different times of day shift the mood. The investigation changes how you *see* the same space. And the portrait system -- Disco Elysium's character portraits communicate personality instantly through artistic interpretation, not photorealism.
**4. Cogmind -- Atmosphere Through Minimalism**
What it gets RIGHT: Cogmind demonstrates that a top-down game with an abstracted visual style can be *atmospheric*. Its ASCII-inspired aesthetic is deliberately minimal -- lots of black space, clean visual hierarchy -- and then particle effects, lighting, and sound create presence. When nothing is happening, the space reads clean. When something happens, it's immediately visible. This is exactly the visual information hierarchy our perception system demands.
What it gets WRONG: Cogmind's setting is a hostile facility. Its visual tension is environmental. Our tension is social and informational. We don't need Cogmind's sense of mechanical dread; we need its principle of *negative space as readability tool*.
What we're referencing: **Visual hierarchy through restraint.** The idea that a clean, readable baseline makes information events (color shifts, perception overlay changes, NPC behavioral tells) visually significant because they emerge from calm, not from visual noise.
**5. Rimworld -- Readability as Design Philosophy**
What it gets RIGHT: Rimworld's art style is the benchmark for "complex simulation, readable at a glance." Simple sprites reduce cognitive load. Visual hierarchy through outline weight (pawns > items > plants) means the eye finds what matters. The style doesn't try to be beautiful -- it tries to be *legible*, and in doing so becomes quietly elegant. Our game will have similar information density: NPC positions, movement patterns, entity colors, fog boundaries, perception overlays.
What it gets WRONG: Rimworld's flat, vector-style sprites have no atmosphere. No mood. No sense of place. A Rimworld colony on an ice sheet feels functionally identical to one in a jungle once you stop reading the labels. We can't afford that flatness -- Sova Transit District needs to *feel* like a specific place.
What we're referencing: **The information design principle.** Every visual element earns its pixel by communicating gameplay-relevant information. Color, outline weight, position, and animation state all serve readability first.
**6. XCOM 2 -- Information Under Pressure**
What it gets RIGHT: XCOM's fog of war is the closest mechanical analogue to our fog of perception in a tactical game. Unknown territory is visually darkened. Pod activation -- the moment aliens are revealed -- creates genuine tension because the player *knows* there are threats beyond their sightlines but can't see them. The camera system lets you rotate to check angles, reinforcing that information is spatial and directional. XCOM also demonstrates that cover and sightline mechanics are readable from a tactical camera when the visual language is clear: full cover vs half cover vs exposed reads instantly.
What it gets WRONG: XCOM is a combat game. Its fog of war creates *combat tension* -- you're scanning for enemies. Our fog of perception creates *investigative tension* -- you're scanning for behavioral signals, contradictions, evidence. The emotional register is different. XCOM's visual escalation (aliens revealed, combat initiated, explosions) is dramatic. Our visual escalation is subtle: an entity color shifts, a monologue triggers, someone is where they shouldn't be.
What we're referencing: **Sightline mechanics as readable gameplay.** The principle that what you *can't* see is as important as what you can, and that the boundary between visible and hidden should be visually crisp. Also the principle that fog should make revealed information feel *earned* -- you saw it because you moved to the right position.
**7. The Sims -- Daily Life as Readable System**
What it gets RIGHT: The Sims is the benchmark for making mundane daily routines engaging from an overhead perspective. It solves the exact problem our life-sim substrate (D-023 Tier 3) needs to solve: how do you make "NPC goes to work, eats lunch, visits bar, goes home" visually interesting? The Sims does it through readable animation states (you can tell what a Sim is doing from across the screen), needs indicators (color-coded satisfaction bars), and environmental interaction (Sims engage with objects in contextually appropriate ways). The plumbob is an elegant information device -- a single floating indicator that communicates mood at a glance.
What it gets WRONG: The Sims is god-game. The player controls from above with full information. We're single-character, limited perception. The Sims' visual language assumes omniscience -- you see every Sim's mood, every room simultaneously. Our visual language must assume *ignorance* -- you only see what your character sees, and even then you're interpreting. The Sims' cheerful, saturated color palette also doesn't fit our "quotidian-with-undertow" mood.
What we're referencing: **Readable NPC routines from overhead.** The principle that daily life can be visually engaging without dramatic events. Animation states that communicate activity at small scale. Environmental interaction that makes spaces feel *used*. And the needs/mood indicator concept -- simplified, diegetic (as lattice overlay data rather than floating icons), but the principle that NPC internal state can be visually hinted at.
**8. Return of the Obra Dinn -- Observation as Core Mechanic**
What it gets RIGHT: Obra Dinn proved that a distinctive visual constraint can make deduction *better*. Its 1-bit rendering forces the player to look harder, notice more, and construct understanding from limited visual information. The graphics "draw into sharp relief those all-important clues that might otherwise be lost in visual complexity." Our fog-of-perception system does the same thing mechanically -- the player sees only what their character can see. The visual style should reinforce that constraint, not fight it.
What it gets WRONG: Obra Dinn's aesthetic is inseparable from its historical maritime setting. The 1-bit rendering is a period-specific choice. Our setting is advanced civilization -- clean enough to have lattice implants, old enough to have 40-year-old patched walls. The visual style needs to communicate technological competence and temporal accumulation simultaneously.
What we're referencing: **The principle that visual restriction creates investigative focus.** When you can't see everything, what you *can* see matters more. Our fog system provides the mechanical restriction. The art style should make the visible zone feel rich enough to reward close observation.
**9. Syd Mead -- The Plausible Future**
Not a game reference, but essential for setting. Syd Mead's industrial design work -- particularly his space habitat interiors and transit system concepts -- embodies the aesthetic register of the Settled Reach. Mead always asked "How does this world work?" before drawing it. His futures feel inhabitable because they follow functional logic: surfaces exist for structural reasons, lighting serves practical purposes, spaces are designed for use rather than for looking impressive.
What we're referencing: **The design philosophy.** Advanced technology that looks *designed*, not fantastical. Equipment with manufacturer logic. Spaces built for function first, adapted for comfort second, decorated third. This is the Settled Reach's material culture: competent, pragmatic, lived-in.
**10. Tactical Breach Wizards (Suspicious Developments / Tom Francis) -- Clarity as Design Origin**
What it gets RIGHT: TBW was born directly from Tom Francis's frustration with XCOM 2's clarity problems -- "This is so good, and yet has so many clarity problems. I wish there were more indie XCOMs." The result is a game where every tactical decision is visually legible. Room layouts read instantly: walls, windows, doors, and sightlines are all unambiguous from the top-down perspective. Entity silhouettes are bold and distinct -- you know which character is which at a glance, even at small scale. Sightline angles are not just visible but *mechanically significant* (they determine knockback direction), which means the visual representation of spatial relationships is load-bearing, not decorative.
The art style threads a needle we need to thread: stylized enough that it doesn't compete with AAA visual fidelity, detailed enough that characters have personality and rooms have character. John Roberts' art direction uses a "tactical + wizard + personality" triad that produces designs readable from above while still being expressive. The characters communicate identity through silhouette and color, not through fine facial detail -- exactly the constraint our top-down, small-scale entities face.
What it gets WRONG: TBW is a puzzle-tactical game. Its rooms are *designed* to be read -- every element exists for a mechanical reason. Our spaces are *lived-in* -- they contain furniture, personal items, and environmental texture that exists for atmosphere, not tactics. TBW's visual clarity comes partly from the fact that non-mechanical elements are stripped away. We can't do that -- the mundane objects ARE the atmosphere of Sova Transit District. We need TBW's clarity for *entities and sightlines* while adding environmental richness that TBW deliberately avoids.
What we're referencing: **Clarity as a design origin, not a design compromise.** The principle that information readability isn't the thing you sacrifice for atmosphere -- it's the thing you build the art direction around. Room layouts that read instantly. Entity silhouettes that differentiate at a glance. Sightlines that are visually obvious. And the proof that a stylized, non-photorealistic art style can achieve all of this while still having personality.
### References to Actively Avoid
| Reference | Why to avoid | IP risk |
|---|---|---|
| Blade Runner / cyberpunk neon | Rain, neon, noir lighting is coded "dystopia." Sova is not dystopic. It's economically stable. The wrongness is social, not environmental. | Low IP risk but high genre-coding risk. Players will expect cyberpunk stories. |
| Star Trek (any era) | Antiseptic corridors and uniforms suggest utopian institutional uniformity. The Reach has institutions, but they're *bureaucracies*, not ideals. | Low risk -- our setting already diverges completely. |
| Star Wars | Romantic visual language -- dramatic lighting, operatic scale, clear good/evil coding in design. Our investigation mechanic requires visual ambiguity. | Moderate risk if we're not careful with span gate visuals. Our gates are infrastructure, not spectacle. |
| Mass Effect (Citadel) | Clean, gleaming future-city. The Citadel is a tourist destination. Sova is a freight district. Nobody visits Sova for the scenery. | Low risk. |
| Dead Space | Grimy industrial horror. Similar material language to Alien but pushed into body-horror visual territory. Wrong emotional register entirely. | Low risk. |
---
## Q2: Mood and Atmosphere -- What Emotional Register?
### The Setting's Visual Thesis
**Sova Transit District looks like a place where you could work a decent job and have an ordinary life.** That's the baseline. The visual mood must make "quiet life is good" feel genuinely appealing before "something is wrong here" can land.
Setting note -- this is the "quotidian-with-undertow" principle from D-036. The surface must read as authentic normality. The undertow is informational, not visual. The player discovers wrongness through *observation and deduction*, not through environmental signposting. If the lighting tells you "this area is suspicious," we've undermined the core mechanic.
### Lighting Language
The setting prescribes specific light sources that constrain the visual design:
**Institutional zones (The Terminal, Commission areas):**
- **Source:** Overhead panel lighting. Institutional standard -- bright, even, slightly cool white. The kind of lighting that makes everything legible and nothing atmospheric.
- **Mood:** Competent neutrality. This is where the system works. Forms get filed. Manifests get checked. The lighting says "nothing to see here" -- and for the smuggler, that's the point.
- **Color temperature:** Cool white, ~5000K equivalent. Institutional.
**Social zones (The Last Shift, break rooms):**
- **Source:** Mixed. The bar has warmer, cheaper fixtures -- amber-toned wall sconces, the glow from display screens, a Meridian feed casting shifting light. The break room is dimmer than the work floor -- fluorescent panels at reduced output, personal items creating visual texture.
- **Mood:** Warmth by contrast with institutional spaces. Not cozy in an absolute sense, but relatively warm. This is where people relax. Where conversations happen. Where the detective observes and the smuggler socializes.
- **Color temperature:** Warm, ~3000-3500K. Human spaces.
**Service corridors and older sections:**
- **Source:** Older fixtures, irregularly maintained. Some corridor sections have been retrofit with newer panels; others still run original 40-year-old lighting that casts slightly different color temperatures. Emergency guide strips along the floor. Maintenance areas may have manual-switch lighting -- dark unless activated.
- **Mood:** This is where "layers" lives. Not horror-dark, but less thoroughly maintained. The kind of space where you notice things the institutional lighting washes out. Where conversations can happen without being observed.
- **Color temperature:** Mixed. Older warm fixtures alongside newer cool replacements. The *inconsistency* is the visual signal that says "this space has history."
### Day/Night Cycle and Dynamic Shadows
**Lighting is not polish. It is infrastructure.**
The project lead has confirmed that day/night cycles, shadow casting, and dynamic shadows are essential. From a setting perspective, this is exactly right. Here's why:
**Station lighting follows the day-phase model (D-031).** Four phases -- Morning, Afternoon, Evening, Night -- drive NPC routines. The lighting must change with them. Not just brightness: *color temperature shifts* across the day cycle. Morning shift: institutional cool-white at full output, the station at its most awake and official. Afternoon: same fixtures, but the rhythm of activity produces different light patterns (more open doors, more bodies blocking overhead panels, more terminals active). Evening: social spaces warm up (The Last Shift's lighting activates as patronage increases), work areas dim to maintenance levels. Night cycle: reduced ambient, emergency strips prominent, security lighting at span gate terminal, the district at its quietest and most atmospheric.
**Dynamic shadows serve the perception system directly.** Godot 4's Light2D and LightOccluder2D nodes give us real-time shadow casting from point and directional light sources. Walls cast shadows. Entities cast shadows. This isn't cosmetic -- it's mechanical. In our shadowcasting-based fog of perception (D-011, D-035), what you can see is defined by what blocks your line of sight. Dynamic visual shadows that align with the LOS computation make the fog feel *physically motivated*. The player sees a shadow falling across a corridor and understands viscerally: "I can't see around that corner."
**Setting-grounded light sources for Godot 4 implementation:**
| Light source | Type | Color | Behavior |
|---|---|---|---|
| Overhead panels (institutional) | Area/PointLight2D | Cool white (~5000K) | Steady. Full output during shifts. Dimmed at night cycle. |
| Span gate glow | PointLight2D | Cool blue-white, subtle pulse | Always on. Intensity varies with gate traffic. The district's heartbeat, visible as light. |
| Bar fixtures | PointLight2D | Warm amber (~3000K) | On during operating hours. Creates warm pool distinct from corridor light. |
| Terminal/display screens | PointLight2D, small radius | Blue-white, slight flicker | Active when in use. Provides ambient screen-glow in dim areas. |
| Emergency guide strips | DirectionalLight2D, low intensity | Soft white-blue | Always on. More visible when ambient is low. |
| Maintenance manual-switch | PointLight2D | Institutional white | Off by default. Activated by NPC or player interaction. Dark areas until lit. |
**Shadow implications for gameplay:**
Shadows are *information*. A shadow moving at the edge of the player's vision cone could be an NPC passing through an adjacent corridor. The player can't see the NPC directly, but they can see the shadow -- and then decide whether to investigate. This creates the medium-range visual information tier that complements D-018's three-range sound model. Close range: see directly. Medium range: see shadows, hear sounds. Long range: lattice data only.
### Color Temperature as Narrative Layer
Here's the setting-driven insight for Araminta: **the smuggler and the detective experience the same lighting differently because of what it means to them.**
- The smuggler sees the warm bar light as *home*. Safety. Social belonging. The cool institutional light of the logistics hub is *work* -- neutral, functional. The dim corridors are *operational space* -- comfortable, familiar.
- The detective sees the warm bar light as *cover*. A social mask. The institutional light is *their territory* -- where their authority functions. The dim corridors are *unknown variables* -- spaces where anything could be happening.
The lighting doesn't change between playthroughs. The *perception overlay* does. D-033's entity color system already provides subjective coloring of NPCs. The visual mood should support this: the same physical light, interpreted through different character lenses.
### What "Something Is Wrong" Looks Like
It doesn't look like anything. That's the point.
The "something is wrong" signal is *behavioral*, not environmental. An NPC in the wrong place at the wrong time, flagged by the character's monologue (D-016). An entity color shifting from green to amber (D-033). A contradiction between what someone said and where they are.
**The visual environment should never tip its hand.** No ominous shadows on the smuggling corridor. No red lighting in the criminal meeting spot. The setting demands that wrongness be invisible to the casual eye and discoverable only through the perception systems. If we light the smuggling route differently from the legitimate corridors, we've told the player the answer before they asked the question.
The one visual shift I'd endorse: **time of day affecting ambient color temperature.** Morning shift is brighter, more populated. Evening is warmer, dimmer. Night cycle is reduced lighting with emergency strips more visible. This gives the environment temporal rhythm without telegraphing narrative.
---
## Q3: Top-Down Art Style Direction
### What the Setting Demands
I'll defer to Araminta on specific technique (pixel, vector, painted, rendered). What I can contribute is what the setting requires the art style to *communicate*:
**1. Technological competence without spectacle.**
The Settled Reach is an advanced civilization. Neural lattices. Span gate networks. The Meridian as ubiquitous information infrastructure. But none of this technology is *visually dramatic* in daily life. A lattice is invisible -- it's inside your head. The Meridian is like WiFi -- you use it constantly without seeing it. Span gates are large infrastructure but they're *transit equipment*, not monuments.
The art style needs to suggest advanced technology through small details: the glow of a lattice interface when someone checks their overlay, the subtle visual indicators of Meridian connectivity, the industrial precision of span gate machinery. Not through dramatic sci-fi visual language.
**2. Material accumulation over time.**
Sova Transit District is 40 years old. The art style must communicate temporal layering: original prefab structure (uniform, modular, institutional grey), early modifications (different materials, visible seams), recent patches and personal touches (signage, decoration, wear patterns). This is the visual story of a place that has been *continuously inhabited and modified*.
From a top-down perspective, this means: floor materials that change between sections. Wall textures that shift from uniform to heterogeneous. Equipment that ranges from new-installation clean to decades-old patina.
**3. Readable entity states at small scale.**
D-033's entity color system, D-011's fog of perception, D-015's vision cone -- these all require entities to be visually readable at small scale. NPCs must be distinguishable from furniture. Moving entities must be distinguishable from static ones. Entity colors must be clearly visible against the environment.
Setting implication: the environment should be *tonally muted* so that entities (colored per D-033) stand out. The station's material palette -- institutional greys, industrial metals, worn surfaces -- naturally provides this. The environment is the canvas; the entities are the paint.
**4. The floor tells the story.**
In top-down, the floor is the dominant visual surface. The setting gives us rich floor vocabulary: metal grating in industrial areas, sealed composite in institutional spaces, worn tile in social areas, exposed structural plating in maintenance corridors. Scuff marks. Cargo tracks. Guide markings. Spills that haven't been cleaned. This is where "lived-in" lives in our perspective.
### Technical Constraints: Setting Meets Engine
The project lead has flagged two technical constraints: **Godot 4** as our renderer and **Nano Banana (Gemini 2.5 Flash)** as our asset generation pipeline. The setting needs to work within both.
**What Godot 4's 2D pipeline gives us:**
Godot's 2D rendering is tilemap-native, shader-capable, and has robust Light2D/shadow support. This aligns well with what the setting demands:
- **Tilemaps** for the three-era floor vocabulary. Different tile sets per construction era -- original prefab, expansion-era modification, recent commercial. Tile variation within sets prevents visual repetition. The setting's material layering maps naturally to tilemap layers.
- **Light2D + LightOccluder2D** for the dynamic shadow system. Walls and entities as occluders. Point lights for every fixture. This gives us the shadow-as-information system described in Q2.
- **CanvasLayer stacking** for the lattice overlay. Base world on one layer, fog of perception mask on another, lattice HUD elements on a third. The setting's distinction between physical world and lattice overlay maps to Godot's rendering architecture.
- **Shaders** for perception mode overlays (future). Thermal vision as a screen-space color remap shader. Camera feed as a viewport texture with institutional post-processing. These are achievable in Godot's shader language.
- **Particle systems** for environmental atmosphere -- span gate glow particles, ventilation draft effects, weather particles (see Weather section below).
**What Nano Banana means for the setting's visual identity:**
AI image generation is strong at: bold silhouettes, distinctive color palettes, consistent style within a single generation, atmospheric backgrounds, icon-style assets. It is weak at: consistency across hundreds of sprites with identical proportions, fine detail at small pixel scales, exact color matching between separately generated assets.
Setting implication: **the art direction should favor distinctive shapes and color coding over fine detail.** This aligns with the readability-first principle from Rimworld analysis (reference #5) and with D-033's color system. If NPCs read as silhouettes-with-color rather than detailed portraits-at-small-scale, both the perception system and the asset pipeline benefit.
For environmental tiles: Nano Banana can generate tile textures (metal panels, worn surfaces, grating, composite floors) that are then assembled into tilemap sets. Style consistency within a material type is achievable. Cross-material consistency (making sure a metal panel and a composite floor look like they belong in the same world) requires a style guide with explicit color palette and texture density constraints -- this is Araminta's deliverable, but the setting provides the material vocabulary.
For entity sprites: bold outlines, distinctive body shapes, color-as-relationship (D-033). The setting's NPC design supports this -- dock workers are physically distinct from administrative staff (build, posture, clothing), which is readable even at small scale with bold art direction.
### Animation Note
Setting consideration for animation: in the Settled Reach, people move like people. No exaggerated sci-fi gaits. Workers carry things, lean on surfaces, gesture in conversation, check their lattice overlays (a subtle hand-to-temple gesture or unfocused gaze). The technology is invisible; the behavior is human. Animation should prioritize *behavioral readability* -- can you tell what an NPC is doing from their animation state? -- over visual fidelity.
---
## Q4: The Station as Character
### What "Lived-In Space Infrastructure" Looks Like
Let me check this against what I established in the Sova setting brief.
Sova Transit District is prefab-modular-retrofitted. Three construction eras visible:
**Era 1 (Original, ~40 years ago):** Prefab modular construction. Uniform wall panels, standard ceiling heights, institutional color coding (possibly faded). Built quickly during Station Sova's logistics expansion. The bones of the district. Still visible in structural elements: load-bearing walls, main corridor routing, utility conduit placement.
**Era 2 (Expansion, ~20-30 years ago):** Modifications to accommodate growing population and shifting use. New partitions subdividing original spaces. Retrofit Meridian junction points (visually distinct from built-in infrastructure -- surface-mounted rather than wall-integrated). Ceiling modifications for additional ventilation or cabling. Different wall materials where new sections join old.
**Era 3 (Recent, ongoing):** Personal and commercial modifications. The bar's interior is distinct from the corridor outside because Lera (the owner) has made it *hers*. Locker personalization in the break room. Hand-lettered signage alongside institutional standard. Maintenance patches in different materials. This is the *living layer* -- the human fingerprint on institutional architecture.
In top-down: these eras read as **changes in floor material, wall thickness/style, lighting consistency, and object density.** Original corridors are wider, more uniform. Modified sections are narrower, more cluttered, more varied in surface treatment.
### Visual Differentiation of Three Zones
**The Terminal (Logistics Hub)**
- **Floor:** Industrial composite with cargo guide markings. Scuff patterns from grav-lift traffic. Clear movement lanes.
- **Walls:** Institutional paneling, mostly original with some retrofit sections. Commission signage. Safety markings. Manifest display terminals mounted at intervals.
- **Objects:** Heavy -- containers, grav-lifts, processing terminals, lockers, industrial equipment. High object density in work areas, open transit lanes between.
- **Lighting:** Bright, even, institutional. The best-maintained lighting in the district.
- **Mood:** Productive, impersonal, systematic. This is where the system operates. The visual language says "things work here."
**The Last Shift (Bar)**
- **Floor:** Older tile, worn smooth, possibly a different color than the corridor outside (the bar occupies a converted commercial space). A slight warmth to the material.
- **Walls:** Owner-modified. Possibly paneling over original structure. Personal touches: a Meridian display showing the news ticker, some decoration (not much -- Lera has taste but not budget), the bar itself as a visual anchor.
- **Objects:** Social -- tables, chairs, the bar counter, bottles/containers, personal items left by regulars. The corner booth (important for gameplay -- discreet conversations) should be visually identifiable as semi-private.
- **Lighting:** Warmer, dimmer than the Terminal. Intentionally different. This is a *chosen* atmosphere, not institutional default.
- **Mood:** Human warmth in an industrial setting. The visual contrast between the bar and the corridor outside should be immediately legible: you step from institutional grey into amber warmth. This is where off-shift life happens.
**Maintenance Corridors / Smuggling Spaces**
- **Floor:** Exposed structural grating. Utility markings that don't match current use (old routing labels, sealed access points). Narrower than main corridors.
- **Walls:** Mixed-era construction visible. Original structure exposed in places. Retrofit panels that don't quite match. Cable runs and utility pipes visible. Some sections that don't appear on current official maps (important for gameplay -- the smuggling ring's operational spaces).
- **Objects:** Sparse -- utility equipment, storage containers (some legitimate, some... not), junction boxes, ventilation units. Less clutter than the Terminal, but less intentional than the bar.
- **Lighting:** Inconsistent. Some sections well-lit (recently maintained), others dimmer (lower maintenance priority). Emergency guide strips more visible here because ambient lighting is lower. Manual-switch sections that are dark by default.
- **Mood:** Functional neglect. Not abandoned, not dangerous -- just *lower priority*. The kind of space that maintenance crews visit on schedule and nobody else thinks about. The visual language says "this space exists for infrastructure, not for people." Which is precisely why it's useful for people who don't want to be observed.
### "Layers" Without Grimy-Dystopia
Setting note -- this is an IP originality issue. "Space station with visible history" can easily slide into Blade Runner territory (decay as aesthetic) or Dead Space territory (deterioration as threat). Neither fits.
The Settled Reach's visual language for accumulation should be **renovation, not decay.** Things aren't falling apart. They've been *fixed*, repeatedly, by different people with different materials. The seams are visible not because the station is failing but because it's been *maintained by a living community over decades.* A patched wall isn't a wound -- it's a repair. A mismatched ceiling panel isn't neglect -- it's a replacement that came from a different manufacturer.
This distinction is critical: **Sova reads as lived-in, not run-down.** The aesthetic is closer to a 40-year-old apartment building that's been well-maintained but never fully renovated -- you can see the history, but the place works fine.
---
## Q5: Information Visualization
### The Neural Lattice as Aesthetic Object
Let me ground this in setting logic.
The neural lattice is not a cyberpunk brain-chip with holographic displays. It's a mature technology that's been standard-issue for over a century. It works the way a phone works for us -- ubiquitously, casually, unreflectively. The lattice overlay is not dramatic. It's not flashy. It's *information presented in a format the user's brain has been trained to process since childhood.*
Setting implication: the lattice overlay should feel **native to cognition**, not like a HUD bolted onto reality. Thin, precise, slightly translucent. Information appears where you need it and fades when you don't. The aesthetic is closer to an extremely refined AR interface than to Iron Man's helmet display.
**Baseline lattice (smuggler):** Minimal overlay. Time display. Basic navigation waypoints for known locations. Meridian message notifications. Simple, clean, unobtrusive. The smuggler deliberately keeps their lattice output minimal -- less overlay means less monitoring surface.
**Augmented lattice (detective):** Richer overlay. Same base elements plus: case file annotations pinned to locations, Commission database query results, enhanced audio analysis readouts, person-of-interest flagging. The detective's visual field has more *institutional information* layered over the same physical environment. This is the core visual divergence between the two characters -- same world, different information density.
### Perception Mode Overlay Aesthetics
For v0.1, only natural vision (D-017). But the setting can inform the future aesthetic language:
- **Natural vision:** No overlay modification. What you see is what's there. The cleanest visual state.
- **Thermal (future):** Setting logic -- bionic thermal imaging. Should feel like processed sensor data, not magic vision. Color-mapped heat signatures with clear artificial quality. The player should understand they're looking at *interpreted data*, not reality.
- **Camera feeds (future):** Setting logic -- tapping into Station Sova's security infrastructure. Fixed-position views, institutional quality, possibly time-stamped. The aesthetic should feel like *watching surveillance footage*, not like being in two places at once.
- **Audio analysis (future):** Setting logic -- lattice processing of audio input. Directional indicators, sound classification text, pattern matching results. This is the most *data-like* perception mode -- information presented as analysis, not as visual.
Each mode should have a visually distinct overlay signature so the player immediately knows which perception they're using. But they should all feel like *tools* -- processed information from a device -- not like supernatural abilities. The lattice is technology. It has a manufacturer's design language. It presents information the way engineered systems present information: clearly, precisely, with acknowledged limitations.
### "Learning Something New" vs "Confirming a Suspicion"
Setting-grounded approach:
**New information** is the lattice flagging something the character hasn't encountered before. The visual language should be *notice* -- a brief highlight, a new annotation appearing, the monologue chime (D-038). It's a moment of "oh, that's interesting." Not dramatic. The character's lattice has logged an observation. The player sees the information appear in the overlay for the first time.
**Confirmation** is the lattice matching new information against existing knowledge (the knowledge graph, D-041). The visual language should be *connection* -- a link forming between two pieces of information, a annotation updating, possibly the knowledge confidence level shifting (Suspects -> KnowsOf). It's a moment of "I knew it." The emotional weight comes from the *content* of the confirmation, not from visual fireworks.
**Contradiction** is the most important visual event -- when observed reality conflicts with reported information (THE FRIEND's arc, D-034, wow moment #3). The visual language should be *disruption* -- the urgent monologue chime (D-038), the entity color shifting (D-033), and something in the overlay that indicates *these two pieces of information cannot both be true.* This is the only moment where the information visualization should feel genuinely unsettling.
Setting justification: the lattice is a precision instrument. It presents information accurately. When accurate information contradicts other accurate information, the system doesn't know what to do -- and that uncertainty should be visible. The human equivalent is the stomach-drop moment when you realize someone lied to you. The lattice equivalent is a brief destabilization of the overlay -- not a glitch, but a *processing pause* while the system reconciles conflicting data.
---
## Solar Light Color: Star Type as Location Identity
**This is my domain.** The project lead is right that solar light color defines a location's mood before any other visual element. Let me ground this in the astrophysics and then map it to visual design.
### The Krenn System (Sova's Star)
D-036 establishes the Krenn System as a G3V star -- a yellow-white main-sequence star slightly cooler than our Sun (which is G2V). This is deliberate: a G3V produces light that reads as *familiar but not identical to Earth sunlight*.
**Krenn System solar color:**
- **Effective temperature:** ~5,700K
- **Visual character:** Warm white with a slight golden cast. Not as warm as a K-type orange, not as neutral as an F-type blue-white. This is "comfortable sunlight" -- the kind of light that makes a place feel *habitable* and *normal*.
- **Station implications:** External viewports and any sections with solar exposure receive this warm-white light. Interior sections rely on artificial lighting, but the ambient solar bleed through station hull and transparent sections gives Sova's "dayside" a warm-neutral base tone. The span gate terminal, which faces the system's primary traffic lane, may have viewport sections where Krenn's light filters in.
- **Artistic direction:** Krenn's light is the *neutral reference point* for the game. When the player is in the Krenn System, colors are "true." This is home. Other systems' light should be measured against Krenn as the baseline.
### A Spectrum of Systems: Solar Color as Design Tool
The Settled Reach spans hundreds of star systems. Star type defines the ambient light of every location. Here's the worldbuilding framework for major system types, mapped to visual implications:
| Star type | Color temp | Visual color | Mood/feel | Example use case |
|---|---|---|---|---|
| **F5-F8V** (hot yellow-white) | 6,200-6,600K | Bright blue-white, crisp | Prosperous, institutional, slightly cold. Core systems with old money. | Concord Assembly capital, major Syndic headquarters. Light feels clean, efficient, slightly clinical. |
| **G0-G4V** (solar-type) | 5,600-6,000K | Warm white, golden cast | Familiar, comfortable, "normal." Mid-Reach prosperity. | Krenn System (Sova). The baseline. |
| **G8-K2V** (cool yellow to orange) | 4,900-5,400K | Noticeably warm, golden-amber | Autumnal, nostalgic, slightly melancholy. Older settlements. | Long-established mid-Reach systems. Light feels like permanent late afternoon. Everything has a warm filter. |
| **K4-K7V** (orange dwarf) | 4,000-4,600K | Deep amber-orange | Intimate, warm, slightly alien. The light feels *different*. | Frontier-adjacent systems. Settlers here have adapted to permanently warm-toned light. Colors shift -- blues look muted, reds are enhanced. |
| **M0-M3V** (red dwarf) | 3,200-3,800K | Deep red-orange | Unsettling, alien, low-energy. Dim. Everything is bathed in red. | Deep frontier, fringe settlements. Stations rely more on artificial light because solar output is weak. The red ambient creates a fundamentally different visual experience. |
| **B/A-type** (blue-white giants) | 8,000-30,000K | Harsh blue-white | Industrial, cold, overwhelming. These stars are dangerous and beautiful. | Resource extraction systems near hot stars. The light is too bright, too blue. Stations are heavily shielded. Interior light is artificial by necessity. |
**The wormhole transition moment:** When the player steps through a span gate from one system to another, **the light changes first.** Before any new NPC appears, before any new architecture is visible, the ambient solar color shifts. Stepping from Krenn (warm white) to a K-type system (deep amber) should feel like stepping from noon into permanent sunset. This is the cheapest, most powerful location-identity tool we have -- a global color temperature shift applied as a CanvasModulate or shader tint in Godot 4.
### Setting Implications for Art Direction
**The muted environmental palette I proposed in Q3 becomes essential here.** If the environment tiles are tonally muted (greys, metals, worn surfaces), then a solar color shift tints the *entire scene* without creating color conflicts. A warm-amber K-type sun turns grey metal panels golden. A red M-dwarf turns them ruddy. The same tile assets work across systems because the *light* does the differentiation, not the material.
This is both a setting principle and a practical pipeline benefit: we don't need system-specific tile sets. We need one material vocabulary and a per-system lighting profile.
**IP note:** Star-type-based lighting is astrophysically grounded, not franchise-specific. No IP concern. If anything, most sci-fi franchises *ignore* star type entirely (Star Wars and Star Trek almost never vary solar color by location). Making stellar classification visually meaningful is a differentiator.
---
## Weather Systems: Atmosphere as Gameplay
The project lead has placed weather in scope. Let me ground this in setting.
### Weather on Sova
Station Sova orbits a terrestrial planet in the Krenn System. The planet (not yet named -- this is a worldbuilding gap I'm flagging) has an atmosphere. Weather on Sova depends on two factors:
1. **Is the player in a station interior or exterior/docking section?** Most of the v0.1 vertical slice is interior. But the span gate terminal has exterior-adjacent sections (loading docks, approach corridors with viewports, possibly open-air sections near the planetary freight depot).
2. **How does exterior weather penetrate the station?** Through atmospheric effects: humidity, pressure changes, temperature shifts, particle infiltration. A rain storm on the planet side means condensation on cold station surfaces, higher humidity in poorly-sealed sections, and the sound of rain on hull plating in exterior-adjacent areas.
**Krenn's planet -- setting establishment:**
Let me define this now since it's needed. The Krenn System's habitable world (the planet Station Sova serves):
- **Type:** Terrestrial, Earth-like atmosphere, ~0.9G
- **Climate zone near Sova's surface connection:** Temperate-maritime analog. Moderate rainfall, occasional heavy storms, mild temperature range. Think the kind of weather that makes freight workers grumble but doesn't shut down operations.
- **Signature weather:** Regular rain patterns. Fog that rolls in during temperature transitions (morning, evening). Occasional heavy squalls that reduce visibility at exterior docking areas and create distinctive sound inside the station.
- **Visual identity:** Krenn's weather is *ordinary*. It rains. Sometimes a lot. The fog is atmospheric, not threatening. This is "working weather" -- the kind that makes you check the forecast before loading cargo, not the kind that makes you fear for your life.
### Weather as Visual System
**Exterior / docking sections:**
| Weather state | Visual effect (top-down) | Gameplay impact | Mood |
|---|---|---|---|
| **Clear** | Full visibility. Krenn sunlight at full color. Sharp shadows. | Baseline perception range. No modifiers. | Open, exposed, institutional. |
| **Overcast** | Reduced shadow contrast. Flatter lighting. Slight color desaturation. | Minor perception range reduction at long distance. | Mundane, working-day. Most common state. |
| **Rain (light)** | Particle overlay -- small droplets on screen. Puddle reflections on floor tiles. Slight fog at vision cone edges. | Reduced sound range (rain noise masks footsteps). Minor sightline reduction. | Atmospheric, immersive. Good cover for quiet movement. |
| **Rain (heavy)** | Dense particle overlay. Reduced visibility radius. Splash effects on surfaces. Viewport sections show streaking water. | Significant sightline reduction. Sound masking. NPC routines shift (fewer exterior workers, dock operations slow). | Tense, isolated. The station feels smaller. |
| **Fog** | Vision cone reduced. Gradual fade at edges instead of sharp cutoff. Entities at distance appear desaturated before disappearing. | Major perception reduction. Sound propagation unchanged (fog doesn't muffle sound in reality). | Eerie, investigative. Perfect for "something moving in the fog" moments. |
| **Storm** | Heavy rain + intermittent lightning flashes (global PointLight2D pulse). Thunder as sound event. | Maximum perception reduction exterior. Interior sections unaffected except atmospheric bleed. Station-wide NPC routine disruption. | Dramatic. Rare. Reserved for storyteller escalation moments. |
**Interior weather effects:**
Even inside the station, the player should *feel* exterior weather through environmental cues:
- **Condensation:** In humid weather, cold surfaces (metal walls, equipment near exterior hull) show moisture. Visual: subtle texture overlay on certain tiles near exterior boundaries.
- **Humidity haze:** Poorly ventilated sections (maintenance corridors) develop a slight visual haze during rain. Particle effect: very light, very subtle. Not fog -- just a sense of dampness in the air.
- **Ventilation drafts:** The station's air circulation carries weather information. Dust/moisture particles drifting in corridors (Godot 4 GPUParticles2D, very low density). The player sees: "the air is moving here." In gameplay terms, this could indicate ventilation routes -- information about the station's hidden infrastructure.
- **Sound bleed:** Rain on hull plating is audible in exterior-adjacent sections. Muffled, ambient, atmospheric. The player knows it's raining outside without seeing it directly. Interior ambient soundscape shifts.
### Weather as Information Modifier
This is where weather connects to the core mechanic:
- **Rain reduces sightlines.** In exterior areas during heavy rain, the vision cone shrinks. NPCs conducting clandestine meetings at the loading docks during a rainstorm are harder to observe. The smuggler might *choose* to schedule operations during bad weather. The detective might notice that the ring's activity correlates with rain.
- **Fog disrupts visual tracking.** You saw an NPC enter the fog at the dock perimeter. You don't see them emerge. Where did they go? Fog turns the exterior into a soft-boundary maze.
- **Sound masking.** Rain noise covers footsteps and conversations. D-018's three-range sound model is compressed in rain -- close range still works, medium range is degraded, long range is unreliable.
- **NPC routine disruption.** Heavy weather changes NPC schedules. Dock workers take cover. The bar fills up earlier. Exterior routes are abandoned. This creates opportunities and risks for both characters.
**Weather is NOT random cosmetic.** It's a gameplay system driven by the simulation, with defined effects on perception, sound, NPC behavior, and player strategy. The storyteller (D-005) should be able to influence weather timing for dramatic pacing -- a fog rolling in during a crucial surveillance window, a storm isolating the district during the contamination event.
### Weather Per System (Future)
Different star systems have different weather. This is a long-term worldbuilding tool:
- **K-type system frontier world:** Constant wind, dust storms, amber light filtering through atmospheric particulates. "Perpetual dust-haze" as visual identity.
- **M-dwarf tidally locked world:** Permanent twilight zone at the terminator. No weather cycle -- constant conditions. Eerie stillness.
- **Gas giant moon station:** No planetary weather, but radiation storms from the primary. "Weather" is electromagnetic, not atmospheric -- affects electronics and lattice connectivity rather than visibility.
- **High-gravity world:** Dense atmosphere, thick clouds, reduced visibility as baseline. Heavy rain is the norm, not the exception.
Weather becomes part of location identity alongside solar color. When you step through a span gate: the light changes, the weather changes, the *feel* of existence changes.
---
## IP Originality Flags
Checking the visual references and proposals above against our guardrails:
| Element | Assessment |
|---|---|
| "Used future" / "truckers in space" visual language | **Clear.** This is a general sci-fi aesthetic tradition, not owned by any franchise. Our application (mid-Reach logistics, not horror or military) is distinct. |
| Lattice overlay as HUD | **Watch carefully.** Must not look like Mass Effect's Omni-tool, Cyberpunk 2077's braindance interface, or any specific franchise's neural interface. Design should be original -- subtle, integrated, not holographic-projector style. |
| Station corridor visual grammar | **Clear.** "Space station corridors" is a shared visual vocabulary. Our specifics (three construction eras, Krenn System signage conventions, Commission institutional branding) make it original. |
| Color-coded entity relationships | **Clear.** Many games use color for entity states. D-033's specific mapping (relationship-to-player, not objective property) is mechanically distinct. |
| Perception mode overlays | **Watch carefully.** Thermal vision in games is common (Predator, Batman, Splinter Cell). Our implementation must feel like *setting-specific technology*, not generic game-vision modes. The lattice as intermediary device is our differentiator. |
| Fog of perception aesthetics | **Clear.** Shadowcasting-based fog is a roguelike tradition. Our application (vision cone, information decay, NPC-symmetric) is mechanically distinct even if the visual implementation is conventional. |
| Solar color per star type | **Clear.** Astrophysically grounded. Most sci-fi franchises ignore this entirely. Making it visually meaningful is a differentiator, not a borrowing. |
| Weather as gameplay system | **Clear.** Weather-as-perception-modifier is used in some stealth games (MGSV, Hitman) but our integration with the fog-of-perception system and storyteller-driven timing is mechanically distinct. |
| Dynamic shadows as information | **Clear.** Shadow-casting exists in many games. Using shadows specifically as a medium-range information tier (between direct LOS and sound) is a novel integration. |
No blocking IP concerns. Two items flagged for careful design attention (lattice overlay, perception mode overlays).
---
## Summary: What the Setting Tells Us About How to Look
1. **The Settled Reach is neither dystopia nor utopia.** It's a civilization that works well enough. The visual register is "functional prosperity" -- not gleaming, not decaying. Maintained.
2. **Technology is invisible.** The most advanced technology in the scene (lattice, Meridian, span gate network) is integrated into infrastructure. The visual spectacle is in what *people do*, not what equipment looks like.
3. **The environment should never tell the player who to suspect.** Lighting, color, and mood are uniform across legitimate and illegitimate spaces. The investigation mechanic requires visual neutrality from the environment. Only the perception systems (D-011, D-015, D-017, D-033) should provide investigative information.
4. **Three construction eras = three visual textures.** Original prefab, expansion-era modification, recent personal touches. This gives Araminta a concrete visual vocabulary for communicating "this place has history" without resorting to decay.
5. **The same space, two experiences.** The smuggler and detective see the same physical environment through different lattice overlays with different information densities. The art direction must support two simultaneous readings of identical geometry.
6. **Readability over expressionism.** The perception systems, entity colors, and fog mechanics all demand visual clarity. The environment should be the muted canvas that makes gameplay information legible. Atmosphere comes from lighting consistency, material texture, and spatial design -- not from visual noise.
7. **Light changes first.** Solar color per star type is the primary location-identity tool. Krenn's G3V warm-white is the baseline. Stepping through a span gate shifts the global color temperature before anything else. Muted environmental tiles enable this: one material vocabulary, many lighting profiles.
8. **Shadows are information.** Dynamic shadows from Godot 4's Light2D system serve both atmosphere and gameplay. A shadow moving at the vision cone's edge is medium-range information -- between direct sight and sound. Day/night cycles are essential, not cosmetic.
9. **Weather is gameplay.** Rain, fog, and storms modify the perception system: reduced sightlines, sound masking, NPC routine disruption. The storyteller can time weather for dramatic effect. Even interior sections feel exterior weather through condensation, humidity haze, and sound bleed.
10. **Design for the pipeline.** Godot 4's tilemap + Light2D + CanvasLayer + shader architecture supports everything the setting demands. Nano Banana's asset generation favors bold silhouettes and color coding over fine detail -- which aligns with readability-first principles. The art direction should be achievable, not aspirational.
---
*Setting note -- I'm genuinely excited about this workshop. The visual identity decisions we make here will define how players experience the setting I've been building. The solar color system alone opens up extraordinary possibilities: every span gate transit becomes a visual journey. Every system has a mood baked into its starlight. And weather -- weather gives the storyteller another instrument to play. A fog rolling across the loading docks during the critical surveillance window. Rain hammering the hull while THE FRIEND's contradiction is discovered in the bar's amber warmth. These aren't cosmetic. They're the setting asserting itself through the player's senses.*
*The Sova Transit District needs to feel like a real place where real people go to work, get drinks, and hide things from each other -- in weather that makes the freight workers grumble and under a star whose light they've stopped noticing. If the art direction can make the ordinary feel ordinary, the extraordinary will take care of itself.*
*Miri out.*
@@ -0,0 +1,330 @@
# Round 1 — Ozzie (Player Experience / Wow Factor)
## Q1: Visual References — What Games/Films/Art Look Like What We're Making?
### Darkwood — THE gold standard for top-down atmosphere
This is the one. Darkwood proves you can make a top-down game that makes people FEEL things in their chest. The cone of vision, the way darkness presses in from all sides, the lighting that conceals as much as it reveals — this is the vocabulary we need.
- **What it gets RIGHT:** The vision cone IS the game. You see what's in front of you. Everything else is threat. The lighting doesn't just look good — it makes you lean forward in your chair. That's what our fog of perception (D-011) needs to do. Not "oh, there's fog." Instead: "what the HELL was that at the edge of my vision?"
- **What it gets WRONG for us:** Darkwood is relentlessly hostile. Every shadow is a threat. We need a station where shadows are sometimes just... shadows. Where the corridor is quiet because it's 3AM and everyone's asleep, not because something is hunting you. Our fear should be slower, more paranoid, more social. The dread of "is my friend lying to me" not "is a monster behind that wall."
- **Specific element:** The lighting system. The way light pools and fades. The way the vision cone creates a natural emotional gradient from "safe" (where I can see) to "unknown" (where I can't). We need this exact emotional gradient, just tuned warmer.
### Alien: Isolation (Sevastopol Station) — The lived-in station
Every time I think about what Sova Transit District should FEEL like, I think about Sevastopol. Not the alien — the STATION. The 40-year-old infrastructure. The retrofitted panels. The way corridors feel like someone designed them for efficiency and then people lived in them for decades and made them human.
- **What it gets RIGHT:** The retro-futurism, the "designed for function, aged by habitation" quality. Sevastopol is chunky keyboards and square monitors and scuffed floor panels and coffee stains on consoles. That's Sova. That's a 40-year-old prefab-modular-retrofitted freight district. You BELIEVE people eat lunch there.
- **What it gets WRONG for us:** It's first-person and extremely high-fidelity. We can't and shouldn't try to match that fidelity in top-down. But we can steal the PRINCIPLE: every surface tells you "people have been here a long time."
- **Specific element:** The environmental storytelling of wear and habitation. Not grimy-dystopia, not gleaming-utopia. Just... USED. Forty years of shift workers and cargo manifests and spilled drinks.
### Hotline Miami — The camera as weapon
Hotline Miami proved that a locked top-down camera can be TERRIFYING. You push into rooms and you can't see what's past the door until you're already committed. That's not a limitation — it's the entire emotional engine.
- **What it gets RIGHT:** Camera creates tension. The top-down view doesn't show you everything — it shows you JUST ENOUGH to make you nervous. The peripheral vision thing where enemies are almost visible at the screen edge? THAT'S what our "forward = detail, peripheral = reduced, behind = blind" (D-015) should feel like. The player should constantly be rotating to check their six.
- **What it gets WRONG for us:** The neon maximalism. Hotline Miami is pure adrenaline, saturated color, synthwave excess. Our game is quiet mundanity punctuated by creeping dread. Totally different emotional register. Also, Hotline Miami is pure action — we need the same camera tension applied to SOCIAL situations. "I can't see who just walked into the bar behind me."
- **Specific element:** The relationship between camera lock and player anxiety. Copy this feeling, not the aesthetic.
### Heat Signature — Top-down space station readability
Heat Signature nails the zoomed-out space station layout. You look down at a ship and you immediately read: rooms, corridors, people, objects. The visual grammar is dead simple and it works perfectly.
- **What it gets RIGHT:** Instant spatial readability. Walls are walls, doors are doors, people are little figures. You never squint. You never wonder "can I walk there?" The information is clean.
- **What it gets WRONG for us:** No atmosphere whatsoever. It's a tactical puzzle, not a place. The ships in Heat Signature are game boards. Our station needs to be a HOME. We need Heat Signature's readability combined with a much stronger sense of place.
- **Specific element:** The entity-to-space ratio. How big people are relative to rooms. How many objects fill a room before it's cluttered vs empty.
### Tactical Breach Wizards — Heat Signature grows up
Same studio (Suspicious Developments, Tom Francis). And look what happened: they went from Heat Signature's pure-diagram readability to a game with PERSONALITY. TBW has stylized 3D characters with strong silhouettes, expressive with minimal features — characters that have nothing but eyes yet communicate emotion perfectly. The rooms are clean tactical spaces, but they feel like PLACES, not diagrams. This is the evolution we need.
- **What it gets RIGHT:** The balance. TBW proves you can have instantly readable tactical information AND visual personality in the same frame. Every character is a distinct silhouette — you never confuse the witch for the navy seer. Every room is a clear layout — you know where the doors are, where the windows are, where the enemies are. But it ALSO has charm, humor, and a visual identity that makes you want to look at it. The "Excellence in Design" IGF award wasn't just for mechanics — it was for the whole package. This is the level of readability-with-soul we should target.
- **What it gets WRONG for us:** Turn-based, not real-time. TBW gives you all the time in the world to read the screen. We don't — our NPCs are MOVING, the simulation is ticking, and the player needs to process information in real time. So our readability bar is actually HIGHER than TBW's. Also TBW's rooms are compact tactical puzzles (6-8 entities in a small space). We have a whole station district with 15+ NPCs on schedules across three zones. Our readability needs to work at a different scale.
- **Specific element:** The silhouette discipline. Every entity in TBW reads as a unique shape before you see any detail. We need this for our NPCs — especially because D-033 colors them by relationship, not by identity. If two green-colored NPCs have the same silhouette, the player can't tell them apart. Different body shapes, different postures, different work animations. Silhouette IS identity in our game, because color is already spoken for.
### Citizen Sleeper — The emotional register of a dying station
The MOOD of Citizen Sleeper is closer to what we need than almost anything else. A space station that's falling apart but people still live there and care about each other. The warmth within the decay. The "quotidian-with-undertow" (D-036) is Citizen Sleeper's entire vibe.
- **What it gets RIGHT:** The emotional register. Citizen Sleeper makes you care about a mushroom farmer and a mechanic and a kid running a food stall. It's not grand space opera spectacle — it's small lives in a big, indifferent structure. THAT'S Sova Transit District.
- **What it gets WRONG for us:** It's a visual novel / TTRPG hybrid, not a spatial sim. The gorgeous character portraits and painterly scenes won't translate to top-down gameplay. We need to achieve the same emotional warmth through spatial design and behavior, not static art.
- **Specific element:** The tonal balance. How to make a space station feel like it could be home.
### Blade Runner 2049 — The lighting language
Roger Deakins' cinematography in BR2049 is the lighting bible. Not the neon-noir stuff — the INTERIOR lighting. K's apartment. The Wallace Corporation. The way motivated light sources create atmosphere through color temperature. Warm amber for safety. Cold blue for institutional spaces. Sickly yellow for decay.
- **What it gets RIGHT:** Lighting as storytelling. Every room in BR2049 tells you how to feel before a single word is spoken. We need that. The Terminal should feel different from The Last Shift which should feel different from the corridors. Not through set dressing alone — through LIGHT.
- **What it gets WRONG for us:** Obviously we're not doing photorealistic cinematography. But the principle translates perfectly to top-down: different zones have different color temperatures, and shifts in lighting signal shifts in mood or danger.
- **Specific element:** Color temperature as emotional language. Warm = inhabited, safe, social. Cool = institutional, exposed, official. Mixed/flickering = something's wrong here.
### Rimworld — Our closest mechanical cousin (and a cautionary tale)
Rimworld is our single biggest design reference (D-005 says so). So I need to be honest about its visuals: Rimworld's art style is BRILLIANT for what it does and we need to learn from WHY it works, then do something different.
- **What it gets RIGHT:** Readability above everything. Rimworld's visual hierarchy is a masterclass — pawns have thicker outlines than items, buildings have solid black outlines while plants have dark green or none. Story-relevant things pop. The principle of "making graphics really simple reduces the noise in the image, which reduces the cognitive load to read the image" is exactly correct. With hundreds of entities on screen, you SCAN, you don't squint. Their art is deliberately abstracted so players tell the story in their minds. That's EXACTLY what we need for asymmetric information — the player's interpretation IS the gameplay.
- **What it gets WRONG for us:** No atmosphere. None. Zero. Rimworld looks like a board game, and that's intentional — it wants you thinking systemically, not emotionally. But our game needs the player to FEEL Sova Transit District as a place. The 30-minute runway (D-027) requires the player to build emotional attachment to daily life BEFORE the conspiracy contaminates it. You can't build attachment to a board game. We need Rimworld's information clarity wrapped in an emotional envelope it deliberately chose not to have.
- **Specific element to steal:** The visual hierarchy system — thicker outlines for more important things, color-coding for state, deliberate simplification that lets the brain process fast. We apply this to D-033's relationship colors.
- **Specific element to AVOID:** The flat lighting, the uniform color temperature, the "everything is equally lit and visible." Our lighting language (zone differentiation, mood shifts) is the main thing that separates us from Rimworld's emotional flatness.
### XCOM 2 — Fog of war as dramatic engine
XCOM's concealment system is the closest mainstream reference for "information you don't have is SCARY." The fog of war isn't decoration — it's where the aliens are. Every tile you can't see is a potential threat. The moment you break concealment and the unknown resolves into specific enemies? THAT'S a feeling.
- **What it gets RIGHT:** The fog makes decisions matter. Moving a soldier one tile forward might reveal three enemies. Or nothing. The tension lives in the NOT-KNOWING, and the visual fog is the representation of that tension. That's our D-011 fog of perception. Also: XCOM's concealment phase — where you're hidden and planning — is exactly the smuggler's daily life. You're operating within a system, trying not to trigger the alarm. The visual shift when concealment breaks (the music changes, the camera tightens, the UI flashes) is a masterclass in "the game just changed."
- **What it gets WRONG for us:** XCOM's fog is binary — seen/unseen. Our fog has gradient (D-015 vision cone: forward/peripheral/behind) and DECAY (D-011: fog returns when you leave). Also XCOM is tactical combat — the fog resolves into shoot-or-be-shot decisions. Our fog resolves into SOCIAL decisions — "I saw Kael in the wrong corridor but does he know I saw him?" That's a completely different kind of tension. More Hitchcock, less action movie.
- **Specific element:** The emotional weight of the concealment break. When things go wrong in XCOM, the visual/audio shift is INSTANT and devastating. We need that for THE FRIEND's contradiction moment — the moment the player's mental model of a trusted person shatters. Same energy, different context.
### The Sims — Making daily life WATCHABLE
Here's a reference nobody expects in a conspiracy game, but it's load-bearing. D-023 says daily life is the substrate. D-027 says 30 minutes of breathing room before contamination. That means we need players to spend HALF AN HOUR watching NPCs go about their day and not be bored. The Sims is the only franchise that's made "watch people eat breakfast" compelling at scale.
- **What it gets RIGHT:** Activity readability. You look at a Sim and you INSTANTLY know: they're cooking, they're sleeping, they're talking, they're upset. The visual language of daily activities is absurdly clear. We need this. When Kael is working at the cargo terminal, the player needs to read "Kael is working" in one glance so they can notice when "Kael is NOT working" — which is the investigation signal. The Sims also proved that watching daily routines creates attachment. You care about these digital people because you watched them live.
- **What it gets WRONG for us:** The emotional register is playful and comedic. Sims are cute. They do funny walks. Their emotions are exaggerated for entertainment. We need the readability without the comedy — "I can see what everyone's doing" but the tone is naturalistic, not cartoonish. Also The Sims gives you god-view and full camera control. We're locked to one character with a vision cone. So we get The Sims readability only within our field of view, and the NPCs beyond our vision are the mystery.
- **Specific element:** The visual vocabulary of daily activities — working, eating, socializing, commuting — rendered clearly enough that DEVIATIONS from routine are instantly noticeable. That's our entire investigation mechanic. The Sims does this for fun. We do it for dread.
### Disco Elysium — Character voice AS visual design
The painterly isometric art of Disco Elysium proves that a game with a strong enough character voice can make you SEE things the art doesn't show. When your Inland Empire skill tells you the pinball machine is sad, you LOOK at that pinball machine differently. The art doesn't change. YOUR perception of it changes.
- **What it gets RIGHT:** Internal monologue changes how you see the world. THAT'S our game. When the smuggler's monologue says "Kael's at the wrong dock again," the player will stare at Kael's little entity dot and see a completely different person than they did five minutes ago. The visuals don't need to change — the PLAYER changes.
- **What it gets WRONG for us:** Disco Elysium is essentially a point-and-click adventure. No real-time simulation, no spatial movement that matters. We need the character-voice-as-perception principle applied to a living, ticking world.
- **Specific element:** The way internal voice recontextualizes visual information. Entity color shifts (D-033) are the mechanical version of this. Green to amber is the visual equivalent of Inland Empire saying "something is wrong with your friend."
---
## Q2: Mood and Atmosphere — What Emotional Register?
### "Quiet life is good" — warm, amber, humming
The default state of Sova Transit District should feel like being inside a well-worn jacket. Warm lighting. Low ambient hum. People moving with purpose but not urgency. The visual language of "this is fine. This is home."
- **Color temperature:** Warm amber and soft gold. The kind of light that comes from fixtures that have been running for 20 years. Not bright, not dim — just present. Like a kitchen at 7PM.
- **Lighting:** Motivated by in-world sources. Cargo bay has industrial whites and occasional warning oranges. The Last Shift has warm amber, slightly yellow, the universal language of "bar." Corridors have cooler, more functional lighting — but not cold. Just... less personal.
- **Movement:** NPCs walking at normal speed. Stopping to talk. Sitting down. The visual rhythm of ordinary life.
- **The key:** The player should feel a pang of reluctance about investigating. "I kind of want to just... stay here. This is nice." That's the D-023 life-sim substrate working. If the quiet life doesn't look and feel GENUINELY appealing, the conspiracies lose all their weight.
### "Something is wrong here" — cooler, sharper, quieter
The shift should be SUBTLE. Not red alerts and flashing lights. The visual temperature drops. Slightly. Like when you notice someone's smile doesn't reach their eyes.
- **Color temperature:** Warm tones desaturate slightly. Shadows deepen. The same room looks 5% colder. The player might not even consciously notice — but they'll feel it.
- **Lighting:** Unchanged in-world, but the rendering might shift contrast slightly. Sharper shadows. The gap between lit and unlit widens.
- **The monologue chime:** This is where sound becomes visual. The urgent chime (D-038 sfx_monologue_chime_urgent.ogg) is the "something is wrong" button. The player hears it and LOOKS harder at the screen. Their perception of the visual shifts without the visuals changing.
- **The key:** We don't paint the paranoia on the screen. We plant it in the player's head and let THEM paint it on the screen. The visuals stay warm. The PLAYER goes cold.
### Smuggler vs Detective visual mood
These should not feel like different games. They should feel like the SAME station seen through different glasses.
- **Smuggler:** Warmer overall. The smuggler belongs here. The cargo bay feels like their office. The bar feels like their living room. The corridors feel like their commute. Then, slowly, the warm places start feeling watched. The comfort curdles.
- **Detective:** Cooler from minute one. Not hostile — just... not home. The detective is a professional in a professional space. The bar is work, not relaxation. The cargo bay is evidence, not workplace. Then, slowly, the social warmth starts creeping in. Colleagues become people. And THAT makes the investigation harder.
- **The reversal:** The smuggler's visual journey is warm → compromised. The detective's is cool → warmed. They cross paths emotionally. THAT'S the divergence reveal (wow moment #4). Same station. Opposite emotional arcs. The player who does both playthroughs feels it in their bones.
---
## Q3: Top-Down Art Style Direction
### Style: Stylized 2D with lighting that does the heavy lifting
Not pixel art. Not painted. Not 3D rendered to 2D. I want clean, readable 2D sprites with an AGGRESSIVE lighting system that creates all the atmosphere.
Here's why:
**Entities need to be readable at a glance.** D-033 says entity color = relationship. That means the entity silhouette + color needs to communicate instantly. Pixel art fights this — too much visual noise at small sizes. Painted art fights this — too much detail competing with the color signal. Clean, slightly stylized 2D lets the color SING.
**Lighting does the emotional work.** If our entities are clean and simple, then the environment lighting becomes the dominant visual voice. Pool of warm light in the bar. Industrial white in the cargo bay. Dim amber in the corridors. The entities move through the light and the light tells you how to feel. Darkwood does this. Teleglitch does a lo-fi version of this. We should do it at a higher fidelity.
**Animation: state-based with smooth transitions.** Not full sprite animation — that's a content pipeline nightmare. State-based (idle, walking, running, talking, working) with smooth interpolated transitions between states. A person sitting down should LOOK like a person sitting down, not like a sprite that teleported to a sitting frame. The smoothness is what makes it feel alive.
### Detail level
More than Rimworld. Less than Hotline Miami. I'd say closer to Heat Signature's entity clarity but with more environmental detail. You should be able to tell at a glance:
- That's a person (not a crate)
- They're doing something specific (working, walking, drinking)
- The room has a function (cargo bay, bar, corridor)
- Where the light sources are
You should NOT need to zoom in to understand anything. If a player squints, we've failed.
### Technical reality check: Godot 4 + Nano Banana
Everything above sounds great. But it has to SHIP. Here's how it maps to what we actually have:
**Godot 4's 2D lighting pipeline is our best friend.** This isn't aspirational — Godot 4 has exactly the tools we need:
- **PointLight2D** nodes for every in-world light source (cargo bay overheads, bar lamp fixtures, corridor strip lights). Each one gets its own color, intensity, falloff. Zone differentiation comes FREE from placing different lights.
- **CanvasModulate** for global mood tinting. The "5% cooler when something's wrong" shift? That's a CanvasModulate color lerp. One line of shader code.
- **LightOccluder2D** on walls and obstacles. This is HOW we get the Darkwood-style "light pools and fades" effect. Light hits a wall and STOPS. Shadows form naturally behind obstacles. The vision cone emotional gradient emerges from the occlusion system, not custom code.
- **Normal maps** on floor tiles and environment objects — lets light interact with surfaces for depth without 3D geometry. The "scuffed floor panels" read as having texture under directional light.
- **CanvasLayer stacking** for the neural insert overlay. Separate rendering layer, separate blend mode, doesn't interfere with world lighting. The "contact lens" effect is literally a semi-transparent CanvasLayer.
The fog of perception (D-011) uses our server-side shadowcasting (D-035) to determine visibility. The client just needs to render visible tiles as lit and non-visible tiles as dark/fogged. PointLight2D + LightOccluder2D handles the visual presentation. The heavy computation is already on the Rust side.
**Nano Banana (Gemini 2.5 Flash) asset generation — what it CAN do for us:**
- **Distinctive silhouettes:** Bold, clear entity shapes that read at small sizes. AI generation excels at this — tell it "top-down character silhouette, worker in industrial station, simple flat style" and it nails it.
- **Tilemap variation:** Floor tiles, wall segments, furniture objects. These are the kind of "many similar but not identical" assets AI generation handles well. Five variants of a cargo crate. Three corridor floor tiles. Consistent enough for a tileset, varied enough to not look stamped.
- **Environment objects:** Terminals, crates, bar fixtures, chairs, tables. Individual assets with clear purpose. This is Nano Banana's sweet spot.
- **Bold color palette compliance:** D-033's relationship colors are strong, distinct hues. AI generation works GREAT with bold palettes — it's subtle gradients and complex textures where it struggles.
**What Nano Banana STRUGGLES with (so we design around it):**
- **Pixel-perfect sprite sheet consistency.** Generating the same character in 8 walking frames with identical proportions is hard. This is why I'm recommending state-based sprites with engine-side interpolation over hand-animated sprite sheets. Fewer frames needed per entity. Each frame is a static pose, not a frame in a sequence.
- **Fine detail at small scale.** If entities are 16x16 or 24x24 pixels, AI generation can't meaningfully differentiate them. We need entities to be big enough that the silhouette carries the information, not fine detail. This pushes toward LARGER entity sprites relative to the world (closer to Heat Signature's ratio than Rimworld's).
- **Style drift across hundreds of assets.** The 200th sprite won't automatically match the 1st. Solution: establish a tight style guide (flat color, bold outline, specific palette) and use reference images in every generation prompt. The simpler and bolder the style, the more consistent AI output is.
**The art direction that WORKS for our pipeline:** Clean, bold, flat-color 2D with strong outlines and distinctive silhouettes. NOT because it's a compromise — because it's the style where (a) readability is maximum, (b) D-033 color signals pop hardest, (c) Godot 4 lighting does maximum atmospheric work, AND (d) Nano Banana produces the most consistent results. The constraints and the design goals actually point to the SAME style. That's how you know it's right.
### The readability / atmosphere balance
This is THE design tension and I have a strong opinion: **readability wins every fight.**
Every time readability and atmosphere conflict, readability wins. Because our game is ABOUT information. The player needs to READ the space — who's here, where are they, what are they doing, which way are they facing. If the atmosphere muddles that, the game breaks.
BUT — the lighting and color temperature and sound design can provide atmosphere WITHOUT touching readability. The entities are clear. The space is clear. The MOOD comes from the light that wraps around them.
Rimworld proves readability can carry a 500-hour game. The Sims proves daily activities can be visually engaging from above. We take their readability and add Darkwood's lighting language on top. That's achievable. That's Godot 4. That's our game.
---
## Q4: The Station as Character
### "Lived-in space infrastructure" in top-down
The top-down constraint is actually a GIFT here. You know what's really readable from above? Floor plans. And floor plans tell you EVERYTHING about how a space has been lived in.
- **Original layout visible:** The prefab grid is there. Regular spacing. Standard corridor widths. Modular room sizes. You can see the bones of the 40-year-old design.
- **Modifications visible:** But someone knocked through a wall between two units to make the bar bigger. Someone ran extra conduit along a corridor wall. Someone added a storage alcove that wasn't in the original plan. The DEVIATIONS from the grid tell you "people adapted this."
- **Wear visible:** Scuffed paths where people walk. A slightly different floor tile where something was repaired. Crates permanently staged in spots that were designed as open space. Not grimy — USED.
### Visual differentiation of the three zones
**The Terminal (Logistics Hub):**
- Industrial lighting — brighter, whiter, more even. Work requires visibility.
- Open floor plan with clearly marked zones. Cargo staging, scanners, manifest terminals.
- High entity density during shifts. Organized movement patterns.
- Visual keywords: FUNCTIONAL. BRIGHT. EXPOSED. Nowhere to hide.
**The Last Shift (Bar):**
- Warmest lighting in the game. Amber/gold. Lower intensity. Shadows in corners.
- Irregular layout — the knocked-through-walls thing. Booths, tables, a bar counter. The only organic-feeling space on the station.
- Varied entity behavior — sitting, drinking, talking, playing. The only place where people are stationary by CHOICE.
- Visual keywords: WARM. IRREGULAR. SOCIAL. The only room that feels like it belongs to the people, not the station.
**Corridors / Maintenance spaces:**
- Dimmest lighting. Functional but minimal. More shadows, more vision cone tension.
- Narrow. The camera feels tighter because there's less visible space.
- Lower entity density. Footsteps echo (D-018 medium range sound). Being alone here FEELS different from being alone in the terminal.
- Visual keywords: DIM. NARROW. TRANSITIONAL. The space between safe spaces.
### "This place has layers" without being grimy
The trick: **variation in maintenance quality.**
Some panels are new. Some are original. Some are patched. Not because the station is falling apart — because different sections were maintained at different times by different people with different budgets. It's not decay. It's HISTORY. Like a city street where you can see three generations of repaving.
The bar is the best-maintained space because Lera cares about it. The terminal is well-maintained because the Syndic requires it. The corridors are... adequately maintained. By whoever got around to it. Last.
---
## Q5: Information Visualization
### The neural insert overlay
The insert overlay should feel like a CONTACT LENS, not a HUD. It's something your character is wearing. It's always slightly there — a subtle shimmer at the edges of perception that becomes more prominent when you actively engage it.
- **Passive state:** Almost invisible. Maybe the faintest geometric grid at the very edge of the viewport. Just enough to remind you "you're augmented." Like how you stop noticing your glasses after a while.
- **Active state (checking insert):** Clean geometric overlay. Thin lines. Small text. The aesthetic of medical equipment displays — precise, minimal, functional. NOT Iron Man holographics. NOT cyberpunk neon. The Commonwealth has technology so mature it doesn't need to show off.
- **Color:** The insert should use a color that doesn't conflict with entity colors (D-033). I'd suggest a subtle cool white or very pale blue — neutral enough to overlay any scene without fighting the amber/green/red relationship colors.
### Perception mode overlay feel
Each perception mode (D-017) should have a distinct visual TEXTURE, not just color:
- **Natural vision:** No overlay. What you see is what you see. The "default."
- **Thermal (future):** Soft, blobby, warm-to-cool gradient. Like looking through water. You lose detail but gain presence information. The world becomes abstract — shapes and heat, not faces and furniture.
- **Camera feeds (future):** Hard-edged, slightly degraded, fixed-angle. The visual quality shifts to "security camera" — grainier, different color balance, a timestamp in the corner. You're seeing THROUGH a lens, not through eyes.
- **Audio analysis (future):** Radial rings emanating from sound sources. Pulsing. The visual representation of hearing. The world goes quieter (visually) and sound becomes visible as ripples.
The KEY: each mode switch should feel like putting on different glasses. The world doesn't change. Your INTERFACE with it changes. And each interface has costs — thermal loses identity, cameras have blind spots, audio can be deceived.
### "Learning something new" vs "confirming a suspicion"
THIS is where the wow moments live. These two states need to feel completely different:
**New information — the discovery hit:**
- A brief brightening. A microsecond where the entity or location or object SHARPENS — like your focus just snapped to it.
- The monologue chime (D-038 sfx_monologue_chime.ogg) fires. The player's eye goes to the monologue display.
- Entity color might shift (D-033 transition, 0.5s fade).
- The feeling: "Wait. WHAT?" A slight jolt. A leaning-forward.
**Confirmed suspicion — the sinking feeling:**
- No brightening. Instead, a brief DEEPENING of whatever you're looking at. The color doesn't change — it intensifies. A saturate-and-hold.
- The urgent chime (D-038 sfx_monologue_chime_urgent.ogg) fires. Different sound = different feeling.
- No color shift — you already flagged this entity. Instead, the monologue text carries heavier weight.
- The feeling: "I knew it. I KNEW it." Not excitement — resignation. The thrill of being right mixed with the dread of what it means.
**The FRIEND's contradiction moment** (wow moment #3) should combine both: discovery hit (wrong place! unknown contact!) followed immediately by confirmation deepening (this explains the deflections, the tells, the avoided conversations). Two emotional beats, bang-bang, in three seconds. THAT'S the game.
---
## Q6: Weather as Visual Identity and Perception Modifier
Weather isn't cosmetic. Weather is a GAMEPLAY SYSTEM that layers onto everything I've said above about lighting, visibility, and mood. And it's also one of our biggest untapped wow-moment generators.
### Rain in top-down — what does it DO to the player?
Rain from above, viewed from above. Think about that. The rain is falling TOWARD the camera. In top-down, rain doesn't streak across the screen like in a side-scroller — it FILLS the screen. Droplets appearing as expanding impact rings on every surface. Puddles forming. The whole visual field gets a layer of animated noise on top of it.
**What this does to readability:** It degrades it. INTENTIONALLY. Rain adds visual noise between the player and the entities. NPCs are harder to identify at distance. The vision cone effectively shortens because even within your sightline, the rain obscures detail at range. This is Darkwood's Great Lake effect — "a continual rainstorm that mutes all sunlight into a cloudy darkness, heavily obscuring vision." But for us it's not horror. It's OPPORTUNITY. The smuggler moves contraband when it rains because the detective can't see as far. The detective dreads rain because their analytical advantage shrinks.
**What this does to mood:** Everything I said about warm amber lighting in Q2? Rain turns the dial. The warm lights are still there, but they're diffused. Haloed. The sharp pools of light from PointLight2D get a soft-edged bloom when rendered through a rain particle layer. The station goes from "crisp and readable" to "impressionistic and close." The bar becomes a beacon — the ONLY place where rain doesn't matter, where the light is warm and dry and people are close.
**Godot 4 implementation:** GPUParticles2D on a CanvasLayer above the world layer. Particle emission shape = box matching viewport width. Lifetime 0.3-0.5s for individual drops (they fall fast from top-down). Add a second GPUParticles2D for impact splashes on surfaces. A rain shader on the world layer adds a subtle ripple distortion. This is stock Godot — no custom engine work.
### Dust storms — the palette shifter
Different planets, different weather. If Sova's world has dust storms, that's not just "less visibility" — it's a complete color palette override.
**The visual hit:** Everything goes amber-orange. The cool whites of the Terminal wash out. The warm golds of the bar intensify. Visibility drops to half. The CanvasModulate shifts the ENTIRE palette warm. NPCs outside seek shelter — entity density shifts indoors. The station becomes claustrophobic, crowded, close.
**The gameplay hit:** During a dust storm, the corridors are empty. NOBODY is outside who doesn't have to be. Which means anyone you see in the corridors during a dust storm is there for a REASON. The smuggler makes their drop. The detective notices the smuggler didn't come inside. Weather creates investigative signal by changing the noise floor.
**The wow moment:** Player's first dust storm. The screen slowly shifts orange. NPCs start moving indoors. The station hum changes pitch (ventilation working harder). The monologue fires: "Storm rolling in. Third one this week." And the player FEELS the planet. They're not just on a station — they're on a world. That's the scale of The Settled Reach in one weather event.
### Interior weather effects — the station breathes
Even inside a sealed station, weather LEAKS.
- **Condensation:** During rain or humidity shifts, surfaces near exterior walls get a subtle moisture sheen. A shader effect — slight specular highlight on tiles adjacent to hull walls. The station is sealed but it's not perfect. Forty years old. Gaskets wear.
- **Ventilation particles:** Dust motes, humidity haze, recycled air carrying trace particles. A subtle GPUParticles2D layer that's always running at low density — you barely notice it until it CHANGES. When the dust storm hits, the particle density in the ventilation corridors increases. The air looks different. You feel the storm through the walls.
- **Temperature lighting shifts:** Exterior weather affects the color of light coming through viewports or transparent sections. A clear day: slightly blue-white light bleeding in from viewport edges. Dust storm: amber-orange. Night: darkness. The station's lighting is motivated by interior sources, but the EDGES show you what's happening outside.
- **Pressure sounds → visual cues:** When weather hammers the hull, the D-018 sound model picks it up. The player hears the storm. And at medium range (fog edge), they see visual indicators of hull stress — subtle particle effects at seam lines, a vibration shader on certain wall tiles. The station is ALIVE and it's weathering the storm.
### Weather as location identity
This is where weather becomes part of the long-term visual DNA:
Each world in the wormhole network has its own weather patterns. When you step through a wormhole onto a new world, the FIRST thing that hits you is the weather. Different light color (star type), different precipitation, different air quality. Weather becomes the visual signature of a planet the way accent becomes the signature of a culture.
Sova's world might be: frequent rain, occasional dust storms, mild temperatures. Another world might be: perpetual overcast, no precipitation, cold. Another: clear skies, harsh sun, no weather. The player learns to READ the weather as location. "It's raining. I'm on Sova's planet." That's world identity through atmosphere, not exposition.
### Weather and the divergence reveal
Here's the thing that excites me MOST about weather as a system:
Weather is the same for both characters. The rain falls the same on the smuggler and the detective. But their RELATIONSHIP to the weather is different. The smuggler sees rain and thinks "good — cover for the drop." The detective sees rain and thinks "visibility is shot — can't surveil the docks." Same rain. Different monologue. Different emotional register. Weather becomes another asymmetric information surface — not what you see, but what you THINK ABOUT what you see.
On the second playthrough, when it rains, and the monologue says the opposite thing about the same weather? THAT'S asymmetric information made visceral. "Last time it rained, I was relieved. This time I'm terrified. It's the SAME rain."
---
## Summary: What Players Should FEEL
The first 5 minutes: "Oh. This is... nice? The hum. The warm light. People going about their day. I think I like it here."
Minute 15: "Something's off. I can't put my finger on it. But something's off."
Minute 25: "Oh no. Oh NO. Not Kael. Anyone but Kael."
Second playthrough, minute 5: "Wait — this is the SAME station? It looks completely different. I was only seeing half of it."
THAT'S the game. The visuals exist to make those four moments hit like a freight train.
@@ -0,0 +1,341 @@
# Art Direction & Mood Board Workshop — Round 1 Tracking Document
**Compiled by:** Qatux (Documenter)
**Date:** 2026-02-12
**Sources:** round1-araminta.md, round1-ozzie.md, round1-miri.md, round1-gore.md
**Workshop brief:** docs/workshops/art-direction-mood-board/workshop-brief.md
---
## 1. Complete Reference List
### Games
| Reference | Araminta | Ozzie | Miri | Gore | Primary Aspect Referenced |
|---|:---:|:---:|:---:|:---:|---|
| **Rimworld** (Ludeon Studios) | X | X | X | X | Readability at scale, visual hierarchy, cognitive load reduction, entity-as-icon |
| **XCOM 2** (Firaxis) | X | X | X | X | Fog of war as emotional mechanic, information under pressure, concealment break |
| **The Sims 3/4** (Maxis/EA) | X | X | X | X | Daily life readability from overhead, routine as visual engagement, activity states |
| **Hotline Miami** (Dennaton) | X | X | | | Locked camera as tension engine, color palette as emotional register |
| **Heat Signature** (Suspicious Developments) | X | X | | | Top-down spatial clarity, entity-to-space ratio, walls as information barriers |
| **Tactical Breach Wizards** (Suspicious Developments) | | X | X | X | Silhouette discipline, readability + personality, clarity as design origin |
| **Disco Elysium** (ZA/UM) | | X | X | X | Internal voice recontextualizing visuals, mood as art, dignity of the mundane |
| **Citizen Sleeper** (Jump Over the Age) | X | X | | | "Quiet life with undertow" mood, muted palette with strategic warmth |
| **Return of the Obra Dinn** (Lucas Pope) | | | X | X | Visual restriction creating investigative focus, observation as core mechanic |
| **Invisible Inc.** (Klei) | X | | | | Information-as-resource visual language, layered UI, stealth + info management |
| **Teleglitch** (Test3 Projects) | X | | | | Terrifying restricted LOS in top-down, sound-at-fog-edge |
| **Door Kickers** (KillHouse Games) | X | | | | Entity detail target level, silhouette readability at correct scale |
| **Darkwood** (Acid Wizard Studio) | | X | | | Vision cone as emotional gradient, lighting that conceals as much as it reveals |
| **Cogmind** (Grid Sage Games) | | | X | | Atmosphere through minimalism, negative space as readability tool |
| **Papers, Please** (Lucas Pope) | | | | X | Visual restraint amplifying thematic weight, mundanity as moral language |
### Films & TV
| Reference | Araminta | Ozzie | Miri | Gore | Primary Aspect Referenced |
|---|:---:|:---:|:---:|:---:|---|
| **Blade Runner 2049** (Villeneuve, 2017) | X | X | | | Color temperature as storytelling, Roger Deakins lighting language |
| **The Expanse** (TV, 2015-2022) | X | | X | | Lived-in station aesthetic, zone differentiation, "workplaces first" |
| **Alien** (Scott, 1979) | | | X | | "Truckers in space," used future, industrial mundane |
| **Alien: Isolation** (Creative Assembly, 2014) | | X | X | | Sevastopol Station — designed for function, aged by habitation |
| **Moon** (Duncan Jones, 2009) | X | | | | Warm/cool spatial contrast, "comfortable but something's off" |
| **Stalker** (Tarkovsky, 1979) | | | | X | Perceptual shift — same space, different truth via attention |
| **The Lives of Others** (von Donnersmarck, 2006) | | | | X | Visual cost of surveillance, observed life richer than observer's |
### Artists, Photographers & Designers
| Reference | Araminta | Ozzie | Miri | Gore | Primary Aspect Referenced |
|---|:---:|:---:|:---:|:---:|---|
| **Syd Mead** | X | | X | | Technology as infrastructure, clean functional futures, "plausible future" |
| **Ron Cobb** (Nostromo interiors) | X | | | | "Can you imagine working here?" test, environmental signage, wear-as-character |
| **Simon Stalenhag** | X | | | | Mundane life alongside extraordinary tech, muted-with-warmth palette |
| **Edward Hopper** (*Nighthawks*) | | | | X | Warm interior light surrounded by unknowable dark, together-but-isolated |
| **Gregory Crewdson** (*Twilight*, *Beneath the Roses*) | | | | X | Ordinary scenes made uncanny through attention, "there but not there" |
| **Roger Deakins** (cinematographer) | X | X | | | Color temperature as emotional language, motivated lighting |
| **Guillaume Singelin** (Citizen Sleeper / Lancer art) | X | | | | Bold shapes, limited palettes, strong silhouettes |
| **Lucas Pope** (designer) | | | X | X | Visual restriction as design tool, observation-as-mechanic |
| **Tom Francis** (designer) | X | X | X | X | Spatial clarity, readability-first philosophy, Heat Signature to TBW evolution |
| **Tynan Sylwester** (Rimworld designer) | | | | X | Abstraction enabling player projection, "solution to a problem" |
| **Tony Noble** (Moon production design) | X | | | | Functional surfaces with just enough warmth |
| **John Roberts** (TBW art direction) | | | X | | "Tactical + wizard + personality" design triad |
---
## 2. Consensus Points
Where multiple agents independently converge on the same conclusion. Ordered by strength of consensus.
### Universal consensus (all four agents)
**C-01: Readability is the non-negotiable top priority.**
All four agents state this explicitly. Araminta: "Readability over beauty." Ozzie: "readability wins every fight." Miri: "Readability over expressionism." Gore: "keep entities simple, let color and shape carry meaning." When readability and atmosphere conflict, readability wins. The game is ABOUT information — the player must parse the game state at a glance.
**C-02: Stylized 2D with bold silhouettes, NOT pixel art, NOT painted, NOT 3D.**
Araminta, Ozzie, and Gore converge independently on "clean 2D with strong silhouettes and limited palette." Miri defers to Araminta on technique but endorses bold silhouettes and muted environments. Pixel art rejected as "retro-coded." Painted style (Disco Elysium) rejected as too expensive and inconsistent for AI pipeline. Photorealistic 3D rejected as uncanny valley at this scale.
**C-03: Lighting is the primary atmosphere engine.**
Araminta: "Sprites provide SHAPE. Light provides MOOD." Ozzie: "Lighting does the emotional work." Miri: "Lighting is not polish. It is infrastructure." Gore: "Warm practical lighting... functional light." All four agree lighting carries emotion while entities carry information.
**C-04: Rimworld = readability benchmark; its emotional flatness is what we must surpass.**
All four cite Rimworld as closest mechanical cousin and readability gold standard. All four identify its uniform, flat, atmosphereless visuals as the specific gap we fill with lighting, zone differentiation, and environmental texture.
**C-05: The environment must NEVER visually telegraph suspicion.**
Miri (strongest): "The visual environment should never tip its hand." Gore: "Nothing changes" when conspiracy activates. Araminta: "Lighting DOESN'T change. The WORLD hasn't shifted." Ozzie: "We don't paint the paranoia on the screen. We plant it in the player's head." No ominous shadows. No red filter. No horror lighting on smuggling routes. Environmental visual neutrality is load-bearing.
**C-06: Weather is a gameplay system, not cosmetic.**
All four treat weather as perception modifier, NPC routine disruptor, and storyteller instrument. Weather modifies sightlines, sound ranges, and entity behavior. The storyteller can time weather for dramatic effect.
**C-07: Godot 4's 2D pipeline is the right technical match.**
All four validate Light2D + LightOccluder2D + CanvasLayer + shaders + GPUParticles2D as sufficient for the proposed art direction. No custom engine work required.
**C-08: Production constraints and design goals converge.**
All four note independently that Nano Banana's strengths (bold silhouettes, distinctive palettes, stylized assets) align with what the design requires (readability-first, color-coded entities, lighting-driven atmosphere). The art direction isn't a compromise — it's the style where all constraints point to the same solution.
### Strong consensus (three or more agents)
**C-09: Smuggler = warmer visual temperature, Detective = cooler.**
Araminta, Ozzie, and Gore describe this explicitly. Miri frames it as same lighting + different information overlay density. The 10% color temperature shift applied consistently across every element compounds over 30 minutes. Same station, opposite emotional arcs.
**C-10: Three construction eras = three visual textures.**
Araminta (detailed hex values per era), Miri (three-era setting brief), and Gore ("layers of modification") all ground the station's visual history in temporal accumulation. Not decay — renovation. Different materials, different maintenance levels, visible seams.
**C-11: Sova is "functional prosperity" — maintained, not decaying.**
Araminta: "Think of a 40-year-old university campus." Miri: "Renovation, not decay." Gore: "Settled." Ozzie: "variation in maintenance quality." The station works. It's been improved by living in it, not degraded.
**C-12: Solar light color per star type as location identity.**
Araminta (specific hex values), Miri (full stellar spectrum table), and Gore (weather as world identity) all endorse this. Krenn System G3V = warm white baseline. Wormhole transit shifts global color temperature before anything else. One material vocabulary, many lighting profiles.
---
## 3. Points of Divergence
Where agents disagree or emphasize fundamentally different things. These are Round 2 discussion candidates.
### D-01: Neural insert overlay — Technical precision vs. Organic cognition
This is the sharpest divergence in Round 1.
| Agent | Position | Key phrase |
|---|---|---|
| **Araminta** | Technical, geometric, precise. "1-pixel lines at partial opacity." Clean digital aesthetic contrasting with organic station. | "The insert renders CLEAN because it's digital." |
| **Gore** | Organic, soft-edged, thought-like. Blooms into visibility. "The amber of D-033 should feel like a gut feeling made visible." | "Should look like thought, not technology." |
| **Ozzie** | Middle ground. "Contact lens, not HUD." Subtle shimmer. Passive state almost invisible. | "Something your character is wearing. Always slightly there." |
| **Miri** | Setting-grounded. "Native to cognition." Thin, precise, translucent. Not dramatic. | "Information appears where you need it and fades when you don't." |
**Tension:** Araminta's geometric precision vs. Gore's organic softness. Both have strong rationale. Araminta's approach provides clear visual separation between insert layer and game world. Gore's approach serves the thematic argument that the insert is consciousness, not equipment. This needs explicit resolution.
### D-02: Entity detail level — Exact target
| Agent | Position |
|---|---|
| **Araminta** | "Between Rimworld and Door Kickers." 24-32px footprint on 64px tile. Specific sprite spec. |
| **Ozzie** | "More than Rimworld. Less than Hotline Miami." Closer to Heat Signature entity clarity. |
| **Gore** | "Rimworld-level entity detail is approximately right" — but environment needs more texture than Rimworld. |
| **Miri** | Defers to Araminta. Emphasizes silhouette readability and muted environment as canvas. |
**Tension:** Minor. Araminta and Ozzie agree on the range. Gore suggests entities could be simpler if the environment compensates. The specific pixel footprint needs Araminta's final call.
### D-03: How much the visual environment shifts when conspiracy activates
| Agent | Position |
|---|---|
| **Araminta** | Subtle but present shifts: "5% color temperature" possible, deeper shadows, minor contrast changes. |
| **Gore** | ZERO visual shift. "The absence of change as horror." The station looks identical; only the player's knowledge changes. |
| **Ozzie** | "Slightly" cooler color temperature, sharper shadows. But the key shift is internal: "the PLAYER goes cold." |
| **Miri** | "It doesn't look like anything." Environmental neutrality is mandatory for the investigation mechanic. |
**Tension:** Araminta allows subtle environmental shifts (CanvasModulate lerps, contrast changes). Gore and Miri argue for strict environmental neutrality — the investigation mechanic breaks if the environment hints at answers. Ozzie is in between. This has mechanical implications: does the rendering change or not?
### D-04: Art style framing / philosophy
All four agree on the *output* (clean 2D, bold silhouettes, lighting-driven) but frame it through different lenses:
| Agent | Framing |
|---|---|
| **Araminta** | Functional design. "Sprites are shape templates that the lighting system completes." Production pipeline logic. |
| **Ozzie** | Player experience. "Darkwood's lighting + Rimworld's readability." Atmosphere-first, emotion-driven. |
| **Miri** | Setting coherence. "Muted canvas for entities." The world's material culture dictates the palette. |
| **Gore** | Thematic. "Should feel like a document." Analytical, architectural, almost investigative. |
**Note:** These framings are complementary, not contradictory. But "document" vs. "warm home" is a real tension in Gore vs. Ozzie's emotional register.
---
## 4. Key Insights
The most important new ideas that emerged in Round 1, with attribution.
**I-01: "Silhouette IS identity because color is spoken for."** (Ozzie, Q3)
Since D-033 colors entities by relationship-to-player, physical silhouette must carry ALL identity differentiation. Two green NPCs with the same silhouette are indistinguishable. Different body shapes, postures, work animations, and one identifying feature per named NPC (Kael's vest, Lera's apron) are non-negotiable production requirements.
**I-02: "Shadows as information tier."** (Miri, Q2)
Dynamic shadows create a medium-range information channel between direct LOS (close) and sound (medium-far). A shadow moving at the vision cone's edge = an NPC in an adjacent corridor that you can't directly see but can partially infer. This is a novel mechanical integration: Close = see directly. Medium = see shadows, hear sounds. Long = lattice data only.
**I-03: "Production constraints converge with design goals."** (All four)
Bold silhouettes + color coding + minimal detail + lighting-driven atmosphere is simultaneously: (a) best for readability, (b) best for D-033 color system, (c) best for Godot 4 Light2D pipeline, (d) best for Nano Banana consistency. When four independent constraints all point to the same solution, that's a strong signal the solution is correct.
**I-04: "The absence of change as horror."** (Gore, Q2)
The most unsettling moment in the game is when the station looks EXACTLY the same after discovering the conspiracy. The lighting is warm. The NPCs are friendly. Nothing has changed except what the player knows. The visual environment's refusal to acknowledge the horror IS the horror.
**I-05: "Visual restraint is thematic confidence."** (Gore, Q1 via Papers, Please)
A game asking "is this life enough?" should be visually quiet enough that the player can hear the question. Papers, Please looks like a spreadsheet and delivers devastating moral weight. Our visual restraint isn't a budget compromise — it's a thematic statement.
**I-06: "One material vocabulary, many lighting profiles."** (Miri, Solar Light Color section)
Muted environment tiles (greys, metals, worn surfaces) can be globally tinted by solar color per star system. Grey metal turns golden under a K-type star, ruddy under an M-dwarf. One asset set works across all systems because the light does the differentiation, not the material. Pipeline win from a setting-driven insight.
**I-07: "Weather creates shared vulnerability."** (Gore, Weather section)
Weather degrades everyone's perception equally. The detective and smuggler are equally blind in a dust storm. For once, the information asymmetry isn't about who knows what — it's about nobody knowing anything. Weather introduces chaos into the information game.
**I-08: "The observed life is richer than the observer's life."** (Gore, Q1 via *The Lives of Others*)
The detective's cooler, more analytical visual world should feel impoverished compared to the smuggler's warmer social world. This has design implications: the detective playthrough is intentionally less warm, less "home." The visual temperature gap IS the cost of surveillance.
**I-09: "The gap between entity simplicity and environment detail IS the game."** (Gore, Q3)
Simple entities in a richly textured station environment. You read the environment through the people, and the people through the environment. Neither is fully legible alone. This is a distinct design position from Rimworld (both simple) or Disco Elysium (both detailed).
**I-10: "Fog is uncertainty made visible."** (Gore, Weather section)
Fog is the most thematically loaded weather state because it does mechanically what the entire game does narratively — hides things in plain sight. Fog doesn't create darkness; it creates ambiguity. Vision cone in fog degrades gracefully: full clarity close, increasingly indistinct at range, entities becoming silhouettes becoming suggestions.
**I-11: "Legible but ambiguous animation."** (Gore, Q3)
Animation should be clear enough that you can see a figure stopped, but not so detailed that you can tell from animation alone whether they stopped to look at something, talk to someone, or check if they're being followed. You need other information channels to interpret movement. This preserves the information game.
---
## 5. Endorsed References — Strongest Support
Ranked by number of endorsers and strength of endorsement.
### Tier 1: Universal reference (all four cite, all endorse specific aspects)
| Reference | Endorsed aspect | Notes |
|---|---|---|
| **Rimworld** | Readability principles, visual hierarchy, cognitive load reduction | Universally cited as readability benchmark. Universally cited as emotionally insufficient. |
| **XCOM 2** | Fog of war as emotional weight, unseen space as tension | Universally cited. All note binary fog limitation vs. our graduated model. |
| **The Sims** | Daily routine legibility, activity-state readability from overhead | Universally cited for life-sim substrate problem. All note omniscient perspective as wrong for us. |
### Tier 2: Strong reference (3 agents, strong endorsement)
| Reference | Endorsed aspect | Strongest advocate |
|---|---|---|
| **Tactical Breach Wizards** | Silhouette discipline, clarity-as-design-origin, readability + personality | Ozzie (evolution of Heat Signature), Miri (clarity as origin not compromise), Gore (legibility-first + personality) |
| **Disco Elysium** | Internal voice recontextualizing visual information, mood-as-art | Ozzie (character voice as visual design), Miri (emotional palette principle), Gore (dignity of the mundane) |
### Tier 3: Targeted reference (2 agents, specific aspect endorsed)
| Reference | Endorsed aspect | Advocates |
|---|---|---|
| **Heat Signature** | Spatial clarity, entity-to-space ratio, top-down station readability | Araminta, Ozzie |
| **Citizen Sleeper** | "Quiet life with undertow" mood, muted palette with strategic warmth | Araminta, Ozzie |
| **Blade Runner 2049** | Color temperature as emotional storytelling | Araminta, Ozzie |
| **The Expanse** | Lived-in station material vocabulary, zone differentiation | Araminta, Miri |
| **Alien / Alien: Isolation** | "Used future," designed-for-function-aged-by-habitation | Ozzie, Miri |
| **Return of the Obra Dinn** | Visual restriction creating investigative focus | Miri, Gore |
| **Syd Mead** | Technology as infrastructure, plausible functional future | Araminta, Miri |
### Tier 4: Unique reference (1 agent, strong advocacy)
| Reference | Endorsed aspect | Advocate |
|---|---|---|
| **Darkwood** | Vision cone as emotional gradient, light pooling and fading | Ozzie (calls it "THE gold standard for top-down atmosphere") |
| **Edward Hopper** (*Nighthawks*) | Warm interior light surrounded by unknowable dark | Gore (calls it "our game's central image") |
| **Gregory Crewdson** | Ordinary made uncanny through quality of attention | Gore |
| **Stalker** (Tarkovsky) | Perceptual shift — same space, different truth | Gore |
| **The Lives of Others** | Visual cost of surveillance, observed > observer | Gore |
| **Papers, Please** | Visual restraint as thematic confidence | Gore |
| **Cogmind** | Atmosphere through minimalism, negative space as readability | Miri |
| **Simon Stalenhag** | Mundane-extraordinary juxtaposition, muted-with-warmth palette | Araminta |
| **Ron Cobb** | "Can you imagine working here?" environmental test | Araminta |
---
## 6. Avoid List
References explicitly flagged as the wrong visual direction.
| Reference / Style | Flagged by | Reason |
|---|---|---|
| **Cyberpunk neon / Blade Runner rain-noir** | Miri (explicit avoid table), Gore, Araminta | Coded "dystopia." Sova is not dystopic. Players will expect cyberpunk stories. Wrong genre signal. |
| **Star Trek antiseptic corridors** | Miri | Utopian institutional uniformity. Our institutions are bureaucracies, not ideals. |
| **Star Wars romantic visual language** | Miri | Operatic scale, good/evil coding. Our investigation requires visual ambiguity. Watch span gate visuals. |
| **Mass Effect Citadel** | Miri | Gleaming future-city. Sova is a freight district, not a tourist destination. |
| **Dead Space grimy horror** | Miri | Body-horror visual territory. Wrong emotional register entirely. |
| **Pixel art style** | Araminta, Ozzie | Communicates "retro" and "indie." The Commonwealth is advanced. Fights information overlay readability. |
| **Painted / Disco Elysium production style** | Araminta | Too expensive per frame, too hard for AI pipeline consistency. (Note: DE's *principles* endorsed; its *production method* rejected.) |
| **Photorealistic 3D rendered to 2D** | Araminta | Too expensive, too slow, uncanny valley risk at top-down scale. |
| **Iron Man helmet display** | Araminta, Gore | Too dramatic, too military for neural insert overlay. |
| **Hotline Miami neon maximalism** | Araminta, Ozzie | Adrenaline-and-horror register. Commonwealth is sleek, subtle. Wrong emotional register entirely. |
| **Teleglitch lo-fi aesthetic** | Araminta | Deliberately ugly, relentlessly oppressive. We need comfort AND unease, not just unease. |
| **Military tactical palette** | Araminta (re: XCOM, Door Kickers) | Combat-centric visual language. Our game has combat as punctuation, not the sentence. |
---
## 7. Open Questions for Round 2
### OQ-01: Neural insert overlay aesthetic — geometric or organic?
Araminta proposes clean digital lines ("the insert renders CLEAN because it's digital"). Gore proposes soft-edged organic awareness ("should look like thought, not technology"). Both positions are well-argued. This directly affects art production — the insert is on screen at all times.
**Possible resolution path:** Could the insert be *technically* clean/geometric but rendered with soft edges via a shader bloom pass? Precision underneath, perceived as organic. Needs Araminta + Gore discussion.
### OQ-02: Does the visual environment shift at ALL when conspiracy activates?
Araminta allows subtle CanvasModulate shifts (5% cooler, deeper shadows). Gore and Miri argue strict zero-shift. This has mechanical implications for the rendering pipeline and for whether the environment ever "helps" the player detect wrongness.
**Possible resolution path:** Could the shift be character-driven (insert overlay density increases, not environment changes)? All agents agree the insert overlay changes — the question is whether the base world layer also shifts.
### OQ-03: Exact entity detail level and sprite dimensions
Araminta proposes 24-32px on 64px tiles. Ozzie suggests Heat Signature's entity-to-space ratio. Gore suggests Rimworld-level simplicity for entities with richer environments. This needs a concrete pixel spec for Nano Banana style guide development.
### OQ-04: Animation ambiguity vs. readability
Gore proposes "legible but ambiguous" animation — you can see a figure stopped but can't tell WHY from animation alone. Araminta proposes clearer activity states (2-3 frames per interaction type). The Sims reference implies clear activity readability. This tension directly affects the investigation mechanic.
**Possible resolution path:** Could animation be clear for EXPECTED activities (working, walking, drinking) but ambiguous for INVESTIGATIVELY RELEVANT behaviors (pausing, looking around, meeting someone)?
### OQ-05: Gore's "document" framing vs. the warmth consensus
Gore proposes the art style should feel "like a document — something being recorded, analyzed, compiled." This sits in tension with the broader consensus that the game must feel warm enough that "quiet life is good" is genuinely appealing. How does a document feel like home?
**Possible resolution path:** The document quality could be the detective's experience specifically, while the smuggler's experience is warmer. The art style itself is clean/technical but the lighting and color temperature carry the warmth.
### OQ-06: Darkwood as primary lighting reference
Ozzie advocates strongly for Darkwood's lighting system as THE model for our vision cone emotional gradient. No other agent cites Darkwood. Does the team endorse this as a primary lighting reference alongside the BR2049 color temperature work?
### OQ-07: Krenn System planet name and climate profile
Miri flags that Sova's planet is unnamed (worldbuilding gap). She proposes temperate-maritime climate with regular rain and morning/evening fog. This needs confirmation as it affects weather system design.
---
## 8. Emerging Art Direction Consensus
### The direction the team is converging on
**Visual identity statement (draft, synthesized from all four responses):**
> The Settled Reach looks like a well-maintained 40-year-old space station seen through the eyes of someone who lives there — or someone who is studying it. Clean 2D art with bold entity silhouettes and muted environmental tones. Lighting is the dominant atmospheric voice: warm amber in social spaces, cool white in institutional zones, dim and uneven in corridors. Entity color (D-033) is the primary information signal; silhouette is the primary identity signal. The fog of perception renders the unknown as genuine absence, not grey overlay. The neural insert adds a subtle technological lens that the player learns to forget is there. The same station reads warm for the smuggler and cool for the detective through overlay density and color temperature, not through environmental changes.
### Confirmed principles (unanimous or near-unanimous)
1. **Readability over beauty.** Always.
2. **Lighting over detail.** Sprites provide shape; light provides mood.
3. **Restraint IS the aesthetic.** Not flashy, not grim, not neon, not grimy.
4. **Environment never tips its hand.** Investigation signals are behavioral and informational, never environmental.
5. **One asset vocabulary, many lighting profiles.** Muted tiles + solar color tinting = every system looks different with the same art.
6. **Weather is gameplay.** Perception modifier, NPC routine disruptor, storyteller tool.
7. **Silhouette carries identity.** Color is spoken for (D-033). Shape must differentiate.
8. **Production constraints = design strengths.** Bold silhouettes + limited palette + lighting-driven atmosphere serves readability, D-033, Godot 4, AND Nano Banana simultaneously.
### Needs Round 2 resolution
1. Neural insert: geometric precision (Araminta) vs. organic cognition (Gore)
2. Environmental visual shift: subtle allowed (Araminta) vs. strict zero (Gore/Miri)
3. Entity detail: specific pixel spec and sprite dimension target
4. Animation: activity-readable (Araminta/Sims model) vs. legibly ambiguous (Gore)
5. Darkwood as primary lighting reference (Ozzie's strong advocacy, needs team response)
---
*For the record: Round 1 produced remarkable convergence on the fundamentals. Four agents working independently arrived at the same art style (clean 2D, bold silhouettes, lighting-driven), the same priority (readability first), and the same technical validation (Godot 4 + Nano Banana alignment). The divergences are real but bounded — they concern the nuance of execution, not the direction. The strongest signal is I-03: when readability, design, engine, and pipeline all point to the same style, that style is correct.*
*Compiled by Qatux. Every claim above is cited to its source document. Cross-reference against the round1-{agent}.md files for full context.*
@@ -0,0 +1,203 @@
# Round 2 Workshop Response: Araminta (Art Direction & Mood Board)
**Agent:** Araminta (Visual Designer / Q-003 Lead)
**Date:** 2026-02-12
**Workshop:** Art Direction & Mood Board
**Status:** Round 2 — Resolving Open Questions
---
## Response to New Input: Tile-Based Base Building
This confirms rather than changes my Round 1 positions. Let me explain why.
My entire art direction thesis is: **sprites are shape templates that the lighting system completes.** A world composed of discrete 1x1 tile objects doesn't fight that — it IS that. Every wall segment, floor tile, chair, desk, crate, and door is a shape template. The lighting system makes them feel like a room.
What this changes practically:
1. **The "environment" isn't a painted backdrop — it's a vocabulary of placeable objects.** This means every object needs to be individually readable at 64x64. No detail hiding behind neighbors. Each tile sprite must communicate "I am a wall / door / chair / console" through silhouette alone, because the player (or simulation) places them on a grid and needs to parse the result.
2. **Rimworld becomes a production reference, not just a readability reference.** Rimworld's object sprites are the right scale, the right level of abstraction, and the right pipeline model. Individual items, top-down readable, tile-aligned. We add what Rimworld lacks: lighting, shadows, atmosphere.
3. **Nano Banana's job description changes.** Instead of "generate room illustrations," it becomes "generate individual object sprites at 64x64 with consistent style, bold silhouettes, and flat lighting (the engine adds atmosphere)." This is actually EASIER for AI consistency — single objects are simpler than scenes.
4. **The three construction eras I proposed in Round 1 now apply to OBJECTS, not wall paint.** Original-era furniture (standard-issue, institutional grey). First-renovation equipment (slightly different material palette). Recent additions (Lera's chosen bar furniture, newer consoles). The visual history is carried by the objects themselves.
5. **Object density IS environmental storytelling.** The bar feels different from the logistics hub not because of painted backgrounds but because of WHAT'S PLACED THERE. Dense, mismatched furniture = bar warmth. Sparse, uniform equipment = institutional efficiency. Exposed conduits with nothing on the floor = maintenance corridor truth. Rimworld does exactly this — a bedroom feels like a bedroom because it has a bed, a dresser, and a lamp, not because the walls are "bedroom walls."
This is good news for the pipeline. One object vocabulary, one style guide, consistent generation rules. The world is assembled, not illustrated.
---
## OQ-01: Neural Insert Overlay — Geometric or Organic?
**The proposed resolution path works. I'll accept it with specifics.**
My Round 1 position was clean geometric. Gore's was organic thought-like. The synthesis is: **precision rendering with organic PERCEPTION.** Here's what that means concretely:
- **Data layer (underneath): Geometric.** Grid lines are straight. Entity markers are clean circles/diamonds. Text is crisp. Connection lines between POIs are 1-pixel straight lines. The data is PRECISE because it comes from a computational lattice.
- **Render layer (on top): Soft bloom pass.** A 2-3 pixel gaussian blur on the entire insert CanvasLayer at maybe 40% blend. The geometric precision is still there but it LOOKS slightly soft. Edges breathe. The grid lines have a gentle glow rather than razor sharpness.
- **The result:** Technical precision that FEELS organic. The player perceives it as "natural awareness" (Gore's framing) but the underlying structure is clean digital (my framing). The lattice IS computational — but the character has worn it so long that the brain has naturalized it. The soft rendering communicates that naturalization.
**Implementation:** One CanvasLayer for insert data (geometric, precise). One shader on that layer: subtle bloom/glow pass. Adjustable bloom radius lets us tune the organic-ness. This is cheap in Godot 4.
**What this means for the D-033 colors on the insert:** The relationship colors (teal, green, amber, red) rendered through the bloom pass will have soft halos rather than hard edges. An amber entity marker won't have a crisp border — it'll have a warm amber glow bleeding slightly into the surrounding space. That actually reinforces Gore's "gut feeling made visible" argument. The amber feels like a vague sense of concern, not a data flag.
Gore was right about the emotional register. I was right about the production structure. Both survive.
---
## OQ-02: Does the Visual Environment Shift When Conspiracy Activates?
**I'm moving to the Gore/Miri position. Strict zero environmental shift.**
Here's why I changed my mind. In Round 1, I proposed "5% cooler, deeper shadows" as subtle CanvasModulate shifts. But after reading Gore's "the absence of change IS the horror" argument and Miri's mechanical reasoning that environmental hints break the investigation mechanic — they're right. The game is ABOUT information asymmetry. If the station itself helps the player detect conspiracy, it undermines the core loop.
**The proposed resolution path is correct: the shift is character-driven, not environmental.**
What changes when conspiracy activates:
- **Insert overlay density increases.** More data points appear. More connection lines. More ambient processing flickers. The character's lattice is working harder because there's more to track. This is visible to the player as "my overlay got busier."
- **Monologue frequency increases.** More internal commentary. The character is THINKING MORE. This is a text/audio change, not a rendering change.
- **Entity color changes.** Green to amber. Amber to red. The D-033 system does the work. The information itself shifts, not the environment presenting the information.
What does NOT change:
- Light color temperature. The bar is still warm. The corridor is still dim.
- Shadow depth or direction. Same Light2D parameters.
- Ambient sound (that's Inigo's call, but from visual: no ambient visual change either).
- Tile colors, wall tones, floor patterns. The station is the same station.
The player walks into the bar after discovering the conspiracy and it's STILL warm and inviting. That's the horror. The warm light isn't ironic — it's indifferent. The station doesn't know what the player knows. It doesn't care. It continues being a perfectly comfortable place to live, which is what makes the player's knowledge so isolating.
**One exception I want to preserve:** The player character's OWN entity sprite could shift subtly. Not the world — the self. If the detective has been running hot (stress mechanic, if we build it), their sprite could show it in posture or a slight animation change. The character carries the conspiracy's weight visually. The station doesn't.
---
## OQ-03: Entity Detail Level, Sprite Dimensions, AND Tile Object Vocabulary
**The base building input actually resolves this question more cleanly than Round 1 could.**
Here's the updated spec:
### Structural tiles (walls, floors, doors)
- **64x64 per tile.** This is confirmed from D-014 and the base building input.
- **Structural tiles are the canvas.** Muted, understated, differentiated by zone palette (Round 1 hex values) and construction era. They MUST NOT compete with entity sprites or object tiles for visual attention.
- **Wall tiles need clear top-down silhouettes:** solid = wall, gap = door, transparent = window/viewport. At this scale, walls are primarily information barriers, not decorative surfaces.
### Object tiles (furniture, equipment, containers)
- **64x64 footprint per 1x1 object.** Larger objects (tables, beds, consoles) can be 2x1 or 2x2 but composed of 1x1 sub-tiles for grid placement.
- **Detail level: Rimworld's object sprites are the right target.** Clear top-down silhouette, identifiable at a glance, flat-lit (engine adds lighting). A chair looks like a chair. A console looks like a console. A crate looks like a crate. No ambiguity.
- **Object sprites must read WITHOUT color.** Because in certain perception modes (thermal), objects lose their natural color. The silhouette alone must identify the object type.
- **Objects carry the three construction eras** through subtle material variation. Original-era desk (institutional grey, squared edges) vs. recent desk (slightly different tone, rounded edges). Not dramatic — a 10% palette shift within the same hue family.
### Entity sprites (NPCs, player)
- **24x32 pixel footprint within a 64x64 tile.** The entity is smaller than the tile it occupies. This creates a clear figure-ground relationship — entities float ON the tile grid, they don't fill it. Ozzie's Heat Signature ratio reference is right here: entities are clearly smaller than the space they inhabit, which makes the space feel like a PLACE, not a character portrait.
- **Headroom matters.** 24 wide x 32 tall gives a slight vertical elongation — human proportions, not chibi. The extra height means the entity reads as "person standing" not "token placed."
- **Silhouette discipline (I-01).** Each named NPC gets one identifying shape feature visible at this scale: body proportion (stocky/lean/broad), one accessory (vest/apron/badge/tool belt), posture (hunched/upright/slouched). Generic NPCs get 3-4 template silhouettes.
### The entity-to-object-to-structure hierarchy
This is new and critical. With base building, we have THREE visual layers in the game world, not two:
1. **Floor/wall tiles (structure):** Muted. The background. Communicates space layout and zone identity.
2. **Object tiles (furniture/equipment):** Mid-detail. Placed ON structure. Communicates function and lived-in character.
3. **Entity sprites (people):** Boldest silhouettes, D-033 color coding. Moving ON objects and structure. Communicates the social/information layer.
The visual hierarchy must be: **entities > objects > structure.** The player's eye goes to people first, then furniture, then walls. This is achieved through:
- Entities have the strongest outline weight (2px at 64x64 equivalent)
- Objects have medium outline weight (1px)
- Structure has minimal or no outline (color fills only)
- D-033 colors on entities are more saturated than any object or structure color
Gore's "simple entities, richer environments" position from Round 1 translates well here: entities are simple bold silhouettes, the OBJECT LAYER provides the environmental richness, and structure provides the spatial backbone. The "richness" is in the density and variety of placed objects, not in individual object detail.
---
## OQ-04: Animation Ambiguity vs. Readability
**The proposed resolution path is exactly right. I'm endorsing it as stated.**
**Clear animation for expected activities:**
- Walking (directional walk cycle, 4-6 frames)
- Working at a station (seated/standing at console, periodic arm movement)
- Eating/drinking (seated at table/bar, arm-to-face cycle)
- Talking (two entities facing each other, subtle gesticulation)
- Sleeping (horizontal sprite, no movement)
These are the "life-sim substrate" animations. The player needs to read daily routine at a glance — The Sims principle. If you can't tell someone is eating vs. working vs. sleeping from across the map, the life-sim readability fails.
**Ambiguous animation for investigatively relevant behaviors:**
- Stopping (entity halts, but WHY? Thinking? Listening? Checking surroundings? Waiting for someone? Just resting?)
- Looking around (head rotation, but the REASON is invisible)
- Meeting someone (two entities near each other — talking? Exchanging something? Coincidence?)
- Entering an unusual space (an NPC in an area they don't usually go — are they lost? On an errand? Sneaking?)
- Handling an object (hands on something — what? Can't tell at this scale)
Gore's "legible but ambiguous" framing is correct for these. You SEE the behavior. You DON'T know the motivation. That's the information gap the player fills through investigation — the monologue, the insert data, other observation channels. If the animation told you WHY someone stopped, the observation mechanic loses its purpose.
**Production implication:** Expected activities need 2-3 distinct animation states each. Investigatively ambiguous behaviors need only 1-2 generic states ("idle-aware," "hands-occupied") that COULD be many things. This is cheaper to produce AND better for gameplay. Win-win.
---
## OQ-05: Darkwood as Primary Lighting Reference
**Yes. I endorse Darkwood as THE primary reference for vision cone lighting.**
I didn't cite it in Round 1 — that's an oversight. Having reviewed Ozzie's detailed advocacy, Darkwood solves the exact problem we have: **how does restricted LOS feel emotionally in a top-down game?**
Darkwood's lighting does three things we need:
1. **Vision cone as emotional gradient.** Full visibility in the cone, degraded at the edges, dark beyond. Not binary (XCOM) — graduated. This maps directly to our four-state fog (visible → fog-edge → hidden → remembered).
2. **Light pooling.** Practical light sources (lamps, fires) create pools of safety. Between the pools: darkness. The player's vision cone interacts with these pools — you can see further into a lit area than a dark one. This gives our zone lighting gameplay weight: the bar's warm lights aren't just atmosphere, they're EXTENDED VISIBILITY.
3. **The darkness has weight.** Darkwood's unseen space isn't just "grey tiles" — it's oppressive, heavy, alive. We want a gentler version of this. Our darkness is calm, neutral, simply "unknown" — not actively threatening like Darkwood's. But the WEIGHT of the darkness, the sense that unseen space is substantive rather than decorative, that's exactly right.
**Where we diverge from Darkwood:**
- Darkwood is HORROR. Our darkness is UNCERTAINTY. Darkwood's dark wants to kill you. Our dark just doesn't inform you. Different emotional register, same visual technique.
- Darkwood's vision cone is the player's only light in hostile darkness. Ours interacts with environmental lighting — the station has its own lights. The cone reveals what's beyond the station's illumination.
- Darkwood has no daily-life warmth. We need the Darkwood vision cone system to coexist with warm, well-lit social spaces where the cone barely matters because everything is already visible.
**Reference hierarchy for lighting:**
1. **Darkwood** — Vision cone mechanics, light pooling, darkness-as-weight
2. **Blade Runner 2049 / Roger Deakins** — Color temperature as emotional language
3. **Edward Hopper (Nighthawks)** — Warm interior light surrounded by unknowable dark (Gore's reference, and it's our central image)
These three aren't competing — they're describing different aspects of the same lighting system. Darkwood for the mechanic. Deakins for the color language. Hopper for the emotional composition.
---
## Proposed Prompt for Mood Board Image #8
**"World as Collection of 1x1 Placed Items"**
The image should capture: a top-down view of a space station interior where the world is VISIBLY assembled from discrete grid-aligned objects. Not a painted scene — an arrangement.
**Prompt:**
> Top-down view of a small section of a space station interior on a visible tile grid. The floor is composed of uniform grey-blue tiles. Against the walls: individual objects placed on the grid — a desk with a terminal (2x1 tiles), a chair (1x1), a storage crate (1x1), a wall-mounted console (1x1 on wall edge), a small table with two chairs (2x2 arrangement). Each object is a distinct, bold silhouette with clean edges, clearly separate from its neighbors. The style is clean 2D illustration, muted palette, flat lighting on objects (no baked shadows — the objects are shape templates). One section shows a transition: grey institutional floor tiles meet warmer-toned floor tiles where someone has renovated a corner into a break area with a coffee setup and a mismatched chair. A doorway gap in the wall shows a corridor beyond. Two small human figures (24x32 pixel scale relative to 64x64 tiles) stand in the space — one at the desk, one walking through the doorway. The figures are bold silhouettes, clearly smaller than the tiles they occupy. The overall impression: a functional space assembled from a vocabulary of objects, like Rimworld's placement system but with the muted, restrained aesthetic of a well-maintained space station. No neon. No grime. No pixel art. Clean, contemporary 2D illustration.
**What this image should prove:** That our art direction (clean 2D, bold silhouettes, muted palette, lighting-driven atmosphere) works when the entire world is composed of individually placed 1x1 objects on a grid. The Rimworld assembly method plus the mood we've converged on.
---
## Summary of Positions
| OQ | Resolution | Status |
|----|-----------|--------|
| **OQ-01** | Geometric data + soft bloom render = precision that feels organic | Resolved — synthesis of Araminta + Gore |
| **OQ-02** | Strict zero environmental shift. Character-driven changes only (insert density, monologue, D-033 colors) | Resolved — moved to Gore/Miri position |
| **OQ-03** | 64x64 tiles, 24x32 entities, three-layer hierarchy (structure > objects > entities), Rimworld object detail target | Resolved — clarified by base building input |
| **OQ-04** | Clear animation for daily activities, ambiguous for investigatively relevant behaviors | Resolved — endorsing proposed path |
| **OQ-05** | Darkwood endorsed as primary vision cone lighting reference alongside BR2049 color and Hopper composition | Resolved — endorsing Ozzie's advocacy |
| **New: Base building** | Confirms and strengthens Round 1 positions. Object vocabulary replaces painted backgrounds. Easier for Nano Banana pipeline. | Integrated |
All five open questions have clear positions. No new divergences introduced. Ready for synthesis.
---
*Written by Araminta. Consistency matters more than beauty at this stage.*
@@ -0,0 +1,83 @@
# Art Direction & Mood Board Workshop — Round 2 Brief
**Date:** 2026-02-12
**Facilitator:** Lead
**Round 1 tracking:** round1-tracking.md
---
## Round 2 Purpose
Resolve the 5 open questions from Round 1 and address one new design input from the lead.
## New Input from Lead (Round 2)
**Base building means tile-based sprites are unavoidable.**
If we want base builder mechanics, the world must be composed of discrete 1x1 tile objects — walls, doors, floors, chairs, tables, beds, consoles, crates. Think Rimworld's object placement: the player (or the simulation) places individual items on a grid. This has major implications for art style:
- Every object needs a clear, distinct top-down tile sprite
- Objects must read at 1x1 tile scale (64x64px per our spec)
- The environment IS a collection of placed objects, not painted backgrounds
- Rimworld's tile vocabulary becomes not just a readability reference but a production reference
- Nano Banana needs to generate consistent tile sprites across an object vocabulary
- Walls, floors, doors are structural tiles; furniture, equipment, containers are object tiles layered on top
This input reframes OQ-03 (entity detail) and connects to the broader question of how our "clean 2D with bold silhouettes" direction works when the entire world is assembled from discrete tile objects.
**Additional request:** Generate a mood board image (#8) capturing this: a top-down view where the world is visibly a collection of 1x1 placed items — walls, doors, floors, chairs — in the style we've been converging on.
---
## Open Questions to Resolve
### OQ-01: Neural insert overlay aesthetic — geometric or organic?
**Round 1 positions:**
- Araminta: Clean digital lines, 1-pixel precision, geometric. "The insert renders CLEAN because it's digital."
- Gore: Soft-edged, organic, thought-like. "Should look like thought, not technology."
- Ozzie: "Contact lens, not HUD." Subtle shimmer. Middle ground.
- Miri: "Native to cognition." Thin, precise, translucent.
**Proposed resolution path from tracking:** Could the insert be technically clean/geometric but rendered with soft edges via a shader bloom pass? Precision underneath, perceived as organic.
### OQ-02: Does the visual environment shift when conspiracy activates?
**Round 1 positions:**
- Araminta: Subtle CanvasModulate shifts allowed (5% cooler, deeper shadows)
- Gore + Miri: Strict zero shift. "The absence of change IS the horror."
- Ozzie: Slight shifts, but the real shift is internal to the player
**Proposed resolution path:** Could the shift be character-driven (insert overlay density increases) rather than environment changes?
### OQ-03: Entity detail level, sprite dimensions, AND tile object vocabulary
**Round 1 positions:**
- Araminta: 24-32px entities on 64px tiles
- Ozzie: Heat Signature entity-to-space ratio
- Gore: Rimworld-level entity simplicity, richer environments
**NEW CONTEXT:** With base building confirmed, the "environment" is itself made of placed tile objects. So "richer environment" means "more distinct tile objects" not "painted detail." How does this change the entity-to-environment relationship? Rimworld IS our structural reference now — not just for readability but for world composition.
### OQ-04: Animation ambiguity vs. readability
**Round 1 positions:**
- Gore: "Legible but ambiguous" — see that someone stopped, not WHY
- Araminta: Clear activity states (2-3 frames per interaction type)
- Sims reference implies clear activity readability
**Proposed resolution path:** Clear animation for expected activities (working, walking, drinking) but ambiguous for investigatively relevant behaviors (pausing, looking around, meeting)?
### OQ-05: Darkwood as primary lighting reference
Ozzie advocates strongly. No other agent cited Darkwood in Round 1. Does the team endorse Darkwood's vision cone lighting as THE model for our emotional gradient, alongside BR2049 color temperature work?
---
## Response Format
For each OQ, state your position clearly. If the proposed resolution path works, say so. If not, propose an alternative. Keep responses focused — we're resolving, not reopening.
Address the new tile-based base building input: how does it change (or confirm) your Round 1 positions?
Propose specific prompts for mood board image #8 if you have ideas for capturing the "world as collection of 1x1 items" aesthetic.
@@ -0,0 +1,109 @@
# Round 2 — Gore (Themes & Endgame Design)
## The Tile-Based World: Settling as an Act of Placement
Before the open questions — because this reframes several of them.
A world composed of placed 1x1 objects is a world that was **made**. Every wall was put there by someone. Every chair was carried in. Every console was installed, wired, and maintained. This is not a landscape you discovered. It's a construction someone assembled, piece by piece, over forty years.
That's what settling IS. To settle is to place things. To arrange your environment deliberately. The tile grid isn't a technical constraint — it's the mechanical expression of the thematic act of settling.
Think about what this means for investigation. When the detective notices that cargo containers have been rearranged to create a hidden meeting space, or that a maintenance panel has been placed over a corridor junction that doesn't need one, they're reading *decisions*. Someone placed that object there. Why? Investigation in a tile-based world is archaeology of intention: why did someone put this here?
And for the smuggler: the world is made of things you helped place. The Terminal's layout is your workplace. You know where every crate goes because you put them there. The smuggler's relationship to the tile world is *ownership through labor*. The detective's relationship is *suspicion through analysis*. Same tiles. Same objects. Different questions about who placed them and why.
This also resolves something I was circling in Round 1. I said the gap between entity simplicity and environment richness IS the game. With tile-based composition, "environment richness" now means **object vocabulary** — many distinct, readable tiles that accumulate into a space that feels inhabited. Not painted backgrounds. Not textures. *Things*. Each one placed by someone, each one carrying meaning. That's richer than a painting because it's *legible as decisions*.
Rimworld proved this works. A Rimworld base isn't beautiful, but it's *yours* — because you placed every wall, every bed, every turret. The emotion comes from the act of placement, not the visual fidelity of the objects. For our game, the NPCs have done that placing over forty years. The player inherits a world that carries the accumulated decisions of its inhabitants, readable tile by tile.
---
## OQ-01: Neural Insert Overlay — Geometric or Organic?
**Position: Accept the proposed resolution. Geometric precision underneath, soft-edged presentation via shader bloom.**
Araminta is right that the insert is digital technology — it renders clean because it IS clean. I was right that it should *feel* like thought. Both are true because one describes the source and the other describes the experience.
When you think precisely, the thought doesn't arrive with pixel borders. It arrives as awareness. The insert is computationally precise, but it's integrated into cognition, so the player experiences it as attention shifting — a soft bloom of awareness, not a hard rectangle snapping on.
Concretely: the insert's data is geometrically accurate (circles, lines, precise positioning), but the rendering applies a subtle bloom/softness pass that makes the edges organic. Like looking at a clean diagram through slightly warm glass. The precision is there if you study it. The first impression is softer. This gives Araminta production clarity (she can spec clean geometric elements) while delivering the perceptual experience I'm arguing for (the player feels awareness, not interface).
The key test: if a player forgets the insert overlay is there during a quiet life-sim stretch, the overlay is working. It should be so integrated into perception that switching it OFF would feel like going deaf, not like closing a window.
---
## OQ-02: Does the Visual Environment Shift When Conspiracy Activates?
**Position: Strict zero shift on the base world layer. Accept the proposed resolution — shift happens in insert overlay density, not in the environment.**
I'll say it more precisely than Round 1: the environment's refusal to acknowledge the conspiracy is **mechanically load-bearing**, not just thematically nice.
If the environment shifts — even 5% cooler, even slightly deeper shadows — it gives the player a signal they didn't earn through observation. It says "the game thinks something is wrong here." That violates the core mechanic. The player should only know what their character knows, and their character doesn't know the lighting changed because a conspiracy module activated. The rendering engine isn't a character. It doesn't have opinions.
The insert overlay, however, IS the character's cognition. If the detective's analytical mode is running hotter — more flags, denser information layer, the amber of D-033 appearing on more entities — that's the character paying more attention, not the environment helping the player cheat.
Araminta's 5% CanvasModulate shift is seductive because it would be invisible consciously. But that's exactly why it's wrong: it would influence the player subliminally, creating a feeling of "something's off" that the player attributes to their own instincts rather than to the game's rendering. That's the game lying about who noticed what. In an investigation game, the chain of evidence must be honest. If the player feels uneasy, it should be because they noticed a pattern, not because the color temperature dropped.
The one exception I'd allow: **time of day and weather** can shift the visual environment, because those are diegetic — the character perceives them too. Shift end, lights dimming, a storm rolling in. Those are real changes in the world. Conspiracy activation is not.
---
## OQ-03: Entity Detail Level + Tile Object Vocabulary
**Position: Entities at Rimworld simplicity (24-32px on 64px tile as Araminta specs). Environment richness comes from object variety and lighting, not from entity detail.**
The base-building reframe confirms this. With potentially dozens of distinct tile objects visible on screen simultaneously, the *objects* carry the visual richness. Entities need to be simple enough to read against that object field. If both entities and tiles are detailed, the screen becomes noise. If entities are simple silhouettes moving through a complex-but-readable tile landscape, you get natural figure-ground separation.
The critical production implication: we need a large, consistent tile object vocabulary more than we need detailed entity sprites. Twenty distinct, bold, instantly-readable object tiles matter more than ten frames of entity animation. The object vocabulary IS the environment vocabulary. Chairs, tables, consoles, crates, wall variants, door states, floor types, equipment — each one a 64x64 tile that's identifiable at a glance.
Ozzie's Insight I-01 stands: silhouette carries identity because color is spoken for (D-033). Kael's vest, Lera's apron, Sera's field kit — one identifying visual element per named NPC. The silhouette differentiation is doing what facial expressions can't at this scale.
---
## OQ-04: Animation — Clear Activities, Ambiguous Investigation Signals
**Position: Accept the proposed resolution. It's the right split.**
Clear animation states for routine activities: working, walking, sitting, eating, drinking, talking. These are the life-sim substrate. The player needs to see at a glance that "Kael is working at the cargo terminal" or "Lera is behind the bar." If routine isn't readable, you can't notice when routine breaks.
Ambiguous animation for investigatively relevant behaviors: pausing, looking around, changing direction, meeting someone in an unusual location. These should be *noticeable* but not *self-interpreting*. You see that Kael stopped walking. You don't know from the animation whether he stopped to check a manifest, to tie his shoe, or to make sure nobody's watching. You need to use other channels — proximity, context, monologue, perception mode — to interpret it.
The thematic logic: routine is readable because routine is public. People performing their daily lives are performing them *for others to see* — that's how social life works. But the moments that matter for investigation are the moments people try to make look normal. The animation system should mirror this: normal looks normal, deception looks like normal but *slightly off*, and the slight offness is noticeable only if you're paying attention.
This also serves the Sims reference: daily life needs to be visually engaging and readable for the 30-minute runway. If routine activities are ambiguous, the life-sim substrate becomes unreadable soup. Clear routine = attachment. Ambiguous deviation = investigation.
---
## OQ-05: Darkwood as Primary Lighting Reference
**Position: Endorse Darkwood as the vision cone lighting model, with a critical emotional retuning.**
I didn't cite Darkwood in Round 1 because I was working at a different altitude. But looking at what Ozzie describes — the way light pools and fades, the emotional gradient from known to unknown, the vision cone as something you *feel* rather than *see* — this aligns with everything I argued about fog as the edge of attention rather than the edge of safety.
The critical retuning: Darkwood uses its lighting to create **dread**. The darkness is hostile. Things in the dark will kill you. That's a horror game's emotional contract.
Our game needs the same lighting *mechanics* with a different emotional *payload*. The darkness — the desaturated space beyond perception — should carry **uncertainty**, not dread. The station is probably fine out there. Those tiles keep going. Those lights stay on. People are living their lives in the rooms you can't see. The unknown isn't hostile. It's just... unknown. And in an investigation game, the unknown is where the answers are.
So: Darkwood's gradient technique (light pooling close, fading at range, soft transition from known to unknown), combined with our warmer palette and the understanding that beyond the cone is *life continuing without you*, not *monsters waiting*. The BR2049 color temperature work gives us the emotional register. Darkwood gives us the cone mechanics. Together: a warm, uncertain, graduated perception field.
The compound reference: **BR2049 for color mood, Darkwood for cone behavior, Hopper for emotional register.** The vision cone pools warm light in known space, fading into soft desaturation — not darkness — at the perception boundary. Not terrifying. Not safe. *Uncertain.*
---
## Mood Board Image #8 Prompt Suggestion
For the "world as collection of 1x1 placed items" concept:
A true top-down view of a small section of The Last Shift bar. Every element is a readable discrete object on a grid: bar counter tiles, stool tiles, booth tiles, table tiles, floor tiles with visible grid alignment. Two or three simple entity silhouettes sitting or standing. Warm amber lighting from overhead Light2D sources. The key quality: you can see that this space was *assembled* — each piece placed deliberately — and yet it reads as a *place*, not a diagram. The warmth of the lighting transforms the grid from schematic into home. One corner shows the corridor outside through a doorway, cooler-toned, fewer objects, more empty floor tiles. The transition from dense-warm-inhabited to sparse-cool-functional should be visible in the tile density itself: the bar is full of placed objects (settled), the corridor is mostly bare floor (unsettled).
The thesis of this image: **settling is visible as object density.** The more tiles someone has placed in a space, the more that space has been made into a home. The corridors are empty because nobody settled them. The bar is full because Lera did.
---
## Summary
The tile-based world is thematically right, not just technically necessary. Settling is placement. Investigation is reading placement. The tile grid is the medium through which both the game's central act (making a home) and its central tension (discovering what that home hides) are expressed.
On the five OQs: accept the geometric-underneath-organic-on-top resolution for the insert. Hold firm on zero environmental shift. Entities simple, object vocabulary rich. Routine animation clear, deviation animation ambiguous. Darkwood mechanics with uncertainty-not-dread emotional retuning.
The art direction is converging on something I'd call **functional warmth** — a world made of readable, placed objects, lit with the care of a place someone called home, seen through the analytical-but-uncertain gaze of a character trying to understand what they're looking at.
@@ -0,0 +1,209 @@
# Round 2 -- Miri's Worldbuilding & Setting Perspective
**Agent:** Miri (Worldbuilder & Setting Designer)
**Date:** 2026-02-12
**Workshop:** Art Direction & Mood Board
---
## OQ-01: Neural Insert Overlay -- Geometric or Organic?
**My position: The proposed resolution path is setting-correct. Endorse it.**
Let me check this against what the lattice actually *is* in the Settled Reach.
The neural lattice is a mature technology. It has been standard-issue for over a century. The information it processes is precise digital data -- Meridian queries, navigation coordinates, Commission database results, sensory analysis outputs. Underneath, the data is clean. It is computed, structured, deterministic.
But the *delivery mechanism* is neural. The lattice interfaces with the visual cortex. It doesn't project onto a screen -- it renders through biology. The human brain receives the data and interprets it the way it interprets all visual input: with the soft edges, the attention-gating, the peripheral fade that biological perception imposes on everything.
So: **technically geometric, perceived as organic.** The data is precise. The experience of the data is human. A waypoint annotation is computationally a point coordinate with a label. What the character *experiences* is a subtle awareness of direction, a sense of "over there," a label that appears when attended to and fades when ignored.
The shader bloom resolution -- clean geometric lines rendered with a soft diffusion pass -- is the exact visual metaphor for this setting reality. Araminta's precision is the data. Gore's organic softness is the neural delivery. Both are correct simultaneously.
**One setting-grounded addition:** The lattice overlay should feel *slightly different* per character because they have different hardware. The smuggler's baseline lattice produces a thinner, sparser overlay -- fewer elements, simpler rendering, more transparent. The detective's augmented lattice produces a denser overlay with more annotation types and slightly crisper rendering (institutional hardware is better maintained and more recently calibrated). The difference is subtle but cumulative: the detective lives in a world with more visible data. The smuggler lives in a world with more visible... world.
---
## OQ-02: Does the Visual Environment Shift When Conspiracy Activates?
**My position: Strict zero environmental shift. The proposed resolution path (character-driven, not environment) is correct.**
I held this position in Round 1 and the tracking confirms Gore agrees. Let me reinforce with setting logic.
The conspiracy has been operating for two years. It was there before the game started. The smuggling ring is part of the district's *normal economic activity*. Nothing in the physical environment changes when the player discovers it -- the environment never changed in the first place. The containers were always mislabeled. The corridors were always used for off-manifest transfers. The station doesn't know it's harboring a conspiracy. The station is the same station it was yesterday.
What changes is the character's *perception*, which manifests through the lattice overlay:
- **New annotations appear.** Flagged persons of interest. Case file notes pinned to locations. Anomaly indicators on manifest displays.
- **Entity colors shift (D-033).** Green to amber. The world of trusted faces develops questions.
- **Monologue density increases.** More observations. More pattern-matching. The character's internal voice becomes more active.
- **Information overlay complexity grows.** The lattice has more to show because the character has more to cross-reference.
The base world layer -- tiles, lighting, environmental objects -- remains identical. The CanvasLayer stack changes: overlay layers gain density. The effect is that the player feels the world becoming *more complex* without the world itself changing. The complexity was always there. The character is just starting to see it.
**Why this matters mechanically:** If the environment shifts (even 5% cooler), a savvy player will learn to read the environmental shift as a meta-signal: "the game is telling me something is happening." That breaks the investigation mechanic. The player should discover wrongness through *their own observation*, not through the rendering engine nudging them. Environmental neutrality is load-bearing.
**The exception I'd allow:** Time-of-day shifts (D-031) continue normally. The station gets dimmer at night regardless of conspiracy state. Weather continues regardless of conspiracy state. These are *physical reality*, not narrative signal. The storyteller can *time* a discovery to coincide with a dimmer evening or a fog rolling in -- but the environmental change is causal (it's evening, so it's dimmer) not correlative (something bad is happening, so it's dimmer).
---
## OQ-03: Entity Detail Level, Sprite Dimensions, AND Tile Object Vocabulary
**The tile-based input changes everything about how I frame the environment. It confirms the setting.**
### Why Base Building Is Setting-Faithful
Sova Transit District was literally built this way. Prefab modular construction (D-036, my Round 1 setting brief) means standardized wall panels, floor sections, door frames, and utility connections assembled on a grid. The original station builders placed components. Twenty years later, expansion crews placed different components alongside the originals. Ten years after that, Lera converted a commercial space into a bar by placing her own furnishings inside the existing structural shell.
A tile-based world isn't an abstraction of the setting. It IS the setting. Stations in the Settled Reach are assembled from discrete modular components. The game's world composition method mirrors the fiction's construction method.
### Tile Vocabulary -- The Setting's Material Inventory
Here's what exists in Sova Transit District, organized by tile type. This is the object vocabulary the art pipeline needs to produce:
**Structural tiles (walls, floors, doors):**
| Tile | Era | Visual character | Zone |
|---|---|---|---|
| Institutional wall panel (standard) | Era 1 | Uniform grey composite, clean lines, may have faded color coding | Terminal, main corridors |
| Institutional wall panel (retrofit Meridian) | Era 2 | Same base panel + surface-mounted Meridian junction box (small, distinct) | Terminal, bar corridor |
| Modified partition wall | Era 2-3 | Lighter material, visible fasteners, different shade from structural walls | Bar interior, modified spaces |
| Exposed structural wall | Era 1 (revealed) | Heavy metal, utility markings, cable runs visible | Maintenance corridors |
| Institutional composite floor | Era 1 | Smooth, cool grey, cargo guide markings in industrial areas | Terminal, main corridors |
| Worn commercial tile floor | Era 2-3 | Warmer tone, slightly uneven, scuff patterns | Bar, break rooms |
| Metal grating floor | Era 1 | Grid pattern, visible understructure, industrial | Maintenance corridors, cargo bays |
| Standard pressure door | Era 1 | Institutional grey, Commission-standard markings | Terminal, corridors |
| Commercial door (modified) | Era 2-3 | Different material/color from surrounding walls, may have signage | Bar entrance, shops |
| Maintenance hatch | Era 1 | Heavy, utility markings, may be sealed or accessible | Maintenance corridors |
**Furniture and equipment tiles (placed objects):**
| Tile | Visual character | Zone |
|---|---|---|
| Manifest processing terminal | Screen glow (blue-white), institutional housing, desk-mounted | Terminal |
| Cargo container (large, 2x1 or 2x2) | Standardized freight container, color-coded by contents category | Terminal, cargo bays |
| Cargo container (small, 1x1) | Crate-sized, may be labeled or unlabeled | Terminal, maintenance |
| Grav-lift (parked) | Industrial yellow/grey, small vehicle footprint | Terminal cargo lanes |
| Worker locker | Metal, personalized (stickers, dents), row-mounted | Terminal break room |
| Bar counter segment | Warm wood-tone or composite, backlit panels | Bar |
| Bar stool | Simple, varied (some are newer replacements) | Bar |
| Table (institutional) | Grey, functional, break-room standard | Terminal break room |
| Table (bar) | Warmer tone, worn surface, not uniform | Bar |
| Chair (institutional) | Matching break room table | Terminal break room |
| Chair (bar) | Mixed, non-matching (accumulated over years) | Bar |
| Corner booth | Semi-enclosed, visually distinct as "private" space | Bar |
| Meridian display (wall-mounted) | Screen with news ticker, small blue-white glow | Bar, Terminal lobby |
| Commission kiosk | Institutional, small footprint, Commission insignia | Terminal entrance |
| Ventilation unit | Ceiling-mounted (may cast shadow), industrial | Corridors, maintenance |
| Cable junction box | Wall-mounted, small, may be Era 1 or Era 2 (different styles) | Maintenance corridors |
| Emergency guide strip | Floor-mounted, soft glow, directional | All corridors |
| Span gate machinery (edge tiles) | Massive industrial housing, warm glow from bore | Terminal (gate-adjacent) |
**Setting note on object mixing:** The three-era system means the *same zone* contains objects from different periods. The Terminal's break room has Era 1 institutional tables alongside Era 3 personal locker decorations. The bar has an Era 1 structural shell with Era 2-3 furnishings. The visual variety within a single room communicates temporal layering without explicit exposition. This is how "40 years of habitation" reads in a tile-based system: mixed-vintage objects sharing the same space.
### Entity Sprite Dimensions
I defer to Araminta on exact pixel spec. From the setting: entities should be large enough for D-033's color system to read clearly and for silhouette differentiation (I-01: "silhouette IS identity because color is spoken for") to work. The worker-in-coveralls must look different from the worker-in-apron must look different from the person-in-institutional-jacket at whatever scale we choose.
One setting-grounded constraint: **no entity should be so detailed that you can read their lattice tier from the sprite.** The detective's augmented lattice is invisible -- it's inside their head. You can't tell someone has institutional hardware by looking at them. The smuggler avoiding enhanced lattice is a *social* choice, not a visual one. Entity sprites should communicate role (dock worker, bartender, office worker) through clothing and build, not technology level.
---
## OQ-04: Animation Ambiguity vs. Readability
**My position: The proposed dual-tier resolution is setting-correct. Clear for public activities, ambiguous for private intention.**
The setting provides the logic for this directly.
In Sova Transit District, daily activities are **socially public.** Everyone can see you working. Everyone can see you walking to the bar. Everyone can see you having a drink. These activities are the social fabric of the district -- the shared routines that make the place feel inhabited. They should be clearly animated because *everyone in the district can read them*. When the player sees an NPC working at a manifest terminal, the animation should be unambiguous: that person is working.
But investigatively relevant behaviors are **privately motivated.** A pause in a corridor could be checking a lattice message, or deciding whether to enter a room, or waiting to see if they're being followed, or just thinking. The social code of Sova doesn't distinguish between these -- all of them look like "person paused." Only additional information channels (monologue interpretation, lattice data, prior observation) allow the player to infer *why*.
This maps to the NPC tell system (D-024, my cultural tell catalog from the Content Gap workshop). A tell is a behavioral deviation from expected routine that is *observable but not self-explanatory*:
| Animation tier | Examples | Readability | Setting basis |
|---|---|---|---|
| **Clear** (public activity) | Working at terminal, carrying cargo, serving drinks, sitting at table, walking on a route | Unambiguous -- you know what they're doing | Shared social activity. Everyone can read this. |
| **Ambiguous** (private intention) | Paused in corridor, looking toward a location, checking lattice frequently, changing direction, entering an unexpected area | Observable -- you see the behavior. Not self-explanatory -- you don't know why. | Private motivation. Requires investigation to interpret. |
The key: **the boundary between tiers should not be visually signaled.** The player doesn't get a "this NPC is doing something suspicious" animation style. They get the same basic movement and posture system, and the *ambiguous* animations look like they could be innocent. An NPC pausing in a corridor looks exactly the same whether they're checking a message or waiting for a dead-drop signal. The player must bring their own interpretation -- which is the game.
---
## OQ-05: Darkwood as Primary Lighting Reference
**My position: Endorse Darkwood's lighting *principle* with setting-specific adaptation.**
I didn't cite Darkwood in Round 1, but having considered Ozzie's advocacy, the principle is setting-compatible: light pools define safe/known space, the boundary between light and dark is where tension lives, and the unknown is genuinely unknown.
**Where Darkwood's model fits our setting:**
- **Maintenance corridors with manual-switch lighting.** These spaces are dark by default. The player (or an NPC) activates a light, creating a pool of visibility. Beyond the pool: genuine darkness. This is Darkwood's core mechanic directly applied. The setting justifies it: these corridors have manual switches because they're not primary traffic routes and automated lighting was never installed (or was disconnected during modifications).
- **Exterior sections during night cycle or weather.** Station exterior walkways at night, in fog, or during heavy rain have reduced ambient light. Darkwood's emotional gradient -- clear close, uncertain at range, unknown beyond -- maps to the weather-modified vision cone.
- **The fog boundary in general.** Darkwood's treatment of the vision cone edge (not a hard line but a gradient fade) aligns with our fog-as-ignorance principle. The world doesn't end at the fog boundary -- it becomes uncertain, then unknown.
**Where Darkwood's model needs adaptation:**
- **Most of Sova is lit.** The Terminal has institutional overhead lighting. The bar has warm fixtures. Main corridors are lit. Darkwood is a horror game set in a dark forest -- most of its world is hostile darkness with small pools of safety. Sova is the opposite: most of the world is adequately lit, with specific areas of reduced lighting. The Darkwood model applies to maybe 20-30% of the game's spaces, not 100%.
- **Light doesn't equal safety.** In Darkwood, light = safe zone. In our game, the bar's warm light is where the smuggler socializes AND where the detective observes AND where THE FRIEND's contradiction might first be noticed. "Well-lit" doesn't mean "nothing to find." The emotional gradient shouldn't code illuminated areas as "safe" -- only as "visible."
**Setting verdict:** Darkwood as the lighting *model* for our reduced-light spaces (maintenance corridors, exterior weather, night cycle) and as the emotional *principle* for the fog boundary everywhere. Not as the universal lighting approach for the whole game.
---
## Tile-Based Base Building: Setting Implications
### What This Confirms
The tile-based world composition confirms three things I established in the setting:
**1. Stations are modular construction.** The tile grid IS the station's construction grid. Prefab panels, standard fixtures, discrete components placed on a structural framework. When the player sees a wall tile next to a door tile next to a floor tile, they're seeing how Station Sova was actually built.
**2. Three construction eras = three tile palettes.** The visual layering I described maps directly to tile palette mixing:
- **Era 1 palette:** Uniform, institutional, limited variety. These are the standardized construction components. Cool greys, clean lines, regulation markings.
- **Era 2 palette:** Modified components. Different manufacturers, different materials, visible where they join Era 1 tiles. Surface-mounted additions (Meridian junction boxes, extra cable runs) as overlay objects on Era 1 walls.
- **Era 3 palette:** Personal/commercial modifications. Lera's bar furnishings. Locker decorations. Hand-lettered signs. The most varied palette because it represents individual human choices, not institutional procurement.
**3. Object mixing tells the station's story.** A single room can contain tiles from all three eras. The Terminal break room: Era 1 walls, Era 1 institutional table, Era 2 Meridian hookup on the wall, Era 3 personal items in lockers. The visual history of the space is told by the objects in it. No exposition needed -- the player can *see* the layers.
### What This Changes About My Round 1 Positions
**Floor tiles become the primary visual surface.** I said "the floor tells the story" in Round 1. With tile-based composition, this is literal. The floor tile type defines the zone more than any other element: institutional composite (Terminal), worn commercial tile (bar), metal grating (maintenance). The player reads the floor to know where they are. Different floor tiles at a threshold = different zone. This is the visual transition I praised in mood board image #7 -- the warm bar floor meeting the cool corridor floor at the doorway.
**Environmental richness comes from object variety, not texture detail.** Gore's I-09 insight ("the gap between entity simplicity and environment detail IS the game") reframes in a tile context. "Detail" doesn't mean painted texture -- it means *more distinct object tiles*. A richly detailed room has many different objects placed in it. A sparse room has few. The bar feels rich because it contains: bar counter, stools, tables, chairs, corner booth, Meridian display, bottles, personal items. A maintenance corridor feels sparse because it contains: junction boxes, a ventilation unit, emergency strips, nothing else. Object density = environmental character.
**Nano Banana's job is tile consistency, not scene painting.** Each tile needs to be individually generated at 64x64 and visually consistent with others in its era palette. This is a different production challenge than painting environmental backgrounds -- more assets, simpler per-asset, with consistency across a vocabulary rather than within a single composition. The style guide needs to define: outline weight, color palette per era, shadow direction, and level of interior detail per tile.
### Prompt Suggestion for Mood Board Image #8
For the "world as collection of 1x1 placed items" image:
**Scene:** A section of Sova Transit District where three zones meet -- the edge of the Terminal (institutional floor tiles, cargo containers, a manifest terminal), a corridor (transition floor tiles, overhead panel lights), and the bar entrance (worn commercial floor, warm light spilling out, the "BAR" sign or "The Last Shift" sign over the door). True top-down perspective, ~64px tile scale. The world should be visibly *assembled* -- you can see the grid, the discrete objects, the placed walls. A few NPC entities at the appropriate scale. The vision cone/fog boundary visible, with the desaturation-style fog.
**Key elements to capture:**
- Visible tile grid -- the world is composed of discrete objects, not painted backgrounds
- Three floor types visible (institutional, corridor, commercial) showing zone transitions
- Objects placed ON floors: containers, terminals, tables, chairs -- each a distinct tile
- Wall tiles with visible modular construction logic (panels, door frames, structural elements)
- Warm light from bar, cool light from Terminal, mixed in corridor -- the zone differentiation through Light2D
- NPC entities at correct scale relative to tiles (24-32px on 64px tiles)
- Clean 2D, bold silhouettes, muted environmental palette with lighting doing the emotional work
---
## Krenn System Planet: Naming and Confirmation
Flagging this from Round 1. The planet Sova orbits needs a name. Proposing now:
**Velen.** Krenn System naming conventions: compact, consonant-weighted. Velen is the primary habitable world. Temperate-maritime climate near Sova's surface connection: regular rainfall, morning and evening fog, mild temperature range, occasional heavy squalls. Gravity ~0.9G.
The name follows the same linguistic register as the NPC names I established (Kael, Voss, Lera, Torek). Simple, two-syllable, slightly Nordic-influenced without mapping to any specific Earth culture. Confirmation requested.
---
*Setting note -- the tile-based input is the kind of constraint that makes worldbuilding better, not harder. When the world is built from discrete components, every object is a worldbuilding decision: what manufacturer made this wall panel? What era was this floor installed? Why is there an Era 2 junction box on an Era 1 wall? The tile grid forces specificity, and specificity is where setting depth lives.*
*Miri out.*
@@ -0,0 +1,174 @@
# Round 2 — Ozzie (Player Experience / Wow Factor)
## OQ-01: Neural Insert Overlay — Geometric or Organic?
**Position: Endorse the proposed resolution. Geometric underneath, bloom-softened on top.**
The resolution path nails it. Here's why from a player experience angle:
The insert is on screen CONSTANTLY. It can't fight for attention with the game world. Araminta's geometric precision ensures the information is CLEAR when you need it — thin lines, precise positioning, readable data. Gore's organic softness ensures it doesn't feel like a tactical HUD bolted onto a warm, lived-in station.
The bloom pass is the bridge: the data is precise, the RENDERING is soft. Like reading glasses that happen to have a heads-up display. The information is sharp when you focus on it. The edges breathe when you don't.
**The player experience test:** After 10 minutes of play, does the player forget the insert is there? If yes, we've succeeded. It should become like your own peripheral vision — invisible until something appears in it. The monologue chime (D-038) is the "hey, look at your insert" signal. Without the chime, the insert fades into perception. WITH the chime, your eyes snap to the overlay and the geometric precision is there, waiting, with the information you need.
Geometric = the insert is TRUSTWORTHY. The data is clean. This is technology that's been refined for centuries. It doesn't flicker. It doesn't glitch. It's as reliable as your own vision. That's Commonwealth tech — so mature it's invisible.
## OQ-02: Does the Visual Environment Shift When Conspiracy Activates?
**Position: Moving to strict zero. Gore and Miri are right. Shift the INSERT, not the WORLD.**
I was on the fence in Round 1. I'm off it now. Here's what changed my mind:
If the environment gets 5% cooler when something bad is happening, the player will LEARN that. Maybe not consciously, but their subconscious will calibrate: "the room feels colder → something's wrong → look harder." At that point, the RENDERING is doing detective work instead of the PLAYER. The investigation mechanic breaks. The player isn't discovering anything — they're reading the environment's cheat sheet.
**The correct path:** The station stays EXACTLY the same. The warmth stays. The lighting stays. The NPCs keep walking their routines. Nothing changes except what the player KNOWS. And the growing gap between "everything looks fine" and "I know it isn't fine" — THAT gap is the horror. Gore called it "the absence of change as horror" and that's one of the most important insights from Round 1.
**What CAN shift:** The insert overlay. As the character processes more information, the insert gets busier. More data points. More flagged entities. More pings. The insert layer density increasing represents the CHARACTER's heightened analytical state, not the WORLD changing. The warm bar still looks warm. But the thin geometric lines multiplying on the overlay say "your character is thinking hard right now."
This also serves the divergence reveal (wow moment #4). On the second playthrough, the station looks IDENTICAL. Same warmth. Same lighting. But the insert overlay is completely different — different entities flagged, different data, different analytical framework. The world didn't change. YOUR LENS changed. That's the game.
**One exception I'd allow:** Weather. Weather can shift the environment because it's DIEGETIC — it's happening in the world, everyone experiences it, it's not a rendering hint. A dust storm making things amber-orange isn't the game telling you "something's wrong." It's a dust storm. The storyteller might TIME the weather for dramatic effect, but the weather itself is neutral.
## OQ-03: Entity Detail Level, Sprite Dimensions, AND Tile Object Vocabulary
**Position: Araminta's 24-32px entities on 64px tiles. Confirmed and STRENGTHENED by base building.**
Base building means tile-based world composition. The environment IS placed objects. This changes the game — literally — in ways that are GREAT for player experience:
### Why tile-based is a player experience WIN
1. **Routine deviations become PHYSICAL.** "That crate wasn't there yesterday" is a tile-object difference, not an atmospheric hint. The player who memorizes the station layout can spot changes. Investigation becomes spatial memory, not mood reading. This is how real investigation works.
2. **The station evolves visibly.** Lera adds a shelf to the bar. Someone stacks new cargo in Bay 04. A maintenance hatch gets sealed. The tile world changes because CHARACTERS change it, not because the rendering engine signals plot beats. Environmental change = social signal. Every moved tile is evidence of someone's decision.
3. **Player ownership.** If the player can place or rearrange objects in their quarters, that's ATTACHMENT. You care about a space you've furnished. You defend a space you've customized. When the conspiracy contaminates your personal space, it hurts more because you BUILT that.
4. **The Sims lesson, applied.** The Sims made "watch people interact with placed objects" compelling for decades. A tile-based station where NPCs sit in specific chairs, use specific terminals, drink at specific bar stools — that's routine readability. The NPC's relationship to placed objects IS their daily life.
### Entity-to-tile relationship
At 24-32px entities on 64px tiles:
- An entity takes up roughly 1/3 to 1/2 of a tile's visual space
- Entities POP off the tile grid — they're clearly ON the objects, not blending into them
- D-033 color applies to the entity, not to the tile underneath. The entity is the information signal sitting on top of the information-neutral environment. This separation is even cleaner with discrete tile objects.
- Silhouette differentiation works at this scale (TBW lesson). Different body shapes read as different people against the uniform tile grid.
### Nano Banana tile generation
The tile object vocabulary IS the Nano Banana production pipeline. Every object type is a 64x64 sprite: wall segments, floor tiles, doors, chairs, tables, bar counter sections, cargo crates, terminals, light fixtures, bed frames, storage lockers. The style guide is: bold top-down silhouette, muted station-palette colors, consistent outline weight. These are exactly the kind of "many similar but distinctive" assets AI generation handles well.
**Rimworld is now both a readability AND a production reference.** The tile vocabulary, the object-placement system, the grid-based world composition. We're building on proven ground.
## OQ-04: Animation Ambiguity vs. Readability
**Position: Endorse the proposed resolution AND strengthen it. Clear for routine, ambiguous for investigation.**
This is one of the most elegant design ideas from Round 1 (Gore's I-11) and the proposed resolution maps it to practical production:
### Tier 1: CLEAR animation (daily life activities)
- Walking, running, standing idle
- Working at a terminal / handling cargo
- Sitting, eating, drinking
- Talking to someone (face-to-face positioning)
- Sleeping
These MUST be instantly readable because they ARE the baseline. The entire investigation mechanic depends on the player knowing "normal" so they can spot "abnormal." If I can't tell at a glance that Kael is working, I can't notice when Kael stops working. The Sims clarity for routine activities is non-negotiable.
2-3 frames per activity is right. State-based with smooth interpolated transitions. A person sitting down should smoothly transition to seated, not teleport.
### Tier 2: AMBIGUOUS animation (investigatively relevant behaviors)
- Pausing / stopping (why did they stop?)
- Looking around (checking for witnesses? admiring the view?)
- Lingering near a location (waiting for someone? taking a break?)
- Changing direction mid-walk (forgot something? avoiding someone?)
- Standing near another person but not in "talking" animation (proximity without interaction — are they together or coincidence?)
These should read as "something happened" but NOT "something suspicious happened." The animation shows the WHAT (person stopped) but not the WHY. You need to get closer, observe longer, check the monologue, or interact to interpret.
### Why this is THE player experience move
This creates a natural investigation loop:
1. **Scan** (from far away, read clear routine animations, spot anyone not doing the expected thing)
2. **Notice** (someone's in Tier 2 — ambiguous animation. "Kael stopped working. Why?")
3. **Approach** (move closer to reduce the ambiguity. Now you can see facing direction, proximity, context)
4. **Observe** (watch for a moment. The monologue fires. Your character interprets what you're seeing)
5. **Decide** (interact? follow? note and move on?)
That's observe→notice→follow→discover (D-027 success criterion #4) emerging DIRECTLY from the animation tier split. The clear/ambiguous division isn't an art decision — it's the investigation mechanic expressed through animation.
## OQ-05: Darkwood as Primary Lighting Reference
**Position: YES. But let me be precise about what I mean.**
I'm not saying "make it look like Darkwood." Darkwood is survival horror in a poisoned forest. We're a conspiracy thriller on a space station. Different genre, different emotional register.
What I'm advocating is Darkwood's PRINCIPLE: **the vision cone creates an emotional gradient from known (lit, visible, safe) to unknown (dark, invisible, uncertain), and the transition zone between them is where the player's emotional engagement lives.**
In Darkwood, the gradient goes: bright → dim → dark → terror.
In The Settled Reach, the gradient goes: clear → reduced → uncertain → ignorant.
Same structure. Different emotions along it. Our "terror" is "I don't know what's happening in the next room" not "a monster might be there." But the MECHANISM — the smooth decay of visual information from center to edge — is identical.
### The two-reference lighting system
**Darkwood provides:** The vision cone emotional gradient. The way light fades and detail reduces. The way entities at the perception edge become ambiguous shapes. The way you lean forward because you WANT to see more than the cone allows.
**Blade Runner 2049 provides:** The color temperature vocabulary. Warm amber for social spaces. Cool white for institutional spaces. Mixed for transitional zones. The way a room's light tells you how to feel before you see anyone in it.
**Together:** Darkwood's gradient mechanics + BR2049's color temperature language = The Settled Reach lighting system. The vision cone fades from full detail to uncertainty (Darkwood). The full-detail zone is warm or cool depending on where you are (BR2049). The result: you're in the bar, the warm amber is clear and readable around you, and it fades to uncertain darkness at the cone's edge. You're in the Terminal, the cool white is crisp around you, and it fades to institutional shadow. Same gradient mechanic, different emotional color per zone.
No other game I know of combines these two lighting approaches. That's our visual identity.
### Godot 4 implementation
This IS the PointLight2D + LightOccluder2D + CanvasModulate system I described in Round 1. The vision cone is a textured PointLight2D on the player (brighter forward, dimmer peripheral, absent behind). The zone lighting is additional PointLight2D nodes on environmental fixtures (warm for bar, cool for terminal). LightOccluder2D on walls creates the natural shadows. CanvasModulate sets the ambient darkness level. It's stock Godot 4 — the engine was built for this.
## New Input: Tile-Based Base Building
### What this means for the wow moments
**Arrival (D-039 #1):** The player sees the station as a collection of placed objects. Tables with chairs around them. Terminals with cargo nearby. This reads as "people arranged this space for living." The tile grid IS the evidence of habitation. The span gate isn't a background painting — it's a massive structural object dominating the tile layout, with tiny worker entities walking past it.
**The Character's Eye (D-039 #2):** The monologue can now reference SPECIFIC OBJECTS. "That crate wasn't in Bay 04 this morning." "Someone moved the maintenance panel." Tile-object changes become investigation triggers. The character notices physical differences in the tile layout that the player might have missed.
**THE FRIEND's Contradiction (D-039 #3):** Kael is in the wrong TILE LOCATION. Not abstractly "in the wrong area" — he's standing next to a specific locked door, near a specific cargo container, in a corridor that has specific objects he has no business being near. The tile precision makes the "wrong place" observation concrete and verifiable.
**The Divergence Reveal (D-039 #4):** Same tile layout. Same objects. Different ENTITY COLORS on the same NPCs. The tile world is identical. Only the D-033 relationship overlay changed. This is STRONGER with tile-based composition because the environmental consistency is pixel-perfect — the divergence is PURELY in how the characters are colored and what the monologue says.
### The "world as collection of objects" should feel ALIVE, not sterile
The risk with tile-based composition: it looks like a Rimworld blueprint. Functional but cold. The save is LIGHTING. The same tile objects in the bar look warm and inviting under amber PointLight2D. The same tile objects in the Terminal look institutional under cool white light. The same tile objects in the corridors look dim and uncertain under minimal strip lighting. The tiles provide SHAPE. The lighting provides SOUL. This was true before the tile-based confirmation — now it's even more critical.
## Mood Board Image #8 Prompt Suggestion
**The Last Shift (bar) as a visible tile composition.**
Show the bar interior — the same warm, inviting space from mood board #1 — but rendered so you can clearly see the discrete tile objects that compose it:
- Individual chair tiles around table tiles
- Bar counter as a set of connected counter-segment tiles
- Wall tiles with light fixture tiles mounted on them
- Floor tiles with slight variation (wear paths, patched sections)
- A few entity silhouettes (bartender behind counter, patron on stool, pair at table)
- Warm amber PointLight2D pools from the light fixture tiles
- True top-down perspective matching #5/#7 scale
- Outside the bar doorway: cooler corridor tiles visible, the temperature transition
The point: you can see the grid. You can see the individual objects. And it STILL feels like a bar where you'd want to sit down and order a drink. The tiles are visible but the WARMTH transcends them. Rimworld's production model + The Last Shift's emotional register.
---
## Summary: What Round 2 Resolves
| Question | My Position | Status |
|---|---|---|
| OQ-01: Insert overlay | Geometric + bloom. Endorse resolution path. | RESOLVED |
| OQ-02: Environment shift | Zero shift. Insert overlay density increases instead. | RESOLVED (moved from middle to Gore/Miri) |
| OQ-03: Entity detail + tiles | 24-32px on 64px tiles. Tiles strengthen the model. | RESOLVED |
| OQ-04: Animation tiers | Clear routine + ambiguous investigation. Endorse and strengthen. | RESOLVED |
| OQ-05: Darkwood lighting | Darkwood gradient + BR2049 temperature = our system. | CLARIFIED |
| Tile-based world | Confirms and strengthens all Round 1 positions. | ENDORSED |
The tile-based confirmation didn't change any of my positions — it made them MORE true. Tiles provide shape. Lighting provides soul. Entities provide information. D-033 provides the emotional signal. Weather provides the variable. The investigation mechanic lives in the gap between what the tiles show and what the player knows.
THAT'S the game.
@@ -0,0 +1,318 @@
# Art Direction & Mood Board Workshop — Round 2 Tracking Document
**Compiled by:** Qatux (Documenter)
**Date:** 2026-02-12
**Sources:** round2-araminta.md, round2-ozzie.md, round2-miri.md, round2-gore.md
**Round 2 brief:** round2-brief.md
**Round 1 tracking:** round1-tracking.md
---
## Executive Summary
All five open questions from Round 1 reached **unanimous consensus** in Round 2. The two agents who held middle positions on OQ-02 (Araminta and Ozzie) both moved to the strict-zero-shift position held by Gore and Miri. No new divergences were introduced. The tile-based base building input was unanimously endorsed and strengthened multiple existing positions.
Gore's proposed label for the converged art direction: **"functional warmth."**
---
## 1. OQ-01 Resolution: Neural Insert Overlay
**Status: RESOLVED — Unanimous**
**Resolution: Geometric data layer + soft bloom render pass = precision that feels organic.**
| Agent | Position | Key contribution |
|---|---|---|
| **Araminta** | Accepts synthesis. Geometric underneath, bloom on top. | Specific implementation: 2-3px gaussian blur at ~40% blend on the insert CanvasLayer. D-033 colors gain soft halos rather than hard edges — "amber feels like a vague sense of concern, not a data flag." |
| **Ozzie** | Endorses. | Player experience test: "After 10 minutes, does the player forget the insert is there? If yes, we've succeeded." Geometric = trustworthy — Commonwealth tech so mature it's invisible. |
| **Miri** | Endorses. Setting-grounded. | Explains WHY both are simultaneously correct: data is computational (geometric), delivery is neural (organic). Brain receives precise data and renders it with biological softness. **New addition:** lattice overlay differs per character — smuggler's is thinner/sparser, detective's is denser/crisper (different hardware). |
| **Gore** | Accepts. | "Araminta is right about the source, I was right about the experience." Test: if switching the overlay OFF would feel like going deaf rather than closing a window, it's working. |
**Araminta's original position (geometric) and Gore's original position (organic) both survive in the synthesis.** The data is precise; the rendering is soft. One CanvasLayer, one shader pass.
**New detail from Miri (not in Round 1):** The smuggler's baseline lattice produces a thinner, sparser overlay; the detective's augmented lattice produces a denser overlay with crisper rendering. This creates a visual difference between playthroughs that is character-driven, not environment-driven — consistent with the zero-shift principle (OQ-02).
---
## 2. OQ-02 Resolution: Environmental Shift When Conspiracy Activates
**Status: RESOLVED — Unanimous (two agents moved)**
**Resolution: Strict zero environmental shift. All changes are character-driven (insert overlay, monologue, D-033 entity colors).**
| Agent | Round 1 position | Round 2 position | What changed their mind |
|---|---|---|---|
| **Araminta** | Subtle shifts allowed (5% cooler CanvasModulate) | **Moved to strict zero** | "If the station itself helps the player detect conspiracy, it undermines the core loop." Gore's "absence of change IS the horror" and Miri's mechanical argument convinced her. |
| **Ozzie** | On the fence — slight shifts, but real shift is internal | **Moved to strict zero** | "If the environment gets 5% cooler, the player will LEARN that... the RENDERING is doing detective work instead of the PLAYER." Subconscious calibration breaks investigation mechanic. |
| **Miri** | Strict zero (held) | Strict zero (confirmed) | Setting logic: the conspiracy was there before the game started. Nothing in the physical environment changes when the player discovers it. |
| **Gore** | Strict zero (held) | Strict zero (confirmed) | "The rendering engine isn't a character. It doesn't have opinions." Araminta's 5% shift is "seductive because it would be invisible consciously. That's exactly why it's wrong." |
**What CAN change:**
- Insert overlay density (more annotations, more flags, more connection lines)
- Entity colors (D-033 shifts: green → amber → red)
- Monologue frequency and urgency
- Player character sprite posture/animation (Araminta's proposal — stress visible on the self, not the world)
**What CANNOT change:**
- Light color temperature
- Shadow depth or direction
- Ambient sound register (visual side — Inigo's call on audio)
- Tile colors, wall tones, floor patterns
- Any CanvasModulate shift correlated with conspiracy state
**Agreed exceptions (diegetic, everyone experiences them):**
- Time-of-day cycle (D-031) — dimmer at night, brighter in morning. Physical reality.
- Weather — storms, fog, rain. Physical reality. The storyteller can TIME weather for dramatic effect, but the weather itself is causally neutral.
**Gore's framing (endorsed by all):** "The player walks into the bar after discovering the conspiracy and it's STILL warm and inviting. That's the horror. The warm light isn't ironic — it's indifferent."
---
## 3. OQ-03 Resolution: Entity Detail + Tile Object Vocabulary
**Status: RESOLVED — Unanimous**
**Resolution: 64x64 tile grid, 24x32 entity sprites, three-layer visual hierarchy, Rimworld object detail as production target.**
### Accepted specifications (Araminta's, endorsed by all)
**Structural tiles (walls, floors, doors):** 64x64. Muted. Minimal or no outlines. Zone palette + construction era differentiation. The visual background.
**Object tiles (furniture, equipment, containers):** 64x64 per 1x1 object. Larger objects composed of 1x1 sub-tiles. Medium outline weight (1px). Mid-detail. Rimworld's object sprites as the right target: clear top-down silhouette, identifiable at a glance, flat-lit (engine adds atmosphere). Must read WITHOUT color (perception modes strip color).
**Entity sprites:** 24x32 pixel footprint within a 64x64 tile. Boldest outlines (2px). D-033 color as primary signal. One identifying silhouette feature per named NPC. 3-4 template silhouettes for generic NPCs. Entity is smaller than the tile it occupies — creates clear figure-ground relationship.
### Visual hierarchy (unanimous)
**Entities > Objects > Structure**
| Layer | Outline weight | Color saturation | Role |
|---|---|---|---|
| Entity sprites | 2px (boldest) | Highest — D-033 relationship colors | Information / social layer |
| Object tiles | 1px (medium) | Moderate — era-appropriate palette | Function / lived-in character |
| Structural tiles | Minimal / none | Lowest — muted zone palette | Spatial backbone |
### Three construction eras as tile palettes (Araminta + Miri)
| Era | Palette character | Object examples | Zones |
|---|---|---|---|
| Era 1 (~40 years ago) | Institutional grey, uniform, regulation markings | Standard wall panels, composite floors, heavy pressure doors | Terminal structure, main corridors, maintenance |
| Era 2 (~20-30 years ago) | Different manufacturers, visible seams, surface-mounted additions | Retrofit Meridian junction boxes, modified partitions, expansion-era floor | Terminal modifications, bar corridor |
| Era 3 (recent/ongoing) | Personal/commercial, most varied, warm tones | Bar furnishings, locker decorations, hand-lettered signs, patch repairs | Bar interior, break rooms, personal spaces |
### Comprehensive tile vocabulary (Miri)
Miri provides a detailed inventory of ~25+ distinct tile types across structural and furniture categories in her Round 2 response, organized by zone. This serves as the base object vocabulary for the Nano Banana style guide. See `round2-miri.md`, OQ-03 section for the full tables.
### Key agent contributions
- **Araminta:** Exact pixel specs, outline hierarchy, production pipeline logic. "Nano Banana's job becomes: generate individual object sprites at 64x64 with consistent style."
- **Ozzie:** Player experience implications. Tile-based world = routine deviations are PHYSICAL ("that crate wasn't there yesterday"). Station evolves visibly. Player ownership through placement.
- **Miri:** Setting grounding. "Stations are literally built this way — prefab modular construction." Full tile vocabulary. Three-era palette system. Floor tiles as primary visual surface.
- **Gore:** Thematic framing. "Settling is placement." "Investigation is archaeology of intention." Object density = how "settled" a space is. Production priority: "Twenty distinct object tiles matter more than ten frames of entity animation."
---
## 4. OQ-04 Resolution: Animation Ambiguity vs. Readability
**Status: RESOLVED — Unanimous**
**Resolution: Two-tier animation system. Clear for daily routine activities. Ambiguous for investigatively relevant behaviors.**
### Tier 1: CLEAR animation (daily life / public activities)
| Activity | Frames | Readability requirement |
|---|---|---|
| Walking/running | 4-6 frames directional cycle | Instantly readable direction and speed |
| Working at terminal/handling cargo | 2-3 states | Unambiguous "this person is working" |
| Eating/drinking | 2-3 states (seated, arm-to-face) | Readable from across the map |
| Talking (two entities face-to-face) | 2-3 states (subtle gesticulation) | Clearly distinguishable from "standing near someone" |
| Sleeping | 1 state (horizontal, no movement) | Unambiguous |
### Tier 2: AMBIGUOUS animation (investigation / private intention)
| Behavior | Animation | What the player sees vs. what they DON'T know |
|---|---|---|
| Pausing/stopping | Entity halts | Sees: stopped. Doesn't know: why (message? listening? checking surroundings? resting?) |
| Looking around | Head rotation | Sees: scanning. Doesn't know: checking for witnesses? admiring view? searching for someone? |
| Lingering near a location | Idle-aware state | Sees: staying. Doesn't know: waiting for contact? taking a break? lost? |
| Changing direction | Walk direction reversal | Sees: turned around. Doesn't know: forgot something? avoiding someone? noticed something? |
| Proximity without clear interaction | Two entities near, NOT in "talking" animation | Sees: nearness. Doesn't know: together intentionally? coincidence? |
### Why this works (agent convergence)
- **Araminta (production):** Clear activities need 2-3 states each. Ambiguous behaviors need only 1-2 generic states. "Cheaper to produce AND better for gameplay."
- **Ozzie (player experience):** Creates natural investigation loop — scan → notice → approach → observe → decide. "The clear/ambiguous division isn't an art decision — it's the investigation mechanic expressed through animation."
- **Miri (setting):** "Daily activities are socially public. Investigatively relevant behaviors are privately motivated." Maps to NPC tell system (D-024). Critical: "The boundary between tiers should NOT be visually signaled."
- **Gore (theme):** "Routine is readable because routine is public. Deception looks like normal but slightly off." Serves the 30-minute life-sim runway — if routine isn't readable, you can't notice when it breaks.
---
## 5. OQ-05 Resolution: Darkwood as Lighting Reference
**Status: RESOLVED — Unanimous**
**Resolution: Darkwood endorsed as primary vision cone lighting model, with emotional retuning from dread to uncertainty. Three-reference lighting system established.**
### The three-reference lighting system (Araminta's formulation, endorsed by all)
| Reference | Provides | Aspect |
|---|---|---|
| **Darkwood** | Vision cone mechanics | Light pooling, graduated fade from known to unknown, darkness-as-weight, the emotional gradient of restricted LOS |
| **Blade Runner 2049 / Roger Deakins** | Color temperature language | Warm amber = social/inhabited, cool white = institutional/official, mixed = transitional. Color as emotional storytelling. |
| **Edward Hopper (*Nighthawks*)** | Emotional composition | Warm interior light surrounded by unknowable dark. The central image of the game. |
### The critical retuning (all four agents)
| Darkwood | The Settled Reach |
|---|---|
| Darkness = hostile | Darkness = uncertain |
| Light = safety | Light = visibility (not necessarily safety) |
| Beyond the cone: monsters | Beyond the cone: life continuing without you |
| Emotional register: dread | Emotional register: uncertainty |
| Gradient: bright → dim → dark → terror | Gradient: clear → reduced → uncertain → ignorant |
### Where Darkwood's model applies (Miri's scoping)
- **Maintenance corridors with manual-switch lighting** (~20-30% of spaces): dark by default, player/NPC activates light, creating pools. This IS Darkwood's core mechanic directly.
- **Exterior sections during night cycle or weather:** reduced ambient light, Darkwood's emotional gradient at the vision cone boundary.
- **The fog boundary everywhere:** graduated fade from known to unknown, not a hard cutoff.
### Where Darkwood's model needs adaptation (Miri)
- **Most of Sova is lit.** The Terminal, bar, and main corridors have adequate institutional/commercial lighting. Darkwood's model applies to limited-light spaces, not the whole game.
- **Light doesn't equal safety.** The bar's warm light is where the detective observes AND where THE FRIEND's contradiction might be noticed. "Well-lit" means "visible," not "nothing to find."
### Godot 4 implementation (Ozzie's confirmation)
PointLight2D (per fixture, per zone) + LightOccluder2D (on walls/obstacles) + CanvasModulate (global ambient) + textured PointLight2D on player (vision cone shape: brighter forward, dimmer peripheral, absent behind). Stock Godot 4 pipeline — no custom engine work.
---
## 6. Tile-Based Base Building — New Input Response
**Status: Unanimously endorsed. No dissent.**
All four agents endorse the tile-based world composition and identify it as strengthening, not changing, their Round 1 positions.
### Agent framings
| Agent | Framing | Key phrase |
|---|---|---|
| **Araminta** | Production confirmation | "The world is assembled, not illustrated." Nano Banana generates individual 64x64 object sprites — easier for consistency than scene painting. |
| **Ozzie** | Player experience wins | "Routine deviations become PHYSICAL." Crate positions, furniture changes, sealed hatches — all are evidence. Investigation becomes spatial memory. Maps to all 4 wow moments. |
| **Miri** | Setting-faithful | "Stations are literally built this way." Prefab modular construction = tile grid IS the construction grid. Full tile vocabulary provided. |
| **Gore** | Thematic expression | "Settling is placement. Investigation is archaeology of intention." Object density = how settled a space is. "Settling is visible as object density." |
### Consensus implications
1. **Object density IS environmental storytelling.** Dense, varied objects = inhabited space (bar). Sparse, uniform objects = institutional space (Terminal). Near-empty = unsettled space (corridors). This replaces "painted background detail" as the source of environmental richness.
2. **Nano Banana pipeline simplifies.** Single-object 64x64 sprites are easier for AI consistency than scene composition. Style guide needs: outline weight, color palette per era, shadow direction (or flat-lit with engine lighting), interior detail density per tile.
3. **Three construction eras apply to OBJECTS, not backgrounds.** Original-era desk vs. renovation-era desk vs. recent desk. Mixed-vintage objects in a single room = visual temporal layering without exposition.
4. **Gore's production priority:** "Twenty distinct, bold, instantly-readable object tiles matter more than ten frames of entity animation." Object vocabulary IS environment vocabulary.
---
## 7. Additional Items from Round 2
### 7a. Planet naming (Miri)
Miri proposes **Velen** as the name for Sova's planet (Krenn System). Temperate-maritime climate, regular rainfall, morning/evening fog, mild temperature range, occasional heavy squalls. Gravity ~0.9G. Naming conventions: compact, consonant-weighted, two-syllable, Nordic-influenced. **Needs confirmation from project lead.**
### 7b. Player character stress visibility (Araminta)
Araminta proposes one exception to the zero-shift rule: "The player character's OWN entity sprite could shift subtly — posture or animation change reflecting stress." The character carries the conspiracy's weight visually; the station doesn't. **Not contested by other agents but not explicitly endorsed either. Flagging as a minor open item.**
### 7c. Lattice overlay density per character (Miri)
New detail not in Round 1: the smuggler's baseline lattice produces thinner, sparser overlay; the detective's augmented lattice produces denser, crisper overlay. Different hardware = different visual density. This creates a playthrough-specific visual difference that is character-driven, not environment-driven. **Endorsed implicitly by Araminta (Round 1 insert density table) and Ozzie (Round 1 smuggler vs detective section). Consistent with all positions.**
### 7d. Outline weight hierarchy (Araminta)
New production spec: entities 2px outline > objects 1px outline > structure minimal/no outline. This creates the visual hierarchy (entities > objects > structure) through outline weight alone — a concrete, implementable rule for the style guide. **Not contested.**
### 7e. Mood board image #8 prompts
All four agents provided prompt suggestions for the "world as collection of 1x1 placed items" image. Common elements across all prompts:
- True top-down, tile-grid visible
- The Last Shift bar as primary subject (warm, dense objects)
- Zone transition visible (warm bar → cool corridor through doorway)
- NPC entities at correct scale (24-32px on 64px tiles)
- Object density difference between zones as visual storytelling
- Lighting doing emotional work on top of tile shapes
---
## 8. Updated Consensus — Full Workshop Resolution
### All Open Questions Resolved
| OQ | Round 1 status | Round 2 resolution | Consensus |
|---|---|---|---|
| **OQ-01:** Insert overlay | Divergent (Araminta geometric vs. Gore organic) | Geometric data + bloom render = precision that feels organic | **Unanimous** |
| **OQ-02:** Environment shift | Split (Araminta/Ozzie allow subtle vs. Gore/Miri strict zero) | Strict zero. Character-driven changes only. | **Unanimous (Araminta + Ozzie moved)** |
| **OQ-03:** Entity detail + tiles | Approximate agreement, no spec | 64x64 tiles, 24x32 entities, three-layer hierarchy, Rimworld object target | **Unanimous** |
| **OQ-04:** Animation | Split (clear vs. ambiguous) | Two-tier: clear for routine, ambiguous for investigation | **Unanimous** |
| **OQ-05:** Darkwood lighting | Ozzie strong advocacy, others hadn't engaged | Darkwood cone mechanics + BR2049 color + Hopper emotion. Uncertainty not dread. | **Unanimous** |
| **New:** Tile-based world | N/A (new Round 2 input) | Unanimously endorsed. Strengthens all existing positions. | **Unanimous** |
### Confirmed Art Direction Principles (Rounds 1 + 2 combined)
1. **Readability over beauty.** Always. (Round 1 C-01, reconfirmed)
2. **Lighting over detail.** Sprites and tiles provide shape; Light2D provides mood. (Round 1 C-03, strengthened by tile input)
3. **Restraint IS the aesthetic.** "Functional warmth" — Gore's label. Not flashy, not grim. (Round 1 C-02, named in Round 2)
4. **Environment never tips its hand.** Strict zero shift. Investigation signals are behavioral and informational only. (Round 1 C-05, made unanimous in Round 2)
5. **Silhouette carries identity.** Color is spoken for by D-033. Shape differentiates. (Round 1 I-01, reconfirmed)
6. **One asset vocabulary, many lighting profiles.** Muted tiles + solar color tinting = every system looks different with the same art. (Round 1 I-06, strengthened by tile input)
7. **Weather is gameplay.** Perception modifier, NPC routine disruptor, storyteller tool. Diegetic — the one thing that CAN shift the environment. (Round 1 C-06, exception clarified in Round 2)
8. **Production constraints = design strengths.** Bold silhouettes + limited palette + tile objects + lighting atmosphere serves readability, D-033, Godot 4, AND Nano Banana simultaneously. (Round 1 I-03, strengthened)
9. **Settling is placement.** Object density IS environmental storytelling. The bar is full because Lera settled it. The corridors are empty because nobody did. (New Round 2, Gore)
10. **Investigation is archaeology of intention.** Every placed tile is someone's decision. Reading the tile world is reading decisions. (New Round 2, Gore)
11. **Two-tier animation.** Clear for public routine, ambiguous for private intention. The boundary is invisible. (New Round 2, resolved)
12. **Three-reference lighting.** Darkwood (cone mechanics) + BR2049 (color temperature) + Hopper (emotional composition). Uncertainty, not dread. (New Round 2, resolved)
13. **Insert = geometric precision, organic perception.** Bloom-softened data layer. Smuggler's is sparse, detective's is dense. Forgotten when quiet, precise when needed. (New Round 2, resolved)
### Remaining Minor Items (not blocking)
| Item | Status | Owner |
|---|---|---|
| Planet name "Velen" | Proposed by Miri, needs project lead confirmation | Miri / Lead |
| Player character stress sprite | Proposed by Araminta, not contested, not formally endorsed | Araminta |
| Mood board image #8 | Prompts provided by all four agents, needs generation | Lead / Araminta |
| Nano Banana style guide | Specs now exist (outline weights, palette per era, tile dimensions) — needs formal document | Araminta |
---
## 9. Decisions Ready for Formal Recording
The following workshop outputs are candidates for formal decision recording in `decisions/` domain files. All have unanimous consensus.
**Candidate D-XXX: Art Direction — Visual Style**
Clean 2D with bold silhouettes, tile-based world composition, lighting-driven atmosphere. Not pixel art, not painted, not 3D. Godot 4 Light2D pipeline. Nano Banana asset generation for 64x64 tile objects and 24x32 entity sprites.
**Candidate D-XXX: Art Direction — Visual Hierarchy**
Three-layer system: entities (2px outline, D-033 color, boldest) > objects (1px outline, era palette, mid-detail) > structure (minimal outline, muted zone palette, background). Readability priority: entities first, always.
**Candidate D-XXX: Art Direction — Environmental Neutrality**
Strict zero visual shift on the base world layer correlated with narrative/conspiracy state. All investigation signals are character-driven: insert overlay density, D-033 entity colors, monologue. Exceptions: time-of-day cycle and weather (diegetic, physical reality). The rendering engine has no narrative opinions.
**Candidate D-XXX: Art Direction — Lighting System**
Three-reference model: Darkwood (vision cone mechanics, light pooling, graduated fog boundary), Blade Runner 2049 / Roger Deakins (color temperature as emotional language), Edward Hopper (warm interior surrounded by unknowable dark). Emotional register: uncertainty, not dread.
**Candidate D-XXX: Art Direction — Animation System**
Two-tier animation: Tier 1 (clear) for public daily activities — walking, working, eating, talking, sleeping. Tier 2 (ambiguous) for privately motivated behaviors — pausing, looking around, lingering, changing direction, unexplained proximity. Boundary between tiers is invisible to the player.
**Candidate D-XXX: Neural Insert Overlay**
Geometric data layer (precise positioning, clean lines, structured information) rendered with soft bloom shader pass (~2-3px gaussian, ~40% blend). Smuggler's overlay: thinner, sparser (baseline lattice). Detective's overlay: denser, crisper (augmented lattice). Passive state nearly invisible; active state clean and precise.
*Formal decision recording pending project lead direction on timing and numbering.*
---
*For the record: this workshop achieved unanimous consensus on all contested items across two rounds. The strongest signal is that Araminta and Ozzie both independently moved to the strict-zero-shift position on OQ-02 after reading Gore and Miri's arguments — convergence through persuasion, not compromise. The tile-based base building input, rather than creating new tensions, resolved existing ones by making the Rimworld production model structural rather than analogical. The art direction is now specific enough for a style guide and a Nano Banana prompt template.*
*Compiled by Qatux. Every claim above is cited to its source document.*
@@ -0,0 +1,275 @@
# Round 3 Workshop Response: Araminta (Art Direction & Mood Board)
**Agent:** Araminta (Visual Designer / Q-003 Lead)
**Date:** 2026-02-12
**Workshop:** Art Direction & Mood Board
**Status:** Round 3 — Quick Round
---
## 1. Lead Decisions — Acknowledged
### Vision shape: CONE, not circle
Confirmed and correct. The Rust occlusion tracers with fall-off give us a directional field of view where turning around is meaningful — the player CHOOSES what to look at, which means choosing what NOT to look at. Visually, this means the vision cone is an asymmetric shape: full clarity forward, degraded at the peripheral edges, blind behind. The cone shape is drawn by the shadowcasting system and rendered by the fog shader. Art implication: entities and objects behind the player character are fogged/hidden even if they're one tile away. The camera shows the space; the cone reveals the information.
### Planet name: Velen
Confirmed. Temperate-maritime, regular rain, morning/evening fog. This grounds the weather system: Sova Transit District on Velen has reliable rain cycles and fog banks. The fog isn't exotic — it's Tuesday. That normality is important: weather as routine, not spectacle (until the storyteller decides otherwise).
### Character temperature: overlay, not world — but WHERE they live matters
This is an elegant resolution. I was trying to push the world's rendering toward the character's mood. Gore and Miri pushed back correctly: the world is neutral. The lead's decision threads the needle: the world IS neutral, but the characters' daily paths naturally take them through different-temperature zones. The smuggler's routine goes bar → social areas → residential → bar. Warm, warm, warm. The detective's routine goes security office → logistics hub → observation posts. Cool, cool, cool. Same station, same lighting, different lived experience through spatial habit. No rendering tricks needed. The level design does the work.
Art implication: zone temperature differences are REAL and matter more than I initially thought. The bar MUST be meaningfully warmer than the logistics hub. The corridor MUST be meaningfully dimmer than both. These aren't subtle — they're the mechanism that makes the two playthroughs feel different.
### Environmental neutrality: confirmed
The world doesn't shift. The world already HAS character through its zones. Noted and locked.
---
## 2. Z-Levels and Sprite Stacking — Art Perspective
### Proposed z-level stack (bottom to top)
I'm thinking in terms of what the PLAYER sees, not Godot implementation (that's Tyre/Stig's problem). But I'll use Godot terminology where it helps.
**Layer 0 — Floor tiles**
The ground. Tile grid. Zone palette colors. Construction-era variation. This is the canvas everything sits on. Always visible unless fogged. No z-sorting issues — it's flat.
**Layer 1 — Floor objects (flat)**
Things ON the floor that entities walk OVER: floor markings, cable runs, drain grates, floor vents, spill stains, track marks. These are cosmetic detail tiles that add texture without blocking anything. Rendered above floor, below everything else. Think Rimworld's "floor filth" layer.
**Layer 2 — Furniture / placed objects (entity-height)**
Desks, chairs, tables, crates, consoles, bar counters, shelves. These are the objects entities interact WITH. Entity sprites render at the SAME z-level but sort by y-position (Godot's y-sort): an entity south of a table renders in front of it; an entity north of a table renders behind it. This is where most of the visual complexity lives.
**Layer 3 — Entity sprites**
NPCs and player character. Y-sorted with Layer 2 objects. D-033 color applied. This isn't a separate CanvasLayer — it's the same layer as furniture, using y-sort to handle occlusion naturally.
**Layer 4 — Overhead / wall tops**
This is the key art question. Things ABOVE entity head height: wall tops, pipe runs, overhead shelving, signage, light fixtures. These render ABOVE entities but with TRANSPARENCY so entities walking beneath are still partially visible. A pipe crossing a corridor: the entity walks under it, the pipe renders on top with maybe 50% opacity or a dithered mask so the entity silhouette is still readable beneath.
**Layer 5 — Fog of perception**
The vision cone shader. Darkens/desaturates everything outside the player's LOS. This affects Layers 0-4 uniformly. Applied as a CanvasItem shader or a separate CanvasLayer with a multiply blend.
**Layer 6 — Insert overlay**
The neural insert. Entity markers, POI indicators, grid lines, data readouts. Rendered with the soft bloom pass from OQ-01. This layer is NOT affected by the fog shader — insert data is computational, not perceptual. You can see insert markers in fogged areas (if your lattice has that data), even though you can't see the entities visually.
**Layer 7 — Monologue / UI text**
Internal monologue text, perception mode labels, any non-diegetic UI elements. Top of the stack. Always visible. Clean rendering, no bloom.
### Wall rendering in top-down
This is the biggest art question in the z-stack. Three options:
**Option A: Walls as boundaries (simplest)**
Walls are rendered as thick lines or filled rectangles between tiles. No "top" surface, no "face." Just barriers. This is Heat Signature / Rimworld style. Walls are INFORMATION (you can't see through here) not OBJECTS (look at this wall).
**Option B: Walls with visible top surface**
The wall occupies its tile(s) and shows a top-down view of the wall top — a narrow strip of wall material. Gives a slight 3D hint. Entities can't walk on wall tiles. This is how most top-down games handle it — you see the "roof" of the wall as a thin strip.
**Option C: Walls with face + top (sprite stacking)**
Wall tiles show the south-facing face of the wall AND the top. This gives pseudo-3D depth. More visually interesting but more complex to produce and raises questions about north-facing walls (do we see the interior face? just the top?).
**My recommendation: Option B for walls, Option A for thin partitions.**
Structural walls (exterior hull, main compartment boundaries): Option B. The visible top surface communicates "this is a REAL wall, thick, structural." The top surface can carry construction-era color variation.
Interior partitions (office dividers, bar booth separators): Option A. Thin lines. These are lighter barriers — removable, recent additions. The visual thinness communicates "someone put this here" versus "this was built into the station."
This distinction also helps with the base building mechanic: the player can see which walls are structural (can't remove) and which are partitions (can relocate).
### Sprite stacking for pseudo-3D: OUT of scope
Sprite stacking (rendering multiple slices to create voxel-like 3D objects) is a cool technique but wrong for us. It's expensive per object, hard to maintain with AI-generated assets, and fights with our y-sort occlusion model. We're flat 2D with y-sorting. The "depth" comes from the z-layer stack above, not from individual objects being 3D. Keep it clean.
### Multi-tile objects and partial occlusion
A 2x1 desk: occupies two tiles. An entity standing at the south side of the desk is rendered in front of it (y-sort). An entity at the north side is rendered behind it. If the entity is ON the desk tile (seated at the desk), the entity renders above the desk sprite via y-sort tiebreaking (entity y-position is at the entity's feet, which is at the bottom of their sprite).
Rule: **entities always win visual ties.** If an entity and an object overlap, the entity's silhouette is preserved. This maintains D-033 color readability. The player must ALWAYS be able to see entity color, even when entities are partially behind furniture. Worst case: a colored outline bleeds through the occluding object, like Rimworld's entity-behind-wall x-ray hint.
---
## 3. v0.1.1 Minimum Sprite Set
This is the smallest set that proves the art direction works in-engine. Every sprite below replaces a colored rectangle from v0.1.
### Floor tiles (4 sprites)
| Tile | Purpose | Visual |
|------|---------|--------|
| `floor_institutional` | Logistics hub / work areas | Cool grey-blue, `#2e2e48` base, subtle grid line at tile edges |
| `floor_bar` | The Last Shift | Warm tan/beige, `#3a3028` base, slight wood-panel texture hint |
| `floor_corridor` | Maintenance / corridors | Neutral grey, `#252535` base, minimal detail |
| `floor_transition` | Where two zones meet | Split tone — one half matches adjacent zone A, other half matches zone B. Visible seam. |
### Wall tiles (3 sprites)
| Tile | Purpose | Visual |
|------|---------|--------|
| `wall_structural` | Exterior hull / main compartments | Dark grey-blue, Option B (visible top surface), thick, heavy |
| `wall_partition` | Interior dividers / office walls | Lighter grey, Option A (boundary line), thin |
| `wall_door` | Door frame + gap | Wall tile with a gap. Door state (open/closed) handled by a separate door sprite in the gap |
### Object tiles (8 sprites)
| Object | Size | Visual |
|--------|------|--------|
| `desk_terminal` | 2x1 | Desk surface with integrated terminal glow. Institutional grey-blue. |
| `chair_office` | 1x1 | Simple swivel chair from above. Dark grey. |
| `chair_bar` | 1x1 | Stool or mismatched chair. Warmer tone. |
| `table_round` | 1x1 | Small round table. Bar zone item. Warm wood tone. |
| `crate_cargo` | 1x1 | Standard cargo container. Olive/military green. Bold silhouette. |
| `bar_counter` | 3x1 | The Last Shift's bar counter. Warm wood, distinct from tables. |
| `light_ceiling` | 1x1 | Overhead light fixture. Doubles as Light2D source position. Cool or warm variant. |
| `door_sliding` | 1x1 | Sliding door sprite for wall gaps. Open/closed states (2 frames). |
### Entity sprites (3 sprites)
| Entity | Purpose | Visual |
|--------|---------|--------|
| `entity_player` | Player character | 24x32, civilian work clothes, neutral silhouette. Tinted by D-033 self-color. |
| `entity_npc_a` | Named NPC (stocky, vest) | 24x32, distinctly different silhouette from player. Kael template. |
| `entity_npc_b` | Named NPC (medium, apron) | 24x32, distinctly different from both above. Lera template. |
Three entities is enough to prove silhouette differentiation works. Each needs: idle frame, walk cycle (4 frames, 4 directions), and one interaction frame (seated). That's 3 entities x (1 + 16 + 1) = 54 frames total. For v0.1.1, we could cut to 2 directions (south-facing + east-facing, mirror for west) = 3 x (1 + 8 + 1) = 30 frames.
### The v0.1.1 test scene
One room (The Last Shift bar) + one corridor + one doorway between them. This scene proves:
1. **Zone temperature difference.** Warm bar tiles + cool corridor tiles = visible mood shift at the boundary.
2. **Object placement as storytelling.** Bar counter, tables, chairs, a crate. The room has character through placed objects.
3. **Entity silhouette differentiation.** Three entities with different silhouettes, all tinted by D-033 color. Can you tell them apart at a glance?
4. **Y-sort occlusion.** Entity walks behind the bar counter — counter renders in front. Entity walks in front of a table — entity renders in front.
5. **Light2D atmosphere.** One warm Light2D in the bar (attached to `light_ceiling`), one cool Light2D in the corridor. Dynamic shadows from walls/furniture. The lighting-driven atmosphere thesis proven in-engine.
6. **Fog of perception.** Vision cone shader applied. Bar is visible inside the cone; corridor beyond the doorway fades to fog. The Darkwood emotional gradient working.
7. **Wall type distinction.** Structural walls (thick, Option B) around the room. A partition (thin, Option A) subdividing a corner of the bar.
If this scene looks right — warm bar with personality, cool corridor with honesty, three distinguishable entities, dynamic lighting carrying the mood — then the art direction is validated in-engine and we scale from there.
### Nano Banana generation workflow
For v0.1.1, the workflow would be:
1. **Write a Settled Reach style guide prompt prefix** (replaces the Lords of Ash one in the existing asset-gen skill). Core constraints: "top-down view, clean 2D illustration, bold silhouette, muted palette, 1-2px outline, flat lighting (no baked shadows), [zone palette] color range."
2. **Generate floor/wall tiles first** — these are simplest and establish the palette baseline. Generate 3-4 variants per tile type, pick the most consistent.
3. **Generate objects second** — using the floor tiles as visual context in the prompt. "A [object] placed on a [floor type] tile, top-down view, matching the established palette."
4. **Generate entity sprites last** — these need the most iteration. Start with idle frames. Get the silhouette right. Then generate directional walk frames with the idle as reference.
5. **Post-processing pass:** Normalize all sprites to exact 64x64 (or 24x32 for entities). Ensure outline weight is consistent. Remove any baked shadows the AI added (we don't want them — Light2D handles lighting). Verify readability at 1x zoom.
Expect 3-5 generation attempts per final sprite. Budget accordingly.
---
## 4. Image Reactions
### Image #8 (tile composition) — Already reviewed in detail
Strongest proof-of-concept in the set. Proves the tile-assembled world can have warmth and personality. The zone transition (cool institutional → warm break area) is exactly right. Object readability is strong. Adjustments needed: flatten to true top-down, correct entity scale to 24x32 on 64x64, strengthen outlines, add light fixtures as visible objects. Direction confirmed.
### Image #9 (bar antagonists) — New review
This is the emotional complement to #8. Where #8 proves the TILE SYSTEM works, #9 proves the MOOD works.
**What lands:**
- **The warm/cool zone contrast is the standout.** The bar is deep amber, the corridor below is cold grey. The boundary is a wall. This IS the game's spatial temperature system — warm social spaces, cool institutional corridors, architectural boundaries between them. The contrast is strong enough that the two zones feel like different buildings. Good — that's the point.
- **The accidental fog-of-perception gradient.** The two antagonists at the center table are fully rendered. Patrons at adjacent tables are less distinct. Patrons at the edges are pure silhouettes. This accidentally demonstrates vision cone rendering: entities in the player's focus = detailed; entities at the periphery = silhouettes fading into darkness. Whether the AI intended this or not, it's EXACTLY how the graduated fog should work. Close = full detail. Mid-range = reduced. Edge = just shape.
- **Lera is identifiable.** Behind the bar counter, wearing an apron. The silhouette-as-identity principle proven: apron + position behind counter = you know who this is without a label. I-01 validated.
- **Object density tells the story.** Bar shelves full of bottles. Multiple table types. Wall sconces. A notice board by the door. This is The Last Shift — a place that's accumulated personality through years of Lera's choices about what goes where. Contrast with the bare corridor below: no objects, just walls and floor and a single overhead light. The object DENSITY is the character difference between zones.
- **The corridor observer.** A figure in the dark corridor, looking toward the warm bar. This is Hopper's Nighthawks: warm interior visible from cold exterior, the watcher outside the warmth. Whether this is the detective observing or just a passerby, the COMPOSITION creates investigative tension. Someone is in the cold, watching people in the warm.
**What needs adjustment:**
1. **Not top-down.** This is 3/4 view — we can see the front face of the bar counter, the sides of tables, the wall's face. Our game is strictly overhead. But as a MOOD reference, the emotional composition is perfect. This image tells us what The Last Shift FEELS like; #8 tells us how it's BUILT.
2. **Chibi proportions.** The main figures have large heads, short bodies. Our spec is 24x32 — slightly elongated, adult proportions. This matters at game scale: chibi reads as "cute" which fights the register.
3. **The silhouetted NPCs are too DARK.** In the actual game, all entities in the vision cone have D-033 color. They're not black silhouettes — they're teal (unknown), green (friendly), etc. The peripheral darkening is fog-of-perception, which only applies at the vision cone's edge. NPCs at adjacent tables inside the cone should still be color-coded.
4. **The two antagonists shouldn't be more detailed than other NPCs.** In our game, the rendering doesn't know who's important. The player's CHARACTER might think those two are interesting (monologue fires, insert flags them), but the SPRITES are the same fidelity as everyone else. The information hierarchy comes from the overlay, not the base rendering.
**Overall:** Image #9 is the best emotional reference for The Last Shift. The warm/cool contrast, the social density, the observation tension — this is what the bar should FEEL like. Combined with #8's tile grid proof and #7's strict top-down perspective, we have three reference points that triangulate the final art direction.
---
## Summary
| Item | Status |
|------|--------|
| Lead decisions (cone, Velen, overlay-not-world, zone paths) | Acknowledged, no concerns |
| Z-levels | 8-layer stack proposed, wall rendering recommendation (Option B structural / Option A partition), sprite stacking OUT of scope |
| v0.1.1 sprite set | 4 floor + 3 wall + 8 object + 3 entity sprites = 18 unique sprites, ~30 frames for entity animation |
| v0.1.1 test scene | Bar + corridor + doorway. Proves zone temperature, object storytelling, silhouette differentiation, y-sort occlusion, Light2D atmosphere, fog of perception, wall type distinction |
| Image #8 | Previously reviewed. Tile composition proven. |
| Image #9 | Emotional register of The Last Shift nailed. Warm/cool contrast is the standout. Accidental fog gradient is a bonus insight. |
---
---
## ADDENDUM: Camera Angle Shift — Rimworld's Shallow Tilt
The lead has indicated a shift from true top-down (D-019) to Rimworld's camera angle: a very shallow tilt, almost orthographic overhead but with just enough forward perspective to see object front faces and give depth cues. This is NOT classic 2:1 isometric. It's NOT 3/4 view. It's ~15-20 degrees off vertical — the flattest end of the tilt spectrum.
This is a significant decision. Let me walk through every area it touches.
### What this changes
**Wall rendering — RESOLVED.** My Option A / Option B distinction from the z-levels section above becomes moot. At Rimworld's angle, walls naturally show their south-facing face. You SEE the wall surface — doorways, signage, damage, construction era — without any special rendering trick. This is strictly better than pure top-down for walls. Walls become more informative and more visually interesting with zero extra production cost. The wall question answers itself.
**Entity sprites — BETTER.** At true top-down, you see the top of a person's head and shoulders. At Rimworld's shallow tilt, you see a slight front/side view — face direction, clothing, body shape, accessories. This is dramatically better for silhouette-as-identity (I-01). You can now see Kael's vest, Lera's apron, Sera's Commission uniform as FRONT-FACING details, not just overhead shape outlines. Sprite readability improves significantly. The Sims' entity readability happens at this angle, not at pure overhead.
**Object sprites — BETTER.** A desk shows its front face. A bar counter shows the bartender's side. A console shows its screen. A crate shows its label. Objects become more self-describing because you can see what makes them DIFFERENT from each other, not just their top surface footprint. This is why Rimworld's objects are so readable — you see enough of the front to identify them instantly.
**Tile grid — UNCHANGED.** Rimworld uses a rectangular tile grid, not a diamond isometric grid. Tiles are still square. 64x64 still works. The grid math doesn't change. No diamond-tile headaches.
**Vision cone math — ESSENTIALLY UNCHANGED.** The vision cone is computed on the flat tile grid. The slight camera tilt is cosmetic — it affects how the cone is RENDERED (the cone shape is slightly projected) but not how it's CALCULATED. The Rust occlusion tracers still work on a 2D grid. Sightlines are still 2D. The tilt just means the rendered cone has a slight perspective projection, which Godot handles natively.
**Lighting — UNCHANGED.** Godot's Light2D system works in 2D sprite space. It doesn't know or care about the "camera angle" — it projects light and shadows based on sprite positions and occluder shapes. The shadows will look slightly different (projecting "down" toward the viewer at the tilt angle) but the system is the same. Light2D + LightOccluder2D still handles everything.
**Fog of perception — UNCHANGED.** The fog shader operates on the 2D rendered output. It darkens/desaturates pixels outside the LOS area. The tilt doesn't affect the shader — it affects what the shader is applied TO, but the same shader works.
**Nano Banana prompts — EASIER.** "Rimworld-style shallow overhead angle" is a more common art perspective than strict top-down. AI image generation produces this angle more naturally — it's closer to how concept art and illustration typically frames overhead scenes. I suspect prompt consistency will IMPROVE with this change. My prompt for image #8 said "top-down view" and the AI produced something closer to Rimworld's angle anyway — that's the AI telling us this is the natural angle for this kind of scene.
### What this means for my image reviews
I flagged images #8 and #9 for being "not top-down enough" and "slightly isometric." With the Rimworld angle confirmed:
- **Image #8** is now CLOSER to correct than I thought. Its slight tilt that shows table thickness and chair backs — that's approximately right. Maybe still a touch too angled, but in the ballpark.
- **Image #9** is probably TOO tilted — it's closer to 3/4 view or classic isometric than Rimworld's shallow tilt. But the emotional register is still the reference target.
- **Image #7** (the convergence attempt with true top-down) is now slightly TOO flat. We need a touch more tilt than #7 had.
The sweet spot is between #7 and #8.
### What does NOT change
- The entire art direction (clean 2D, bold silhouettes, lighting-driven atmosphere)
- D-033 entity color system
- The z-level stack (all 8 layers still apply, just rendered at the shallow tilt)
- The v0.1.1 sprite set (same sprites, slightly different angle for generation prompts)
- The fog of perception rendering
- The insert overlay (this is a screen-space layer, camera angle doesn't affect it)
- Y-sort occlusion (Rimworld uses y-sort — entities further north render first)
### My position
**I support this change.** Rimworld's angle is strictly better than pure top-down for our game because:
1. **Walls become informative.** You see faces, doors, signage without special rendering.
2. **Entities become more readable.** Front-facing silhouettes carry more identity information than overhead silhouettes.
3. **Objects become self-describing.** You see what makes a desk different from a table.
4. **No mechanical cost.** Vision cone, lighting, fog, tile grid — all work the same.
5. **Production cost is minimal.** Sprite generation prompts change from "strict overhead" to "shallow overhead angle." The style guide adjusts. The pipeline is the same.
6. **It's what the AI generates naturally.** Every mood board image gravitated toward this angle. Stop fighting the current.
The only thing we lose is the purity of "true top-down as stated in D-019." We should update D-019 to reflect "shallow tilt / Rimworld perspective" rather than "strict top-down." The decision's intent (2D, overhead, not isometric diamond grid) is preserved.
**Recommend: Update D-019 to "Rimworld-angle shallow tilt, rectangular grid, y-sort depth ordering." All other decisions remain valid.**
---
*Written by Araminta. That palette communicates the right mood.*
@@ -0,0 +1,63 @@
# Art Direction & Mood Board Workshop — Round 3 Brief (Quick Round)
**Date:** 2026-02-12
**Purpose:** Incorporate lead decisions, discuss two new topics, react to images #8 and #9.
---
## Lead Decisions (Confirmed)
These are now settled. Record them in your responses for the record:
1. **Vision shape: CONE, not circle.** Following the Rust occlusion tracers with fall-off. D-015's forward/peripheral/behind is mechanical, not cosmetic. Turning around is a meaningful action.
2. **Planet name: Velen.** Krenn System's habitable planet. Temperate-maritime, regular rain, morning/evening fog. Confirmed per Miri's proposal.
3. **Character temperature shift lives in the overlay layer, not the world.** Reality does not look more villainous for a criminal. HOWEVER — the tone of their home locations is free game. The mood comes from WHERE THEY HANG. The smuggler's home spaces (bar, social areas) are naturally warm. The detective's work spaces (security office, observation posts) are naturally cool. This isn't the overlay lying — it's the characters' lives having different color temperatures because they spend time in different places.
4. **Environmental neutrality clarification:** The world doesn't shift. But the world already HAS warm and cool zones. The smuggler lives in the warm ones. The detective works in the cool ones. Same station, same lighting, different daily paths through it.
---
## New Discussion Topics
### Topic A: Z-Levels and Sprite Stacking Layers
We need a thorough discussion about how visual layers stack in the renderer. With tile-based composition confirmed, this is critical:
- How many z-levels does the game need? (Floor → objects → entities → overhead → insert overlay → UI?)
- How do walls render in top-down? Do we see wall tops? Wall faces? Just boundaries?
- What happens when entities walk behind/under things? (Shelving, overhangs, pipes)
- How does the three-layer hierarchy (structure > objects > entities) map to actual Godot CanvasLayer / z-index implementation?
- Sprite stacking for pseudo-3D effect — is this in scope or out?
- How do we handle multi-tile objects that entities can partially occlude?
This is a joint art + technical question. Give your perspective from your domain.
### Topic B: v0.1.1 Sprite Release Planning
The lead wants to plan a v0.1.1 release where we play with initial sprites. What would the minimum viable sprite set look like?
- What tiles are essential for a single test room?
- What entity sprites are needed for basic movement/interaction testing?
- What's the Nano Banana generation workflow for producing consistent tile sprites?
- What Godot scene structure demonstrates the art direction working?
---
## Images
- **Image #8** (tile composition): `moodboard_08_tile_composition.png` — Review and react.
- **Image #9** (bar antagonists): Being generated — will be shared when ready. Two antagonists sitting at a bar table, tile-based world, warm lighting.
---
## Response Format
Quick round. For each section:
1. Acknowledge the lead decisions (any concerns? speak now)
2. Your take on z-levels/sprite stacking from your domain
3. Your take on v0.1.1 minimum sprite set from your domain
4. React to image #8 (and #9 when available)
Keep it focused. This is resolution and planning, not exploration.
@@ -0,0 +1,133 @@
# Round 3 — Gore (Themes & Endgame Design)
## 1. Lead Decisions — Acknowledged
**Cone, not circle.** Right. Turning around is a meaningful action because attention is directional. You choose what to face. That choice — what you look at and what you leave behind — is the investigation mechanic distilled to a single input. The circle was democratic: everything at range is equally known. The cone is opinionated: you see what you face, and what's behind you is the question you're choosing not to answer. That's thematically richer.
**Planet name: Velen.** Noted. Temperate-maritime with regular rain and morning/evening fog. Fog as the default weather personality of the place where the game happens. That's good — fog is uncertainty's weather, and uncertainty is what the game sells.
**Character temperature lives in life paths, not overlays.** This is the best version of the idea. The smuggler's world is warmer not because the rendering lies, but because the smuggler *lives in warmer places*. The detective's world is cooler because they *work in cooler places*. The visual temperature difference is real, material, diegetic — it's a consequence of who you are and where your life takes you. Nobody's lying. The station's honest. The characters just walk different paths through it. That's the cleanest expression of the dual-lens concept.
**Environmental neutrality confirmed.** No concerns. This was my strongest Round 1 position and it's now settled correctly. The world doesn't have opinions about your investigation. Your investigation has opinions about the world.
---
## 2. Z-Levels and Sprite Stacking — Thematic Perspective
I'll leave the technical layer-count to Araminta and Tyre. But there are thematic implications to how visual depth works in a top-down investigation game.
### What you can walk under matters
If entities can pass behind shelving, under overhangs, through pipe clusters — then the station has **places where you can't be fully seen**. That's not a rendering question. That's an information question. Overhead occlusion creates partial concealment: you see someone enter a shelving aisle, you lose visual confirmation, they emerge on the other side. What happened in between? In that two-second gap, did they drop something? Pick something up? Signal someone?
This is the corridor problem at micro-scale. The corridors are spaces people don't pay attention to. Overhead-occluded spaces are the same principle within a room — the shelf aisle, the pipe junction, the cargo stack that blocks your sightline for three tiles. If the renderer supports entities passing behind foreground objects, the level designer gets a tool for creating *local uncertainty* within otherwise known spaces.
This matters for THE FRIEND. Kael's contradiction (D-034) is meeting with an unknown contact in a restricted corridor. But imagine a version where you see Kael walk behind a cargo stack in The Terminal, and when he emerges, he's walking slightly differently. You didn't see what happened. The overhead occlusion created a gap in your knowledge. That's the z-level system serving the investigation mechanic.
### Wall rendering as information boundary
Walls should read as opaque boundaries, not as surfaces you examine. The top of a wall in top-down is just a line that says "you can't see past this." The wall isn't interesting to look at. What it *hides* is interesting. Walls are the visual grammar of "something exists that you don't know." Keep them simple, readable, and functionally clear. The wall's only job is to be an edge between known and unknown.
### Sprite stacking for pseudo-3D
Out of scope for v0.1, and I'd argue thematically unnecessary. The flat top-down view IS the analytical perspective — the document view, the surveillance camera, the omniscient-but-blind gaze that sees everything spatially and nothing emotionally. Pseudo-3D adds visual interest but softens that analytical quality. If we ever go 3D, it should be the cutscene shift (D-019) — a dramatic register change, not a subtle enhancement to the daily view.
---
## 3. v0.1.1 Minimum Sprite Set — "What Proves the Concept"
Everyone else will answer this as a production question: what tiles and sprites do we need to test rendering, movement, interaction. I'll answer it as a thematic question: **what's the minimum set that proves the game's visual identity works?**
The concept is: a warm, readable, tile-based world where two characters see the same space differently, investigation happens through observation, and the station feels settled.
### To prove that, v0.1.1 needs:
**One room that feels settled.** Not a test room — a *place*. The minimum for "settled" is: floor tiles (two types — warm and cool to show zone transition), walls, a door, and at least 5 placed objects that create the sense of human accumulation. A table, two mismatched chairs, a console, something consumable (coffee maker, water dispenser), and one personal item (a jacket on a hook, a plant, a posted notice). If you look at this room and think "someone works here," it's working. If you think "this is a test environment," it's not.
**Two entity silhouettes that are NOT the same.** Not "placeholder capsule" and "placeholder capsule." Two distinct body shapes with one identifying feature each. This proves silhouette-as-identity at the target scale. They don't need animation beyond idle and walk. But they need to be *different people*, not two copies of a template. If you can tell them apart at a glance, D-033's color system has room to work.
**The warm/cool zone transition in a single screen.** A doorway connecting a warm-tiled social space to a cool-tiled work/corridor space. If you can feel the temperature shift when an entity crosses the threshold, the lighting system is carrying its thematic weight. This is the single most important visual test: does the same tile geometry feel different under different Light2D conditions?
**One moment of fog-of-perception.** An entity walking into fog — from known to unknown. Does the transition feel like attention fading, or like a game mechanic asserting itself? If the entity softens into uncertainty rather than hitting a wall of black, the fog treatment is correct. The cone shape doesn't need to be final. But the *quality* of the boundary — gradient, not binary — needs to be proven here.
### What v0.1.1 does NOT need to prove:
- Animation variety (walk + idle is enough)
- Weather effects
- Insert overlay
- Multiple rooms/zones
- NPC behavior or routines
One room. Two people. Warm light. A doorway to cooler space. Fog at the edges. If that feels like a place where someone lives — where you could imagine having coffee before shift — and you can also imagine studying it with detective eyes, the art direction is proven.
---
## 4. Image Reactions
### Image #8 (Tile Composition)
Already reviewed in detail between rounds. Summary: the warm/cool floor transition works, the mismatched chairs carry forty years of history in two tile objects, the coffee maker is a settling decision made visible, the figure in the doorway maintains the threshold motif. Grid seams should recede; workspace area needs more occupancy signals. The image proves that tile-based composition can carry thematic weight.
### Image #9 (Bar Antagonists)
This is the first image that shows **the bar populated**. And it changes the emotional register substantially.
What lands: the two detailed characters leaning across the table, talking intently. They're *inside* the perception cone (or close enough to be fully rendered), and you can read their posture — this conversation matters. Around them, the bar is full of silhouetted figures. Lera behind the counter, detailed, cloth in hand. The warm amber glow. The bottles on the shelves. This feels like a *place people go*. Not the Hopper diner's parallel solitudes — this is the version I asked for in my #1 reaction: connection, not just co-presence. These two are engaged. The bar is alive.
The figure outside in the corridor — detailed, clearly the player character — looking in. And this is where the image becomes thematic. They're standing in the cool corridor, looking into the warm bar where two people are having a conversation they can observe but can't hear. That's the game. The player on the outside, looking in. The warmth is inside. The information is inside. Are you going to walk in and join them, or stay out here and watch?
What's off: the silhouetted background figures are too uniformly dark. They read as decoration rather than as people the player hasn't focused on yet. Some should be slightly more visible — a shoulder turned this way, a gesture mid-conversation — to suggest that there's more to see if you shifted your attention. The fog-of-perception within a room isn't "invisible beyond cone." It's "less detailed at range." Even peripheral NPCs should have a hint of presence, of being readable *if you chose to look*.
Also: the proportions are still in the chibi register from #7. Noting for the record — this is a style discussion for Araminta to resolve, not a thematic blocker.
What this image proves: **a populated warm space viewed from the cool outside is the game's emotional core composition.** Every other image has been about the station as physical space. This one is about the station as *social* space. The bar isn't warm because of the tiles. It's warm because people chose to be there together. That's the answer to "is this life enough?" — and the player is standing in the corridor, deciding whether to accept it or analyze it.
---
## 5. The Perspective Shift: Slight Tilt / Shallow 3/4 View
The lead is considering moving from true top-down toward a slight tilt — not full 2:1 isometric, but somewhere in the "slight tilt to 3/4 view" range. Image #9 may already be close to the target.
This is worth thinking about carefully, because camera angle isn't a rendering decision. It's an *epistemological* decision. It determines the player's relationship to the world they're observing.
### What changes thematically
**True top-down is the surveillance camera.** It's analytical, diagrammatic, impersonal. You're looking down at a system. That's what I meant by "document" — the flat view has the quality of a floor plan, a case file, a diagram someone drew to explain a crime scene. It serves the detective's gaze. It keeps the player at intellectual distance.
**A slight tilt is the person standing in the doorway.** You're not above the scene — you're *at* the scene, just slightly elevated. You can see the front face of the bar counter. You can see that a shelf has things on it, not just a rectangle on a grid. Entities have a front and a back, not just a top. The world gains a dimension of *facing* that flat top-down eliminates.
That facing dimension is thematically significant. In true top-down, everyone is equally visible from the same angle. In a tilted view, someone facing toward you is more readable than someone facing away. You see posture. You see gesture. The tilt makes entities more *human* — less like tokens on a board, more like people seen from across a room. And "seen from across a room" is exactly the smuggler's experience at the bar, and the detective's experience at the doorway.
### What this means for the workshop's positions
**The warmth consensus gets stronger.** Slight tilt shows wall faces, counter fronts, shelf contents. The bar becomes more of a *place* and less of a *diagram*. The warm amber light catches surfaces it couldn't reach in flat top-down. The visual warmth I've been arguing for — the Hopper diner, the place worth staying in — is easier to achieve when you can see more of the surfaces that carry the light.
**The vision cone changes meaning.** In true top-down, the cone is a geometric shape on a flat plane. In tilted view, the cone maps more intuitively to how a person actually sees — things in front are visible, things behind the character's body are obscured. The cone feels less like a game mechanic and more like *being a person facing a direction*. That strengthens the thematic argument: the vision cone should feel like natural perception, not a tool.
**The "document" framing weakens, and that's correct.** My Round 1 argument for the analytical, documentary quality of the view was strongest at true top-down. A slight tilt trades some of that clinical distance for embodiment. You're less of a analyst studying a floor plan and more of a witness present in a space. That's a tradeoff I accept — because the game isn't *only* about analysis. It's about being present in a place where people live, and deciding how to see it. The tilt puts you closer to that lived experience without sacrificing the spatial clarity that makes investigation work.
**Tile readability improves.** At true top-down, a chair is an outline. At slight tilt, a chair is a *chair* — you can see the seat, the back, the way it's pulled slightly away from the table. The placed-object vocabulary we defined becomes more expressive. The mismatched chairs from #8 would read even more clearly as "different eras, different sourcing" with a slight perspective showing their different shapes. The tile-based world gets more character from the same asset set.
**Overhead occlusion gets MORE interesting.** In true top-down, walking under something means an entity disappears behind a rectangle. In slight tilt, walking behind a shelf means the entity is partially visible — head and shoulders above the shelf line, body hidden. That *partial visibility* is more information-rich than full occlusion. You know someone is behind the shelf. You can't see what their hands are doing. That's a graduated uncertainty: more than "absent," less than "fully known." It maps to our graduated perception model better than binary occlusion.
### What I'd caution
**Don't go far enough to need front-facing entity sprites.** If the tilt demands that entities have distinct front/side/back views, the sprite production cost multiplies. Rimworld works at flat top-down because entities are essentially one view. A slight tilt might work with a single three-quarter sprite per entity. Full isometric demands four or eight directions. Keep the tilt shallow enough that entities still read from a single angle with minor rotation variants at most.
**The cone geometry gets more complex.** In true top-down, the vision cone is a 2D shape on a 2D plane. In tilted view, the cone has to account for the visual foreshortening — things "above" on screen (further from camera) appear smaller, the cone narrows with distance in screen space even if it's the same width in world space. Tyre should confirm this is manageable in Godot before we commit.
**Don't chase image #9's angle without testing it in-engine.** #9 looks beautiful as a concept image, but concept art can promise a perspective that's difficult to sustain at game resolution across dynamic entities. The target angle needs to be validated in Godot with actual tile sprites and Light2D before it's locked.
### Where this leaves the art direction
If the tilt is shallow enough to maintain spatial clarity and single-angle entity sprites, it's thematically better than true top-down. The game becomes slightly more *present* and slightly less *clinical*. The bar feels more like a room you're standing in. The investigation feels more like watching from across the space and less like studying a diagram. The trade is worth it.
The art direction I've been arguing for — functional warmth, readable tiles, silhouette-first entities, lighting as the atmosphere engine, environmental neutrality, fog as uncertainty — all of this survives the tilt. The tilt changes how you *see* the game, not what you see.
---
## Summary
Lead decisions confirmed — cone over circle, Velen, temperature through life-paths. Z-levels matter thematically where overhead occlusion creates local information gaps. v0.1.1 proves the concept with one settled room, two distinct silhouettes, warm/cool transition, and one fog boundary. Image #9 is the first image that captures the social dimension of warmth — the bar is warm because people chose to be there, and the player is on the outside looking in.
Perspective shift to slight tilt: thematically stronger than true top-down. Entities become more human (facing, posture), the warm spaces become more spatial (visible wall faces, shelf contents), the vision cone maps more naturally to embodied perception. Caution: keep tilt shallow enough for single-angle entity sprites, validate cone geometry in Godot, test at game resolution before committing. The art direction survives the tilt. The game becomes more present, less clinical. Worth the trade.
@@ -0,0 +1,215 @@
# Round 3 -- Miri's Worldbuilding & Setting Perspective
**Agent:** Miri (Worldbuilder & Setting Designer)
**Date:** 2026-02-12
**Workshop:** Art Direction & Mood Board
---
## 1. Lead Decisions — Acknowledged
**Vision shape: CONE.** Confirmed. Setting-correct. The asymmetry is what makes turning around a decision, not a reflex. The character physically has forward vision and peripheral awareness — the Rust occlusion tracers are simulating biology, not technology. The lattice doesn't expand your visual field; it annotates what you already see.
**Planet name: Velen.** Confirmed. Entering the wiki as canonical. Krenn System's primary habitable world. Temperate-maritime, ~0.9G, regular rain, morning/evening fog. Station Sova orbits Velen. The span gate connects Sova to a planetary freight depot on Velen's surface.
**Character temperature in the overlay layer — with the "where they hang" clarification.** This is the correct resolution. Let me state it precisely for the record:
The *world* has warm zones and cool zones. This is physical reality — the bar has amber fixtures, the Terminal has cool-white panels. Neither character's overlay changes the base lighting. What differs is the character's *daily path through the station*:
- The smuggler's routine moves through warm spaces: the dock floor (active, industrial-warm from machinery), the break room, the bar. Their life is socially embedded. They *live in* the warm zones.
- The detective's routine moves through cool spaces: the Commission kiosk area, observation positions, corridor patrols, data review at institutional terminals. Their life is institutionally framed. They *work in* the cool zones.
Same station, same Light2D fixtures, same tile-based world. The character's daily experience has a different average color temperature because they physically spend time in different places. The smuggler's playthrough *feels* warmer because the smuggler's life *is* warmer. No overlay manipulation needed.
This is more honest and more powerful than a shader tint. It's the setting doing the work.
**Environmental neutrality.** No concerns. Confirmed and consistent with my Round 1 and Round 2 positions.
---
## 2. Topic A: Z-Levels and Sprite Stacking — Setting Perspective
### How Many Z-Levels Does the Setting Demand?
The setting has architectural z-level implications. Sova Transit District is not a single flat plane — it's a station interior with infrastructure depth:
**Physical z-levels in the setting:**
1. **Sub-floor.** Utility conduits, maintenance crawlspaces, sealed sections from original construction. Not visually rendered as a separate level, but the *existence* of underfloor space is setting-relevant — maintenance hatches access it, and some smuggling ring operations use sealed sub-floor sections as caches.
2. **Floor level.** Where people walk, where objects sit. The primary gameplay plane. This is where 95%+ of v0.1 takes place.
3. **Overhead.** Pipes, cable runs, ventilation ducts, overhead lighting fixtures. Visible from top-down as elements that entities walk *under*. In the setting, the ceiling height varies by zone and era: Terminal has high institutional ceilings (Era 1 standard), the bar has a lower modified ceiling (Era 2-3 partition), maintenance corridors vary (some original height, some reduced by retrofit ductwork).
**What this means for rendering layers (setting input, not technical spec):**
| Layer | Setting content | Visual role |
|---|---|---|
| Floor tiles | Composite, tile, grating, guide strips | Zone identity, movement surface |
| Wall bases | Wall panels at floor level, door frames | Occlusion boundaries, room definition |
| Floor objects | Furniture, equipment, containers, small items | Environmental character, interaction targets |
| Entity layer | NPCs, player character | Gameplay information (color, silhouette, animation) |
| Overhead | Pipes, ducts, lighting fixtures, overhead signage | Atmosphere, partial occlusion, architectural character |
| Fog/perception | Vision cone mask, fog of perception | Information boundary |
| Insert overlay | Lattice HUD elements, annotations, waypoints | Character-specific information layer |
| UI | Non-diegetic elements (if any) | Player interface |
### Wall Rendering — Setting Input
From the setting: station walls are modular panels. From true top-down, you'd see the *top edge* of a wall — a thin strip. But this communicates almost nothing about the wall's character (era, material, condition).
Setting recommendation: walls should show a narrow face strip (the top ~25% of the wall visible as a slight perspective cheat) to communicate material and era. An Era 1 institutional wall panel looks different from an Era 2 modified partition looks different from a maintenance corridor's exposed structural wall. If walls are only thin lines from above, we lose the three-era visual vocabulary.
This is the same perspective cheat Rimworld and Prison Architect use — not fully isometric, but enough wall face to communicate what the wall is made of.
### Entities Under Overhead Elements
Setting-relevant: in Sova's maintenance corridors, low-hanging pipes and ductwork partially obscure the view. An NPC walking under a pipe run is momentarily harder to see — the overhead element partially occludes them.
This is both atmospheric (the corridors feel more enclosed, more layered) and mechanical (overhead elements could create brief occlusion gaps in the vision cone — you lose sight of someone for a moment as they pass under a pipe cluster). Whether this is v0.1 scope is a technical question, but the setting supports it.
### Sprite Stacking for Pseudo-3D
Setting position: not needed for v0.1. The station's visual character comes from floor variation, object variety, and lighting — not from vertical dimension. Sprite stacking is a future enhancement that could add depth to multi-level areas (looking down from a mezzanine, for instance), but it's not where the setting's visual identity lives.
---
## 3. Topic B: v0.1.1 Minimum Sprite Set — Setting Perspective
### What One Test Room Needs
The minimum viable test should prove the art direction works for a *setting-recognizable* space. I'd recommend the Terminal break room — it's the space where institutional and personal elements coexist, which tests the three-era tile vocabulary.
**Structural tiles (minimum):**
| Tile | Count | Purpose |
|---|---|---|
| Era 1 institutional wall panel | ~12 | Room perimeter |
| Standard pressure door | 1-2 | Entry/exit |
| Institutional composite floor | ~20 | Main floor surface |
| Worn commercial tile floor | ~6 | Break area sub-zone (proves zone transition within a room) |
**Object tiles (minimum):**
| Tile | Count | Purpose |
|---|---|---|
| Manifest terminal + desk | 1 | Work surface, proves "institutional" |
| Institutional table | 1 | Break area |
| Institutional chair | 1 | Matching set (Era 1) |
| Mismatched chair | 1 | Non-matching (Era 3) — proves temporal layering |
| Worker locker | 1-2 | Personal storage, possible personalization |
| Coffee machine or equivalent | 1 | Human comfort detail |
| Cargo container (small) | 1 | Industrial context |
| Meridian junction box (wall-mounted) | 1 | Era 2 retrofit, proves surface-mounted technology |
| Emergency guide strip | 2-3 | Floor-level infrastructure |
**Entity sprites (minimum):**
| Sprite | Purpose |
|---|---|
| Player character (coveralls, civilian) | Movement, vision cone origin |
| NPC dock worker (different silhouette from player) | Proves silhouette differentiation |
| NPC at terminal (seated, working animation) | Proves clear activity animation tier |
That's ~10-12 structural tiles, ~9-10 object tiles, and 3 entity sprites. Total: ~22-25 distinct assets. Enough to prove: floor transition works, object variety communicates setting, entities read against muted environment, Light2D makes it atmospheric.
### Setting Constraints for Nano Banana Generation
For the tile generation workflow, the setting provides these consistency anchors:
- **Era 1 palette:** Cool grey (#7a7f85 range), clean edges, minimal wear, institutional markings
- **Era 2 palette:** Similar grey but subtly different tone (#6d7178 range), surface-mounted elements, visible fasteners where they join Era 1 material
- **Era 3 palette:** Warmer tones for commercial/personal items, more color variety, less uniform. Chairs don't match. Mugs have different colors. Personal items are idiosyncratic.
- **Consistent outline weight** across all tiles — the Rimworld principle. Entities have heavier outlines than objects, objects have heavier outlines than floor tiles.
- **Consistent shadow direction** — all tiles cast shadows to the same side. Top-down convention: light from upper-left, shadows to lower-right.
- **Consistent scale** — a chair tile and a desk tile should imply the same spatial scale.
---
## 4. Image Reactions
### Image #8 (Tile Composition) — Already Reviewed
My full review was sent previously. Summary: **tile-based approach validated.** Floor material transition works, object tiles read as discrete placed items, the mismatched chairs are setting-perfect. Minor adjustments: entity scale slightly large for tile grid, corridors need more infrastructure tiles.
### Image #9 (Bar Antagonists) — New Review
**What lands:**
**The population density is right for peak evening.** Multiple patrons at tables, people at the bar counter, a sense of social activity. This is The Last Shift during evening shift turnover — the busiest it gets. Setting-correct.
**Lera behind the bar.** The figure with the apron at the bar counter, shelves of bottles behind her. She reads instantly as "bartender" from silhouette alone. The bar counter as visual anchor dominates the space — you know this is a bar before reading any sign. Setting-correct.
**The two detailed NPCs at the center table.** These two are rendered with more detail than the surrounding silhouetted patrons. From a setting perspective, this could represent the player's focused attention — the vision cone's detail gradient. You're looking at these two specifically. Everyone else is peripheral. The *reason* you're looking at them is the game: are they ring members meeting? Just colleagues having a drink? The visual doesn't tell you. The ambiguity is correct.
**The warm/cool zone boundary.** The bar's amber warmth (upper half) meeting the corridor's cool institutional light (lower half) with the wall and doorway as the boundary. The floor tile transition is visible. The player character standing in the cool corridor, looking into the warm bar — this is the smuggler deciding to go in, or the detective observing from outside. Either reading works. Setting-correct.
**What needs adjustment:**
**The perspective has drifted from true top-down.** The bar shelves, the wall face, the bottle detail — these read as 3/4 isometric rather than the true top-down we converged on in the #7 discussion. For a mood board this is fine (it communicates the bar's character effectively), but the actual game perspective should be closer to #8's overhead angle. The bar counter from true top-down would be a horizontal band, not a front-facing surface. *(See Addendum §5 — this criticism partially retracts under Rimworld's shallow tilt. #9's angle may be close to the new target.)*
**The detail-vs-silhouette boundary needs setting logic.** The two detailed NPCs surrounded by silhouettes is visually striking, but in the actual game this distinction should be driven by the vision cone and attention, not by arbitrary spotlight. Everyone in the player's forward vision cone should have entity color and readable silhouette. Peripheral figures get reduced detail. Figures in fog disappear entirely. The current image suggests a "focus on two, everyone else dark" approach which is more cinematic than mechanical.
**The figure in the far-left corridor (with helmet/hat)** reads slightly industrial/military again. Minor note — maintain civilian reads for all corridor entities.
**Setting verdict:** The bar scene captures the social atmosphere of The Last Shift. Population density, spatial layout, the bartender, the warm lighting — all correct. The visual differentiation between detailed and silhouetted figures is a compelling direction if it maps to the vision cone's attention gradient. The warm-cool zone boundary continues to work.
---
## 5. Addendum: Camera Angle Shift — Rimworld's Shallow Tilt
The lead has signaled a move away from pure top-down toward Rimworld's camera angle: a very shallow tilt, nearly orthographic top-down but with just enough forward perspective to show object front faces and give depth cues.
Let me check this against what we've established.
### What Changes (Setting Impact)
**Walls: My Round 2 recommendation becomes native.** I advocated for a "perspective cheat" — showing a narrow wall face strip (~25%) to communicate the three-era material vocabulary. With Rimworld's shallow tilt, that face strip is no longer a cheat. It's what you'd actually see. Era 1 institutional panels, Era 2 modified partitions, and maintenance corridor exposed structural walls all become *more* readable because the camera naturally reveals their front face. This is a setting win — the three-era visual vocabulary gets better, not worse.
**Furniture and objects: Front faces visible.** The manifest terminal shows its screen face. The bar counter shows its front panel. Lera's bottle shelves are readable. Worker lockers show their doors. Every object tile gains a narrow front face that communicates what it IS, not just its footprint shape. This directly improves the tile vocabulary from Round 2 — an institutional chair and a mismatched bar chair become even more distinguishable.
**Entities: Silhouette improves.** From pure top-down, you mostly see the top of someone's head and shoulders. With Rimworld's tilt, you see a sliver of their front — enough to read coveralls vs. apron vs. institutional jacket. The silhouette differentiation I flagged as critical (I-01: "silhouette IS identity because color is spoken for") gets *more* information to work with. Setting benefit.
**The bar shelves in Image #9 start to make sense.** I flagged #9's perspective as "drifted from true top-down" — the bar shelves and bottle detail read as 3/4 isometric. With Rimworld's angle as the target, #9's perspective may actually be *close to correct*. The bottles, the bar counter front face, the shelf detail — these are visible because the camera has a slight tilt. My criticism of #9's perspective partially retracts. The image may have been ahead of the decision.
### What Doesn't Change
**Vision cone math stays simple.** Rimworld's tilt is shallow enough that the gameplay plane is essentially flat. The Rust occlusion tracers (D-015) still operate on a 2D grid. A wall still blocks line of sight. A doorway still creates a sight line. The tilt is cosmetic depth, not mechanical depth. The asymmetric cone — forward, peripheral, behind — works identically.
**Tile grid stays orthogonal.** Rimworld uses a square grid, not an isometric diamond grid. Tiles are still placed on a rectilinear grid. The tilt just means each tile sprite includes a narrow front face in addition to its top face. This means the Nano Banana generation spec changes slightly (tiles need a consistent front-face strip at the bottom of each sprite) but the grid math, pathfinding, and room definitions are unchanged.
**Z-level rendering stack stays the same.** The 8-layer stack I proposed above still applies. Floor → wall bases → floor objects → entities → overhead → fog → insert overlay → UI. The tilt just means "wall bases" naturally show a face strip instead of requiring a perspective cheat.
**Lighting model unchanged.** Light2D, CanvasModulate, zone-based warm/cool — all work the same way. The tilt doesn't affect the lighting pipeline.
**Environmental neutrality unchanged.** No impact.
### What Needs Attention
**Overhead elements become more visually prominent.** With a tilt, pipes and ducts that cross the ceiling aren't just directly above — they have a slight visual offset. In maintenance corridors, this could actually *help* the enclosed feeling I described: you see overhead infrastructure slightly forward of where it sits, creating a layered depth that communicates "low ceiling, dense infrastructure." But sprite artists need a consistent rule for how far forward overhead elements visually project.
**Shadow direction needs revisiting.** With true top-down, the convention is light from upper-left, shadow to lower-right. With a tilt, the shadow convention should account for the implied light angle. Rimworld's shadows are consistent but subtle — we should match that: all shadows cast in the same direction, derived from a consistent implied light source position.
**Entity facing direction becomes partially visible.** With true top-down, you see the same entity sprite regardless of which way they face (just rotated). With a tilt, a figure facing toward the camera shows a sliver of face/front; a figure facing away shows their back. This is *mechanically useful*: you can tell which direction an NPC is looking, which feeds into the vision cone awareness system. If I can see an NPC's back, I know they're facing away from me. Setting note: this is how you read body orientation in real life — it's biologically correct.
### Setting Verdict on the Shift
**Endorse.** Rimworld's shallow tilt is the best position on the spectrum for our setting needs. It gives us:
- The three-era wall vocabulary I fought for, natively
- Better entity silhouette differentiation
- Object front faces that improve readability
- Facing direction as visual information
- No mechanical complexity increase (grid stays square, LOS stays 2D)
The shift is cosmetic-beneficial, mechanically neutral, and setting-positive. Image #9's angle is closer to the target than I initially assessed.
**One caution:** the tilt must stay shallow enough that the player reads the game as "top-down with depth" not "isometric." The moment entities start looking like they're standing on a tilted plane rather than a flat floor, we've gone too far. Rimworld's angle is the ceiling, not the floor.
---
*Setting note — this workshop has produced something I wasn't sure was possible: an art direction that is simultaneously setting-faithful, mechanically supportive, technically achievable, and visually distinctive. The tile-based composition IS how stations are built. The lighting IS how different zones feel. The entity colors ARE how relationships are perceived. The fog IS what you don't know. Nothing in the visual direction is fighting the setting — it's all expressing it. That convergence is rare and valuable.*
*Velen is now canonical. The Settled Reach has its first named planet.*
*Miri out.*
@@ -0,0 +1,242 @@
# Round 3 — Ozzie (Player Experience / Wow Factor)
## 1. Lead Decisions — Acknowledged
### Vision shape: CONE. Confirmed.
YES. This is the single most important art direction decision in the workshop. The cone means turning around is a meaningful action. "What's behind me?" is a real question. The Hotline Miami tension is ON. Directional awareness changes EVERYTHING about how movement through the station feels — you're not a drone floating over a map, you're a person who can be surprised from behind. That's the investigation game. That's the dread.
### Planet name: Velen. Confirmed.
Temperate-maritime with regular rain and morning/evening fog. Weather is identity. "It's raining on Velen" will become something players say. The fog is mechanically loaded — foggy mornings mean shorter vision cones for everyone, which means the smuggler's early morning drops are safer and the detective's dawn surveillance is harder. Weather as asymmetric advantage, confirmed.
### Character temperature: lives where they hang, not in the overlay.
This is the ELEGANT solution. I love it because it's not a trick — it's a truth. The smuggler spends their day in the bar and the cargo bay social spaces. Those ARE warm. The detective spends their day in the security office and observation posts. Those ARE cool. Same station, same physics, different daily routes through the color temperature map. Nobody's lying. The world isn't shifting. The characters just LIVE in different emotional temperatures because their LIVES have different emotional temperatures. That's asymmetric information expressed through architecture. Beautiful.
### Environmental neutrality: confirmed.
The world has warm zones and cool zones. They're fixed. They don't shift. The investigation signal is behavioral (NPC out of routine) not environmental (room got spooky). Fully endorsed.
No concerns on any of the four. These are clean decisions.
---
## 2. Z-Levels and Sprite Stacking — Player Experience Perspective
### What the player needs to FEEL about vertical space
The player is looking down. They should feel like they're looking at a FLOOR PLAN that has depth — not a flat diagram, and not a complex 3D scene. The z-level system exists to create two player-facing feelings:
**"I can see the layout"** — the floor plan is readable. Walls define spaces. Furniture sits on floors. Entities move through the space. This is the baseline.
**"Things can block my view"** — and THAT'S where it gets interesting. A shelf unit blocks the sightline. An overhead pipe creates a shadow. A wall hides the next room. The z-level system creates moments where the player's visual information is incomplete because OBJECTS ARE IN THE WAY. That's not a rendering problem — it's a gameplay feature. Every occluding object is an information barrier.
### My proposed z-level hierarchy (from player experience):
**Layer 0: Floor.** Tile-based. Always visible within the vision cone. Carries zone identity through color. Patched tiles, wear paths, maintenance hatches. The "ground truth."
**Layer 1: Low objects.** Placed on the floor. Chairs, crates, small equipment, floor-level containers. These DON'T block sightlines — you can see over them from top-down. But entities interact with them (sitting in chairs, working at terminals). They provide context for entity behavior.
**Layer 2: Entities.** NPCs and the player character. Rendered ON TOP of low objects (you see the person IN the chair). D-033 colored. This is the primary information layer — the player reads entities first, environment second.
**Layer 3: Walls and tall objects.** Walls, tall shelving units, equipment racks, bar counter. These BLOCK sightlines. From top-down, you see the TOP of the wall (a cap/edge sprite) and it occludes what's behind it from the vision cone. This is where the fog of perception and the z-level system INTERSECT — a wall blocks your vision cone the same way it blocks light in the LightOccluder2D system.
**Layer 4: Overhead / ceiling elements.** Pipes, ducts, overhead signage, ceiling-mounted lights. These render as semi-transparent or outlined over the scene below. You can see THROUGH them to the floor, but they add visual texture and can cast shadows. They're atmosphere, not information barriers.
**Layer 5: Insert overlay.** The neural insert. Separate CanvasLayer. Rendered on top of everything. Geometric + bloom. Doesn't interact with world lighting.
**Layer 6: UI.** Monologue display, minimap border arrows, interaction prompts. Top layer.
### Key player experience rules:
**Entities should NEVER be fully hidden by furniture.** If Kael is behind a shelf, I need to see SOMETHING — the top of his head, a color hint, movement. If entities vanish completely behind objects, the player loses track of NPCs and the investigation mechanic breaks. Partial occlusion is fine and creates information tension ("someone's behind that shelf but I can't tell who"). Full occlusion is only acceptable behind WALLS (which define room boundaries and are the primary sightline blockers).
**Wall rendering: show the top edge, block everything behind it.** The player should see that a wall EXISTS and roughly where it ends. A thin top-cap sprite (maybe 8px wide) sitting on top of the wall's footprint. Everything on the other side of the wall is in the fog. This is the simplest approach and it works — Heat Signature does exactly this.
**Sprite stacking for pseudo-3D: OUT OF SCOPE for v0.1.** It's cool but it's polish. The emotional impact comes from the lighting and the information system, not from visual depth on furniture. Flat top-down sprites with the z-layer system above will carry v0.1. Revisit for v0.2 if the team has bandwidth.
---
## 3. v0.1.1 Minimum Sprite Set — Player Experience Perspective
### What does "the art direction is working" mean in a test room?
The v0.1.1 test needs to answer ONE question: **does moving through this space FEEL like the game we're designing?** Not "does it look finished" — "does it feel right?"
That means the test room needs to demonstrate:
1. Zone temperature transition (walk from cool to warm)
2. Vision cone (what's in front of me vs what's not)
3. Entity readability (I can tell who's doing what)
4. Tile-based composition (the world is made of placed objects)
5. Lighting as atmosphere (the mood comes from the light, not the sprites)
### Minimum tile set for a single test room
**Structure tiles (walls, floors, doors):**
- Floor tile: cool grey (corridor/terminal) — 2-3 variants for patching
- Floor tile: warm tan (bar/social) — 2-3 variants
- Wall segment: horizontal, vertical, corner, T-junction
- Door: closed state, open state
- Wall cap/top edge sprites
**Furniture tiles (objects):**
- Table (round, 2x2) — bar/breakroom
- Chair (at least 2 variants — stool, regular)
- Terminal/desk (2x1) with screen glow
- Cargo crate (1x1)
- Bar counter segment (1x1, connectable)
- Light fixture (wall-mounted, warm amber)
- Light fixture (ceiling, cool white)
**Entity sprites:**
- Player character: idle, walking (4 directions), sitting
- NPC type A (dock worker — Kael-like silhouette): idle, walking, working-at-terminal, sitting
- NPC type B (bartender — Lera-like silhouette): idle, walking, behind-counter
That's roughly: 8-10 floor/wall tiles, 7-8 furniture tiles, ~15 entity frames across 3 character types. Maybe 30-35 sprites total.
### What the test scene should BE
**Two connected rooms with a corridor between them.** One warm (breakroom/bar fragment with amber lighting). One cool (terminal workspace with white lighting). Connected by a short corridor (dim, minimal furniture).
The player starts in the corridor. Walks toward the warm room — the light changes, the floor tiles change, there's a table with chairs and a wall-mounted lamp. An NPC is sitting at the table. Walk to the cool room — light shifts, floor tiles change, there's a terminal with a working NPC. The vision cone illuminates what's ahead and fades behind.
If the player feels the temperature shift between rooms, reads the NPC activities at a glance, and feels the cone creating directional awareness — v0.1.1 has validated the art direction.
### Nano Banana workflow
For consistency across the tile set:
1. **Define the style guide FIRST:** top-down, flat color, bold outline (2px black), muted station palette, 64x64px per tile. Write it as a generation prompt prefix.
2. **Generate structural tiles first** (walls, floors) — these set the visual baseline.
3. **Generate furniture tiles second** — using the structural tiles as style reference in the prompt.
4. **Generate entity sprites last** — using the furniture tiles as scale reference. Entities must POP against the tile background.
5. **Review pass:** check that every sprite reads correctly at 64x64 on screen. If you squint, regenerate.
Each prompt should include: "top-down view, 64x64 pixel tile, flat color, bold 2px black outline, muted space station palette, consistent with [reference tile]."
---
## 4. Image Reactions
### Image #8 (tile composition): Already reviewed — 8/10.
Quick recap: Tile grid visible and readable. Zone temperature transition through FLOOR TILE COLOR works. Every object is a distinct tile sprite. Coffee mugs on table = environmental storytelling through placed objects. Needs: D-033 entity colors, more floor tile variation, consistent entity scale.
### Image #9 (bar antagonists): THIS is a scene from the game.
Okay. This image tells a STORY and I can feel it in my gut.
The player character is standing in the cool grey corridor OUTSIDE the bar, looking in through the entrance. Inside the warm amber bar: two figures at a center table, leaning toward each other, in close conversation. They're rendered with detail — THESE are the people that matter. Around them: silhouetted patrons at other tables, at the bar counter, Lera behind the counter with her apron. Background noise. Social wallpaper. The two center figures are the SIGNAL in the NOISE.
**What this captures:**
1. **The surveillance moment.** The player is OUTSIDE the warmth, looking IN. Cool corridor, warm bar. The emotional temperature gap IS the detective's experience — you're not part of this. You're watching. The corridor is your office. The bar is their life. And you're trying to read their conversation from the cold side of the doorway.
2. **The signal-to-noise ratio.** Two detailed figures (persons of interest) surrounded by silhouettes (background NPCs). In the actual game with D-033 colors, those two would be amber (flagged) while the silhouettes would be teal (unknown) or green (known). The eye goes STRAIGHT to the detailed pair. That's the visual hierarchy working — important things pop.
3. **The Sims readability applied to investigation.** I can read the two center figures' activity: sitting, talking, leaning in. That's Tier 1 clear animation. But I CAN'T read WHY they're talking or WHAT about. That's Tier 2 ambiguity. I need to get closer, enter the bar, approach the table, maybe trigger a monologue observation. The image captures the exact moment where Tier 1 readability creates Tier 2 investigation impulse.
4. **The vision cone implication.** The player is facing the bar entrance. If the cone were rendered, everything inside the bar in the forward arc would be visible. The corridor behind the player would be fogged. The helmeted worker silhouette to the left would be in peripheral vision — barely readable. If someone approaches from behind while the player is watching the bar? They wouldn't see them coming. THAT'S the cone.
**What needs adjustment:**
The chibi proportions on the player character still bug me — the center bar figures have more naturalistic proportions and it creates a visual mismatch. But this is a mood board, not final art.
**Verdict: 9/10.** This is the closest thing to an actual game screenshot we've produced. The warm/cool divide, the surveillance positioning, the signal-in-noise composition, the activity readability — this is what the game FEELS like when you're playing the detective and watching someone you're not sure about.
The moment captured here? This is wow moment #2 (The Character's Eye) territory. The player is watching the bar. The monologue fires: "Kael's at table four with someone I don't recognize. He doesn't usually stay this late." The urgent chime. The player leans forward. The investigation begins.
---
## 5. The Tilt Question: True Top-Down → Slight 3/4 View
### My gut reaction: YES. And image #9 is the proof.
Look at image #9 again. Look at how the bar READS. The bottles on the shelves. The seated figures leaning toward each other — you can see their POSTURE, their facing direction, the intimacy of the conversation. The bar counter has DEPTH. The corridor below has WALLS you can see the faces of. The door frame is a door frame, not a line on the floor.
Now look at image #8 (pure top-down tile composition). Clean. Readable. Functional. But emotionally... flatter. The seated figure at the terminal is a shape on a tile. The furniture is geometric outlines. It reads as a floor plan. Image #9 reads as a PLACE.
The slight tilt is what makes a floor plan feel like a world.
### Where on the spectrum?
```
Pure top-down → Slight tilt → 3/4 view → Full isometric
#5/#8 Rimworld #9/TBW Disco Elysium
```
Lead confirmed: **Rimworld's angle.** That's the shallow end of the spectrum — almost top-down but with just enough forward tilt to show object fronts. This is the RIGHT call. Enough tilt that:
- Wall faces are visible (walls become visual features, not boundary lines)
- Entities have a visible front (facing direction readable from the sprite, not just from the cone)
- Furniture has minor depth (a chair has a back, a bar counter has a front face)
- The station feels like a 3D space rendered in 2D
But NOT so much tilt that:
- We need full isometric sprite sheets (8-direction sprites at an angled perspective = massive asset pipeline)
- The floor plan becomes hard to read (too much wall face obscures layout)
- The lighting system fights the perspective (Light2D operates in screen space, works fine, but the light/shadow illusion needs to match the tilt angle consistently)
Image #9 is actually MORE tilted than the target — Rimworld's angle is shallower. That's fine. #9 proved the concept; the actual game will be flatter, which is EASIER on every front.
This is excellent news for three reasons:
- **Vision cone math stays simple.** Near-orthographic means the cone is basically a 2D shape on a flat plane with minimal perspective distortion. The Rust shadowcasting (D-035) maps cleanly.
- **No isometric diamond geometry.** Tiles stay rectangular on screen. The grid stays square. No 2:1 diamond tile math, no isometric sorting headaches.
- **Sprite production stays close to top-down.** Objects need a small visible front face but nothing like full isometric sprite work. Nano Banana generates "top-down with slight forward tilt" — much more consistent than full isometric angles.
### What changes from the workshop decisions?
**Nothing changes in principle. Several things change in production.**
The core positions all HOLD at slight tilt:
- Readability first: still true. TBW proves this at the tilted angle.
- Lighting as primary atmosphere: still true. The tilt doesn't change how PointLight2D and LightOccluder2D work.
- D-033 entity color = relationship: still true. Color works at any angle.
- Silhouette as identity: STRONGER at tilt. You see more of the character's body shape, clothing, posture. Silhouette differentiation is actually EASIER with some tilt because you have more visual information per entity.
- Vision cone: still works. The cone renders on the ground plane with the same forward/peripheral/behind gradient. The tilt just changes how it LOOKS on screen (slightly narrower vertically due to perspective).
- Environmental neutrality: still true. The world has the same zones at any camera angle.
- Tile-based composition: still works but tiles now need to be drawn at the tilt angle. A 64x64 tile is still 64x64 on screen, but it represents a tilted view of the floor.
**What changes in production:**
1. **Tile sprites need consistent tilt angle.** Every tile drawn at the same shallow perspective. At Rimworld's angle this is close to top-down — the front face is a narrow strip, not a major feature. Nano Banana prompt: "top-down view with very slight forward tilt, 64x64 pixel tile." Consistency is easier at a shallow angle because there's less perspective to get wrong.
2. **Wall sprites show a thin front face.** Not a full wall surface — a narrow strip that says "this is a wall with height." Enough to show material and maybe signage. Much less sprite work than a full 3/4 view wall face.
3. **Entity sprites gain a small front.** You see mostly the top of the character's head/shoulders plus a narrow front strip showing posture and clothing. This is MORE information than pure top-down (silhouette differentiation is easier) without the full complexity of isometric character art.
4. **Multi-direction entity sprites.** At Rimworld's shallow angle, the difference between directions is small. Front-facing vs back-facing is visible. Left vs right is a mirror. That's 2 actual direction variants per state, not 4. Manageable.
5. **References shift slightly.** Rimworld becomes the DIRECT camera reference (not just a readability reference). TBW is still relevant for readability + personality at a slightly steeper angle. Heat Signature is less directly relevant. Darkwood's lighting principles apply at any angle.
### The player experience case FOR the tilt
The tilt gives us THREE things we can't get from pure top-down:
**1. Characters have FACES.** Not detailed faces — but a visible front. You can see that Kael is FACING the unknown contact at the table. You can see that Lera is turned toward the bar, not toward the room. Facing direction becomes a visual signal without needing a separate indicator. The detective watching from the corridor can see WHO is looking at WHOM.
**2. The bar feels like a bar.** The bottles on shelves. The counter surface. The warmth of the space has depth and visual richness. Image #9's bar makes me want to walk in and sit down. Image #8's bar (less tilted) makes me want to click on a table. That's the difference between a PLACE and a PLAN.
**3. The corridors feel TALL.** With tilt, you see corridor walls rising above the floor. The space has vertical presence. A narrow corridor with tall walls pressing in from both sides feels claustrophobic — you can see how enclosed you are. In true top-down, corridors are just narrow paths. With tilt, they're narrow paths BETWEEN WALLS. The z-level system I proposed becomes visually intuitive — you can SEE that the shelf is taller than the crate, that the wall is taller than the shelf.
### The player experience RISK of the tilt
**Scope creep.** More directions × more activity states × more character types = more art. But at Rimworld's shallow angle, the overhead is modest — 2 drawn directions (front/back) with mirroring for left/right. The shallow tilt also means Nano Banana prompts stay close to "top-down" which is its most consistent output angle.
**At Rimworld's angle, the risk is LOW.** This isn't a full isometric pivot. It's a subtle depth cue that gives walls height, entities posture, and furniture presence. The sprite pipeline doesn't fundamentally change — it gets slightly more complex per asset but the production model stays the same.
### My position
I'm FOR the tilt. Image #9 convinced me. The station comes alive with even a small amount of perspective. The characters become PEOPLE, not dots. The spaces become PLACES, not plans.
Rimworld's angle is the right call. Shallow enough that the production overhead is modest. Deep enough that the station has depth. The tiles stay square on screen. The cone math stays simple. The sprites stay close to top-down. And the player sees a PLACE instead of a PLAN.
This is a player experience win that the production pipeline can afford. No caveats. Let's do it.
---
## Summary
All four lead decisions endorsed without reservation. Z-levels: 7-layer hierarchy, entities never fully hidden by furniture, walls are the primary sightline blockers, sprite stacking deferred to v0.2. v0.1.1: ~35 sprites (may increase with tilt), two connected rooms with temperature transition, answer "does this FEEL right?" Image #9 is the emotional proof of concept — AND the evidence that a slight tilt makes the station come alive.
The tilt doesn't change any design PRINCIPLES. It changes production SCOPE. The station is the same. The lighting is the same. The information system is the same. But the player sees characters with faces, bars with depth, and corridors with walls. That's worth it — if production can handle it.
@@ -0,0 +1,65 @@
# Workshop: Art Direction & Mood Board
**Date:** 2026-02-12
**Facilitator:** Team Leader (Jeroen)
**Participants:** Araminta (lead), Ozzie, Miri, Gore, Qatux (documenter)
**Status:** Round 1
---
## Purpose
Explore visual references, art direction, and mood for The Settled Reach. Build a mood board of reference images, games, films, and art styles that capture what this game should LOOK and FEEL like.
This is exploratory — not about v0.1 placeholder art (that's defined), but about the long-term visual identity. What does The Settled Reach look like when it grows up? What's the target aesthetic?
## Context
- **Q-003** (Art direction) is open. v0.1 visual grammar is defined (D-033, Araminta's workshop output) but long-term art direction is unresolved.
- **D-019:** Top-down confirmed as primary camera. 3D cutscenes for key moments.
- **D-005:** Single character, first-person experience through top-down view. Asymmetric information is the master mechanic.
- **D-011:** Fog of perception — shadowcasting, vision cone, information decay.
- **D-015:** Camera locked to character. Forward = detail, peripheral = reduced, behind = blind.
- **Setting:** Space opera, advanced civilization, wormhole network. Sova Transit District — a working-class station hub. Not gleaming spaceport, not grimy dystopia. Lived-in, functional, 40 years old.
## Key Questions for Round 1
### Q1: Visual References — What games/films/art look like what we're making?
Find and describe specific visual references. For each reference, explain:
- What it gets RIGHT for our game
- What it gets WRONG or what we'd change
- Which specific visual element we're referencing (lighting? color? camera angle? UI? atmosphere?)
Think broadly: games, films, concept art, photography, graphic novels, architectural photography, UI design.
### Q2: Mood and Atmosphere — What emotional register?
The Settled Reach is a space station where people live ordinary lives while conspiracies run underneath. What's the visual mood?
- How does "quiet life is good" look vs "something is wrong here"?
- What's the lighting language? (harsh fluorescent? warm ambient? mixed?)
- What's the color temperature of normal life vs investigation mode?
- How does the visual mood shift between the smuggler's perspective and the detective's?
### Q3: Top-Down Art Style Direction
Given D-019 (top-down), what art style should we target long-term?
- Pixel art? Vector? Painted? Stylized 3D rendered to 2D? Something else?
- What level of detail per entity? (Rimworld-level? Hotline Miami? Teleglitch? Something unique?)
- How do we balance readability (information design) with atmosphere?
- What about animation — smooth sprite animation? State-based frames? Procedural?
### Q4: The Station as Character
Sova Transit District is 40 years old. It's been repaired, modified, lived in. How does that read visually?
- What does "lived-in space infrastructure" look like in top-down?
- How do we visually differentiate the three zones (Logistics Hub, Bar, Corridors)?
- What visual language says "this place has layers" without being grimy-dystopia?
### Q5: Information Visualization
The game is about what you know and don't know. Beyond the color system (D-033), how should information itself look?
- What does the neural insert overlay look like aesthetically?
- How do perception mode overlays feel different from each other?
- What's the visual language for "I'm learning something new" vs "this confirms what I suspected"?
## Format
Each participant writes their Round 1 response as `round1-{agent}.md` in this directory. Search the web for real visual references — name specific games, artists, films, screenshots. Be concrete, not abstract.
Qatux: Track all references mentioned, noting which aspects are endorsed and which are cautioned against. Maintain a running reference list.
@@ -0,0 +1,701 @@
# Art Direction & Mood Board Workshop — Final Outcomes
**Date:** 2026-02-12
**Facilitator:** Team Leader (Jeroen)
**Participants:** Araminta (Visual Designer, lead), Ozzie (Player Experience), Miri (Worldbuilder), Gore (Themes & Endgame), Qatux (Documenter)
**Rounds:** 3 + Closing + Post-workshop technical session (Tyre, Araminta, Stig)
**Status:** Complete — all items resolved, ready for formal decision recording. Pipeline test in progress.
---
## Visual Identity Statements
Each participant's one-sentence answer to "What is this game's visual identity?"
> **Araminta:** A well-maintained space station where the lighting tells you how to feel and the people tell you what to fear.
> **Ozzie:** A warm, lived-in space station seen through one person's eyes — where the lighting tells you how to feel, the fog tells you what you don't know, and the same room looks like home or like evidence depending on who you are.
**Lead correction on Ozzie's statement:** The final clause — "the same room looks like home or like evidence depending on who you are" — inverts the actual mechanism. The room is ALWAYS the room. It renders identically for every character. The visual temperature difference between playthroughs comes from characters choosing to spend time in *different* rooms, not from the same room looking different. Ozzie's formulation implies per-character rendering of shared spaces, which contradicts environmental neutrality (§1.10). The other three statements are closer to the mark. See §1.11 for the precise mechanism.
> **Miri:** A lived-in station assembled from forty years of modular parts, seen through a cone of imperfect knowledge, where the warmth of the bar and the cold of the corridor are the same reality experienced by different lives.
> **Gore:** A place worth living in, seen by someone trying to decide if they believe it.
---
## 1. Workshop Decisions
### 1.1 Visual Style
**Clean 2D with bold silhouettes, tile-based world composition, lighting-driven atmosphere.**
- Not pixel art (retro-coded), not painted (too expensive for AI pipeline), not 3D (uncanny at this scale)
- Godot 4 Light2D pipeline: PointLight2D per fixture, LightOccluder2D on walls, CanvasModulate for global ambient
- Nano Banana / Gemini 2.5 Flash asset generation for tiles and entities
- Gore's label for the converged direction: **"functional warmth"**
- Araminta's production principle: **"Sprites are shape templates that the lighting system completes."** No baked shadows, no baked lighting, no baked mood. Sprites are neutral; Light2D does the rest.
**Consensus:** Unanimous (Rounds 1-2). All four agents independently arrived at the same art style.
### 1.2 Camera Angle — D-019 Amendment
**Rimworld-angle shallow tilt: nearly orthographic top-down with enough forward perspective to reveal object and wall front faces.**
- This is NOT classic 2:1 isometric. NOT 3/4 view. NOT full top-down.
- The grid remains square/orthogonal (no diamond tile geometry)
- Vision cone math remains 2D — the tilt is cosmetic, not mechanical
- "Top-down with depth" is the correct description; "isometric" is incorrect terminology
**What the tilt gives us:**
- Three-era wall vocabulary becomes native (wall faces visible without a rendering cheat)
- Entity silhouette differentiation improves (front-facing details: vest, apron, uniform)
- Object sprites become self-describing (desk shows screen, bar counter shows front panel)
- Entity facing direction becomes readable (see someone's front vs. back)
**What doesn't change:**
- Tile grid stays orthogonal (64x64, square)
- Vision cone computed on 2D grid (Rust shadowcasting, D-035)
- Light2D pipeline unchanged
- Fog of perception unchanged
- Environmental neutrality unchanged
- All art direction principles hold
**Consensus:** Unanimous (Round 3 + Closing). All four endorse. Miri: "setting-positive." Gore: "thematically stronger — person standing in doorway, not surveillance camera." Ozzie: "the station comes alive." Araminta: "strictly better."
**Recording note (Araminta, Miri, Ozzie):** Record as "Rimworld-angle shallow tilt," NOT "slight isometric." Rimworld is orthographic with a cosmetic forward tilt. Isometric implies diamond grid geometry. The spirit of D-019 (top-down as primary, not isometric or first-person) holds; this is an amendment to the angle, not a contradiction.
### 1.3 World Composition — Tile-Based
**64x64 tile grid, 1x1 placed objects, base-builder compatible.**
- Structural tiles (walls, floors, doors): 64x64, muted, minimal outlines, zone palette + era differentiation
- Object tiles (furniture, equipment, containers): 64x64 per 1x1, medium outline (1px), Rimworld object detail as target
- Larger objects composed of 1x1 sub-tiles (e.g., 3x1 bar counter, 2x1 desk)
- Three construction eras as tile palettes (see §1.8)
- Stations are literally built this way — prefab modular construction = tile grid IS the construction grid (Miri)
**Design principle — "Settling is placement" (Gore):** Object density IS environmental storytelling. The bar is full because Lera settled it. The corridors are empty because nobody did. Investigation is archaeology of intention — every placed tile is someone's decision.
**Consensus:** Unanimous (Rounds 2-3). Tile-based input strengthened all existing positions.
### 1.4 Visual Hierarchy
**Three-layer system: Entities > Objects > Structure.**
| Layer | Outline weight | Color saturation | Role |
|---|---|---|---|
| Entity sprites | 2px (boldest) | Highest — D-033 relationship colors | Information / social layer |
| Object tiles | 1px (medium) | Moderate — era-appropriate palette | Function / lived-in character |
| Structural tiles | Minimal / none | Lowest — muted zone palette | Spatial backbone |
**Hard rendering rule (Araminta):** Entity always wins visual ties. If an entity and an object overlap, the entity's D-033 color must remain visible. This is a readability guarantee, not an aesthetic preference.
**Consensus:** Unanimous (Rounds 2-3).
### 1.5 Entity System
**24x32 pixel footprint within 64x64 tiles.**
- Entity smaller than tile = clear figure-ground relationship
- D-033 color as primary information signal
- Silhouette as primary identity signal (I-01: "Silhouette IS identity because color is spoken for" — Ozzie)
- One identifying silhouette feature per named NPC (Kael's vest, Lera's apron, Sera's Commission uniform)
- 3-4 template silhouettes for generic NPCs
- At Rimworld's tilt: front-facing details visible, facing direction readable, 2 drawn directions (front/back) with mirroring for left/right
**Consensus:** Unanimous (Rounds 2-3).
### 1.6 Lighting System — Three-Reference Model
| Reference | Provides | Aspect |
|---|---|---|
| **Darkwood** (Acid Wizard Studio) | Vision cone mechanics | Light pooling, graduated fade from known to unknown, darkness-as-weight, emotional gradient of restricted LOS |
| **Blade Runner 2049** / Roger Deakins | Color temperature language | Warm amber = social/inhabited, cool white = institutional/official, mixed = transitional |
| **Edward Hopper** (*Nighthawks*) | Emotional composition | Warm interior light surrounded by unknowable dark — "our game's central image" (Gore) |
**Critical retuning from Darkwood:**
| Darkwood | The Settled Reach |
|---|---|
| Darkness = hostile | Darkness = uncertain |
| Light = safety | Light = visibility (not necessarily safety) |
| Beyond the cone: monsters | Beyond the cone: life continuing without you |
| Emotional register: dread | Emotional register: uncertainty |
**Where Darkwood's model applies directly:** Maintenance corridors with manual-switch lighting (~20-30% of spaces), exterior sections during night/weather, the fog boundary everywhere.
**Where it needs adaptation:** Most of Sova is lit. Light doesn't equal safety — the bar's warm light is where THE FRIEND's contradiction might be noticed.
**Godot implementation:** PointLight2D (per fixture, per zone) + LightOccluder2D (on walls/obstacles) + CanvasModulate (global ambient) + textured PointLight2D on player (vision cone shape). Stock Godot 4 pipeline — no custom engine work.
**Consensus:** Unanimous (Rounds 1-2). Ozzie's strong Round 1 advocacy for Darkwood endorsed by all in Round 2.
### 1.7 Fog of Perception — Cone Shape
**Vision cone is asymmetric: full clarity forward, degraded at peripheral edges, blind behind (D-015).**
- Cone, not circle. Turning around is a meaningful decision, not a reflex.
- The Rust occlusion tracers (D-035) simulate biology, not technology
- The lattice doesn't expand your visual field; it annotates what you already see
- Fog = absence of knowledge, not visual decoration
- Graduated: clear → reduced → uncertain → ignorant (not binary)
- Quality of fog boundary: gradient desaturation, not a hard wall of black
**Consensus:** Confirmed by lead decision, endorsed unanimously (Round 3).
### 1.8 Three Construction Eras
| Era | Age | Palette character | Object examples | Zones |
|---|---|---|---|---|
| Era 1 | ~40 years | Institutional grey (#7a7f85 range), cool, uniform, regulation markings, clean edges, minimal wear | Standard wall panels, composite floors, heavy pressure doors | Terminal structure, main corridors, maintenance |
| Era 2 | ~20-30 years | Similar grey, subtly different tone (#6d7178 range), surface-mounted elements, visible fasteners | Retrofit Meridian junction boxes, modified partitions, expansion-era floor | Terminal modifications, bar corridor |
| Era 3 | Recent/ongoing | Warmer tones, most varied, more color variety, less uniform | Bar furnishings, locker decorations, hand-lettered signs, mismatched chairs, personal items | Bar interior, break rooms, personal spaces |
**Consistent outline weight** across all tiles — entities heavier than objects, objects heavier than floor tiles.
**Consistent shadow direction** — all tiles cast shadows to the same side (convention TBD for tilt angle).
**Consistent scale** — a chair tile and a desk tile imply the same spatial scale.
**Consensus:** Unanimous (Rounds 1-3). Araminta provides hex values, Miri provides setting grounding, Gore provides thematic framing.
### 1.9 Neural Insert Overlay
**Geometric data layer rendered with soft bloom shader pass.**
- Data is computational (geometric): precise positioning, clean lines, structured information
- Delivery is neural (organic): ~2-3px gaussian blur at ~40% blend on the insert CanvasLayer
- D-033 colors gain soft halos rather than hard edges — "amber feels like a vague sense of concern, not a data flag" (Araminta)
- Smuggler's overlay: thinner, sparser (baseline lattice hardware)
- Detective's overlay: denser, crisper (augmented lattice hardware)
- Passive state nearly invisible; active state clean and precise
- Test (Gore): "If switching the overlay OFF would feel like going deaf rather than closing a window, it's working"
- Test (Ozzie): "After 10 minutes, does the player forget the insert is there? If yes, we've succeeded"
**Insert overlay is NOT affected by the fog shader** — insert data is computational, not perceptual. Insert markers can appear in fogged areas if the lattice has that data.
**Consensus:** Unanimous (Round 2). Resolves Round 1 divergence between Araminta (geometric) and Gore (organic) — both positions survive in the synthesis.
### 1.10 Environmental Neutrality — Strict Zero Shift
**The base world layer never shifts in response to conspiracy activation or investigation state.**
This is a mechanical constraint, not an aesthetic preference — it protects the information model.
**What CANNOT change when conspiracy activates:**
- Light color temperature
- Shadow depth or direction
- Tile colors, wall tones, floor patterns
- Any CanvasModulate shift correlated with narrative state
**What CAN change:**
- Insert overlay density (more annotations, flags, connection lines)
- Entity colors (D-033 shifts: green → amber → red)
- Monologue frequency and urgency
- Player character sprite posture/animation (Araminta's proposal: stress visible on the self, not the world)
**Allowed diegetic changes (everyone experiences them):**
- Time-of-day cycle (D-031) — physical reality
- Weather — physical reality. The storyteller can TIME weather for dramatic effect, but weather itself is causally neutral.
- Insert overlay density — character cognition, not world state
**The horror (Gore):** "The player walks into the bar after discovering the conspiracy and it's STILL warm and inviting. That's the horror. The warm light isn't ironic — it's indifferent."
**Recording note (Ozzie):** Three systems with different rules: (1) Zone lighting is fixed. (2) Weather is the storyteller's instrument. (3) Insert overlay is the character's analytical state. The prohibition is specifically: the rendering pipeline never modifies world-layer visuals in response to narrative state.
**Consensus:** Unanimous (Round 2). Araminta and Ozzie both independently moved from "subtle shifts allowed" to strict zero after reading Gore and Miri's arguments — convergence through persuasion, not compromise.
### 1.11 Character Temperature via Spatial Paths
**The smuggler's playthrough feels warmer because the smuggler physically spends time in warm-lit spaces. The detective's playthrough feels cooler because the detective physically spends time in cool-lit spaces.**
This is NOT an overlay tint. Same Light2D fixtures. Same station. Different daily paths.
- Smuggler's routine: dock floor (industrial-warm) → break room → bar (amber). Their *work* takes them through social spaces.
- Detective's routine: Commission kiosk → observation posts → corridor patrols → institutional terminals. Their *work* takes them through institutional spaces.
The setting produces the feeling. No rendering tricks needed. The level design does the work.
**Critical clarification (Lead):** The temperature difference comes from where each character's WORK takes them, not from who they are as people. Both characters are fully realized people with personal spaces, tastes, and lives. The detective is not a cold analytical camera — they have their own quarters, their own furniture choices, their own version of home. Both characters settle. Both characters decorate. Both characters make their space their own. The visual temperature gap between playthroughs is a consequence of *occupation*, not *personality*. The detective's home may be just as warm as the smuggler's — but the detective spends more working hours in cool institutional spaces, shifting the average color temperature of their daily experience. This protects against the "detective = cold robot" interpretation.
**Recording note (Miri):** Record the mechanism precisely. This is character spatial behavior, not a rendering system. The world IS neutral. The characters' lives have different average color temperatures because they physically occupy different zones.
**Consensus:** Unanimous (Round 3). Lead decision, endorsed by all. Miri: "more honest and more powerful than a shader tint." Gore: "the cleanest expression of the dual-lens concept."
### 1.12 Animation System — Two-Tier
**Tier 1: CLEAR animation** (daily life / public activities)
| Activity | Frames | Readability requirement |
|---|---|---|
| Walking/running | 4-6 frames directional cycle | Instantly readable direction and speed |
| Working at terminal/handling cargo | 2-3 states | Unambiguous "this person is working" |
| Eating/drinking | 2-3 states | Readable from across the map |
| Talking (two entities face-to-face) | 2-3 states | Clearly distinguishable from "standing near someone" |
| Sleeping | 1 state | Unambiguous |
**Tier 2: AMBIGUOUS animation** (investigation / private intention)
| Behavior | Animation | Player sees / doesn't know |
|---|---|---|
| Pausing/stopping | Entity halts | Sees: stopped. Doesn't know: why |
| Looking around | Head rotation | Sees: scanning. Doesn't know: purpose |
| Lingering near a location | Idle-aware state | Sees: staying. Doesn't know: reason |
| Changing direction | Walk direction reversal | Sees: turned around. Doesn't know: why |
| Proximity without clear interaction | Two entities near, NOT in "talking" animation | Sees: nearness. Doesn't know: intentional or coincidence |
**The boundary between Tier 1 and Tier 2 is invisible to the player.** There is no visual signal that tells the player they're now seeing "investigation-relevant" behavior.
**Why this works:** "The clear/ambiguous division isn't an art decision — it's the investigation mechanic expressed through animation" (Ozzie). Routine is readable because routine is public. Deception looks like normal but slightly off (Gore). The 30-minute life-sim runway requires readable routine so the player can notice when it breaks.
**Consensus:** Unanimous (Round 2).
### 1.13 Weather as Gameplay System
Weather is a perception modifier, NPC routine disruptor, and storyteller instrument. Weather is diegetic — the one thing that CAN shift the environment, because it's physical reality.
- **Planet:** Velen (Krenn System). Temperate-maritime, ~0.9G, regular rain, morning/evening fog.
- Fog degrades everyone's vision cones equally — shared vulnerability (Gore: I-07)
- The storyteller can TIME weather for dramatic effect without breaking environmental neutrality
- Foggy mornings = shorter vision cones for everyone = smuggler's early drops safer, detective's dawn surveillance harder
**Consensus:** Unanimous (Rounds 1-3). Weather as gameplay, not cosmetic, held from Round 1.
### 1.14 Z-Level Rendering Stack
**8-layer stack (Araminta's proposal, endorsed by all):**
| Layer | Content | Role |
|---|---|---|
| 0 — Floor tiles | Composite, tile, grating, guide strips | Zone identity, movement surface |
| 1 — Floor objects | Cable runs, drain grates, floor markings, spills | Cosmetic detail, walked over |
| 2 — Furniture / placed objects | Desks, chairs, tables, crates, consoles | Y-sorted with entities. Visual complexity center. |
| 3 — Entity sprites | NPCs, player character | Y-sorted with Layer 2. D-033 colored. |
| 4 — Overhead / wall tops | Pipes, ducts, lighting fixtures, signage | Atmosphere, partial occlusion (semi-transparent) |
| 5 — Fog of perception | Vision cone mask, fog shader | Information boundary — affects layers 0-4 |
| 6 — Insert overlay | Lattice HUD elements, annotations, waypoints | Bloom-rendered, NOT affected by fog |
| 7 — Monologue / UI | Internal monologue, perception labels, UI | Top of stack, always visible |
**Wall rendering:**
- Option B (visible top surface + face) for structural walls — exterior hull, main compartments
- Option A (boundary lines) for interior partitions — dividers, booth separators
- At Rimworld's tilt, wall faces are visible naturally — no rendering cheat needed
**Note (Araminta):** Wall rendering distinction "confirmed in principle, implementation detail pending Tyre/Stig" since the shallow tilt naturally shows wall faces. The Option A/B distinction may collapse to thickness/color rather than rendering method.
**Sprite stacking:** OUT of scope. Not needed for v0.1. Station's visual character comes from floor variation, object variety, and lighting — not vertical dimension.
**Overhead occlusion (Gore):** Thematically significant. Entities passing behind shelving/under overhangs creates local information gaps within otherwise known spaces. Partial visibility is information-rich: you know someone is behind the shelf but can't see what their hands are doing.
**Consensus:** Unanimous (Round 3).
### 1.15 Planet Name: Velen
**Velen** is canonical as the primary habitable world of the Krenn System.
- Temperate-maritime, ~0.9G
- Regular rain, morning/evening fog
- Mild temperature range, occasional heavy squalls
- Station Sova orbits Velen
- The span gate connects Sova to a planetary freight depot on Velen's surface
- Naming convention: compact, consonant-weighted, two-syllable, Nordic-influenced
**Consensus:** Proposed by Miri (Round 2), confirmed by lead (Round 3), endorsed unanimously.
---
## 2. Complete Art Direction Principles
Confirmed principles, accumulated across all three rounds. All have unanimous consensus.
1. **Readability over beauty.** Always. When readability and atmosphere conflict, readability wins. The game is ABOUT information. (Round 1 C-01)
2. **Lighting over detail.** Sprites and tiles provide shape; Light2D provides mood. (Round 1 C-03)
3. **Restraint IS the aesthetic.** "Functional warmth." Not flashy, not grim, not neon, not grimy. (Round 1, named Round 2)
4. **Environment never tips its hand.** Strict zero shift. Investigation signals are behavioral and informational only. (Round 1 C-05, made unanimous Round 2)
5. **Silhouette carries identity.** Color is spoken for by D-033. Shape differentiates. (Round 1 I-01)
6. **One asset vocabulary, many lighting profiles.** Muted tiles + solar color tinting = every system looks different with the same art. (Round 1 I-06)
7. **Weather is gameplay.** Perception modifier, NPC routine disruptor, storyteller tool. (Round 1 C-06)
8. **Production constraints = design strengths.** Bold silhouettes + limited palette + tile objects + lighting atmosphere serves readability, D-033, Godot 4, AND Nano Banana simultaneously. (Round 1 I-03)
9. **Settling is placement.** Object density IS environmental storytelling. (Round 2, Gore)
10. **Investigation is archaeology of intention.** Every placed tile is someone's decision. Reading the tile world is reading decisions. (Round 2, Gore)
11. **Two-tier animation.** Clear for public routine, ambiguous for private intention. The boundary is invisible. (Round 2)
12. **Three-reference lighting.** Darkwood (cone mechanics) + BR2049 (color temperature) + Hopper (emotional composition). Uncertainty, not dread. (Round 2)
13. **Insert = geometric precision, organic perception.** Bloom-softened data layer. Smuggler's sparse, detective's dense. (Round 2)
14. **Sprites are shape templates that the lighting system completes.** No baked shadows, no baked lighting, no baked mood. (Round 3 closing, Araminta)
15. **Entity always wins visual ties.** If an entity and an object overlap, the entity's D-033 color must remain visible. Readability guarantee. (Round 3, Araminta)
16. **The gap between entity simplicity and environment detail IS the game.** Simple entities in a richly textured station. Neither fully legible alone. (Round 1 I-09, Gore)
---
## 3. v0.1.1 Plan — Minimum Sprite Set
### 3.1 Purpose
The smallest asset set that proves the art direction works in-engine. Every sprite replaces a colored rectangle from v0.1. The test answers one question: **"Does moving through this space FEEL like the game we're designing?"** (Ozzie)
### 3.2 Test Scene
**The Last Shift bar + one corridor + one doorway between them.**
This scene proves:
1. Zone temperature difference — warm bar tiles + cool corridor tiles = visible mood shift at boundary
2. Object placement as storytelling — bar counter, tables, chairs, crate = room with character
3. Entity silhouette differentiation — three entities with different silhouettes, all D-033 tinted
4. Y-sort occlusion — entity walks behind bar counter, counter renders in front
5. Light2D atmosphere — warm PointLight2D in bar, cool PointLight2D in corridor, dynamic shadows
6. Fog of perception — vision cone applied, corridor beyond doorway fades to fog
7. Wall type distinction — structural (thick) around room, partitions (thin) subdividing corners
### 3.3 Sprite Set
**Floor tiles (4 sprites):**
| Tile | Visual | Hex base |
|---|---|---|
| `floor_institutional` | Cool grey-blue, subtle grid lines | `#2e2e48` |
| `floor_bar` | Warm tan/beige, wood-panel hint | `#3a3028` |
| `floor_corridor` | Neutral grey, minimal detail | `#252535` |
| `floor_transition` | Split tone — half matches each adjacent zone | — |
**Wall tiles (3 sprites):**
| Tile | Visual |
|---|---|
| `wall_structural` | Dark grey-blue, thick, visible face (Option B) |
| `wall_partition` | Lighter grey, thin (Option A) |
| `wall_door` | Wall tile with gap + separate door sprite |
**Object tiles (8-10 sprites):**
| Object | Size | Visual |
|---|---|---|
| `desk_terminal` | 2x1 | Desk + integrated terminal glow, institutional grey-blue |
| `chair_office` | 1x1 | Swivel chair, dark grey (Era 1) |
| `chair_bar` | 1x1 | Mismatched stool/chair, warmer tone (Era 3) |
| `table_round` | 1x1 | Small round table, warm wood |
| `crate_cargo` | 1x1 | Standard cargo container, olive/green |
| `bar_counter` | 3x1 | Bar counter, warm wood, distinct from tables |
| `light_ceiling` | 1x1 | Overhead light fixture (warm/cool variant) |
| `door_sliding` | 1x1 | Sliding door, open/closed states (2 frames) |
| `coffee_machine` | 1x1 | Human comfort detail (Miri) |
| `junction_box` | 1x1 | Wall-mounted Era 2 retrofit (Miri) |
**Entity sprites (3 sprites × frames):**
| Entity | Purpose | Identifying feature |
|---|---|---|
| `entity_player` | Player character | 24x32, civilian work clothes, neutral silhouette |
| `entity_npc_a` | Named NPC (Kael template) | Stocky, vest |
| `entity_npc_b` | Named NPC (Lera template) | Medium, apron |
**Frame count (Araminta):** 3 entities × (1 idle + 8 walk [4 dir × 2, or 2 dir mirrored × 4] + 1 interaction) = ~30 frames minimum.
**Total unique sprites:** ~18-25 tile/object sprites + ~30 entity frames = **~48-55 assets**.
### 3.4 v0.1.1 Success Criteria
Each participant identified the single most important thing for v0.1.1:
| Agent | Priority |
|---|---|
| **Araminta** | Light2D warm/cool zone contrast working WITH fog-of-perception cone in a single scene |
| **Ozzie** | Temperature shift walking from corridor into bar must make you FEEL something |
| **Miri** | Floor tile transition between two zones must read instantly — zone identity from floor alone |
| **Gore** | Warm/cool doorway transition — if crossing a threshold changes how the room FEELS through lighting alone, the art direction is proven |
**Common thread:** The warm-to-cool zone transition through the doorway is the singular test. If walking from the cool corridor into the warm bar produces an emotional shift through lighting and floor tiles alone, the art direction works.
### 3.5 Nano Banana Generation Workflow
1. Write a Settled Reach style guide prompt prefix (core constraints: "Rimworld-angle shallow overhead, clean 2D illustration, bold silhouette, muted palette, 1-2px outline, flat lighting, [zone palette] color range")
2. Generate floor/wall tiles first — establish palette baseline, 3-4 variants per type
3. Generate objects second — using floor tiles as visual context in prompt
4. Generate entity sprites last — most iteration needed, start with idle, get silhouette right
5. Post-processing pass: normalize to exact dimensions, ensure consistent outline weight, remove any baked shadows AI added, verify readability at 1x zoom
Expect 3-5 generation attempts per final sprite. (Araminta)
---
## 4. Key Insights
Ideas that emerged during the workshop and informed the converged direction. Attribution preserved.
| ID | Insight | Source |
|---|---|---|
| I-01 | "Silhouette IS identity because color is spoken for." | Ozzie, Round 1 |
| I-02 | Shadows as information tier: close = see directly, medium = see shadows/hear sounds, long = lattice data only. | Miri, Round 1 |
| I-03 | Production constraints converge with design goals — all four constraints point to the same style. | All, Round 1 |
| I-04 | "The absence of change as horror." The station looks identical after discovering the conspiracy. | Gore, Round 1 |
| I-05 | "Visual restraint is thematic confidence." Papers, Please looks like a spreadsheet and delivers devastating moral weight. | Gore, Round 1 |
| I-06 | "One material vocabulary, many lighting profiles." Grey metal turns golden under a K-type star. | Miri, Round 1 |
| I-07 | "Weather creates shared vulnerability." Everyone is equally blind in a dust storm. | Gore, Round 1 |
| I-08 | "The observed life is richer than the observer's life." The detective's visual world should feel impoverished compared to the smuggler's. | Gore, Round 1 |
| I-09 | "The gap between entity simplicity and environment detail IS the game." Neither fully legible alone. | Gore, Round 1 |
| I-10 | "Fog is uncertainty made visible." Fog hides things in plain sight — ambiguity, not darkness. | Gore, Round 1 |
| I-11 | "Legible but ambiguous animation." Clear enough to see a figure stopped, not detailed enough to know why. | Gore, Round 1 |
---
## 5. Mood Board Summary
9 concept images generated during the workshop. Key images for reference:
| # | Subject | Workshop role | Key takeaway |
|---|---|---|---|
| 1 | Bar (The Last Shift) | Warmth register | Amber warmth, social atmosphere. Slightly too grimy. |
| 2 | Logistics hub | Zone differentiation | Warm/cool contrast between zones. Slightly too cold. |
| 3 | Maintenance corridors | Mixed-era materials | Proves layered construction. Too decayed — station should be maintained. |
| 4 | Rain exterior | Weather as atmosphere | Strongest mood match. Rain as routine, not spectacle. |
| 5 | Vision cone / fog | Camera scale + perspective | Scale and perspective are right. Character too military, fog too hostile, cone too literal. |
| 6 | Span gate | Emotional register | Mundane-extraordinary register. The everyday mundanity of interstellar transit. |
| 7 | Convergence (top-down) | First synthesis | Closer: civilian character, fog as desaturation, warm/cool zones. Needs cone not circle, adult proportions. |
| 8 | Tile composition | Tile system proof | Most production-relevant. Proves tile-based composition carries warmth and personality. Zone transition through floor tiles works. |
| 9 | Bar antagonists | Emotional core | Strongest emotional hit. Populated bar, surveillance composition (cold corridor, warm bar, observer outside). Accidentally demonstrates graduated fog (detailed center → silhouette periphery). Slightly too tilted for Rimworld angle — but emotional register is the target. |
**Convergence formula (lead):** #5's perspective/scale + #1/#4 warmth + #1-vs-#2 zone differentiation + #6 register + civilian characters + fog as absence.
**After camera angle amendment:** The sweet spot is between #7 (too flat) and #8 (approximately right). #9 is slightly too tilted but captures the emotional register.
---
## 6. Reference Canon
### Tier 1 — Universal (all four agents cite)
| Reference | Endorsed aspect |
|---|---|
| **Rimworld** | Readability principles, visual hierarchy, camera angle (now also direct camera reference) |
| **XCOM 2** | Fog of war as emotional weight |
| **The Sims 3/4** | Daily routine legibility from overhead |
### Tier 2 — Strong (3 agents, strong endorsement)
| Reference | Endorsed aspect |
|---|---|
| **Tactical Breach Wizards** | Silhouette discipline, clarity-as-design-origin |
| **Disco Elysium** | Internal voice recontextualizing visual information (principles, not production method) |
### Tier 3 — Targeted (2 agents, specific aspect)
Darkwood, Blade Runner 2049, Hopper's *Nighthawks*, Heat Signature, Citizen Sleeper, The Expanse, Alien/Alien: Isolation, Return of the Obra Dinn, Syd Mead
### Avoid List
Cyberpunk neon / Blade Runner rain-noir, Star Trek antiseptic, Star Wars romantic, Mass Effect Citadel gleam, Dead Space grimy horror, pixel art, Iron Man HUD, Hotline Miami neon maximalism, military tactical palette
---
## 7. Candidate Decisions for Formal Recording
The following workshop outputs are ready for formal recording in `decisions/` domain files. All have unanimous consensus. Proposed numbering starts at D-042 (D-041 is the current highest).
### Candidate D-042: Art Direction — Visual Style
- **Decision:** Clean 2D with bold silhouettes, tile-based world composition (64x64 grid), lighting-driven atmosphere. "Functional warmth." Not pixel art, not painted, not 3D. Godot 4 Light2D pipeline. Sprites are shape templates that the lighting system completes — no baked shadows, no baked lighting, no baked mood.
- **Rationale:** Four independent agents converged on the same style. Production constraints (Nano Banana), engine capabilities (Godot 4 Light2D), design requirements (D-033 readability), and thematic goals (restraint as confidence) all point to the same solution.
- **Domain:** perception.md (cross-ref: architecture.md for Godot pipeline)
- **Raised by:** Araminta (lead), endorsed unanimously
### Candidate D-043: Art Direction — Visual Hierarchy
- **Decision:** Three-layer visual hierarchy: entities (2px outline, D-033 color) > objects (1px outline, era palette) > structure (minimal outline, muted zone palette). Entity always wins visual ties — if an entity and object overlap, entity D-033 color must remain visible.
- **Rationale:** Readability guarantee. The player must parse the game state at a glance with entities as the primary information layer.
- **Domain:** perception.md
- **Raised by:** Araminta (outline spec), Ozzie (readability priority)
### Candidate D-044: Art Direction — Environmental Neutrality
- **Decision:** Strict zero visual shift on the base world layer correlated with narrative/conspiracy state. The rendering pipeline never modifies world-layer visuals in response to narrative state. Zone lighting is fixed. Weather is the storyteller's instrument (diegetic). Insert overlay is character cognition. These three are different systems with different rules. Character-driven changes allowed: insert overlay density, D-033 entity colors, monologue, player sprite posture.
- **Rationale:** If the station itself helps the player detect conspiracy, it undermines the core information-asymmetry loop. The rendering engine has no narrative opinions. The absence of change IS the horror.
- **Domain:** perception.md (cross-ref: content.md for narrative state)
- **Raised by:** Gore and Miri (strict zero position), Araminta and Ozzie moved to this position in Round 2
### Candidate D-045: Art Direction — Lighting System
- **Decision:** Three-reference lighting model. Darkwood: vision cone mechanics, light pooling, graduated fog boundary. BR2049/Deakins: color temperature as emotional language (warm amber = social, cool white = institutional). Hopper: warm interior surrounded by unknowable dark. Emotional register: uncertainty, not dread. Character temperature via spatial paths — smuggler in warm zones, detective in cool zones — same fixtures, different daily routes.
- **Rationale:** Lighting is the primary atmosphere engine. The three references provide mechanics (Darkwood), color language (BR2049), and emotional composition (Hopper). Character temperature is diegetic, not rendered.
- **Domain:** perception.md
- **Raised by:** Ozzie (Darkwood advocacy), Araminta (BR2049 color), Gore (Hopper), Miri (character spatial paths)
### Candidate D-046: Art Direction — Animation System
- **Decision:** Two-tier animation. Tier 1 (clear): public daily activities — walking, working, eating, talking, sleeping. Instantly readable. Tier 2 (ambiguous): privately motivated behaviors — pausing, looking around, lingering, changing direction, unexplained proximity. The player sees the action but cannot determine the intention. The boundary between tiers is invisible.
- **Rationale:** The clear/ambiguous division is the investigation mechanic expressed through animation. Routine must be readable so deviations are noticeable. Maps to NPC tell system (D-024).
- **Domain:** perception.md (cross-ref: content.md D-024)
- **Raised by:** Ozzie and Gore (two-tier proposal), Araminta (production spec), Miri (setting grounding)
### Candidate D-047: Neural Insert Overlay — Visual Design
- **Decision:** Geometric data layer (precise positioning, clean lines) rendered with soft bloom shader pass (~2-3px gaussian, ~40% blend). Smuggler's overlay: thinner, sparser (baseline lattice). Detective's overlay: denser, crisper (augmented lattice). Insert CanvasLayer is not affected by fog shader. Passive state nearly invisible; active state clean and precise.
- **Rationale:** Data is computational (geometric), delivery is neural (organic). Bloom synthesis resolves the geometric-vs-organic divergence from Round 1 — precision underneath, perceived as organic.
- **Domain:** perception.md
- **Raised by:** Araminta (geometric spec), Gore (organic requirement), Miri (per-character density)
### Candidate D-048: Z-Level Rendering Stack
- **Decision:** 8-layer z-stack: Floor → Floor objects → Furniture (y-sorted with entities) → Entities (y-sorted with furniture) → Overhead (semi-transparent) → Fog of perception → Insert overlay → UI. Walls: Option B (visible face) for structural, Option A (boundary) for partitions — implementation detail pending shallow-tilt evaluation. Sprite stacking out of scope.
- **Rationale:** Creates two player-facing feelings: "I can see the layout" (baseline) and "things can block my view" (gameplay feature). Every occluding object is an information barrier.
- **Domain:** architecture.md (rendering) or perception.md (visual hierarchy)
- **Raised by:** Araminta (8-layer spec), Ozzie (player experience rules), Gore (overhead as thematic tool)
### D-019 Amendment: Camera Angle — "The Angle"
- **Amendment:** D-019 currently states "Top-down confirmed." Amend to: "~15-20 degrees from vertical, rendered in sprite art, orthographic camera. Sprites are drawn as if viewed from a shallow tilt (south-facing front faces visible on objects, entities, and walls), but the Godot camera is purely orthographic. The perspective is an art convention, not a camera setting. Tile grid remains square/orthogonal. Vision cone math remains pure 2D. This is referred to internally as 'the angle.'"
- **Rationale:** Matches Rimworld's approach exactly: orthographic camera, perspective faked in the art. Strictly better than pure top-down for entity readability, wall vocabulary, and object identification. Zero mechanical cost — the tilt exists only in how sprites are drawn. Production cost minimal.
- **Raised by:** Team Leader (Jeroen), endorsed unanimously, locked Round 3
### Candidate D-049: Velen — Krenn System Primary World
- **Decision:** Velen is the canonical name for the Krenn System's primary habitable world. Temperate-maritime climate, ~0.9G, regular rain, morning/evening fog. Station Sova orbits Velen. Span gate connects Sova to a planetary freight depot on Velen's surface.
- **Rationale:** Grounds the weather system and gives the setting a first named planet.
- **Domain:** content.md (setting)
- **Raised by:** Miri, confirmed by project lead
### Candidate D-050: "Settling Is Placement" — Design Principle
- **Decision:** Object density in a space correlates with how settled it is. Investigation reads placement as intention. The bar is full because someone made it home. Empty corridors are unsettled. This is a design principle, not a production note — the tile-based world is thematically load-bearing.
- **Rationale:** Grounds the base-building system in the game's thematic core. Every placed object is a decision; reading the world is reading decisions.
- **Domain:** scope.md (design principles)
- **Raised by:** Gore, endorsed unanimously
### Candidate D-051: Character Favorite Colors — Object-Layer Identification
- **Decision:** Each NPC has a favorite color expressed through personal objects (bedding, cushions, mugs, personal items), NOT on entity sprites. Muted register: dusty blue, warm terracotta, faded olive — personal, not faction. Creates a secondary identification system: D-033 = relationship to player (entity layer), favorite color = person's identity in space (object layer). Investigation mechanic: recognizing whose stuff is where ("that's Kael's blue cushion in the cargo bay — what's his stuff doing here?").
- **Constraint:** Favorite color saturation must stay below D-033 entity color saturation to preserve visual hierarchy (entities > objects > structure). These are muted personal tones, not vivid signals.
- **Rationale:** Reinforces "settling is placement" (D-050). Both characters settle, both decorate — the detective is a person too. Personal objects in favorite colors make spaces readable as *someone's* even when they're absent. Complements D-033 on a separate information channel without conflicting.
- **Production note:** Godot 4 supports per-instance shader recoloring (color uniform on base sprite). One base object sprite + color uniform = all favorite color variants without duplicating sprites. Same technique applies to D-033 entity variants. Significant sprite count reduction. (Flagged for Tyre feasibility assessment.)
- **Domain:** perception.md (cross-ref: scope.md D-050)
- **Raised by:** Team Leader (Jeroen), endorsed unanimously (post-closing addendum)
---
## 8. Follow-Up Items
| Item | Status | Owner | Notes |
|---|---|---|---|
| Span gate visual language — IP check | Open | Araminta | Confirm span gate visuals don't drift toward Stargate. Miri flagged as follow-up, not a workshop gap. |
| Wall rendering implementation detail | Open | Tyre, Stig | Option A/B distinction may collapse under shallow tilt. Confirmed in principle, implementation pending. |
| Nano Banana style guide | Open | Araminta | Specs now exist (outline weights, palette per era, tile dimensions). Needs formal document. |
| Shadow direction convention | Open | Araminta | Needs revisiting for shallow tilt. Consistent direction derived from implied light source at tilt angle. |
| Overhead element projection rule | Open | Araminta | With tilt, overhead elements (pipes, ducts) project slightly forward visually. Need consistent rule for how far. |
| Cone geometry at tilt | Open | Tyre | Confirm vision cone projection is manageable in Godot at Rimworld's angle. Near-orthographic should be fine. |
| Player character stress sprite | Minor | Araminta | Proposed: character sprite shifts subtly under stress. Not contested, not formally endorsed. |
| In-engine tilt validation | Open | Tyre, Stig | #9's angle needs validation in Godot with actual tiles and Light2D before final lock. |
| Shader recoloring feasibility | **Done** | Tyre | D-033: `Sprite2D.modulate` (free, zero code). Favorite colors: mask shader + material duplication (~0.5-1ms at 100 objects). `instance_uniform` broken in 2D (GH #62943), worked around by material duplication. Both compose with y-sort, Light2D, fog. Build D-033 for v0.1.1, favorite colors for v0.1.2+. |
| Velen orbit logistics | Open | Miri | If Sova orbits Velen, freight access to surface needs intra-system wormhole or shuttle service. Flag for next worldbuilding session. |
| 3D render pipeline for 2D sprites | **Resolved** | Tyre, Araminta, Stig | See §10. 3D used only as offline render pipeline — runtime is pure 2D. SubViewport + Camera3D at "the angle" + DirectionalLight3D (neutral). Nano Banana textures UV-mapped onto 3D models, rendered from 4 cardinal directions. |
| Resolution chain | **Resolved** | Tyre, Araminta | 1024x1024 source (archive master) → 256x256 (working, outlines applied at 4-8px) → 64x64 (runtime). Bilinear interpolation both passes. |
| Pipeline test: Era 1 wall texture | **In progress** | Stig, Araminta | Nano Banana texture generated, QA'd (color PASS, flat lighting PASS, tileability PARTIAL — top/bottom seam, acceptable because tiling happens at 3D geometry level). Godot render scene built at `client/tooling/sprite_renderer/`. Render skill created at `.claude/skills/render-sprite/`. Blocked on Godot texture import — needs editor opened once to import the PNG. |
| Camera angle: exact value | **Locked** | Lead | "The angle" = ~15-20° from vertical. Rimworld uses orthographic camera with tilt entirely in sprite art. Research confirms no camera projection — the 3D render pipeline Camera3D rotation of -72.5° (from horizontal) produces this. Midpoint of range, tunable by Araminta. |
---
## 9. Post-Workshop Technical Decisions
### 9.1 3D Render Pipeline for 2D Sprites
**Decision:** Use a 3D render setup in Godot as an **offline pipeline** for producing 2D sprites. Runtime rendering remains pure 2D (Light2D, CanvasLayer, TileMap).
**The middle ground (Lead proposal):** Instead of either (a) taming Nano Banana prompts to produce perspective-correct sprites, or (b) switching the game runtime to 3D, set up a dedicated 3D render scene that:
1. Takes a Nano Banana-generated **flat texture** (no perspective, no baked lighting)
2. UV-maps it onto a simple **3D model** (BoxMesh for walls, CSGBox/CylinderMesh for objects)
3. Renders from an orthographic camera at **"the angle"** (-72.5° from horizontal)
4. Exports 2D sprites from **4 cardinal directions** (north, east, south, west)
**Why this works:**
- Textures are easy to generate flat — Nano Banana excels at this
- 3D geometry provides mathematically correct perspective at "the angle"
- Camera rotation is a number, not an art convention to enforce per-prompt
- Every new object follows the same pipeline: model → texture → render → sprite
- Runtime stays pure 2D — no 3D engine cost, Light2D pipeline unchanged
**Production cost:** ~half day to build the pipeline (done), then minutes per new object type.
**Tyre's assessment:** At 15-20° tilt, the 3D case collapses for runtime (front face is only 17-22px, perspective distortion negligible). But for PRODUCTION, 3D geometry guarantees consistency across all objects. Best of both worlds.
### 9.2 Resolution Chain
| Stage | Resolution | Purpose |
|---|---|---|
| Source render | 1024x1024 | Archive master. Maximum detail from SubViewport. |
| Working | 256x256 | Outlines applied here (4-8px → 1-2px at runtime). Asset QA resolution. |
| Runtime | 64x64 | Final in-game sprite. Must read through silhouette and color, not texture detail. |
- **Interpolation:** Bilinear for both downscale passes
- **Outline application:** At 256 working resolution. 4px outline at 256 = 1px at 64 runtime. Color: dark blue-grey `#333340`.
- **Why not outline at 1024?** An 8px outline at 1024 downscales to sub-pixel artifacts at 64. Applying at working resolution gives crisp, consistent weight at runtime.
### 9.3 Camera Angle Research
**Research result:** Rimworld uses an orthographic camera with the "tilt" entirely in how sprites are drawn. The camera itself looks straight down. The ~15-20° forward lean exists only in the art — objects are drawn as if viewed from slightly in front and above.
**For our pipeline:** The 3D render scene's Camera3D is set to orthographic projection with X rotation of -72.5° (90° - 17.5°, midpoint of the 15-20° range). This produces the exact same visual result as Rimworld's sprite art convention, but mathematically correct and consistent.
**Locked value:** -72.5° (tunable by Araminta). Named internally as "the angle."
### 9.4 Shader Recoloring Feasibility (Tyre Assessment)
**D-033 entity colors:** `Sprite2D.modulate` — free, zero shader code, works with y-sort, Light2D, fog, everything. Use for v0.1.1.
**Favorite color object recoloring:** Mask shader + material duplication per unique color.
```glsl
shader_type canvas_item;
uniform vec4 accent_color : source_color = vec4(0.5, 0.5, 0.5, 1.0);
uniform sampler2D mask_tex : hint_default_white;
void fragment() {
vec4 sprite = texture(TEXTURE, UV);
float mask = texture(mask_tex, UV).r;
vec3 tinted = mix(sprite.rgb, accent_color.rgb * sprite.rgb, mask);
COLOR = vec4(tinted, sprite.a) * COLOR;
}
```
**`instance_uniform` broken in Godot 4 2D** (GH #62943). Workaround: material duplication. At 50-100 objects, ~0.5-1ms overhead — negligible. Build for v0.1.2+.
### 9.5 Pipeline Test Status
**Era 1 institutional wall texture** generated via Nano Banana. QA results:
| Criterion | Result |
|---|---|
| Color range (#6e7580#7a7f85) | **PASS** — Mean #727980 |
| Flat lighting (no baked shadows) | **PASS** — Quadrant spread 3.1 |
| Neutral for Light2D tinting | **PASS** — Low saturation, low std dev |
| Tileability (3x3 no seams) | **PARTIAL** — Left/right seamless, top/bottom seam visible |
| 64x64 reads as "wall" | **PASS** — Color preserved, reads as surface |
**Render pipeline tooling** built by Stig at `client/tooling/sprite_renderer/`. CLI skill at `.claude/skills/render-sprite/`. First test run blocked on Godot texture import (needs editor opened once to import the PNG into `.godot/imported/`).
**Next step:** Open Godot editor to trigger import, then run render pipeline.
---
## 10. Appendix: Round-by-Round Resolution Path (formerly §9)
### Round 1
- Established fundamentals: readability first, clean 2D, bold silhouettes, lighting-driven
- 12 consensus points, 4 divergence points, 11 key insights
- 7 open questions identified
- Sources: `round1-araminta.md`, `round1-ozzie.md`, `round1-miri.md`, `round1-gore.md`
- Tracking: `round1-tracking.md`
### Round 2
- New input: tile-based base building confirmed
- All 5 open questions resolved unanimously
- Araminta and Ozzie moved to strict zero environmental shift (OQ-02)
- 13 confirmed art direction principles
- Planet name Velen proposed
- Sources: `round2-araminta.md`, `round2-ozzie.md`, `round2-miri.md`, `round2-gore.md`
- Tracking: `round2-tracking.md`
### Round 3
- Lead decisions confirmed: cone shape, Velen, overlay-not-world temperature, environmental neutrality
- Z-level rendering stack defined (8 layers)
- v0.1.1 sprite set scoped (~18-35 unique sprites)
- Camera angle shift to Rimworld shallow tilt — unanimously endorsed
- Image #8 and #9 reviewed (tile composition proof, emotional core)
- Sources: `round3-araminta.md`, `round3-ozzie.md`, `round3-miri.md`, `round3-gore.md`
### Closing
- All four confirm workshop captures their domain's concerns
- v0.1.1 priorities converge on warm/cool doorway transition
- Decision recording flags provided
- Visual identity statements provided
- Sources: `closing-araminta.md`, `closing-ozzie.md`, `closing-miri.md`, `closing-gore.md`
---
*Workshop complete. All contested items resolved through consensus across three rounds. The art direction is now specific enough for a style guide, a Nano Banana prompt template, and in-engine implementation. The strongest signal across the entire workshop: when readability, design, engine, pipeline, setting, and theme all point to the same style, that style is correct.*
*All files referenced above are in `docs/workshops/art-direction-mood-board/`.*
*Compiled by Qatux. Every claim is cited to its source document.*
+15
View File
@@ -0,0 +1,15 @@
[gd_scene load_steps=3 format=3]
[ext_resource type="Texture2D" path="res://textures/wall_bar_green_panels.png" id="1_wall_texture"]
[sub_resource type="BoxMesh" id="BoxMesh_wall"]
size = Vector3(1.0, 0.8, 0.2)
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_wall"]
albedo_texture = ExtResource("1_wall_texture")
metallic = 0.0
roughness = 0.9
[node name="WallBarGreen" type="MeshInstance3D"]
mesh = SubResource("BoxMesh_wall")
surface_material_override/0 = SubResource("StandardMaterial3D_wall")
+15
View File
@@ -0,0 +1,15 @@
[gd_scene load_steps=3 format=3 uid="uid://cyh0xf1nv2j3e"]
[ext_resource type="Texture2D" path="res://textures/wall_institutional_era1.png" id="1_wall_texture"]
[sub_resource type="BoxMesh" id="BoxMesh_wall"]
size = Vector3(1.0, 0.8, 0.2)
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_wall"]
albedo_texture = ExtResource("1_wall_texture")
metallic = 0.0
roughness = 0.9
[node name="WallStructural" type="MeshInstance3D"]
mesh = SubResource("BoxMesh_wall")
surface_material_override/0 = SubResource("StandardMaterial3D_wall")
View File
+14
View File
@@ -0,0 +1,14 @@
; Sprite Renderer — standalone Godot project for 3D-to-2D sprite pipeline.
; No game logic, no autoloads, no plugins. Just rendering.
config_version=5
[application]
config/name="Settled Reach Sprite Renderer"
config/features=PackedStringArray("4.6", "GL Compatibility")
[rendering]
renderer/rendering_method="gl_compatibility"
renderer/rendering_method.mobile="gl_compatibility"
+151
View File
@@ -0,0 +1,151 @@
@tool
extends Node3D
const RENDER_SIZE := 1024
const WORKING_SIZE := 256
const RUNTIME_SIZE := 64
const DIRECTIONS: PackedStringArray = ["north", "east", "south", "west"]
const ROTATIONS: PackedFloat64Array = [0.0, 90.0, 180.0, 270.0]
@export var model_scene: PackedScene
@export var output_name: String = "wall_structural"
@export var outline_width_px: int = 4 # at 256 = 1px at 64
@export var outline_color: Color = Color(0.2, 0.2, 0.25, 1.0)
@export var render_now: bool = false:
set(value):
if value and model_scene:
_execute_render()
render_now = false
@onready var viewport: SubViewport = $SubViewport
@onready var model_root: Node3D = $SubViewport/ModelRoot
func _ready() -> void:
if Engine.is_editor_hint():
return
# CLI mode: godot --path client/ res://render_scene.tscn -- wall_structural
var args := OS.get_cmdline_user_args()
if args.size() == 0:
return
var model_name := args[0]
var model_path := "res://models/%s.tscn" % model_name
if not ResourceLoader.exists(model_path):
push_error("Model not found: %s" % model_path)
get_tree().quit(1)
return
model_scene = load(model_path)
output_name = model_name
# Wait for viewport to initialize
await get_tree().process_frame
await get_tree().process_frame
await _execute_render()
get_tree().quit()
func _execute_render() -> void:
if not model_scene:
push_error("No model scene assigned")
return
print("Starting sprite render for: %s" % output_name)
# Clear any existing model
for child in model_root.get_children():
child.queue_free()
# Instantiate the model
var model_instance = model_scene.instantiate()
model_root.add_child(model_instance)
# Ensure output directory exists
var output_dir := "res://output"
if not DirAccess.dir_exists_absolute(output_dir):
DirAccess.make_dir_recursive_absolute(output_dir)
# Render each cardinal direction
for i in range(DIRECTIONS.size()):
var direction := DIRECTIONS[i]
var rotation := ROTATIONS[i]
print(" Rendering direction: %s (%.1f°)" % [direction, rotation])
# Rotate model root
model_root.rotation_degrees.y = rotation
# Force viewport to render
viewport.render_target_update_mode = SubViewport.UPDATE_ONCE
await RenderingServer.frame_post_draw
# Capture the viewport texture
var img := viewport.get_texture().get_image()
# Save 1024x1024 source
var path_1024 := "%s/%s_%s_1024.png" % [output_dir, output_name, direction]
img.save_png(path_1024)
print(" Saved: %s" % path_1024)
# Downscale to 256x256
var img_256 := Image.create_from_data(img.get_width(), img.get_height(), false, img.get_format(), img.get_data())
img_256.resize(WORKING_SIZE, WORKING_SIZE, Image.INTERPOLATE_BILINEAR)
# Apply outline at 256x256
var img_256_outlined := _apply_outline(img_256, outline_width_px, outline_color)
# Save 256x256 with outline
var path_256 := "%s/%s_%s_256.png" % [output_dir, output_name, direction]
img_256_outlined.save_png(path_256)
print(" Saved: %s" % path_256)
# Downscale to 64x64
var img_64 := Image.create_from_data(img_256_outlined.get_width(), img_256_outlined.get_height(), false, img_256_outlined.get_format(), img_256_outlined.get_data())
img_64.resize(RUNTIME_SIZE, RUNTIME_SIZE, Image.INTERPOLATE_BILINEAR)
# Save 64x64
var path_64 := "%s/%s_%s_64.png" % [output_dir, output_name, direction]
img_64.save_png(path_64)
print(" Saved: %s" % path_64)
print("Render complete! Generated %d sprites." % (DIRECTIONS.size() * 3))
func _apply_outline(img: Image, width: int, color: Color) -> Image:
"""Apply outline by dilating the alpha mask and drawing outline color."""
var result := Image.create(img.get_width(), img.get_height(), false, Image.FORMAT_RGBA8)
result.blit_rect(img, Rect2i(0, 0, img.get_width(), img.get_height()), Vector2i(0, 0))
var w := img.get_width()
var h := img.get_height()
# Create dilated alpha mask
var dilated_alpha := PackedByteArray()
dilated_alpha.resize(w * h)
for y in range(h):
for x in range(w):
var max_alpha := 0.0
# Check all pixels within outline_width radius
for dy in range(-width, width + 1):
for dx in range(-width, width + 1):
var check_x := x + dx
var check_y := y + dy
if check_x >= 0 and check_x < w and check_y >= 0 and check_y < h:
var pixel := img.get_pixel(check_x, check_y)
max_alpha = max(max_alpha, pixel.a)
dilated_alpha[y * w + x] = int(max_alpha * 255)
# Draw outline where dilated exceeds original alpha
for y in range(h):
for x in range(w):
var original_pixel := img.get_pixel(x, y)
var dilated_a := float(dilated_alpha[y * w + x]) / 255.0
if dilated_a > original_pixel.a:
# This pixel is in the outline region
var outline_pixel := Color(color.r, color.g, color.b, dilated_a)
result.set_pixel(x, y, outline_pixel)
return result
+27
View File
@@ -0,0 +1,27 @@
[gd_scene load_steps=2 format=3 uid="uid://cqvfv4uhbgq6h"]
[ext_resource type="Script" path="res://render_export.gd" id="1_render_script"]
[node name="RenderScene" type="Node3D"]
script = ExtResource("1_render_script")
[node name="SubViewport" type="SubViewport" parent="."]
transparent_bg = true
size = Vector2i(1024, 1024)
render_target_update_mode = 1
own_world_3d = true
[node name="Camera3D" type="Camera3D" parent="SubViewport"]
transform = Transform3D(1, 0, 0, 0, 0.309017, 0.951057, 0, -0.951057, 0.309017, 0, 3, 1)
projection = 1
size = 1.4
near = 0.01
far = 10.0
[node name="DirectionalLight3D" type="DirectionalLight3D" parent="SubViewport"]
transform = Transform3D(1, 0, 0, 0, 0.309017, 0.951057, 0, -0.951057, 0.309017, 0, 3, 1)
light_color = Color(1, 1, 1, 1)
light_energy = 1.0
shadow_enabled = false
[node name="ModelRoot" type="Node3D" parent="SubViewport"]
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 991 KiB

+11 -4
View File
@@ -145,18 +145,25 @@ impl Plugin for BridgePlugin {
fn build(&self, app: &mut App) {
app.init_resource::<SnapshotBuffer>()
.init_resource::<ServerRunning>()
.init_resource::<crate::perception::query::VisibilityGeometry>()
.init_resource::<crate::perception::query::ActivePerceptionMode>()
.add_systems(
Update,
(
receive_bridge_inputs.before(crate::simulation::input::process_player_input),
receive_bridge_inputs
.before(crate::simulation::input::process_player_input),
crate::perception::observer::compute_visibility_geometry
.after(crate::simulation::movement::validate_movement),
crate::simulation::interaction::compute_nearby_interactions
.after(crate::simulation::movement::validate_movement),
crate::perception::observer::compute_observer_snapshot
.after(crate::simulation::movement::validate_movement)
.after(crate::perception::observer::compute_visibility_geometry)
.after(crate::simulation::interaction::compute_nearby_interactions)
.before(crate::simulation::time::advance_tick),
crate::perception::observation::emit_observation_events
.after(crate::perception::observer::compute_observer_snapshot),
send_bridge_snapshot
.after(crate::perception::observer::compute_observer_snapshot)
.after(crate::simulation::interaction::compute_nearby_interactions),
.after(crate::perception::observer::compute_observer_snapshot),
),
);
tracing::debug!("BridgePlugin initialized");
+2
View File
@@ -14,6 +14,7 @@ use settled_reach_server::npc::{
Want, WantKind,
};
use settled_reach_server::perception::vision_cone::Facing;
use settled_reach_server::simulation::interaction::NearbyInteractionBuffer;
use settled_reach_server::simulation::movement::{PlayerCharacter, TilePosition, WalkabilityMap};
use settled_reach_server::simulation::path_follow::MovementSpeed;
use settled_reach_server::simulation::time::DayPhase;
@@ -68,6 +69,7 @@ fn main() {
TilePosition::new(16, 16, 0),
Facing::default(),
KnowledgeGraph::new(),
NearbyInteractionBuffer::default(),
))
.id();
registry.register(player);
+13 -6
View File
@@ -186,7 +186,8 @@ mod tests {
use super::*;
use crate::knowledge::registry::EntityRegistry;
use crate::npc::RoutineEntry;
use crate::perception::observer::compute_observer_snapshot;
use crate::perception::observer::{compute_observer_snapshot, compute_visibility_geometry};
use crate::perception::query::{ActivePerceptionMode, VisibilityGeometry};
use crate::perception::vision_cone::Facing;
use crate::simulation::movement::WalkabilityMap;
use crate::simulation::time::{DayPhase, MINUTES_PER_PHASE, TICKS_PER_GAME_MINUTE};
@@ -199,17 +200,18 @@ mod tests {
world.init_resource::<crate::knowledge::KnowledgeEventQueue>();
world.init_resource::<EntityRegistry>();
world.init_resource::<ObservationEventQueue>();
world.init_resource::<crate::simulation::interaction::NearbyInteractionBuffer>();
world.init_resource::<VisibilityGeometry>();
world.init_resource::<ActivePerceptionMode>();
world
}
/// Run the observation pipeline: snapshot -> emit -> interpret -> knowledge update.
/// Interpretation runs BEFORE knowledge updates so it can detect new entities
/// and compare against the PREVIOUS tick's knowledge state.
/// Run the observation pipeline: geometry -> snapshot -> emit -> interpret -> knowledge.
fn run_pipeline(world: &mut World) {
let mut schedule = bevy_ecs::schedule::Schedule::default();
schedule.add_systems((
compute_observer_snapshot,
compute_visibility_geometry,
compute_observer_snapshot
.after(compute_visibility_geometry),
crate::perception::observation::emit_observation_events
.after(compute_observer_snapshot),
generate_observation_events
@@ -235,6 +237,7 @@ mod tests {
TilePosition::new(16, 16, 0),
Facing(FacingDirection::North),
KnowledgeGraph::new(),
crate::simulation::interaction::NearbyInteractionBuffer::default(),
))
.id();
registry.register(player);
@@ -285,6 +288,7 @@ mod tests {
TilePosition::new(16, 16, 0),
Facing(FacingDirection::North),
KnowledgeGraph::new(),
crate::simulation::interaction::NearbyInteractionBuffer::default(),
))
.id();
registry.register(player);
@@ -355,6 +359,7 @@ mod tests {
TilePosition::new(16, 16, 0),
Facing(FacingDirection::North),
kg,
crate::simulation::interaction::NearbyInteractionBuffer::default(),
))
.id();
registry.register(player);
@@ -387,6 +392,7 @@ mod tests {
TilePosition::new(16, 16, 0),
Facing(FacingDirection::North),
KnowledgeGraph::new(), // Empty — never seen anyone
crate::simulation::interaction::NearbyInteractionBuffer::default(),
))
.id();
registry.register(player);
@@ -433,6 +439,7 @@ mod tests {
TilePosition::new(16, 16, 0),
Facing(FacingDirection::North),
kg,
crate::simulation::interaction::NearbyInteractionBuffer::default(),
))
.id();
registry.register(player);
+3
View File
@@ -8,6 +8,7 @@ use bevy_ecs::schedule::IntoScheduleConfigs;
pub mod interpretation;
pub mod observation;
pub mod observer;
pub mod query;
pub mod shadowcast;
pub mod vision_cone;
@@ -18,6 +19,8 @@ pub struct PerceptionPlugin;
impl Plugin for PerceptionPlugin {
fn build(&self, app: &mut App) {
app.init_resource::<interpretation::ObservationEventQueue>()
.init_resource::<query::VisibilityGeometry>()
.init_resource::<query::ActivePerceptionMode>()
.add_systems(
Update,
interpretation::generate_observation_events
+22 -5
View File
@@ -90,7 +90,8 @@ mod tests {
use super::*;
use crate::knowledge::{EntityRegistry, KnowledgeGraph};
use crate::npc::Npc;
use crate::perception::observer::compute_observer_snapshot;
use crate::perception::observer::{compute_observer_snapshot, compute_visibility_geometry};
use crate::perception::query::{ActivePerceptionMode, VisibilityGeometry};
use crate::perception::vision_cone::Facing;
use crate::simulation::movement::WalkabilityMap;
@@ -101,7 +102,8 @@ mod tests {
world.init_resource::<SnapshotBuffer>();
world.init_resource::<KnowledgeEventQueue>();
world.init_resource::<EntityRegistry>();
world.init_resource::<crate::simulation::interaction::NearbyInteractionBuffer>();
world.init_resource::<VisibilityGeometry>();
world.init_resource::<ActivePerceptionMode>();
world
}
@@ -116,6 +118,7 @@ mod tests {
TilePosition::new(16, 16, 0),
Facing(FacingDirection::North),
KnowledgeGraph::new(),
crate::simulation::interaction::NearbyInteractionBuffer::default(),
))
.id();
registry.register(player);
@@ -129,7 +132,11 @@ mod tests {
// First: compute snapshot so NPC is visible
let mut schedule = bevy_ecs::schedule::Schedule::default();
schedule.add_systems((compute_observer_snapshot, emit_observation_events).chain());
schedule.add_systems((
compute_visibility_geometry,
compute_observer_snapshot.after(compute_visibility_geometry),
emit_observation_events.after(compute_observer_snapshot),
));
schedule.run(&mut world);
let queue = world.resource::<KnowledgeEventQueue>();
@@ -159,6 +166,7 @@ mod tests {
TilePosition::new(16, 16, 0),
Facing(FacingDirection::North),
kg,
crate::simulation::interaction::NearbyInteractionBuffer::default(),
))
.id();
registry.register(player);
@@ -170,7 +178,11 @@ mod tests {
walkability.set_walkable(&TilePosition::new(16, 15, 0), false);
let mut schedule = bevy_ecs::schedule::Schedule::default();
schedule.add_systems((compute_observer_snapshot, emit_observation_events).chain());
schedule.add_systems((
compute_visibility_geometry,
compute_observer_snapshot.after(compute_visibility_geometry),
emit_observation_events.after(compute_observer_snapshot),
));
schedule.run(&mut world);
let queue = world.resource::<KnowledgeEventQueue>();
@@ -195,13 +207,18 @@ mod tests {
TilePosition::new(16, 16, 0),
Facing::default(),
KnowledgeGraph::new(),
crate::simulation::interaction::NearbyInteractionBuffer::default(),
))
.id();
registry.register(player);
world.insert_resource(registry);
let mut schedule = bevy_ecs::schedule::Schedule::default();
schedule.add_systems((compute_observer_snapshot, emit_observation_events).chain());
schedule.add_systems((
compute_visibility_geometry,
compute_observer_snapshot.after(compute_visibility_geometry),
emit_observation_events.after(compute_observer_snapshot),
));
schedule.run(&mut world);
let queue = world.resource::<KnowledgeEventQueue>();
-798
View File
@@ -1,798 +0,0 @@
//! Observer visibility query system (#112)
//!
//! Replaces the unfiltered `generate_snapshot` with a visibility-aware version.
//! Combines shadowcasting + vision cone to determine what the observer can see,
//! then populates ObserverSnapshot v2 with only visible entities and tiles.
use bevy_ecs::prelude::*;
use std::collections::HashSet;
use crate::bridge::types::*;
use crate::knowledge::{EntityRegistry, KnowledgeGraph};
use crate::perception::shadowcast::compute_fov;
use crate::perception::vision_cone::{apply_vision_cone, Facing, VisionConeConfig};
use crate::simulation::interaction::NearbyInteractionBuffer;
use crate::simulation::movement::{PlayerCharacter, TilePosition, WalkabilityMap};
use crate::simulation::time::SimulationTime;
/// Compute observer snapshot with LOS filtering and vision cone.
///
/// System ordering: after validate_movement, before advance_tick.
/// Replaces bridge::generate_snapshot.
pub fn compute_observer_snapshot(
time: Res<SimulationTime>,
walkability: Res<WalkabilityMap>,
registry: Res<EntityRegistry>,
mut interaction_buffer: ResMut<NearbyInteractionBuffer>,
observer_query: Query<(&TilePosition, Option<&Facing>, &KnowledgeGraph), With<PlayerCharacter>>,
all_entities: Query<(
Entity,
&TilePosition,
Option<&PlayerCharacter>,
Option<&crate::npc::Npc>,
)>,
mut buffer: ResMut<SnapshotBuffer>,
) {
let Ok((observer_pos, facing_opt, observer_kg)) = observer_query.single() else {
return;
};
let facing = facing_opt
.map(|f| f.0)
.unwrap_or(FacingDirection::default());
let config = VisionConeConfig::default();
let z = observer_pos.z;
// Step 1: Compute raw FOV using symmetric shadowcasting
let fov = compute_fov(
|x, y| !walkability.can_move_to(&TilePosition::new(x, y, z)),
observer_pos.x,
observer_pos.y,
config.forward_range,
z,
);
// Step 2: Apply vision cone to get sector-tagged tiles
let cone_tiles =
apply_vision_cone(&fov, observer_pos.x, observer_pos.y, facing, &config);
// Step 3: Build visible_tiles for the snapshot
let visible_tiles: Vec<VisibleTile> = cone_tiles
.iter()
.map(|&(x, y, sector)| VisibleTile {
x,
y,
z,
visibility: sector,
})
.collect();
// Step 4: Build lookup set for fast entity visibility check
let visible_positions: HashSet<(i32, i32)> =
cone_tiles.iter().map(|&(x, y, _)| (x, y)).collect();
// Build sector lookup (position -> sector)
let sector_lookup: std::collections::HashMap<(i32, i32), VisibilitySector> = cone_tiles
.iter()
.map(|&(x, y, sector)| ((x, y), sector))
.collect();
// Step 5: Filter entities by visibility, overlay knowledge
let mut entities = Vec::new();
let mut visible_entity_bits: HashSet<u64> = HashSet::new();
for (entity, pos, is_player, is_npc) in all_entities.iter() {
// Different z-level: not visible
if pos.z != z {
continue;
}
// Not in visible tile set: not visible
if !visible_positions.contains(&(pos.x, pos.y)) {
continue;
}
let (rx, ry, rz) = pos.to_render_coords();
let kind = if is_player.is_some() {
EntityKind::Player
} else if is_npc.is_some() {
EntityKind::Npc
} else {
EntityKind::Object
};
let sector = sector_lookup
.get(&(pos.x, pos.y))
.copied()
.unwrap_or(VisibilitySector::Peripheral);
// Look up relationship from knowledge graph (D-033 entity color)
let relationship = if is_player.is_some() {
RelationshipState::Known // Self
} else if let Some(stable_id) = registry.to_stable(entity) {
observer_kg.relationship_with(&stable_id)
} else {
RelationshipState::Unknown
};
let wire_id = registry
.to_stable(entity)
.map(|sid| sid.0)
.unwrap_or_else(|| {
tracing::error!(?entity, "entity visible but not in EntityRegistry");
entity.to_bits()
});
visible_entity_bits.insert(wire_id);
entities.push(VisibleEntity {
entity_id: wire_id,
x: rx,
y: ry,
z: rz,
kind,
visibility: sector,
relationship,
observation: EntityVisibility::Visible,
});
}
// Step 6: Add remembered entities from knowledge graph (#366)
collect_remembered_entities(
observer_kg,
&visible_entity_bits,
&visible_positions,
z,
time.tick,
&mut entities,
);
// Step 7: Build GameTime from SimulationTime
let game_time = GameTime {
day: time.day(),
time_of_day: time.time_of_day_minutes(),
day_phase: time.day_phase(),
tick_rate: time.tick_rate,
};
tracing::trace!(
"compute_observer_snapshot: tick={}, visible={}, remembered={}, tiles={}",
time.tick,
visible_entity_bits.len(),
entities.len() - visible_entity_bits.len(),
visible_tiles.len(),
);
// Step 8: Assemble snapshot (v4: added nearby_interactions)
buffer.snapshot = Some(ObserverSnapshot {
version: crate::bridge::types::PROTOCOL_VERSION,
tick: time.tick,
game_time,
player_facing: facing,
entities,
visible_tiles,
nearby_interactions: interaction_buffer.take(),
});
}
/// Collect remembered entities from the knowledge graph — entities the observer
/// knows about but can't currently see. Filters out: already-visible entities,
/// entities without known positions, wrong z-level, visible-tile ghosts, and
/// transient Direct-confidence inconsistencies.
fn collect_remembered_entities(
observer_kg: &KnowledgeGraph,
visible_ids: &HashSet<u64>,
visible_positions: &HashSet<(i32, i32)>,
observer_z: i32,
current_tick: u64,
entities: &mut Vec<VisibleEntity>,
) {
for (stable_id, knowledge) in observer_kg.known_entities_iter() {
if visible_ids.contains(&stable_id.0) {
continue;
}
let Some(position) = knowledge.last_known_position else {
continue;
};
if position.z != observer_z {
continue;
}
// Tile is visible but entity isn't there — player knows it moved
if visible_positions.contains(&(position.x, position.y)) {
continue;
}
// Direct confidence = should be in LOS; skip transient inconsistency
if knowledge.confidence == KnowledgeConfidence::Direct {
continue;
}
let (rx, ry, rz) = position.to_render_coords();
debug_assert!(
knowledge.last_observed_tick <= current_tick,
"last_observed_tick {} > current tick {}",
knowledge.last_observed_tick,
current_tick,
);
let age_ticks = current_tick.saturating_sub(knowledge.last_observed_tick);
entities.push(VisibleEntity {
entity_id: stable_id.0,
x: rx,
y: ry,
z: rz,
kind: EntityKind::Npc,
visibility: VisibilitySector::Forward,
relationship: knowledge.relationship,
observation: EntityVisibility::Remembered {
confidence: knowledge.confidence,
age_ticks,
},
});
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::knowledge::{EntityRegistry, KnowledgeGraph};
use crate::perception::vision_cone::Facing;
use bevy_ecs::world::World;
/// Helper: set up a test world with player, walkability map, and knowledge resources
fn setup_world(width: i32, height: i32) -> World {
let mut world = World::new();
world.insert_resource(SimulationTime::default());
world.insert_resource(WalkabilityMap::new(width, height, 1));
world.init_resource::<SnapshotBuffer>();
world.init_resource::<EntityRegistry>();
world.init_resource::<NearbyInteractionBuffer>();
world
}
#[test]
fn player_always_visible_in_snapshot() {
let mut world = setup_world(32, 32);
world.spawn((
PlayerCharacter,
TilePosition::new(16, 16, 0),
Facing::default(),
KnowledgeGraph::new(),
));
let mut schedule = bevy_ecs::schedule::Schedule::default();
schedule.add_systems(compute_observer_snapshot);
schedule.run(&mut world);
let buffer = world.resource::<SnapshotBuffer>();
let snapshot = buffer.snapshot.as_ref().expect("snapshot should exist");
assert_eq!(snapshot.version, 4);
assert_eq!(snapshot.entities.len(), 1);
assert!(matches!(snapshot.entities[0].kind, EntityKind::Player));
assert_eq!(snapshot.entities[0].observation, EntityVisibility::Visible);
}
#[test]
fn npc_in_los_visible() {
let mut world = setup_world(32, 32);
world.spawn((
PlayerCharacter,
TilePosition::new(16, 16, 0),
Facing(FacingDirection::North),
KnowledgeGraph::new(),
));
// NPC directly north of player (in forward cone)
world.spawn((crate::npc::Npc, TilePosition::new(16, 14, 0)));
let mut schedule = bevy_ecs::schedule::Schedule::default();
schedule.add_systems(compute_observer_snapshot);
schedule.run(&mut world);
let buffer = world.resource::<SnapshotBuffer>();
let snapshot = buffer.snapshot.as_ref().unwrap();
assert_eq!(snapshot.entities.len(), 2);
let npc = snapshot
.entities
.iter()
.find(|e| matches!(e.kind, EntityKind::Npc))
.expect("NPC should be visible");
assert_eq!(npc.visibility, VisibilitySector::Forward);
assert_eq!(npc.observation, EntityVisibility::Visible);
}
#[test]
fn npc_behind_wall_not_visible() {
let mut world = setup_world(32, 32);
world.spawn((
PlayerCharacter,
TilePosition::new(16, 16, 0),
Facing(FacingDirection::North),
KnowledgeGraph::new(),
));
// Wall between player and NPC
let mut walkability = world.resource_mut::<WalkabilityMap>();
walkability.set_walkable(&TilePosition::new(16, 14, 0), false);
// NPC behind the wall
world.spawn((crate::npc::Npc, TilePosition::new(16, 12, 0)));
let mut schedule = bevy_ecs::schedule::Schedule::default();
schedule.add_systems(compute_observer_snapshot);
schedule.run(&mut world);
let buffer = world.resource::<SnapshotBuffer>();
let snapshot = buffer.snapshot.as_ref().unwrap();
// Only player should be visible, not the NPC behind the wall
let npcs: Vec<_> = snapshot
.entities
.iter()
.filter(|e| matches!(e.kind, EntityKind::Npc))
.collect();
assert!(npcs.is_empty(), "NPC behind wall should not be visible");
}
#[test]
fn npc_behind_player_not_visible() {
let mut world = setup_world(32, 32);
world.spawn((
PlayerCharacter,
TilePosition::new(16, 16, 0),
Facing(FacingDirection::North),
KnowledgeGraph::new(),
));
// NPC far behind player (south, in blind spot)
world.spawn((crate::npc::Npc, TilePosition::new(16, 26, 0)));
let mut schedule = bevy_ecs::schedule::Schedule::default();
schedule.add_systems(compute_observer_snapshot);
schedule.run(&mut world);
let buffer = world.resource::<SnapshotBuffer>();
let snapshot = buffer.snapshot.as_ref().unwrap();
let npcs: Vec<_> = snapshot
.entities
.iter()
.filter(|e| matches!(e.kind, EntityKind::Npc))
.collect();
assert!(npcs.is_empty(), "NPC in blind spot should not be visible");
}
#[test]
fn different_z_level_not_visible() {
let mut world = setup_world(32, 32);
world.spawn((
PlayerCharacter,
TilePosition::new(16, 16, 0),
Facing::default(),
KnowledgeGraph::new(),
));
// NPC on different z-level
world.spawn((crate::npc::Npc, TilePosition::new(16, 14, 1)));
let mut schedule = bevy_ecs::schedule::Schedule::default();
schedule.add_systems(compute_observer_snapshot);
schedule.run(&mut world);
let buffer = world.resource::<SnapshotBuffer>();
let snapshot = buffer.snapshot.as_ref().unwrap();
let npcs: Vec<_> = snapshot
.entities
.iter()
.filter(|e| matches!(e.kind, EntityKind::Npc))
.collect();
assert!(npcs.is_empty(), "NPC on different z should not be visible");
}
#[test]
fn game_time_populated() {
let mut world = setup_world(32, 32);
let mut time = SimulationTime::default();
time.tick = 7200; // 720 minutes = Evening
time.tick_rate = crate::simulation::time::TickRate::Paused;
world.insert_resource(time);
world.spawn((
PlayerCharacter,
TilePosition::new(16, 16, 0),
Facing::default(),
KnowledgeGraph::new(),
));
let mut schedule = bevy_ecs::schedule::Schedule::default();
schedule.add_systems(compute_observer_snapshot);
schedule.run(&mut world);
let buffer = world.resource::<SnapshotBuffer>();
let snapshot = buffer.snapshot.as_ref().unwrap();
assert_eq!(snapshot.game_time.time_of_day, 720);
assert_eq!(
snapshot.game_time.day_phase,
crate::simulation::time::DayPhase::Evening
);
assert_eq!(snapshot.game_time.tick_rate, crate::simulation::time::TickRate::Paused);
}
#[test]
fn visible_tiles_populated() {
let mut world = setup_world(32, 32);
world.spawn((
PlayerCharacter,
TilePosition::new(16, 16, 0),
Facing::default(),
KnowledgeGraph::new(),
));
let mut schedule = bevy_ecs::schedule::Schedule::default();
schedule.add_systems(compute_observer_snapshot);
schedule.run(&mut world);
let buffer = world.resource::<SnapshotBuffer>();
let snapshot = buffer.snapshot.as_ref().unwrap();
assert!(
!snapshot.visible_tiles.is_empty(),
"should have visible tiles"
);
// Observer's tile should be in the list
let has_observer_tile = snapshot
.visible_tiles
.iter()
.any(|t| t.x == 16 && t.y == 16 && t.z == 0);
assert!(has_observer_tile, "observer tile should be visible");
}
#[test]
fn visible_npc_has_relationship_from_knowledge() {
let mut world = setup_world(32, 32);
let mut registry = EntityRegistry::new(0);
let npc = world
.spawn((crate::npc::Npc, TilePosition::new(16, 14, 0)))
.id();
let npc_sid = registry.register(npc);
// Player knows NPC is hostile
let mut kg = KnowledgeGraph::new();
kg.observe_entity(npc_sid, TilePosition::new(16, 14, 0), 50);
kg.set_relationship(&npc_sid, RelationshipState::Hostile);
world.spawn((
PlayerCharacter,
TilePosition::new(16, 16, 0),
Facing(FacingDirection::North),
kg,
));
world.insert_resource(registry);
let mut schedule = bevy_ecs::schedule::Schedule::default();
schedule.add_systems(compute_observer_snapshot);
schedule.run(&mut world);
let buffer = world.resource::<SnapshotBuffer>();
let snapshot = buffer.snapshot.as_ref().unwrap();
let npc_entity = snapshot
.entities
.iter()
.find(|e| matches!(e.kind, EntityKind::Npc))
.expect("NPC should be visible");
assert_eq!(npc_entity.relationship, RelationshipState::Hostile);
assert_eq!(npc_entity.observation, EntityVisibility::Visible);
}
#[test]
fn remembered_entity_appears_as_ghost() {
let mut world = setup_world(32, 32);
let mut registry = EntityRegistry::new(0);
// NPC exists far behind the player (not visible)
let npc = world
.spawn((crate::npc::Npc, TilePosition::new(16, 30, 0)))
.id();
let npc_sid = registry.register(npc);
// Player previously saw NPC at (16, 28) — behind the player (south),
// well beyond peripheral range. The tile is NOT in the player's FOV.
let mut kg = KnowledgeGraph::new();
kg.observe_entity(npc_sid, TilePosition::new(16, 28, 0), 50);
kg.observe_entity_leaving_los(&npc_sid, 60);
kg.set_relationship(&npc_sid, RelationshipState::PersonOfInterest);
let player = world
.spawn((
PlayerCharacter,
TilePosition::new(16, 16, 0),
Facing(FacingDirection::North),
kg,
))
.id();
registry.register(player);
world.insert_resource(registry);
world.insert_resource({ let mut t = SimulationTime::default(); t.tick = 100; t });
let mut schedule = bevy_ecs::schedule::Schedule::default();
schedule.add_systems(compute_observer_snapshot);
schedule.run(&mut world);
let buffer = world.resource::<SnapshotBuffer>();
let snapshot = buffer.snapshot.as_ref().unwrap();
// Should have player (visible) + NPC (remembered)
let remembered: Vec<_> = snapshot
.entities
.iter()
.filter(|e| matches!(e.observation, EntityVisibility::Remembered { .. }))
.collect();
assert_eq!(remembered.len(), 1, "should have one remembered entity");
assert_eq!(remembered[0].relationship, RelationshipState::PersonOfInterest);
// Remembered entity at last_known_position (16, 28), not actual (16, 30)
assert_eq!(remembered[0].x, 16.5);
assert_eq!(remembered[0].y, 28.5);
if let EntityVisibility::Remembered { confidence, age_ticks } = &remembered[0].observation {
assert_eq!(*confidence, KnowledgeConfidence::KnowsDetails);
assert_eq!(*age_ticks, 50); // tick 100 - last_observed 50
}
}
#[test]
fn direct_confidence_not_shown_as_remembered() {
let mut world = setup_world(32, 32);
let mut registry = EntityRegistry::new(0);
// NPC exists but not in LOS
let npc = world
.spawn((crate::npc::Npc, TilePosition::new(16, 10, 0)))
.id();
let npc_sid = registry.register(npc);
// Knowledge still shows Direct (transient inconsistency)
let mut kg = KnowledgeGraph::new();
kg.observe_entity(npc_sid, TilePosition::new(16, 14, 0), 50);
// Still Direct — don't show as ghost
let player = world
.spawn((
PlayerCharacter,
TilePosition::new(16, 16, 0),
Facing(FacingDirection::North),
kg,
))
.id();
registry.register(player);
// Wall blocks actual NPC position
let mut walkability = world.resource_mut::<WalkabilityMap>();
walkability.set_walkable(&TilePosition::new(16, 12, 0), false);
world.insert_resource(registry);
let mut schedule = bevy_ecs::schedule::Schedule::default();
schedule.add_systems(compute_observer_snapshot);
schedule.run(&mut world);
let buffer = world.resource::<SnapshotBuffer>();
let snapshot = buffer.snapshot.as_ref().unwrap();
let remembered: Vec<_> = snapshot
.entities
.iter()
.filter(|e| matches!(e.observation, EntityVisibility::Remembered { .. }))
.collect();
assert!(
remembered.is_empty(),
"Direct-confidence entities should not appear as remembered ghosts"
);
}
#[test]
fn remembered_entity_on_visible_tile_not_shown() {
// If the player can see a tile and the entity isn't there,
// don't show a ghost — the player knows it moved.
let mut world = setup_world(32, 32);
let mut registry = EntityRegistry::new(0);
let npc = world
.spawn((crate::npc::Npc, TilePosition::new(30, 30, 0)))
.id();
let npc_sid = registry.register(npc);
// Player remembers NPC at (16, 15) — a tile the player can currently see
let mut kg = KnowledgeGraph::new();
kg.observe_entity(npc_sid, TilePosition::new(16, 15, 0), 50);
kg.observe_entity_leaving_los(&npc_sid, 60);
let player = world
.spawn((
PlayerCharacter,
TilePosition::new(16, 16, 0),
Facing(FacingDirection::North),
kg,
))
.id();
registry.register(player);
world.insert_resource(registry);
let mut schedule = bevy_ecs::schedule::Schedule::default();
schedule.add_systems(compute_observer_snapshot);
schedule.run(&mut world);
let buffer = world.resource::<SnapshotBuffer>();
let snapshot = buffer.snapshot.as_ref().unwrap();
let remembered: Vec<_> = snapshot
.entities
.iter()
.filter(|e| matches!(e.observation, EntityVisibility::Remembered { .. }))
.collect();
assert!(
remembered.is_empty(),
"ghost should not appear on a tile the player can currently see"
);
}
#[test]
fn remembered_entity_different_z_not_shown() {
// Remembered entity on a different z-level should not appear
let mut world = setup_world(32, 32);
let mut registry = EntityRegistry::new(0);
let npc = world
.spawn((crate::npc::Npc, TilePosition::new(5, 5, 1)))
.id();
let npc_sid = registry.register(npc);
// Player remembers NPC at z=1, but player is at z=0
let mut kg = KnowledgeGraph::new();
kg.observe_entity(npc_sid, TilePosition::new(5, 5, 1), 50);
kg.observe_entity_leaving_los(&npc_sid, 60);
let player = world
.spawn((
PlayerCharacter,
TilePosition::new(16, 16, 0),
Facing(FacingDirection::North),
kg,
))
.id();
registry.register(player);
world.insert_resource(registry);
let mut schedule = bevy_ecs::schedule::Schedule::default();
schedule.add_systems(compute_observer_snapshot);
schedule.run(&mut world);
let buffer = world.resource::<SnapshotBuffer>();
let snapshot = buffer.snapshot.as_ref().unwrap();
let remembered: Vec<_> = snapshot
.entities
.iter()
.filter(|e| matches!(e.observation, EntityVisibility::Remembered { .. }))
.collect();
assert!(
remembered.is_empty(),
"remembered entity on different z-level should not appear in snapshot"
);
}
#[test]
fn knowledge_without_position_not_shown() {
// Entity known via gossip (no last_known_position) should not appear
let mut world = setup_world(32, 32);
let mut registry = EntityRegistry::new(0);
let npc = world
.spawn((crate::npc::Npc, TilePosition::new(5, 5, 0)))
.id();
let npc_sid = registry.register(npc);
// Player knows about NPC but has never seen it (no position)
let mut kg = KnowledgeGraph::new();
// Insert knowledge manually without a position
kg.entities.insert(npc_sid, crate::knowledge::EntityKnowledge {
last_known_position: None,
last_observed_tick: 0,
last_updated_tick: 50,
confidence: KnowledgeConfidence::KnowsOf,
source: crate::knowledge::KnowledgeSource::Background,
state: crate::knowledge::KnowledgeState::Active,
relationship: RelationshipState::PersonOfInterest,
known_attributes: std::collections::BTreeMap::new(),
});
let player = world
.spawn((
PlayerCharacter,
TilePosition::new(16, 16, 0),
Facing(FacingDirection::North),
kg,
))
.id();
registry.register(player);
world.insert_resource(registry);
let mut schedule = bevy_ecs::schedule::Schedule::default();
schedule.add_systems(compute_observer_snapshot);
schedule.run(&mut world);
let buffer = world.resource::<SnapshotBuffer>();
let snapshot = buffer.snapshot.as_ref().unwrap();
let remembered: Vec<_> = snapshot
.entities
.iter()
.filter(|e| matches!(e.observation, EntityVisibility::Remembered { .. }))
.collect();
assert!(
remembered.is_empty(),
"entity without last_known_position should not appear as ghost"
);
}
#[test]
fn multiple_npcs_in_los_all_visible() {
let mut world = setup_world(32, 32);
world.spawn((
PlayerCharacter,
TilePosition::new(16, 16, 0),
Facing(FacingDirection::North),
KnowledgeGraph::new(),
));
// Three NPCs in front of player, no walls
world.spawn((crate::npc::Npc, TilePosition::new(16, 14, 0)));
world.spawn((crate::npc::Npc, TilePosition::new(14, 14, 0)));
world.spawn((crate::npc::Npc, TilePosition::new(18, 14, 0)));
let mut schedule = bevy_ecs::schedule::Schedule::default();
schedule.add_systems(compute_observer_snapshot);
schedule.run(&mut world);
let buffer = world.resource::<SnapshotBuffer>();
let snapshot = buffer.snapshot.as_ref().unwrap();
// Player + 3 NPCs = 4 entities
assert_eq!(snapshot.entities.len(), 4);
let npcs: Vec<_> = snapshot
.entities
.iter()
.filter(|e| matches!(e.kind, EntityKind::Npc))
.collect();
assert_eq!(npcs.len(), 3);
assert!(npcs.iter().all(|n| n.observation == EntityVisibility::Visible));
}
#[test]
fn npc_behind_wall_excluded_from_multi_entity_snapshot() {
let mut world = setup_world(32, 32);
// Wall at (16,14)
world
.resource_mut::<WalkabilityMap>()
.set_walkable(&TilePosition::new(16, 14, 0), false);
world.spawn((
PlayerCharacter,
TilePosition::new(16, 16, 0),
Facing(FacingDirection::North),
KnowledgeGraph::new(),
));
// NPC 1: behind wall (should be hidden)
world.spawn((crate::npc::Npc, TilePosition::new(16, 13, 0)));
// NPC 2: to the side, no wall (should be visible)
world.spawn((crate::npc::Npc, TilePosition::new(14, 14, 0)));
// NPC 3: also visible
world.spawn((crate::npc::Npc, TilePosition::new(18, 15, 0)));
let mut schedule = bevy_ecs::schedule::Schedule::default();
schedule.add_systems(compute_observer_snapshot);
schedule.run(&mut world);
let buffer = world.resource::<SnapshotBuffer>();
let snapshot = buffer.snapshot.as_ref().unwrap();
// Player + 2 visible NPCs = 3 (NPC behind wall excluded)
let npcs: Vec<_> = snapshot
.entities
.iter()
.filter(|e| matches!(e.kind, EntityKind::Npc))
.collect();
assert_eq!(npcs.len(), 2, "NPC behind wall should be excluded");
}
}
+273
View File
@@ -0,0 +1,273 @@
//! Observer visibility query system (#112)
//!
//! Two-stage pipeline:
//! 1. compute_visibility_geometry — FOV + vision cone → VisibilityGeometry resource
//! 2. compute_observer_snapshot — entity filtering + knowledge overlay → ObserverSnapshot
//!
//! D-017 perception modes swap the geometry producer via PerceptionQuery trait.
use bevy_ecs::prelude::*;
use std::collections::HashSet;
use crate::bridge::types::*;
use crate::knowledge::{EntityRegistry, KnowledgeGraph, StableId};
use crate::perception::query::{ActivePerceptionMode, VisibilityGeometry};
use crate::perception::vision_cone::Facing;
use crate::simulation::interaction::NearbyInteractionBuffer;
use crate::simulation::movement::{PlayerCharacter, TilePosition, WalkabilityMap};
use crate::simulation::time::SimulationTime;
/// Compute visibility geometry using the active perception mode.
/// Stage 1 of the observer pipeline: FOV + vision cone → VisibilityGeometry.
///
/// System ordering: after validate_movement, before compute_observer_snapshot.
pub fn compute_visibility_geometry(
walkability: Res<WalkabilityMap>,
mode: Res<ActivePerceptionMode>,
observer_query: Query<(&TilePosition, Option<&Facing>), With<PlayerCharacter>>,
mut geometry: ResMut<VisibilityGeometry>,
) {
let Ok((observer_pos, facing_opt)) = observer_query.single() else {
return;
};
let facing = facing_opt
.map(|f| f.0)
.unwrap_or(FacingDirection::default());
*geometry = mode.0.compute_geometry(observer_pos, facing, &walkability);
}
/// Assemble observer snapshot from precomputed geometry and entity state.
/// Stage 2 of the observer pipeline: entity filtering + knowledge overlay → snapshot.
///
/// System ordering: after compute_visibility_geometry + compute_nearby_interactions,
/// before advance_tick.
#[allow(clippy::type_complexity)]
pub fn compute_observer_snapshot(
time: Res<SimulationTime>,
geometry: Res<VisibilityGeometry>,
registry: Res<EntityRegistry>,
mut observer_query: Query<
(&TilePosition, Option<&Facing>, &KnowledgeGraph, &mut NearbyInteractionBuffer),
With<PlayerCharacter>,
>,
all_entities: Query<(
Entity,
&TilePosition,
Option<&PlayerCharacter>,
Option<&crate::npc::Npc>,
)>,
mut buffer: ResMut<SnapshotBuffer>,
) {
let Ok((_observer_pos, facing_opt, observer_kg, mut interaction_buffer)) =
observer_query.single_mut()
else {
return;
};
let facing = facing_opt
.map(|f| f.0)
.unwrap_or(FacingDirection::default());
let (mut entities, visible_ids) =
filter_visible_entities(&geometry, &registry, observer_kg, &all_entities);
collect_remembered_entities(
observer_kg,
&visible_ids,
&geometry.visible_positions,
geometry.observer_z,
time.tick,
&mut entities,
);
let game_time = GameTime {
day: time.day(),
time_of_day: time.time_of_day_minutes(),
day_phase: time.day_phase(),
tick_rate: time.tick_rate,
};
// Take interactions and adjust POI verb priority (D-060)
let mut nearby_interactions = interaction_buffer.take();
apply_poi_verb_priority(&mut nearby_interactions, observer_kg);
tracing::trace!(
"compute_observer_snapshot: tick={}, visible={}, remembered={}, tiles={}",
time.tick,
visible_ids.len(),
entities.len() - visible_ids.len(),
geometry.visible_tiles.len(),
);
buffer.snapshot = Some(ObserverSnapshot {
version: crate::bridge::types::PROTOCOL_VERSION,
tick: time.tick,
game_time,
player_facing: facing,
entities,
visible_tiles: geometry.visible_tiles.clone(),
nearby_interactions,
});
}
/// Filter entities by visibility using precomputed geometry.
/// Returns (visible entities, set of visible wire IDs).
#[allow(clippy::type_complexity)]
fn filter_visible_entities(
geometry: &VisibilityGeometry,
registry: &EntityRegistry,
observer_kg: &KnowledgeGraph,
all_entities: &Query<(
Entity,
&TilePosition,
Option<&PlayerCharacter>,
Option<&crate::npc::Npc>,
)>,
) -> (Vec<VisibleEntity>, HashSet<u64>) {
let mut entities = Vec::new();
let mut visible_ids: HashSet<u64> = HashSet::new();
for (entity, pos, is_player, is_npc) in all_entities.iter() {
if pos.z != geometry.observer_z {
continue;
}
if !geometry.visible_positions.contains(&(pos.x, pos.y)) {
continue;
}
let (rx, ry, rz) = pos.to_render_coords();
let kind = if is_player.is_some() {
EntityKind::Player
} else if is_npc.is_some() {
EntityKind::Npc
} else {
EntityKind::Object
};
let sector = geometry
.sector_lookup
.get(&(pos.x, pos.y))
.copied()
.unwrap_or(VisibilitySector::Peripheral);
let relationship = if is_player.is_some() {
RelationshipState::Known // Self
} else if let Some(stable_id) = registry.to_stable(entity) {
observer_kg.relationship_with(&stable_id)
} else {
RelationshipState::Unknown
};
// Fallback to Entity::to_bits() is intentional for per-frame systems:
// panicking would crash the server every tick. The error log makes this
// loud enough to catch in testing while keeping the server alive.
let wire_id = registry
.to_stable(entity)
.map(|sid| sid.0)
.unwrap_or_else(|| {
tracing::error!(?entity, "entity visible but not in EntityRegistry");
entity.to_bits()
});
visible_ids.insert(wire_id);
entities.push(VisibleEntity {
entity_id: wire_id,
x: rx,
y: ry,
z: rz,
kind,
visibility: sector,
relationship,
observation: EntityVisibility::Visible,
});
}
(entities, visible_ids)
}
/// Collect remembered entities from the knowledge graph — entities the observer
/// knows about but can't currently see. Filters out: already-visible entities,
/// entities without known positions, wrong z-level, visible-tile ghosts, and
/// transient Direct-confidence inconsistencies.
fn collect_remembered_entities(
observer_kg: &KnowledgeGraph,
visible_ids: &HashSet<u64>,
visible_positions: &HashSet<(i32, i32)>,
observer_z: i32,
current_tick: u64,
entities: &mut Vec<VisibleEntity>,
) {
for (stable_id, knowledge) in observer_kg.known_entities_iter() {
if visible_ids.contains(&stable_id.0) {
continue;
}
let Some(position) = knowledge.last_known_position else {
continue;
};
if position.z != observer_z {
continue;
}
// Tile is visible but entity isn't there — player knows it moved
if visible_positions.contains(&(position.x, position.y)) {
continue;
}
// Direct confidence = should be in LOS; skip transient inconsistency
if knowledge.confidence == KnowledgeConfidence::Direct {
continue;
}
let (rx, ry, rz) = position.to_render_coords();
debug_assert!(
knowledge.last_observed_tick <= current_tick,
"last_observed_tick {} > current tick {}",
knowledge.last_observed_tick,
current_tick,
);
let age_ticks = current_tick.saturating_sub(knowledge.last_observed_tick);
entities.push(VisibleEntity {
entity_id: stable_id.0,
x: rx,
y: ry,
z: rz,
kind: EntityKind::Npc,
visibility: VisibilitySector::Forward,
relationship: knowledge.relationship,
observation: EntityVisibility::Remembered {
confidence: knowledge.confidence,
age_ticks,
},
});
}
}
/// Adjust verb priority for PersonOfInterest NPCs (D-060).
/// Moves ExamineNpc to priority 1 and Talk to priority 2 when the observer
/// knows the entity as POI. Called after interaction buffer is taken.
fn apply_poi_verb_priority(
interactions: &mut [NearbyInteraction],
observer_kg: &KnowledgeGraph,
) {
for interaction in interactions.iter_mut() {
let stable_id = StableId(interaction.entity_id);
let relationship = observer_kg.relationship_with(&stable_id);
if relationship == RelationshipState::PersonOfInterest {
for verb in &mut interaction.verbs {
match verb.kind {
VerbKind::ExamineNpc => verb.priority = 1,
VerbKind::Talk => verb.priority = 2,
_ => {}
}
}
interaction.verbs.sort_by_key(|v| (v.priority, v.kind as u8));
}
}
}
#[cfg(test)]
mod tests;
+617
View File
@@ -0,0 +1,617 @@
use super::*;
use crate::knowledge::{EntityRegistry, KnowledgeGraph};
use crate::perception::query::{ActivePerceptionMode, VisibilityGeometry};
use crate::perception::vision_cone::Facing;
use bevy_ecs::world::World;
/// Helper: set up a test world with resources for the two-stage observer pipeline.
fn setup_world(width: i32, height: i32) -> World {
let mut world = World::new();
world.insert_resource(SimulationTime::default());
world.insert_resource(WalkabilityMap::new(width, height, 1));
world.init_resource::<SnapshotBuffer>();
world.init_resource::<EntityRegistry>();
world.init_resource::<VisibilityGeometry>();
world.init_resource::<ActivePerceptionMode>();
world
}
/// Run the two-stage observer pipeline: geometry + snapshot.
fn run_observer_pipeline(world: &mut World) {
let mut schedule = bevy_ecs::schedule::Schedule::default();
schedule.add_systems((
compute_visibility_geometry,
compute_observer_snapshot.after(compute_visibility_geometry),
));
schedule.run(world);
}
/// Run the full pipeline including interaction system.
fn run_full_pipeline(world: &mut World) {
let mut schedule = bevy_ecs::schedule::Schedule::default();
schedule.add_systems((
crate::simulation::interaction::compute_nearby_interactions,
compute_visibility_geometry,
compute_observer_snapshot
.after(compute_visibility_geometry)
.after(crate::simulation::interaction::compute_nearby_interactions),
));
schedule.run(world);
}
#[test]
fn player_always_visible_in_snapshot() {
let mut world = setup_world(32, 32);
world.spawn((
PlayerCharacter,
TilePosition::new(16, 16, 0),
Facing::default(),
KnowledgeGraph::new(),
NearbyInteractionBuffer::default(),
));
run_observer_pipeline(&mut world);
let buffer = world.resource::<SnapshotBuffer>();
let snapshot = buffer.snapshot.as_ref().expect("snapshot should exist");
assert_eq!(snapshot.version, 4);
assert_eq!(snapshot.entities.len(), 1);
assert!(matches!(snapshot.entities[0].kind, EntityKind::Player));
assert_eq!(snapshot.entities[0].observation, EntityVisibility::Visible);
}
#[test]
fn npc_in_los_visible() {
let mut world = setup_world(32, 32);
world.spawn((
PlayerCharacter,
TilePosition::new(16, 16, 0),
Facing(FacingDirection::North),
KnowledgeGraph::new(),
NearbyInteractionBuffer::default(),
));
// NPC directly north of player (in forward cone)
world.spawn((crate::npc::Npc, TilePosition::new(16, 14, 0)));
run_observer_pipeline(&mut world);
let buffer = world.resource::<SnapshotBuffer>();
let snapshot = buffer.snapshot.as_ref().unwrap();
assert_eq!(snapshot.entities.len(), 2);
let npc = snapshot
.entities
.iter()
.find(|e| matches!(e.kind, EntityKind::Npc))
.expect("NPC should be visible");
assert_eq!(npc.visibility, VisibilitySector::Forward);
assert_eq!(npc.observation, EntityVisibility::Visible);
}
#[test]
fn npc_behind_wall_not_visible() {
let mut world = setup_world(32, 32);
world.spawn((
PlayerCharacter,
TilePosition::new(16, 16, 0),
Facing(FacingDirection::North),
KnowledgeGraph::new(),
NearbyInteractionBuffer::default(),
));
// Wall between player and NPC
let mut walkability = world.resource_mut::<WalkabilityMap>();
walkability.set_walkable(&TilePosition::new(16, 14, 0), false);
// NPC behind the wall
world.spawn((crate::npc::Npc, TilePosition::new(16, 12, 0)));
run_observer_pipeline(&mut world);
let buffer = world.resource::<SnapshotBuffer>();
let snapshot = buffer.snapshot.as_ref().unwrap();
// Only player should be visible, not the NPC behind the wall
let npcs: Vec<_> = snapshot
.entities
.iter()
.filter(|e| matches!(e.kind, EntityKind::Npc))
.collect();
assert!(npcs.is_empty(), "NPC behind wall should not be visible");
}
#[test]
fn npc_behind_player_not_visible() {
let mut world = setup_world(32, 32);
world.spawn((
PlayerCharacter,
TilePosition::new(16, 16, 0),
Facing(FacingDirection::North),
KnowledgeGraph::new(),
NearbyInteractionBuffer::default(),
));
// NPC far behind player (south, in blind spot)
world.spawn((crate::npc::Npc, TilePosition::new(16, 26, 0)));
run_observer_pipeline(&mut world);
let buffer = world.resource::<SnapshotBuffer>();
let snapshot = buffer.snapshot.as_ref().unwrap();
let npcs: Vec<_> = snapshot
.entities
.iter()
.filter(|e| matches!(e.kind, EntityKind::Npc))
.collect();
assert!(npcs.is_empty(), "NPC in blind spot should not be visible");
}
#[test]
fn different_z_level_not_visible() {
let mut world = setup_world(32, 32);
world.spawn((
PlayerCharacter,
TilePosition::new(16, 16, 0),
Facing::default(),
KnowledgeGraph::new(),
NearbyInteractionBuffer::default(),
));
// NPC on different z-level
world.spawn((crate::npc::Npc, TilePosition::new(16, 14, 1)));
run_observer_pipeline(&mut world);
let buffer = world.resource::<SnapshotBuffer>();
let snapshot = buffer.snapshot.as_ref().unwrap();
let npcs: Vec<_> = snapshot
.entities
.iter()
.filter(|e| matches!(e.kind, EntityKind::Npc))
.collect();
assert!(npcs.is_empty(), "NPC on different z should not be visible");
}
#[test]
fn game_time_populated() {
let mut world = setup_world(32, 32);
let mut time = SimulationTime::default();
time.tick = 7200; // 720 minutes = Evening
time.tick_rate = crate::simulation::time::TickRate::Paused;
world.insert_resource(time);
world.spawn((
PlayerCharacter,
TilePosition::new(16, 16, 0),
Facing::default(),
KnowledgeGraph::new(),
NearbyInteractionBuffer::default(),
));
run_observer_pipeline(&mut world);
let buffer = world.resource::<SnapshotBuffer>();
let snapshot = buffer.snapshot.as_ref().unwrap();
assert_eq!(snapshot.game_time.time_of_day, 720);
assert_eq!(
snapshot.game_time.day_phase,
crate::simulation::time::DayPhase::Evening
);
assert_eq!(snapshot.game_time.tick_rate, crate::simulation::time::TickRate::Paused);
}
#[test]
fn visible_tiles_populated() {
let mut world = setup_world(32, 32);
world.spawn((
PlayerCharacter,
TilePosition::new(16, 16, 0),
Facing::default(),
KnowledgeGraph::new(),
NearbyInteractionBuffer::default(),
));
run_observer_pipeline(&mut world);
let buffer = world.resource::<SnapshotBuffer>();
let snapshot = buffer.snapshot.as_ref().unwrap();
assert!(
!snapshot.visible_tiles.is_empty(),
"should have visible tiles"
);
// Observer's tile should be in the list
let has_observer_tile = snapshot
.visible_tiles
.iter()
.any(|t| t.x == 16 && t.y == 16 && t.z == 0);
assert!(has_observer_tile, "observer tile should be visible");
}
#[test]
fn visible_npc_has_relationship_from_knowledge() {
let mut world = setup_world(32, 32);
let mut registry = EntityRegistry::new(0);
let npc = world
.spawn((crate::npc::Npc, TilePosition::new(16, 14, 0)))
.id();
let npc_sid = registry.register(npc);
// Player knows NPC is hostile
let mut kg = KnowledgeGraph::new();
kg.observe_entity(npc_sid, TilePosition::new(16, 14, 0), 50);
kg.set_relationship(&npc_sid, RelationshipState::Hostile);
world.spawn((
PlayerCharacter,
TilePosition::new(16, 16, 0),
Facing(FacingDirection::North),
kg,
NearbyInteractionBuffer::default(),
));
world.insert_resource(registry);
run_observer_pipeline(&mut world);
let buffer = world.resource::<SnapshotBuffer>();
let snapshot = buffer.snapshot.as_ref().unwrap();
let npc_entity = snapshot
.entities
.iter()
.find(|e| matches!(e.kind, EntityKind::Npc))
.expect("NPC should be visible");
assert_eq!(npc_entity.relationship, RelationshipState::Hostile);
assert_eq!(npc_entity.observation, EntityVisibility::Visible);
}
#[test]
fn remembered_entity_appears_as_ghost() {
let mut world = setup_world(32, 32);
let mut registry = EntityRegistry::new(0);
// NPC exists far behind the player (not visible)
let npc = world
.spawn((crate::npc::Npc, TilePosition::new(16, 30, 0)))
.id();
let npc_sid = registry.register(npc);
// Player previously saw NPC at (16, 28) — behind the player (south),
// well beyond peripheral range. The tile is NOT in the player's FOV.
let mut kg = KnowledgeGraph::new();
kg.observe_entity(npc_sid, TilePosition::new(16, 28, 0), 50);
kg.observe_entity_leaving_los(&npc_sid, 60);
kg.set_relationship(&npc_sid, RelationshipState::PersonOfInterest);
let player = world
.spawn((
PlayerCharacter,
TilePosition::new(16, 16, 0),
Facing(FacingDirection::North),
kg,
NearbyInteractionBuffer::default(),
))
.id();
registry.register(player);
world.insert_resource(registry);
world.insert_resource({ let mut t = SimulationTime::default(); t.tick = 100; t });
run_observer_pipeline(&mut world);
let buffer = world.resource::<SnapshotBuffer>();
let snapshot = buffer.snapshot.as_ref().unwrap();
// Should have player (visible) + NPC (remembered)
let remembered: Vec<_> = snapshot
.entities
.iter()
.filter(|e| matches!(e.observation, EntityVisibility::Remembered { .. }))
.collect();
assert_eq!(remembered.len(), 1, "should have one remembered entity");
assert_eq!(remembered[0].relationship, RelationshipState::PersonOfInterest);
// Remembered entity at last_known_position (16, 28), not actual (16, 30)
assert_eq!(remembered[0].x, 16.5);
assert_eq!(remembered[0].y, 28.5);
if let EntityVisibility::Remembered { confidence, age_ticks } = &remembered[0].observation {
assert_eq!(*confidence, KnowledgeConfidence::KnowsDetails);
assert_eq!(*age_ticks, 50); // tick 100 - last_observed 50
}
}
#[test]
fn direct_confidence_not_shown_as_remembered() {
let mut world = setup_world(32, 32);
let mut registry = EntityRegistry::new(0);
// NPC exists but not in LOS
let npc = world
.spawn((crate::npc::Npc, TilePosition::new(16, 10, 0)))
.id();
let npc_sid = registry.register(npc);
// Knowledge still shows Direct (transient inconsistency)
let mut kg = KnowledgeGraph::new();
kg.observe_entity(npc_sid, TilePosition::new(16, 14, 0), 50);
// Still Direct — don't show as ghost
let player = world
.spawn((
PlayerCharacter,
TilePosition::new(16, 16, 0),
Facing(FacingDirection::North),
kg,
NearbyInteractionBuffer::default(),
))
.id();
registry.register(player);
// Wall blocks actual NPC position
let mut walkability = world.resource_mut::<WalkabilityMap>();
walkability.set_walkable(&TilePosition::new(16, 12, 0), false);
world.insert_resource(registry);
run_observer_pipeline(&mut world);
let buffer = world.resource::<SnapshotBuffer>();
let snapshot = buffer.snapshot.as_ref().unwrap();
let remembered: Vec<_> = snapshot
.entities
.iter()
.filter(|e| matches!(e.observation, EntityVisibility::Remembered { .. }))
.collect();
assert!(
remembered.is_empty(),
"Direct-confidence entities should not appear as remembered ghosts"
);
}
#[test]
fn remembered_entity_on_visible_tile_not_shown() {
// If the player can see a tile and the entity isn't there,
// don't show a ghost — the player knows it moved.
let mut world = setup_world(32, 32);
let mut registry = EntityRegistry::new(0);
let npc = world
.spawn((crate::npc::Npc, TilePosition::new(30, 30, 0)))
.id();
let npc_sid = registry.register(npc);
// Player remembers NPC at (16, 15) — a tile the player can currently see
let mut kg = KnowledgeGraph::new();
kg.observe_entity(npc_sid, TilePosition::new(16, 15, 0), 50);
kg.observe_entity_leaving_los(&npc_sid, 60);
let player = world
.spawn((
PlayerCharacter,
TilePosition::new(16, 16, 0),
Facing(FacingDirection::North),
kg,
NearbyInteractionBuffer::default(),
))
.id();
registry.register(player);
world.insert_resource(registry);
run_observer_pipeline(&mut world);
let buffer = world.resource::<SnapshotBuffer>();
let snapshot = buffer.snapshot.as_ref().unwrap();
let remembered: Vec<_> = snapshot
.entities
.iter()
.filter(|e| matches!(e.observation, EntityVisibility::Remembered { .. }))
.collect();
assert!(
remembered.is_empty(),
"ghost should not appear on a tile the player can currently see"
);
}
#[test]
fn remembered_entity_different_z_not_shown() {
// Remembered entity on a different z-level should not appear
let mut world = setup_world(32, 32);
let mut registry = EntityRegistry::new(0);
let npc = world
.spawn((crate::npc::Npc, TilePosition::new(5, 5, 1)))
.id();
let npc_sid = registry.register(npc);
// Player remembers NPC at z=1, but player is at z=0
let mut kg = KnowledgeGraph::new();
kg.observe_entity(npc_sid, TilePosition::new(5, 5, 1), 50);
kg.observe_entity_leaving_los(&npc_sid, 60);
let player = world
.spawn((
PlayerCharacter,
TilePosition::new(16, 16, 0),
Facing(FacingDirection::North),
kg,
NearbyInteractionBuffer::default(),
))
.id();
registry.register(player);
world.insert_resource(registry);
run_observer_pipeline(&mut world);
let buffer = world.resource::<SnapshotBuffer>();
let snapshot = buffer.snapshot.as_ref().unwrap();
let remembered: Vec<_> = snapshot
.entities
.iter()
.filter(|e| matches!(e.observation, EntityVisibility::Remembered { .. }))
.collect();
assert!(
remembered.is_empty(),
"remembered entity on different z-level should not appear in snapshot"
);
}
#[test]
fn knowledge_without_position_not_shown() {
// Entity known via gossip (no last_known_position) should not appear
let mut world = setup_world(32, 32);
let mut registry = EntityRegistry::new(0);
let npc = world
.spawn((crate::npc::Npc, TilePosition::new(5, 5, 0)))
.id();
let npc_sid = registry.register(npc);
// Player knows about NPC but has never seen it (no position)
let mut kg = KnowledgeGraph::new();
// Insert knowledge manually without a position
kg.entities.insert(npc_sid, crate::knowledge::EntityKnowledge {
last_known_position: None,
last_observed_tick: 0,
last_updated_tick: 50,
confidence: KnowledgeConfidence::KnowsOf,
source: crate::knowledge::KnowledgeSource::Background,
state: crate::knowledge::KnowledgeState::Active,
relationship: RelationshipState::PersonOfInterest,
known_attributes: std::collections::BTreeMap::new(),
});
let player = world
.spawn((
PlayerCharacter,
TilePosition::new(16, 16, 0),
Facing(FacingDirection::North),
kg,
NearbyInteractionBuffer::default(),
))
.id();
registry.register(player);
world.insert_resource(registry);
run_observer_pipeline(&mut world);
let buffer = world.resource::<SnapshotBuffer>();
let snapshot = buffer.snapshot.as_ref().unwrap();
let remembered: Vec<_> = snapshot
.entities
.iter()
.filter(|e| matches!(e.observation, EntityVisibility::Remembered { .. }))
.collect();
assert!(
remembered.is_empty(),
"entity without last_known_position should not appear as ghost"
);
}
#[test]
fn multiple_npcs_in_los_all_visible() {
let mut world = setup_world(32, 32);
world.spawn((
PlayerCharacter,
TilePosition::new(16, 16, 0),
Facing(FacingDirection::North),
KnowledgeGraph::new(),
NearbyInteractionBuffer::default(),
));
// Three NPCs in front of player, no walls
world.spawn((crate::npc::Npc, TilePosition::new(16, 14, 0)));
world.spawn((crate::npc::Npc, TilePosition::new(14, 14, 0)));
world.spawn((crate::npc::Npc, TilePosition::new(18, 14, 0)));
run_observer_pipeline(&mut world);
let buffer = world.resource::<SnapshotBuffer>();
let snapshot = buffer.snapshot.as_ref().unwrap();
// Player + 3 NPCs = 4 entities
assert_eq!(snapshot.entities.len(), 4);
let npcs: Vec<_> = snapshot
.entities
.iter()
.filter(|e| matches!(e.kind, EntityKind::Npc))
.collect();
assert_eq!(npcs.len(), 3);
assert!(npcs.iter().all(|n| n.observation == EntityVisibility::Visible));
}
#[test]
fn npc_behind_wall_excluded_from_multi_entity_snapshot() {
let mut world = setup_world(32, 32);
// Wall at (16,14)
world
.resource_mut::<WalkabilityMap>()
.set_walkable(&TilePosition::new(16, 14, 0), false);
world.spawn((
PlayerCharacter,
TilePosition::new(16, 16, 0),
Facing(FacingDirection::North),
KnowledgeGraph::new(),
NearbyInteractionBuffer::default(),
));
// NPC 1: behind wall (should be hidden)
world.spawn((crate::npc::Npc, TilePosition::new(16, 13, 0)));
// NPC 2: to the side, no wall (should be visible)
world.spawn((crate::npc::Npc, TilePosition::new(14, 14, 0)));
// NPC 3: also visible
world.spawn((crate::npc::Npc, TilePosition::new(18, 15, 0)));
run_observer_pipeline(&mut world);
let buffer = world.resource::<SnapshotBuffer>();
let snapshot = buffer.snapshot.as_ref().unwrap();
// Player + 2 visible NPCs = 3 (NPC behind wall excluded)
let npcs: Vec<_> = snapshot
.entities
.iter()
.filter(|e| matches!(e.kind, EntityKind::Npc))
.collect();
assert_eq!(npcs.len(), 2, "NPC behind wall should be excluded");
}
#[test]
fn poi_interaction_gets_observe_first_priority() {
let mut world = setup_world(32, 32);
let mut registry = EntityRegistry::new(0);
// NPC in close range, directly north of player and in LOS
let npc = world
.spawn((
crate::npc::Npc,
TilePosition::new(16, 15, 0),
crate::simulation::interaction::Interactable,
))
.id();
let npc_sid = registry.register(npc);
// Player knows NPC as PersonOfInterest
let mut kg = KnowledgeGraph::new();
kg.observe_entity(npc_sid, TilePosition::new(16, 15, 0), 50);
kg.set_relationship(&npc_sid, RelationshipState::PersonOfInterest);
let player = world
.spawn((
PlayerCharacter,
TilePosition::new(16, 16, 0),
Facing(FacingDirection::North),
kg,
NearbyInteractionBuffer::default(),
))
.id();
registry.register(player);
world.insert_resource(registry);
// Run full pipeline: interaction computes default priority,
// then observer applies POI adjustment
run_full_pipeline(&mut world);
let buffer = world.resource::<SnapshotBuffer>();
let snapshot = buffer.snapshot.as_ref().unwrap();
assert_eq!(snapshot.nearby_interactions.len(), 1);
let interaction = &snapshot.nearby_interactions[0];
// POI: Observe takes priority over Talk
assert_eq!(interaction.verbs[0].kind, VerbKind::ExamineNpc);
assert_eq!(interaction.verbs[0].priority, 1);
assert_eq!(interaction.verbs[1].kind, VerbKind::Talk);
assert_eq!(interaction.verbs[1].priority, 2);
}
+103
View File
@@ -0,0 +1,103 @@
//! Perception query trait (D-017).
//!
//! Abstraction for perception mode geometry computation. Each mode
//! (natural vision, thermal, EM, etc.) implements PerceptionQuery to
//! provide mode-specific FOV and visibility sector computation.
//! v0.1 implements only NaturalVision.
use std::collections::{HashMap, HashSet};
use bevy_ecs::prelude::*;
use crate::bridge::types::{FacingDirection, VisibilitySector, VisibleTile};
use crate::perception::shadowcast::compute_fov;
use crate::perception::vision_cone::{apply_vision_cone, VisionConeConfig};
use crate::simulation::movement::{TilePosition, WalkabilityMap};
/// Cached FOV geometry for the current frame. Produced by
/// compute_visibility_geometry, consumed by compute_observer_snapshot.
/// D-017 perception modes swap the geometry producer while the consumer
/// remains unchanged.
#[derive(Resource, Default)]
pub struct VisibilityGeometry {
pub visible_tiles: Vec<VisibleTile>,
pub visible_positions: HashSet<(i32, i32)>,
pub sector_lookup: HashMap<(i32, i32), VisibilitySector>,
pub observer_z: i32,
}
/// Trait for perception mode geometry computation (D-017).
///
/// Each perception mode implements this to produce a VisibilityGeometry
/// from the observer's position and facing. v0.1 only implements
/// NaturalVision; D-017 adds Thermal, EM, etc.
pub trait PerceptionQuery: Send + Sync {
fn compute_geometry(
&self,
observer_pos: &TilePosition,
facing: FacingDirection,
walkability: &WalkabilityMap,
) -> VisibilityGeometry;
}
/// Natural vision — default perception mode.
/// Uses symmetric shadowcasting (D-011) + directional vision cone (D-015).
pub struct NaturalVision;
impl PerceptionQuery for NaturalVision {
fn compute_geometry(
&self,
observer_pos: &TilePosition,
facing: FacingDirection,
walkability: &WalkabilityMap,
) -> VisibilityGeometry {
let config = VisionConeConfig::default();
let z = observer_pos.z;
let fov = compute_fov(
|x, y| !walkability.can_move_to(&TilePosition::new(x, y, z)),
observer_pos.x,
observer_pos.y,
config.forward_range,
z,
);
let cone_tiles =
apply_vision_cone(&fov, observer_pos.x, observer_pos.y, facing, &config);
let visible_tiles = cone_tiles
.iter()
.map(|&(x, y, sector)| VisibleTile {
x,
y,
z,
visibility: sector,
})
.collect();
let visible_positions = cone_tiles.iter().map(|&(x, y, _)| (x, y)).collect();
let sector_lookup = cone_tiles
.iter()
.map(|&(x, y, sector)| ((x, y), sector))
.collect();
VisibilityGeometry {
visible_tiles,
visible_positions,
sector_lookup,
observer_z: z,
}
}
}
/// Resource wrapping the active perception mode (D-017).
/// Defaults to NaturalVision. Swap this resource to change perception modes.
#[derive(Resource)]
pub struct ActivePerceptionMode(pub Box<dyn PerceptionQuery>);
impl Default for ActivePerceptionMode {
fn default() -> Self {
Self(Box::new(NaturalVision))
}
}
+72 -165
View File
@@ -2,11 +2,14 @@
// Implements #404: server-side verb computation for context-sensitive [E] key
// Spec: docs/design/interaction-verbs-v0.1.md
// D-060: actions[] renamed to verbs[] across all surfaces
//
// Phase boundary: this system determines verb AVAILABILITY based on proximity
// and entity type only. Verb PRIORITY adjustment (e.g. POI flipping Observe
// above Talk) is a perception concern handled by the observer system.
use bevy_ecs::prelude::*;
use crate::bridge::types::{EntityKind, NearbyInteraction, VerbKind, VerbOption};
use crate::knowledge::types::RelationshipState;
use crate::knowledge::{EntityRegistry, KnowledgeGraph};
use crate::knowledge::EntityRegistry;
use crate::npc::Npc;
use crate::simulation::movement::{PlayerCharacter, TilePosition};
@@ -23,24 +26,26 @@ pub struct Interactable;
/// For each entity in range, determines available verbs sorted by priority.
/// Results are written to the NearbyInteractionBuffer for inclusion in ObserverSnapshot.
///
/// NOTE: Checks proximity only, not line-of-sight. The client filters
/// interaction prompts against visible entities. Server-side LOS filtering
/// is deferred until the interaction system can read the observer's visible set.
/// NOTE: Determines verb availability and default priority only. Relationship-based
/// priority adjustment (e.g. POI → Observe first) is applied by the observer
/// system after taking the buffer. This keeps the simulation phase free of
/// knowledge graph dependencies (D-010 phase boundary).
#[allow(clippy::type_complexity)]
pub fn compute_nearby_interactions(
player_query: Query<(&TilePosition, &KnowledgeGraph), With<PlayerCharacter>>,
mut player_query: Query<
(&TilePosition, &mut NearbyInteractionBuffer),
With<PlayerCharacter>,
>,
registry: Res<EntityRegistry>,
interactables: Query<
(Entity, &TilePosition, Option<&Npc>),
(With<Interactable>, Without<PlayerCharacter>),
>,
mut buffer: ResMut<NearbyInteractionBuffer>,
) {
buffer.interactions.clear();
let Ok((player_pos, knowledge)) = player_query.single() else {
let Ok((player_pos, mut buffer)) = player_query.single_mut() else {
return;
};
buffer.interactions.clear();
for (entity, pos, is_npc) in interactables.iter() {
let Some(distance) = player_pos.manhattan_distance(pos) else {
@@ -57,50 +62,26 @@ pub fn compute_nearby_interactions(
EntityKind::Object
};
// Look up relationship state from knowledge graph
let relationship = if let Some(stable_id) = registry.to_stable(entity) {
knowledge.relationship_with(&stable_id)
} else {
RelationshipState::Unknown
};
let is_poi = relationship == RelationshipState::PersonOfInterest;
let is_close = distance <= CLOSE_RANGE;
let mut verbs = Vec::new();
match entity_type {
EntityKind::Npc => {
if is_close {
if is_poi {
// Post-contradiction: Observe takes priority over Talk
verbs.push(VerbOption {
kind: VerbKind::ExamineNpc,
label: "Observe".into(),
priority: 1,
available: true,
});
verbs.push(VerbOption {
kind: VerbKind::Talk,
label: "Talk".into(),
priority: 2,
available: true,
});
} else {
// Default: Talk takes priority
verbs.push(VerbOption {
kind: VerbKind::Talk,
label: "Talk".into(),
priority: 1,
available: true,
});
verbs.push(VerbOption {
kind: VerbKind::ExamineNpc,
label: "Observe".into(),
priority: 2,
available: true,
});
}
// Default priority: Talk first, Observe second.
// Observer adjusts priority for POI entities.
verbs.push(VerbOption {
kind: VerbKind::Talk,
label: "Talk".into(),
priority: 1,
available: true,
});
verbs.push(VerbOption {
kind: VerbKind::ExamineNpc,
label: "Observe".into(),
priority: 2,
available: true,
});
} else {
// Mid range: only Examine NPC (Talk requires close range)
verbs.push(VerbOption {
@@ -131,6 +112,9 @@ pub fn compute_nearby_interactions(
// Sort by priority (lower = higher), then by kind discriminant for stability
verbs.sort_by_key(|v| (v.priority, v.kind as u8));
// Fallback to Entity::to_bits() is intentional for per-frame systems:
// panicking would crash the server every tick. The error log makes this
// loud enough to catch in testing while keeping the server alive.
let wire_id = registry
.to_stable(entity)
.map(|sid| sid.0)
@@ -156,9 +140,9 @@ pub fn compute_nearby_interactions(
/// Buffer for nearby interaction results, consumed by snapshot generation.
/// Field is private — use `take()` to drain results into the snapshot.
///
/// Global Resource — single-observer assumption (v0.1). D-009 multiplayer
/// will refactor the entire observer + interaction pipeline to per-entity.
#[derive(Resource, Debug, Default)]
/// Per-entity Component attached to the PlayerCharacter. Each observer gets
/// their own interaction buffer, so D-009 multiplayer works without refactoring.
#[derive(Component, Debug, Default)]
pub struct NearbyInteractionBuffer {
interactions: Vec<NearbyInteraction>,
}
@@ -180,116 +164,88 @@ mod tests {
fn setup_world() -> World {
let mut world = World::new();
world.init_resource::<EntityRegistry>();
world.init_resource::<NearbyInteractionBuffer>();
world
}
/// Spawn player with standard components (no KnowledgeGraph — interaction
/// system doesn't access it; POI priority is handled by observer).
fn spawn_player(world: &mut World, x: i32, y: i32) -> Entity {
world
.spawn((
PlayerCharacter,
TilePosition::new(x, y, 0),
NearbyInteractionBuffer::default(),
))
.id()
}
/// Read the player's NearbyInteractionBuffer component
fn read_buffer(world: &mut World) -> &NearbyInteractionBuffer {
let mut query = world.query_filtered::<&NearbyInteractionBuffer, With<PlayerCharacter>>();
query.single(world).unwrap()
}
#[test]
fn npc_in_close_range_gets_talk_and_observe() {
let mut world = setup_world();
world.spawn((
PlayerCharacter,
TilePosition::new(5, 5, 0),
KnowledgeGraph::new(),
));
spawn_player(&mut world, 5, 5);
world.spawn((Npc, TilePosition::new(5, 6, 0), Interactable));
let mut schedule = bevy_ecs::schedule::Schedule::default();
schedule.add_systems(compute_nearby_interactions);
schedule.run(&mut world);
let buffer = world.resource::<NearbyInteractionBuffer>();
let buffer = read_buffer(&mut world);
assert_eq!(buffer.interactions.len(), 1);
assert_eq!(buffer.interactions[0].verbs.len(), 2);
// Talk should be priority 1 (default, not POI)
assert_eq!(buffer.interactions[0].verbs[0].kind, VerbKind::Talk);
assert_eq!(buffer.interactions[0].verbs[0].priority, 1);
assert_eq!(buffer.interactions[0].verbs[1].kind, VerbKind::ExamineNpc);
assert_eq!(buffer.interactions[0].verbs[1].priority, 2);
}
#[test]
fn npc_in_mid_range_gets_observe_only() {
let mut world = setup_world();
world.spawn((
PlayerCharacter,
TilePosition::new(5, 5, 0),
KnowledgeGraph::new(),
));
// Distance 4 (mid range, beyond close)
spawn_player(&mut world, 5, 5);
world.spawn((Npc, TilePosition::new(5, 9, 0), Interactable));
let mut schedule = bevy_ecs::schedule::Schedule::default();
schedule.add_systems(compute_nearby_interactions);
schedule.run(&mut world);
let buffer = world.resource::<NearbyInteractionBuffer>();
let buffer = read_buffer(&mut world);
assert_eq!(buffer.interactions.len(), 1);
assert_eq!(buffer.interactions[0].verbs.len(), 1);
assert_eq!(buffer.interactions[0].verbs[0].kind, VerbKind::ExamineNpc);
assert_eq!(buffer.interactions[0].verbs[0].priority, 1);
}
#[test]
fn npc_out_of_range_no_interactions() {
let mut world = setup_world();
world.spawn((
PlayerCharacter,
TilePosition::new(5, 5, 0),
KnowledgeGraph::new(),
));
// Distance 8 (beyond mid range)
spawn_player(&mut world, 5, 5);
world.spawn((Npc, TilePosition::new(5, 13, 0), Interactable));
let mut schedule = bevy_ecs::schedule::Schedule::default();
schedule.add_systems(compute_nearby_interactions);
schedule.run(&mut world);
let buffer = world.resource::<NearbyInteractionBuffer>();
let buffer = read_buffer(&mut world);
assert!(buffer.interactions.is_empty());
}
#[test]
fn poi_npc_observe_takes_priority() {
let mut world = setup_world();
let mut registry = EntityRegistry::new(0);
let npc = world
.spawn((Npc, TilePosition::new(5, 6, 0), Interactable))
.id();
let npc_sid = registry.register(npc);
let mut kg = KnowledgeGraph::new();
kg.observe_entity(npc_sid, TilePosition::new(5, 6, 0), 50);
kg.set_relationship(&npc_sid, RelationshipState::PersonOfInterest);
world.spawn((PlayerCharacter, TilePosition::new(5, 5, 0), kg));
world.insert_resource(registry);
let mut schedule = bevy_ecs::schedule::Schedule::default();
schedule.add_systems(compute_nearby_interactions);
schedule.run(&mut world);
let buffer = world.resource::<NearbyInteractionBuffer>();
assert_eq!(buffer.interactions.len(), 1);
// Observe should be priority 1 for POI NPC
assert_eq!(buffer.interactions[0].verbs[0].kind, VerbKind::ExamineNpc);
assert_eq!(buffer.interactions[0].verbs[1].kind, VerbKind::Talk);
}
#[test]
fn object_in_close_range_gets_examine() {
let mut world = setup_world();
world.spawn((
PlayerCharacter,
TilePosition::new(5, 5, 0),
KnowledgeGraph::new(),
));
// Object (no Npc component) at close range
spawn_player(&mut world, 5, 5);
world.spawn((TilePosition::new(5, 6, 0), Interactable));
let mut schedule = bevy_ecs::schedule::Schedule::default();
schedule.add_systems(compute_nearby_interactions);
schedule.run(&mut world);
let buffer = world.resource::<NearbyInteractionBuffer>();
let buffer = read_buffer(&mut world);
assert_eq!(buffer.interactions.len(), 1);
assert_eq!(buffer.interactions[0].verbs.len(), 1);
assert_eq!(buffer.interactions[0].verbs[0].kind, VerbKind::ExamineObject);
@@ -298,39 +254,29 @@ mod tests {
#[test]
fn different_z_level_no_interactions() {
let mut world = setup_world();
world.spawn((
PlayerCharacter,
TilePosition::new(5, 5, 0),
KnowledgeGraph::new(),
));
spawn_player(&mut world, 5, 5);
world.spawn((Npc, TilePosition::new(5, 6, 1), Interactable));
let mut schedule = bevy_ecs::schedule::Schedule::default();
schedule.add_systems(compute_nearby_interactions);
schedule.run(&mut world);
let buffer = world.resource::<NearbyInteractionBuffer>();
let buffer = read_buffer(&mut world);
assert!(buffer.interactions.is_empty());
}
#[test]
fn multiple_entities_sorted_by_distance() {
let mut world = setup_world();
world.spawn((
PlayerCharacter,
TilePosition::new(5, 5, 0),
KnowledgeGraph::new(),
));
// Farther NPC
spawn_player(&mut world, 5, 5);
world.spawn((Npc, TilePosition::new(5, 9, 0), Interactable));
// Closer NPC
world.spawn((Npc, TilePosition::new(5, 6, 0), Interactable));
let mut schedule = bevy_ecs::schedule::Schedule::default();
schedule.add_systems(compute_nearby_interactions);
schedule.run(&mut world);
let buffer = world.resource::<NearbyInteractionBuffer>();
let buffer = read_buffer(&mut world);
assert_eq!(buffer.interactions.len(), 2);
assert!(buffer.interactions[0].distance < buffer.interactions[1].distance);
}
@@ -338,59 +284,21 @@ mod tests {
#[test]
fn non_interactable_entity_ignored() {
let mut world = setup_world();
world.spawn((
PlayerCharacter,
TilePosition::new(5, 5, 0),
KnowledgeGraph::new(),
));
// NPC without Interactable component
spawn_player(&mut world, 5, 5);
world.spawn((Npc, TilePosition::new(5, 6, 0)));
let mut schedule = bevy_ecs::schedule::Schedule::default();
schedule.add_systems(compute_nearby_interactions);
schedule.run(&mut world);
let buffer = world.resource::<NearbyInteractionBuffer>();
let buffer = read_buffer(&mut world);
assert!(buffer.interactions.is_empty());
}
#[test]
fn poi_npc_at_mid_range_gets_observe_only() {
// POI priority flip only applies at close range — mid range always Observe-only
let mut world = setup_world();
let mut registry = EntityRegistry::new(0);
let npc = world
.spawn((Npc, TilePosition::new(5, 9, 0), Interactable))
.id();
let npc_sid = registry.register(npc);
let mut kg = KnowledgeGraph::new();
kg.observe_entity(npc_sid, TilePosition::new(5, 9, 0), 50);
kg.set_relationship(&npc_sid, RelationshipState::PersonOfInterest);
world.spawn((PlayerCharacter, TilePosition::new(5, 5, 0), kg));
world.insert_resource(registry);
let mut schedule = bevy_ecs::schedule::Schedule::default();
schedule.add_systems(compute_nearby_interactions);
schedule.run(&mut world);
let buffer = world.resource::<NearbyInteractionBuffer>();
assert_eq!(buffer.interactions.len(), 1);
assert_eq!(buffer.interactions[0].verbs.len(), 1);
assert_eq!(buffer.interactions[0].verbs[0].kind, VerbKind::ExamineNpc);
}
#[test]
fn equidistant_npcs_sorted_deterministically() {
let mut world = setup_world();
// Two NPCs at equal distance (1 tile each)
world.spawn((
PlayerCharacter,
TilePosition::new(5, 5, 0),
KnowledgeGraph::new(),
));
spawn_player(&mut world, 5, 5);
world.spawn((Npc, TilePosition::new(6, 5, 0), Interactable));
world.spawn((Npc, TilePosition::new(4, 5, 0), Interactable));
@@ -398,9 +306,8 @@ mod tests {
schedule.add_systems(compute_nearby_interactions);
schedule.run(&mut world);
let buffer = world.resource::<NearbyInteractionBuffer>();
let buffer = read_buffer(&mut world);
assert_eq!(buffer.interactions.len(), 2);
// Both at distance 1 — order should be stable across runs
assert_eq!(buffer.interactions[0].distance, buffer.interactions[1].distance);
}
}
+1 -5
View File
@@ -23,7 +23,6 @@ impl Plugin for SimulationPlugin {
app.init_resource::<time::SimulationTime>()
.insert_resource(rng::SimRng::new(0))
.init_resource::<input::InputQueue>()
.init_resource::<interaction::NearbyInteractionBuffer>()
.init_resource::<crate::knowledge::EntityRegistry>()
.add_systems(
Update,
@@ -32,12 +31,9 @@ impl Plugin for SimulationPlugin {
pathfinding::compute_paths.after(input::process_player_input),
path_follow::follow_paths.after(pathfinding::compute_paths),
movement::validate_movement.after(path_follow::follow_paths),
interaction::compute_nearby_interactions
.after(movement::validate_movement),
path_follow::cleanup_path_blocked.after(movement::validate_movement),
time::advance_tick
.after(path_follow::cleanup_path_blocked)
.after(interaction::compute_nearby_interactions),
.after(path_follow::cleanup_path_blocked),
),
);
+31
View File
@@ -199,6 +199,37 @@ mod tests {
assert_eq!(time.day(), 3);
}
#[test]
fn tick_rate_switch_mid_accumulation() {
// Half->Full with 0.5 remainder: Full should tick immediately (0.5 + 1.0 >= 1.0)
let mut world = bevy_ecs::world::World::new();
world.insert_resource(SimulationTime { tick_rate: TickRate::Half, ..Default::default() });
let mut schedule = bevy_ecs::schedule::Schedule::default();
schedule.add_systems(advance_tick);
// Frame 1: Half rate, accumulate 0.5, no tick
schedule.run(&mut world);
assert_eq!(world.resource::<SimulationTime>().tick, 0);
// Switch to Full mid-accumulation (0.5 remainder)
world.resource_mut::<SimulationTime>().tick_rate = TickRate::Full;
// Frame 2: Full rate adds 1.0 to 0.5 remainder → tick fires
schedule.run(&mut world);
assert_eq!(world.resource::<SimulationTime>().tick, 1);
// Switch to Paused: no advance regardless of accumulator
world.resource_mut::<SimulationTime>().tick_rate = TickRate::Paused;
schedule.run(&mut world);
assert_eq!(world.resource::<SimulationTime>().tick, 1);
// Switch back to Half: accumulator still has 0.5 from overshoot
world.resource_mut::<SimulationTime>().tick_rate = TickRate::Half;
schedule.run(&mut world);
// 0.5 (leftover) + 0.5 (Half) = 1.0 → tick fires
assert_eq!(world.resource::<SimulationTime>().tick, 2);
}
#[test]
fn half_rate_no_drift_over_10000_frames() {
let mut world = bevy_ecs::world::World::new();
+2
View File
@@ -7,6 +7,7 @@ use settled_reach_server::bridge::tcp::TcpBridge;
use settled_reach_server::bridge::types::*;
use settled_reach_server::bridge::{BridgePlugin, BridgeResource};
use settled_reach_server::knowledge::{KnowledgeGraph, KnowledgePlugin};
use settled_reach_server::simulation::interaction::NearbyInteractionBuffer;
use settled_reach_server::simulation::movement::{PlayerCharacter, TilePosition, WalkabilityMap};
use settled_reach_server::simulation::SimulationPlugin;
use std::io::{BufReader, BufWriter};
@@ -34,6 +35,7 @@ fn player_moves_north_through_full_pipeline() {
PlayerCharacter,
TilePosition::new(16, 16, 0),
KnowledgeGraph::new(),
NearbyInteractionBuffer::default(),
));
// Run one tick: receive input, process, validate movement, generate snapshot, send
+30 -1
View File
@@ -7,7 +7,7 @@ use std::fs;
/// Helper to create a minimal v2 snapshot for tests
fn test_snapshot(tick: u64, entities: Vec<VisibleEntity>) -> ObserverSnapshot {
ObserverSnapshot {
version: 4,
version: PROTOCOL_VERSION,
tick,
game_time: GameTime {
day: 0,
@@ -231,6 +231,35 @@ fn snapshot_v2_fields_roundtrip() {
assert_eq!(decoded.entities[0].visibility, VisibilitySector::Forward);
}
/// Entity::to_bits() must roundtrip through from_bits() — guards against
/// bevy version changes silently breaking wire IDs (Hoshe #12).
#[test]
fn entity_to_bits_roundtrip() {
use bevy_ecs::entity::Entity;
// Create entities via a World so we get valid index+generation pairs
let mut world = bevy_ecs::world::World::new();
let e1 = world.spawn_empty().id();
let e2 = world.spawn_empty().id();
let e3 = world.spawn_empty().id();
// Despawn and respawn to get a higher generation
world.despawn(e2);
let e4 = world.spawn_empty().id();
for entity in [e1, e2, e3, e4] {
let bits = entity.to_bits();
let restored = Entity::from_bits(bits);
assert_eq!(entity, restored, "Entity::to_bits() roundtrip failed for {:?}", entity);
}
}
/// PROTOCOL_VERSION constant matches snapshot version field
#[test]
fn protocol_version_constant_matches_snapshot() {
let snapshot = test_snapshot(0, vec![]);
assert_eq!(snapshot.version, PROTOCOL_VERSION);
assert_eq!(PROTOCOL_VERSION, 4, "bump this assertion when protocol version changes");
}
/// All FacingDirection variants round-trip
#[test]
fn all_facing_direction_variants_roundtrip() {
+132
View File
@@ -0,0 +1,132 @@
#!/usr/bin/env python3
"""Validate content YAML files against their JSON schemas.
Schema mapping is by directory context:
campaign.yaml → campaign.schema.json
system.yaml → system.schema.json
station.yaml → station.schema.json
district.yaml → district.schema.json
npcs/*.yaml → npc-profile.schema.json
locations/*.yaml → location.schema.json
triangles/*.yaml → triangle.schema.json
dialogue/**/*.yaml → dialogue-pool.schema.json
monologue/**/*.yaml → monologue-pool.schema.json
routines/*.yaml → routine.schema.json
Files under global/ and content.yaml are skipped (no schema yet).
Exit code 0 = all valid, 1 = validation errors found.
"""
import json
import sys
from pathlib import Path
import jsonschema
import yaml
CONTENT_DIR = Path(__file__).resolve().parent.parent / "content"
SCHEMA_DIR = CONTENT_DIR / "_schema"
# Map directory parent name (or filename) to schema file
FILENAME_SCHEMAS = {
"campaign.yaml": "campaign.schema.json",
"system.yaml": "system.schema.json",
"station.yaml": "station.schema.json",
"district.yaml": "district.schema.json",
}
DIR_SCHEMAS = {
"npcs": "npc-profile.schema.json",
"locations": "location.schema.json",
"triangles": "triangle.schema.json",
"dialogue": "dialogue-pool.schema.json",
"monologue": "monologue-pool.schema.json",
"routines": "routine.schema.json",
}
def resolve_schema(yaml_path: Path) -> Path | None:
"""Determine which schema applies to a content YAML file."""
name = yaml_path.name
if name in FILENAME_SCHEMAS:
return SCHEMA_DIR / FILENAME_SCHEMAS[name]
# Walk up parents to find a matching directory name
rel = yaml_path.relative_to(CONTENT_DIR)
for part in reversed(rel.parts[:-1]):
if part in DIR_SCHEMAS:
return SCHEMA_DIR / DIR_SCHEMAS[part]
return None
def main() -> int:
errors = 0
validated = 0
skipped = 0
# Cache loaded schemas
schema_cache: dict[str, dict] = {}
campaigns_dir = CONTENT_DIR / "campaigns"
if not campaigns_dir.exists():
print(f"No campaigns directory at {campaigns_dir}", file=sys.stderr)
return 1
yaml_files = sorted(campaigns_dir.rglob("*.yaml"))
if not yaml_files:
print("No YAML files found under campaigns/", file=sys.stderr)
return 1
for yaml_path in yaml_files:
schema_path = resolve_schema(yaml_path)
if schema_path is None:
skipped += 1
continue
if not schema_path.exists():
print(f"MISSING SCHEMA: {schema_path.name} for {yaml_path.relative_to(CONTENT_DIR)}")
errors += 1
continue
# Load schema (cached)
schema_key = str(schema_path)
if schema_key not in schema_cache:
with open(schema_path) as f:
schema_cache[schema_key] = json.load(f)
schema = schema_cache[schema_key]
# Load YAML
try:
with open(yaml_path) as f:
data = yaml.safe_load(f)
except yaml.YAMLError as e:
print(f"YAML ERROR: {yaml_path.relative_to(CONTENT_DIR)}: {e}")
errors += 1
continue
if data is None:
# Comment-only or empty placeholder files are valid stubs
skipped += 1
continue
# Validate
try:
jsonschema.validate(instance=data, schema=schema)
validated += 1
except jsonschema.ValidationError as e:
rel = yaml_path.relative_to(CONTENT_DIR)
print(f"INVALID: {rel}")
print(f" Schema: {schema_path.name}")
print(f" Error: {e.message}")
if e.absolute_path:
print(f" Path: {'.'.join(str(p) for p in e.absolute_path)}")
errors += 1
print(f"\nValidated {validated} files, {skipped} skipped, {errors} errors")
return 1 if errors else 0
if __name__ == "__main__":
sys.exit(main())