3 rounds of SW1 (endgame feature vision) with 5 agents + Qatux. Produced endgame-feature-vision.md (692 lines) covering the full galaxy-to-ground generation pipeline, cultural cascade, replayability architecture, and player experience beats. Workshop was cut short when PO redirected to a 6-phase development cascade (wiki content → economics → planetary maps → player control → world gen → detail coloring). v0.2 target dropped. Heritage roots (D-104/D-105/D-101/D-107) flagged for supersession — real-world cultural corridors replace abstract roots. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
40 KiB
Tyre — SW1-R3: Endgame Pipeline Architecture
Sub-workshop 1, Round 3 — Final Technical Synthesis Declarative. This is the architecture as it will be.
1. The 8-Layer Pipeline — Updated Status
Layer 1: Galaxy (300 systems) ██████████ BUILT — systems.db + wiki + star map
Layer 2: Location profiles ░░░░░░░░░░ AUTHORED — wiki pipeline, not generated
Layer 3: Planetary topography ░░░░░░░░░░ AUTHORED — annotated heightmaps + metadata
Layer 4: Inbound gateways ████░░░░░░ DESIGNED — D-093/D-095, template stamped
Layer 5: Content spidering ████░░░░░░ ARCH BUILT — D-026 tiers + chunk streaming
Layer 6: Settlement generation ░░░░░░░░░░ NOT BUILT — placement on authored terrain
Layer 7: District → building ███░░░░░░░ DATA MODEL — generator.rs, no algorithms
Layer 8: 2D → 3D rendering ███░░░░░░░ MIGRATING — D-148/D-149 in progress
Layer-by-Layer Architecture
Layer 1: Galaxy — READ ONLY
The game server loads systems.db at startup and builds an in-memory graph of 300+1 systems. Each node carries: economic base, cultural corridor, gate topology, faction presence, habitable body count, narrative hooks, silence topics, ambient anxiety. The graph supports BFS pathfinding, neighbor lookup, and attribute filtering. Memory footprint: ~1-2MB. Load time: milliseconds.
The galaxy is fixed and canonical. Same 300 systems, same wormhole routes, every run. Internal economic/political state starts identical and drifts via background simulation. Player knowledge of system geography is durable across playthroughs.
Engineering required: Rust loader for systems.db → in-memory graph. Cost: days.
Layer 2: Location Profiles — AUTHORED, READ ONLY
Every inhabited planet, station, moon, and orbital in the 300 systems receives a wiki-authored profile. The generator does not generate profiles — it reads them.
Profile structure (per location):
LocationProfile
├─ location_type: Planet | Station | Moon | Orbital | AsteroidBase
├─ physical: gravity, atmosphere, biome_summary, terrain_reference (→ Layer 3 heightmap)
├─ settlement: population, economic_role[], founding_age, founding_culture
├─ cultural: cultural_corridor (from systems.db), heritage_persistence, industrial_corridor
├─ infrastructure: spaceport_class, transit_connections, district_count
├─ economy: industries[], power_players[], trade_imports[], trade_exports[]
└─ narrative: gttr_entry_path, planetary_screenshot_path, arrival_type_default
The wiki → data pipeline parses authored markdown into structured LocationProfile records. Validation ensures completeness and internal consistency (a location with spaceport_class: none cannot be a gate_topology: hub).
Engineering required: Wiki markdown → LocationProfile parser + validator. Cost: 1-2 weeks. Content authoring is external and runs in parallel.
Placeholder strategy: For engineering testing before wiki content is complete, generate provisional profiles from systems.db attributes using the same rules that would have been the R1b generation grammar. These are flagged "source": "provisional" and replaced by authored content on merge.
Layer 3: Planetary Topography — AUTHORED, READ ONLY
Each inhabited planet/moon receives an annotated heightmap package:
heightmaps/
GJ-699/
proxima-b/
heightmap.png # Grayscale 16-bit PNG. Elevation data.
metadata.json # Scale, origin, resolution, sea_level, units
rivers.png # Binary overlay: river channels
coastlines.png # Binary overlay: coastline boundaries
biome_zones.json # Named polygon regions + biome type per region
settlements.json # Settlement center points + approximate radius
screenshot.png # Planetary shader screenshot for GTTR/wiki
The generator consumes this package at settlement generation time (Layer 6). The heightmap provides hard constraints: mountains block corridors, rivers create district boundaries, coastlines produce port districts. The biome_zones.json maps regions to SettingType constraints (coastal → Urban/Maritime, mountain valley → Agricultural/Wilderness).
Heightmap ingestion pipeline:
- Load grayscale PNG → elevation grid at authored resolution
- Overlay rivers and coastlines as passability constraints
- Read biome zone polygons → tag each settlement site with terrain type
- Cache the processed constraint data per location (MessagePack, ~50-200KB per planet)
Engineering required: Heightmap ingestion + constraint extraction + caching. Cost: 2-3 weeks. Authoring tooling (the planetary shader plugin for screenshots) is separate scope.
For stations and orbitals: No heightmap. Module topology is authored as a structured JSON (ring sections, module connections, access corridors). Same principle: authored structure, generator reads it.
Layer 4: Inbound Gateways — TEMPLATE STAMPED
When the player arrives at a new system, they come through a horizon gate. The gate infrastructure is a Station SettingType district generated from template blocks.
Gate arrival architecture:
- Gate transit: Player exits through a horizon gate aperture into a gate cluster block (D-093). Template-stamped: customs inspection area, transit hub, Commission/Institute monitoring presence scaled by system importance.
- Transit to destination: Diegetic loading transition. Charter flight = port arrival scene. Normal transit = orbital view with GTTR insert window. Smuggler route = low-orbit darkness.
- First impression: The guarantee system (D-097) enforces that from the gate entry point, the player sees at least one zone transition within 30 tiles and at least one readable arrival signal (security density, commercial activity, maintenance condition).
Engineering required: Gate cluster templates + arrival guarantee constraints. Cost: included in Station SettingType Phase 2 + 1-2 days for gate-specific templates.
Layer 5: Content Spidering — PROGRESSIVE GENERATION
Generation is triggered progressively, hidden behind diegetic transitions. Maps to D-026 simulation tiers:
UNGENERATED ──[player enters system]──→ STATE-SAVED ──[player approaches]──→ BACKGROUND ──[player arrives]──→ ACTIVE
│ │ │ │
│ systems.db graph node only │ LocationProfile loaded │ Phase 2 chunks filled │ Full sim running
│ No generation data │ Phase 1 skeleton generated │ NPC state machines active │ 10 tps, full perception
│ │ ~1-2KB per district skeleton │ Chunk streaming ready │ All components attached
Progressive generation trigger sequence:
| Player Action | Generation Triggered | Runs Where | Latency Budget |
|---|---|---|---|
| Starts new game | Load systems.db. Generate LocationProfiles for home system. Phase 1 + Phase 2 for starting district. | Main thread (one-time startup) | 2-5 seconds (acceptable for new game) |
| Enters transit to new system | LocationProfiles for destination (if not authored yet → load from wiki data). Phase 1 skeletons for destination district + neighbors. | tokio background tasks during transit loading | Hidden behind transit scene (5-30 seconds of diegetic travel) |
| Arrives at destination | Phase 2 chunk fill for destination district within streaming radius. Activate NPCs. | Main thread (generation) + chunk_streaming.rs (loading) | <500ms total. BSP fills a chunk in <5ms. 8-chunk radius = ~40ms. |
| Walks toward district boundary | Phase 2 for adjacent district chunks (pre-generation). | tokio background tasks | Hidden behind walking time. Pre-generate 2 chunks ahead. |
Heightmap consumption in progressive generation:
When generating a settlement (Layer 6), the generator loads the planet's cached constraint data. Settlement center points from settlements.json are already authored. The generator places districts around those points, respecting elevation constraints, river boundaries, and biome zones. This happens once per settlement, during the transit-to-system phase.
Engineering required: Generation trigger hooks in chunk_streaming.rs (the TODO already exists). Async task spawning for background generation. Cache management (LRU + MessagePack persistence). Cost: 3-4 weeks.
Layer 6: Settlement Generation — CONSTRAINT PLACEMENT ON AUTHORED TERRAIN
Given a LocationProfile (population, economic role, industries) and authored terrain constraints (heightmap + settlements.json), generate the settlement's district layout.
Settlement generation algorithm:
Input: LocationProfile + terrain constraints
│
├─ 1. Read settlement center point from settlements.json
├─ 2. Determine district count from population + economic complexity
│ (population 500K → 8-12 districts, population 2K → 1-2 districts)
├─ 3. Assign district types from economic role
│ (manufacturing base → 3 Industrial, 2 Commercial, 2 Residential, 1 Transit, 1-4 Mixed)
├─ 4. Place districts on terrain, respecting:
│ - River boundaries (districts don't cross rivers; bridges connect them)
│ - Elevation constraints (no district on a mountain peak)
│ - Coastline (Maritime districts on coast, port infrastructure)
│ - Biome zones (Agricultural districts on arable land)
├─ 5. Generate road/transit network between districts
│ (graph: districts as nodes, roads as edges, respecting terrain)
├─ 6. Compute district boundary access points
│ (each shared boundary has 1-3 access points for stitching)
└─ Output: SettlementSkeleton with district positions, types, boundaries, road network
CityCrafter3D patterns apply at this level: grid-based block layout, noise-based density variation, road merging at intersections, multi-block reservations. The key difference: our placement is constrained by authored terrain, not by noise alone.
Engineering required: Settlement placement algorithm + road network generation + district boundary access point computation. Cost: 2-4 weeks. District boundary stitching (ensuring adjacent districts have compatible edges) is the hardest sub-problem.
Layer 7: District → Building — FULL GENERATION (Phase 1 + Phase 2)
This is the algorithmic heart of the generator. Data model is built (generator.rs, 579 lines). Algorithms are not.
Phase 1: Skeleton Generation
Input: DistrictType + SettingType + boundary constraints + economic pressure state
│
├─ 1. 4×4 block grid allocation
├─ 2. Zoning assignment (Residential/Commercial/Industrial/Mixed/Civic/Transit/Green)
│ driven by DistrictType + economic pressure
├─ 3. Multi-block reservation placement (landmarks, parks, terminals)
├─ 4. Corridor spine carving (main paths through district)
├─ 5. Social site placement (bars, markets, offices, clinics) with triangle assignments
├─ 6. Era tagging per block
│ driven by simulation state, not seed
│ (Prosperous district → Commission-era blocks. Declining district → Decay/EmergencyExtension blocks.)
├─ 7. Guarantee audit (D-097)
│ Tier 1: every inhabited district has Social Hub + Informal Zone + Encounter Corridor
│ Tier 2: Full-complexity districts get full 13-check audit
│ Initially: audit logs warnings, does not block generation
└─ Output: DistrictSkeleton
Phase 2: Chunk Fill
Input: BlockSkeleton + ZonePalette + economic pressure state
│
├─ 1. BSP room subdivision (Binary Space Partition)
│ Recursive split produces organic room layouts
│ Integer math only (D-010 determinism)
│ Seeded per-block via SimRng for consistent regeneration
├─ 2. Template stamping for social site rooms
│ Bar template, market template, office template → placed in BSP-carved space
├─ 3. Corridor carving (L-shaped or A* between room centers)
├─ 4. Door placement at room-corridor intersections
├─ 5. Object/furniture placement
│ driven by room function + economic pressure + cultural corridor
│ a prosperous Commonwealth bar ≠ a declining East Asian bar
├─ 6. Condition application
│ Pristine/Functional/Worn/Derelict derived from economic pressure state
│ Block era modifies baseline (Decay-era blocks start at Worn minimum)
└─ Output: chunk tile data (TileKind grid + object placements + door positions)
The generator reads simulation pressure state, not seeds. Two players visiting the same district at different simulation times get different era tags, different condition levels, different object density. The canonical starting state produces the same district on game-start. Simulation drift produces visible changes over time.
Zone type families share Phase 2 fill logic:
| Family | Members | Fill Algorithm | Shared Code |
|---|---|---|---|
| Interior | Station, Orbital, Specialized | BSP rooms + corridors + doors | ~80% shared. Station is the base implementation. |
| Urban | Urban, Industrial, Maritime | Lot subdivision + building footprints + street network | ~70% shared. Building density and height differ. |
| Natural | Wilderness, Agricultural, Water | Terrain mesh + vegetation/crop placement + path network | ~60% shared. Agricultural has regular grid patterns; wilderness is noise-based. |
Engineering required: Phase 1 skeleton (3-5 weeks, constraint satisfaction is the hard part). Phase 2 fill for Interior family (2-3 weeks, BSP is well-understood). Phase 2 for Urban family (2-3 weeks, CityCrafter3D patterns transfer). Phase 2 for Natural family (2-3 weeks, RetroTerrain shader technique applies). Total: 9-14 weeks.
Layer 8: 2D → 3D Rendering — RENDERING-AGNOSTIC DATA
The server generates tile grids with type + z-level + position. The renderer interprets them. The current 2D TileMapLayer renderer filters z > 0 and displays what it can. The 3D renderer (D-148 Camera3D + GridMap) replaces it when ready. Zero throwaway work in the generation pipeline.
Engineering required: 3D renderer migration (ongoing, Sprint 28+). Cost: 2-4 weeks for the GridMap rendering path. Not on the generation critical path.
2. Data Flow
┌─────────────────────────────────────────────────────────────────────┐
│ AUTHORED CONTENT │
│ │
│ wiki/star-systems/ ──→ systems.db (300 systems, 7 tables) │
│ wiki/locations/ ──→ LocationProfile[] (per planet/station) │
│ heightmaps/ ──→ Terrain constraint cache (per planet) │
│ wiki/gttr/ ──→ GTTR entries (per system) │
│ cultural-migration- ──→ Cultural corridor data (per system) │
│ pressure.md │
└────────────────┬────────────────────────────────────────────────────┘
│ loaded at game start
▼
┌─────────────────────────────────────────────────────────────────────┐
│ RUST SERVER (bevy_ecs) │
│ │
│ ┌──────────────────┐ ┌──────────────────────────────────┐ │
│ │ Galaxy Graph │ │ Background Pressure Sim (tokio) │ │
│ │ 300 system nodes │◄──►│ Economic: trade flows, industry │ │
│ │ gate topology │ │ Social: satisfaction, tension │ │
│ │ faction state │ │ Political: faction influence │ │
│ └────────┬─────────┘ │ Updates every game-minute │ │
│ │ └──────────────┬───────────────────┘ │
│ │ │ pressure deltas │
│ ▼ ▼ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ GENERATOR PIPELINE │ │
│ │ │ │
│ │ LocationProfile + TerrainConstraints + PressureState │ │
│ │ │ │ │
│ │ ├─→ Settlement placement (Layer 6) │ │
│ │ ├─→ DistrictSkeleton (Phase 1) │ │
│ │ └─→ Chunk tile data (Phase 2) │ │
│ │ │ │ │
│ └────────────────────┼──────────────────────────────────────┘ │
│ │ │
│ ┌────────────────────┼──────────────────────────────────────┐ │
│ │ ENTITY SIMULATION (D-026 tiers) │ │
│ │ │ │ │
│ │ Active (80 NPCs) ◄── tile data + NPC spawns │ │
│ │ Background (2K) ◄── state machine updates from pressure │ │
│ │ State-saved (10K+)◄── skeleton data only │ │
│ └────────────────────┼──────────────────────────────────────┘ │
│ │ ObserverSnapshot (MessagePack) │
│ ▼ │
└─────────────────────────────────────────────────────────────────────┘
│ IPC (MessagePack, D-020)
▼
┌─────────────────────────────────────────────────────────────────────┐
│ GODOT CLIENT │
│ │
│ ObserverSnapshot ──→ WorldRenderer ──→ GridMap (3D tiles) │
│ ──→ EntityRenderer ──→ CharacterCompositor (3D) │
│ ──→ FogRenderer ──→ Fog shader │
│ ──→ UIRenderer ──→ Insert HUD, galaxy map, GTTR │
└─────────────────────────────────────────────────────────────────────┘
3. Progressive Generation Behind Diegetic Transitions
Every generation step is hidden behind a moment where the player is already waiting for something else.
| Diegetic Moment | What the Player Experiences | What Generates Behind It |
|---|---|---|
| New game start | Character creation + opening narrative | Home system LocationProfiles loaded. Starting district Phase 1 + Phase 2. Starting NPCs spawned. (2-5 seconds) |
| Gate transit | Transit scene: gate aperture VFX, insert shows GTTR entry + planetary screenshot, system stats | Destination system LocationProfiles. Destination terrain constraints loaded. Settlement placement. Phase 1 skeletons for destination + neighbor districts. (5-15 seconds, hidden behind 10-30 second transit scene) |
| Shuttle/transit ride | Interior of shuttle (MobileChunk) or loading screen with diegetic framing | Phase 2 fill for destination district. NPC population spawned for destination. Adjacent district Phase 1 pre-generation. (1-5 seconds) |
| Walking to district boundary | Player walks through corridor/street toward next district | Adjacent district Phase 2 chunks pre-generated 2 chunks ahead of player position. Chunk streaming activates. (<100ms per chunk, invisible) |
| Entering a building | Door transition (brief fade or animation) | Building interior LOD promotion: LOD 0 → LOD 1 (BusinessStats derived). Staff/patron NPCs spawned if first visit. (<50ms) |
Heightmap consumption timing: Terrain constraint data is loaded once per planet, during the gate transit phase. The processed constraint cache (~50-200KB) is kept in memory for the duration of the player's stay in the system. When the player leaves the system, the cache is evicted (LRU). If they return, it reloads from disk cache.
The player never sees a "generating..." screen after new-game startup. All subsequent generation runs during diegetic transitions that are already part of the game's pacing.
4. Background Simulation Architecture
Two-Layer Model
The simulation runs on two independent layers with different execution models:
| Layer | Scope | Data Model | Execution | Clock |
|---|---|---|---|---|
| Entity Simulation | Per-NPC: movement, perception, dialogue, routines | bevy_ecs World (Components on Entities) | Main tick thread, 10 tps | SimulationTime (tick counter) |
| Pressure Simulation | Per-system, per-settlement: economics, faction balance, social tension, trade flows | Standalone structs (not ECS) | tokio thread pool, capped | Game-minute clock (deterministic) |
Pressure Simulation — tokio Architecture
// Conceptual structure — not final API
struct PressureSimRuntime {
runtime: tokio::runtime::Runtime, // Capped thread pool (N = available_cores - 2)
state: Arc<RwLock<PressureState>>,
result_rx: crossbeam::channel::Receiver<PressureDelta>,
snapshot_tx: crossbeam::channel::Sender<PressureSnapshot>,
}
struct PressureState {
systems: BTreeMap<SystemId, SystemPressure>,
trade_flows: BTreeMap<(SystemId, SystemId), TradeFlow>,
faction_influence: BTreeMap<(FactionId, SystemId), f64>,
// All collections BTreeMap for D-010 determinism
}
struct SystemPressure {
economic_health: ProsperityLevel, // Prosperous/Growing/Stable/Faltering/Declining
industries: BTreeMap<IndustryId, IndustryHealth>,
social_satisfaction: f64,
political_stability: f64,
trade_imports: Vec<TradeGood>,
trade_exports: Vec<TradeGood>,
recent_events: Vec<PressureEvent>,
}
Game-Minute Clock — Determinism Guarantee
Main sim tick (100ms real-time at 10 tps)
│
├─ Every 10 ticks (= 1 game-minute):
│ ├─ Snapshot current PressureState → PressureSnapshot
│ ├─ Send snapshot to tokio runtime via channel
│ └─ Collect any completed PressureDelta from previous minute
│ └─ Apply deltas to PressureState in fixed order (system_id ascending)
│
tokio runtime (background, capped CPU):
│
├─ Receives PressureSnapshot
├─ Runs pressure simulation for game-minute N:
│ ├─ Trade flow propagation (bulk imports/exports between connected systems)
│ ├─ Industry health updates (supply availability, demand, corporate events)
│ ├─ Social pressure calculation (satisfaction from economic health + political stability)
│ ├─ Faction influence shifts (from economic leverage, political events, player actions)
│ └─ Event injection (storyteller may inject corporate disasters, portal closings, political shifts)
├─ Produces PressureDelta
└─ Sends delta via channel → main sim consumes on next game-minute boundary
Determinism: The tokio runtime scheduling is non-deterministic (tasks may run in any order), but each task is internally deterministic (BTreeMap iteration order, seeded where needed). Results are collected and applied in a fixed order (system_id ascending) at the game-minute boundary. Slower machines accrue sim debt (pressure sim falls behind) but produce identical results when they catch up. This is the frame-independent physics pattern applied to economic simulation.
CPU capping: tokio::runtime::Builder::new_multi_thread().worker_threads(N) where N = std::thread::available_parallelism() - 2 (reserve 2 cores for main sim + OS). Minimum N = 1. The runtime processes as many game-minutes as it can within available CPU. If it falls behind, the pressure sim is simply older — entities read slightly stale pressure data, which is acceptable (economic conditions don't change meaningfully within seconds of game-time).
Propagation Network Model
The PO's reframe: the background sim is a propagation network, not a chaos engine. Events propagate through the trade/political network:
Event: Corporate disaster at System A (factory explosion)
│
├─ System A: industry health drops, unemployment rises, export volume decreases
├─ Trade partners of A: import shortage for A's exports, price increase
├─ 2nd-degree partners: substitution effects (who else can supply what A exported?)
├─ Faction with presence at A: influence drops at A, may shift resources from other systems
└─ Political: governance stability at A drops, Commission may intervene
Bulk import/export lists per system (already implied by systems.db economic data) drive the trade flow graph. Every system has inputs and outputs. Disruptions cascade. The interconnected network topology naturally creates non-trivial propagation patterns.
Equilibrium is signal, not failure. A stable economy shows the effect of the player's choices (or non-choices). The player reads economic stability or instability as information. The system doesn't need artificial chaos injection — event injection from the storyteller + player actions provide sufficient perturbation.
5. NPC/News Generation Hook Architecture
Hook System
┌─────────────────────────────────────────────────────────┐
│ GENERATION MANDATE QUEUE │
│ │
│ Mandate { location, trigger, constraints, priority } │
│ │
│ Sources: │
│ ├─ Player exploration (progressive generation) │
│ ├─ NPC-driven hook (deep interaction with distant NPC) │
│ ├─ News-driven hook (simulation event at a location) │
│ └─ Storyteller (narrative-paced world expansion) │
│ │
│ Consumer: Generator pipeline (background tokio tasks) │
└─────────────────────────────────────────────────────────┘
NPC-Driven Generation
Trigger: Player forms a deep relationship (not casual) with an NPC whose home_location references an ungenerated or partially-generated location.
Flow:
- Deep interaction fires → system reads NPC's
home_location: { system: GJ-699, settlement: Proxima Station, district: 7, block: 3, unit: 12 } - System creates a GenerationMandate:
Mandate { location: GJ-699 / Proxima Station / District 7, trigger: NpcHome { npc_id, block: 3, unit: 12 }, constraints: { block_3_economic_tier: must match NPC's presented wealth, block_3_condition: must match NPC's cultural presentation, unit_12_function: Residential, unit_12_cultural_corridor: NPC's corridor identity, }, priority: Background (not urgent — player must travel to visit) } - Mandate enters the queue. Background generation processes it during idle time.
- When processed: Phase 1 skeleton for District 7 places Block 3 with the mandated economic tier. Phase 2 fills Block 3, creating Unit 12 as a residential space matching the NPC's cultural corridor. A ChunkMutation marks Unit 12 as the NPC's home.
- NPC's home is now visitable. If the player travels to GJ-699, District 7 is already generated.
Consistency guarantee: The mandate carries constraints derived from the NPC's presentation. The guarantee audit ensures these constraints are satisfied. If they can't be (pathological edge case), the audit logs a warning and places the home in the closest valid block.
Latency: Not real-time. The player meets the NPC now; generation runs in background. Even worst-case (immediate teleportation), Phase 1 + Phase 2 for one district is <1 second total.
News-Driven Generation
Trigger: Background simulation produces an event (gas main explosion, factory closure, political uprising) at a specific location.
Critical design principle: events read world state, don't mandate content. The storyteller generates news about things that already exist in the simulation's model of the world — including locations that have Phase 1 skeletons but no Phase 2 fill.
Flow:
- Pressure simulation produces event:
{ type: InfrastructureFailure, system: Kepler Haven, district: East, affected_block: 5 } - If the location has a Phase 1 skeleton: event becomes a ChunkMutation (damage overlay) applied to block 5. Phase 2 fill for that block incorporates the damage when generated.
- If the location is ungenerated: the mandate creates a skeleton with the event pre-baked. When the player visits, the damage is part of the initial generation — there's no "pristine then damaged" transition.
- News system reports the event via the Reach-wide news ticker. If the player visits, the damage exists.
Frequency: Sparse. The PO specified "sparse frequency to avoid disruption." The storyteller gates news-driven generation to ~1-3 events per game-day across the entire Reach. Most events are economic (prices, shortages) not physical (explosions, construction).
6. LOD Economic Primitives
Four LOD levels for business/social site entities, implemented as ECS component promotion:
LOD 0: Exists (metadata only)
Trigger: Phase 1 skeleton generation places the social site.
Components: SocialSite { site_type: SocialSiteType, block_id: BlockId, stable_id: StableId }
Cost: Zero simulation cost. Metadata only.
LOD 1: Observable (derived stats)
Trigger: Player enters the district containing the business. Or: player queries the business via insert device. Components added:
struct BusinessStats {
turnover_bracket: TurnoverBracket, // High/Medium/Low/Failing
cost_tier: CostTier, // Luxury/Standard/Budget/Derelict
employee_count: u16,
patron_density: PatronDensity, // Packed/Busy/Moderate/Quiet/Empty
condition: ConditionTier, // Pristine/Functional/Worn/Derelict
}
Derivation: All fields derived from district economic pressure + business type. No independent simulation. Updated when pressure state changes (per game-hour at most).
district_economic_health: Prosperous
+ site_type: Bar
+ cultural_corridor: Commonwealth
→ turnover_bracket: High
→ cost_tier: Standard (Commonwealth bars are never Luxury — cultural norm)
→ patron_density: Busy (evening), Moderate (day)
→ condition: Functional (well-maintained but not ostentatious)
Cost: Near-zero. Table lookup on pressure change.
LOD 2: Visited (entity-level)
Trigger: Player enters the building. Components added:
struct BusinessState {
current_staff: Vec<StableEntityId>,
current_patrons: Vec<StableEntityId>,
today_revenue: u32,
inventory_level: InventoryLevel, // Full/Adequate/Low/Critical
open_hours: (GameHour, GameHour),
}
Simulation: Staff and patron NPC entities are spawned (or promoted from Background tier) when the player enters. Patron flow follows daily patterns (quiet morning, lunch rush, evening peak). Revenue accumulates per patron visit. Inventory depletes and restocks on a daily cycle.
Cost: Low. Entity list management, per-tick patron flow check. ~10-20 NPCs per business.
LOD 3: Invested (full financial sim)
Trigger: Player purchases the business, takes employment, or forms a deep economic relationship. Components added:
struct BusinessFinancials {
daily_revenue: u32,
daily_expenses: u32,
staff_satisfaction: f32,
supply_chain: Vec<SupplyLink>,
customer_demographics: BTreeMap<CulturalCorridor, f32>,
monthly_profit_history: VecDeque<i32>, // Last 12 months
competitive_position: CompetitivePosition,
}
struct SupplyLink {
supplier_id: StableEntityId,
good: TradeGood,
reliability: f32,
cost: u32,
}
Simulation: Full economic tick. Supply chain queries (are suppliers delivering? at what price?). Staff satisfaction from wages vs district norms. Customer demographics from district cultural composition + foot traffic patterns. Competitive position from nearby similar businesses.
Economic success follows regional prosperity: The business's revenue baseline is derived from the district's pressure state. A thriving bar in a declining district fights against the current. A mediocre bar in a booming district rides the wave. The tycoon reads the economic landscape and invests accordingly.
Cost: Medium. Full economic tick per game-minute for invested businesses. Budget: 10-50 invested businesses per active district (player can't meaningfully manage more).
LOD Demotion
| Transition | Trigger | Action |
|---|---|---|
| LOD 3 → LOD 2 | Player sells business / quits job | Snapshot BusinessFinancials to save data. Remove component. Business continues at LOD 2 with stats derived from last financial state decaying toward district norm over game-days. |
| LOD 2 → LOD 1 | Player leaves district | Despawn transient patron NPCs. Save staff state. Remove BusinessState. Stats revert to pressure-derived values. |
| LOD 1 → LOD 0 | Player leaves system (district enters State-saved tier) | Remove BusinessStats. Metadata persists in skeleton. |
7. Revised Cost Estimate and Critical Path
Cost Summary
| Layer / System | Effort | Category |
|---|---|---|
| Layer 1: Galaxy loader | Days | Data pipeline |
| Layer 2: Wiki → LocationProfile pipeline | 1-2 weeks | Data pipeline |
| Layer 3: Heightmap ingestion + constraint extraction | 2-3 weeks | Data pipeline |
| Layer 4: Gate templates + arrival guarantees | Included in Layer 7 | Generation |
| Layer 5: Progressive generation triggers + cache | 3-4 weeks | Generation infrastructure |
| Layer 6: Settlement placement on terrain | 2-4 weeks | Generation |
| Layer 7: Phase 1 skeleton + Phase 2 fill (3 families) | 9-14 weeks | Generation (core) |
| Layer 8: 3D renderer migration | 2-4 weeks | Rendering (parallel) |
| Background pressure simulation | 9-14 weeks | Simulation (new) |
| LOD economic primitives | 3-5 weeks | Simulation (new) |
| NPC/news generation hooks | 2-3 weeks | Generation hooks |
| Content pipeline tooling | 1-2 weeks | Tooling |
| TOTAL | ~35-55 weeks |
Category Breakdown
| Category | Weeks | Character |
|---|---|---|
| Data pipelines (wiki, heightmaps, content tooling) | 4-7 | Well-understood. No design risk. |
| Generation core (Layers 5-7 + hooks) | 16-25 | Algorithmic. BSP is proven. Constraint satisfaction (Phase 1 guarantees) is the hardest part. |
| Simulation (pressure sim + LOD primitives) | 12-19 | Requires dedicated design workshop before engineering. Highest design risk. |
| Rendering (Layer 8) | 2-4 | Parallel track. Not on critical path. |
Critical Path
┌─────────────────────────────────┐
│ Background Simulation Workshop │
│ (design: pressure model spec) │
└──────────────┬──────────────────┘
│
┌────────────────────┼────────────────────┐
│ │ │
▼ ▼ ▼
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Pressure Sim │ │ LOD Primitives │ │ Content Pipeline │
│ Engineering │ │ (3-5 weeks) │ │ (4-7 weeks) │
│ (9-14 weeks) │ └────────┬────────┘ └────────┬────────┘
└────────┬────────┘ │ │
│ │ │
▼ ▼ ▼
┌─────────────────────────────────────────────────────────────┐
│ GENERATION PIPELINE │
│ Layer 5 (triggers) → Layer 6 (settlement) → Layer 7 (fill) │
│ (16-25 weeks) │
│ Reads: pressure state + authored content + terrain data │
└─────────────────────────────────────────────────────────────┘
The background simulation design workshop is the gate. Nothing in the simulation category can start without a pressure model specification. The generation pipeline can start in parallel with the content pipeline (using provisional placeholder data), but it needs the pressure-to-entity bridge before Phase 1 era tagging and Phase 2 condition application are meaningful.
Parallelizable tracks:
- Content pipeline (Layers 2-3 data ingestion) — can start immediately
- Generation infrastructure (Layer 5 trigger hooks, Layer 6 settlement placement) — can start immediately with placeholder terrain data
- Phase 2 BSP + templates (Layer 7 Interior family) — can start immediately, pressure integration added later
- Background simulation — blocked on design workshop
- Rendering (Layer 8) — independent track, already in progress
If all parallelizable tracks run concurrently, the wall-clock critical path is approximately 20-25 weeks from workshop completion to feature-complete pipeline. The 35-55 week total reflects serial execution; with 2-3 engineers running parallel tracks, calendar time compresses significantly.