//! World generator data model — district skeleton and mobile chunk types. //! //! Phase 1 generator output: the `DistrictSkeleton` produced by the world generation //! pipeline. Consumed by Phase 2 (chunk fill) and by the simulation startup path. //! //! **D-110 (signed z-levels):** All z-level *position* fields use `i8` (negative values //! represent basements/sub-levels). Z-level *count* fields use `u8` (always ≥ 1). //! The distinction: //! - `base_z: i8` — where the bottom floor starts (can be negative) //! - `z_levels: u8` — how many floors total (always positive) //! //! **D-108 (MobileChunk):** Vessel/vehicle interior chunks attached to mobile entities. //! Uses the same chunk primitives as static districts in a simpler flat structure //! (no Phase 1/Phase 2 split, template-stamped interiors). //! //! Sources: workshop-outcomes.md, tyre-round4.md use serde::{Deserialize, Serialize}; // --------------------------------------------------------------------------- // Concept ID type aliases // --------------------------------------------------------------------------- /// Unique district identifier (content-addressable via hash of world position + seed). pub type DistrictId = u64; /// Unique multi-block reservation identifier within a district. pub type ReservationId = u64; /// Unique social site identifier within a district. pub type SocialSiteId = u64; /// Unique role slot identifier within a social site. pub type RoleSlotId = u64; // --------------------------------------------------------------------------- // Stub types for concepts not yet implemented (defined here to allow // the data model to compile; will be replaced with proper types when // the respective systems are implemented) // --------------------------------------------------------------------------- /// Biome classification for wilderness districts. Stub — full taxonomy deferred. pub type Biome = String; /// Water body sub-classification. Stub — full taxonomy deferred. pub type WaterType = String; /// Specialized district function (e.g. military, medical, penal). Stub. pub type SpecializedFunction = String; /// Cultural / architectural era tag. Stub. pub type Era = String; /// Single era modification applied to a block (conversion, overlay, hybrid). Stub. pub type EraModification = String; /// Landmark slot description within a block. Stub. pub type LandmarkSlot = String; /// Chunk layout specification within a block (how the 2×2 chunks are arranged). Stub. pub type ChunkLayout = String; /// Corridor spine connecting district access points. Stub. pub type CorridorSpine = String; /// District context — neighboring districts, world position, system. Stub. pub type DistrictContext = String; /// Society profile reference (Miri's cultural ingredients). Stub. pub type SocietyProfileRef = String; /// Zone definition — base palette + modifiers + zone name. Stub. pub type ZoneDefinition = String; /// District boundary system — edge descriptors with neighbors. Stub. pub type DistrictBoundaries = String; /// Guarantee audit result — tier-appropriate spatial invariant checks. Stub. pub type GuaranteeAuditResult = String; /// District access point (entry/exit to neighboring district). Stub. pub type AccessPoint = String; /// Base visual palette for a zone. Stub. pub type BasePalette = String; /// Economic modifier on a zone palette. Stub. pub type EconomicModifier = String; /// Faction presence modifier on a zone palette. Stub. pub type FactionModifier = String; /// Condition modifier on a zone palette (worn, pristine, damaged). Stub. pub type ConditionModifier = String; /// Season modifier (affects palette and ambient conditions). Stub. pub type Season = String; /// Role slot within a social site template. Stub. pub type RoleSlot = String; /// Day phase (morning, afternoon, evening, night, late-night). Stub. pub type DayPhase = String; /// Triangle template reference (links to server/content/templates/). Stub. pub type TriangleTemplate = String; /// Raw chunk tile data for generator use (64×64 bool grid, true = walkable). Stub. pub type GeneratorChunkData = Vec; /// Tile type identifier (references visual tile set). Stub. pub type TileId = String; /// Entity identifier for mutation causality tracking. Stub. pub type EntityId = u64; /// Action identifier for player-caused mutations. Stub. pub type ActionId = String; /// Object instance identifier within a chunk. Stub. pub type ObjectId = u64; /// Placed object (item, furniture, fixture) within a chunk. Stub. pub type PlacedObject = String; // --------------------------------------------------------------------------- // World and complexity tier enums // --------------------------------------------------------------------------- /// Network importance of a world in the galaxy. /// Determines simulation fidelity budget and NPC complexity ceiling. /// /// Source: tyre-round4.md §2.1, workshop-outcomes.md #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] pub enum WorldTier { /// Background system — minimal simulation, sparse NPCs. Pure environmental. Peripheral, /// Standard Settled Reach system — full simulation, complex social sites. Connected, /// Major hub — maximum fidelity, multi-faction politics, all triangle types. Core, } /// Generator content budget for a district. /// Derived from WorldTier + SettingType at Phase 1. #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] pub enum ComplexityTier { /// Full social architecture. All Tier 1+2 guarantees. 20-80+ NPCs. Full, /// Moderate social architecture. Tier 1 + partial Tier 2. 8-20 NPCs. Moderate, /// Minimal social architecture. Tier 1 only. 1-8 NPCs. Minimal, /// No social architecture. Pure terrain. 0 NPCs. No guarantees. Empty, } /// Physical setting type for a district. /// Merged from Gestalt's SettingGeometry and Tyre's TerrainType. #[derive(Serialize, Deserialize, Clone, Debug)] pub enum SettingType { Station, Urban, Agricultural, Maritime, Wilderness { biome: Biome }, Water { water_type: WaterType }, Transitional, Orbital, Specialized { function: SpecializedFunction }, } /// Classification of a district (high-level function). #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] pub enum DistrictType { LogisticsHub, Residential, Commercial, Industrial, Administrative, Entertainment, MixedUse, Transit, Specialized, } /// How blocks are placed within the district's 512×512 footprint. #[derive(Serialize, Deserialize, Clone, Debug)] pub enum DistrictLayoutMode { /// Standard Cartesian grid — perpendicular streets. Grid, /// Organic placement with per-block offsets and rotations. Organic { placements: [[BlockPlacement; 4]; 4], }, } /// Zoning classification for a block or floor zone. #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] pub enum ZoningType { Commercial, Residential, Industrial, Administrative, Transit, Recreational, Restricted, Mixed, } /// Reservation function — what purpose a multi-block reservation serves. #[derive(Serialize, Deserialize, Clone, Debug)] pub enum ReservationFunction { Skyscraper, Park, Terminal, Plaza, Monument, IndustrialComplex, MilitaryBase, ResearchFacility, UndergroundComplex, } /// Access tier — who is allowed into a zone under normal circumstances. #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] pub enum AccessTier { /// Anyone can enter. Public, /// Requires employment or residence credential. Credentialed, /// Requires explicit invitation or escort. Restricted, /// Law enforcement / military only. Secured, /// Breach access only — normally sealed. BreachOnly, } /// Vertical corridor type (how floors connect in a multi-level reservation). #[derive(Serialize, Deserialize, Clone, Debug)] pub enum VerticalCorridorType { Stairwell, Elevator, FreightLift, AccessHatch, EmergencyLadder, } /// Load state for a z-level within a reservation. /// Controls lazy loading of floor data during play. #[derive(Serialize, Deserialize, Clone, Debug)] pub enum ZLevelLoadState { /// Floor is fully generated and in memory. Loaded(GeneratorChunkData), /// Floor skeleton only — full data not yet generated. Skeleton(FloorZone), /// Floor not yet generated (deferred to Phase 2 on first entry). Ungenerated, } /// Cause of a chunk mutation (for save/load audit trail). #[derive(Serialize, Deserialize, Clone, Debug)] pub enum MutationCause { Explosion { radius: u8, source: EntityId }, Fire { spread_from: Option<(u16, u16)> }, Construction { builder: EntityId }, Decay { time_since_maintenance: u32 }, PlayerAction { action: ActionId }, } /// Type of structural change to a region of chunk tiles. #[derive(Serialize, Deserialize, Clone, Debug)] pub enum StructuralChangeType { WallDestroyed, FloorCollapsed, CeilingBreached, AreaSealed, WallConstructed, } /// Purpose classification for a triangle within a social site. #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] pub enum TrianglePurpose { Investigation, Economic, Social, Political, Tactical, Mundane, } /// What exists on the far side of a wall tile (for destructible geometry). /// Every wall tile in a generated chunk is tagged with one of these. #[derive(Serialize, Deserialize, Clone, Debug)] pub enum WallBackside { /// Another room/corridor exists (tiles already generated). AdjacentSpace, /// Solid structural material — 1-2 tiles of fill, then another wall. StructuralFill, /// Narrow utility gap (1-3 tiles): pipes, conduits, non-navigable. ServiceVoid, /// Edge of chunk — adjacent chunk's boundary tiles on the far side. ChunkBoundary, /// Faces outside (hull, exterior wall) — breach has catastrophic consequences. Exterior, } /// Era cause — why a block has the era tag it has. #[derive(Serialize, Deserialize, Clone, Debug)] pub enum EraCause { Original, CorporateMerger, EmergencyExtension, OrganicGrowth, InstitutionalIncursion, EconomicDisruption, CulturalShift, } // --------------------------------------------------------------------------- // Supporting structs // --------------------------------------------------------------------------- /// Organic layout placement for a single block. #[derive(Serialize, Deserialize, Clone, Debug)] pub struct BlockPlacement { /// Offset from grid-aligned position (±16 sim tiles per axis max). pub offset: (i16, i16), /// Rotation in 15° increments (0–3, max 45°). pub rotation_steps: u8, /// Street width in basis points (10000 = 1.0×, range 7500–20000 for 0.75–2.0×). /// Default 10000 = 4 visual tiles. Uses integer to avoid f32 non-determinism (D-010). pub street_width_bps: u16, } /// Single block within a district's 4×4 block grid. /// Each block = 128×128 sim tiles = 2×2 chunks. #[derive(Serialize, Deserialize, Clone, Debug)] pub struct BlockSkeleton { /// Grid position (0–3, 0–3). pub position: (u8, u8), pub zoning: ZoningType, /// Which multi-block reservation this block belongs to (if any). pub reservation: Option, pub chunk_layout: ChunkLayout, pub hosted_sites: Vec, pub era: Era, pub era_modifications: Vec, pub era_cause: Option, /// Build density percentage (0 = open/empty, 100 = fully built-up). /// Integer to avoid f32 non-determinism (D-010). pub density_pct: u8, pub landmark: Option, } /// Floor zone within a multi-level reservation. /// /// **D-110:** `z_level` is `i8` — negative values represent basements /// (e.g. `z_level = -1` for a sub-basement). This differs from `z_levels: u8` /// on the parent reservation which counts total floors (always ≥ 1). #[derive(Serialize, Deserialize, Clone, Debug)] pub struct FloorZone { /// Absolute z-level of this floor. Negative for sub-ground levels (D-110). pub z_level: i8, pub zone_type: ZoningType, pub zone_palette: ZonePalette, pub access_tier: AccessTier, } /// Vertical connection spec within a multi-level reservation. #[derive(Serialize, Deserialize, Clone, Debug)] pub struct VerticalCorridorSpec { /// Which blocks this vertical corridor passes through. pub block_coords: Vec<(u8, u8)>, /// Which z-bands (0-indexed band indices, not absolute z-levels) this /// corridor connects. See D-110 for signed z-level coordinate system. pub z_bands_connected: Vec, pub access_tier: AccessTier, pub corridor_type: VerticalCorridorType, } /// Visual palette for a zone — base material + contextual modifiers. #[derive(Serialize, Deserialize, Clone, Debug)] pub struct ZonePalette { pub base: BasePalette, pub modifiers: Vec, } /// Contextual modifier applied on top of a base palette. #[derive(Serialize, Deserialize, Clone, Debug)] pub enum PaletteModifier { EconomicFunction(EconomicModifier), Era(Era), FactionPresence(FactionModifier), Condition(ConditionModifier), Season(Season), } /// Social site placement within a district. #[derive(Serialize, Deserialize, Clone, Debug)] pub struct SocialSitePlacement { pub site_id: SocialSiteId, /// Which blocks this site spans. pub blocks: Vec<(u8, u8)>, pub template_tag: String, pub access_tier: AccessTier, pub triangles: Vec, pub role_slots: Vec, pub active_phases: Vec, } /// Triangle assignment within a social site. #[derive(Serialize, Deserialize, Clone, Debug)] pub struct TriangleAssignment { pub template: TriangleTemplate, pub purposes: Vec, pub participants: Vec, /// Which block provides the primary staging area (optional for multi-block sites). pub staging_block: Option<(u8, u8)>, } /// Mutations applied to an already-generated chunk. /// Stored alongside the chunk in the save file. #[derive(Serialize, Deserialize, Clone, Debug, Default)] pub struct ChunkMutations { pub tile_overrides: Vec, pub structural_changes: Vec, pub placed_objects: Vec, pub removed_objects: Vec, } /// Single tile override within a chunk mutation. #[derive(Serialize, Deserialize, Clone, Debug)] pub struct TileOverride { /// (x, y, z) within the chunk (z is relative to chunk base, always ≥ 0). pub position: (u16, u16, u8), pub new_tile: TileId, pub cause: MutationCause, } /// Rectangular structural change within a chunk. #[derive(Serialize, Deserialize, Clone, Debug)] pub struct StructuralChange { pub min: (u16, u16, u8), pub max: (u16, u16, u8), pub change_type: StructuralChangeType, } // --------------------------------------------------------------------------- // Multi-block reservation // --------------------------------------------------------------------------- /// A reservation spanning multiple blocks within a district. /// /// Used for skyscrapers, parks, transit terminals, plazas, and any structure /// that requires more than one 128×128 block. /// /// **D-110:** `base_z: i8` — the lowest floor of this reservation. /// Negative values represent sub-ground levels (basements, underground complexes). /// `z_levels: u8` remains unsigned — it counts total floors (always ≥ 1). /// /// Example: a building with a 3-level basement at ground + 20 floors above: /// `base_z: -3, z_levels: 24` /// A deep mine shaft descending 30 floors below ground: /// `base_z: -30, z_levels: 30` #[derive(Serialize, Deserialize, Clone, Debug)] pub struct MultiBlockReservation { /// Which blocks (grid positions) are part of this reservation. pub blocks: Vec<(u8, u8)>, pub template_tag: String, pub function: ReservationFunction, /// Total number of floors (always ≥ 1, unsigned). D-110. pub z_levels: u8, /// Lowest floor's z-level (can be negative for sub-basement). D-110. pub base_z: i8, /// Per-floor zone specifications. pub floor_zones: Vec, /// Number of z-bands (logical groupings of floors by zoning). pub z_band_count: u8, /// Zone definitions for each z-band. pub z_band_zones: Vec, /// Vertical connection specs (stairs, elevators, ladders). pub vertical_corridors: Vec, pub hosted_sites: Vec, } // --------------------------------------------------------------------------- // District skeleton // --------------------------------------------------------------------------- /// Phase 1 generator output for one district. /// /// Produced by the async background generation pipeline. /// Consumed by Phase 2 (chunk-by-chunk fill, on demand) and by /// the production simulation startup path. /// /// ## Z-level naming convention (D-110) /// /// - `z_levels: u8` — how many floor levels exist in this district (count, always ≥ 1) /// - `base_z` does not appear on `DistrictSkeleton` itself — the district's /// ground level is always 0. Sub-ground structures use [`MultiBlockReservation`] /// with a negative `base_z` field. /// /// ## Determinism (D-010) /// /// All `Vec<_>` fields are stable-ordered at generation time (sorted by a /// deterministic key). Generation reproduces identical output for the same seed. #[derive(Serialize, Deserialize, Clone, Debug)] pub struct DistrictSkeleton { // ── Identity ────────────────────────────────────────── pub district_id: DistrictId, /// Deterministic seed (derived from master seed via SeedChain). pub seed: u64, pub district_type: DistrictType, /// World context (system, world, neighboring districts). pub context: DistrictContext, // ── Classification ──────────────────────────────────── pub world_tier: WorldTier, pub complexity: ComplexityTier, pub setting: SettingType, pub layout_mode: DistrictLayoutMode, // ── Spatial Structure ───────────────────────────────── /// The 4×4 block grid (each block = 128×128 sim tiles = 2×2 chunks). pub blocks: [[BlockSkeleton; 4]; 4], /// Multi-block reservations (skyscrapers, parks, terminals, plazas). pub reservations: Vec, /// Corridor spines connecting key access points. pub corridors: Vec, /// Number of floor levels in this district (count, unsigned — D-110). pub z_levels: u8, // ── Social Structure ────────────────────────────────── pub social_sites: Vec, pub access_points: Vec, // ── Cultural / World Context ────────────────────────── pub society_profile: SocietyProfileRef, pub zone_palette: Vec, // ── Boundary System ─────────────────────────────────── pub boundaries: DistrictBoundaries, // ── Validation ──────────────────────────────────────── /// Guarantee audit result. `None` for Empty districts. pub guarantee_audit: Option, } // --------------------------------------------------------------------------- // Tests // --------------------------------------------------------------------------- #[cfg(test)] mod tests { use super::*; /// D-110: FloorZone.z_level must be i8 (signed) to support negative sub-levels. #[test] fn floor_zone_z_level_is_signed() { let fz = FloorZone { z_level: -2, zone_type: ZoningType::Restricted, zone_palette: ZonePalette { base: String::new(), modifiers: vec![], }, access_tier: AccessTier::BreachOnly, }; assert_eq!(fz.z_level, -2, "sub-basement z_level must round-trip as i8"); } /// D-110: MultiBlockReservation.base_z must be i8 (signed) for underground buildings. #[test] fn reservation_base_z_is_signed() { let r = MultiBlockReservation { blocks: vec![(0, 0)], template_tag: "deep-mine".into(), function: ReservationFunction::UndergroundComplex, z_levels: 30, base_z: -30, floor_zones: vec![], z_band_count: 5, z_band_zones: vec![], vertical_corridors: vec![], hosted_sites: vec![], }; assert_eq!( r.base_z, -30, "deep mine base_z must be representable as i8" ); assert_eq!(r.z_levels, 30u8, "z_levels count must remain u8"); } /// D-110: z_levels (count) remains u8 on DistrictSkeleton. #[test] fn district_skeleton_z_levels_is_unsigned() { // Verify z_levels is u8 (cannot hold negative value). Static assertion: // if this compiled with z_levels = 255u8, the type is correct. fn assert_u8(_: u8) {} let z_levels: u8 = 4; assert_u8(z_levels); assert!(z_levels > 0, "z_levels is always at least 1"); } }