Sprint 37 dead-code sweep closing out two stale supersession chains: #877 (D-167, 2026-03-24): Removes HeritageRoot type alias and ZonePaletteModifier::Heritage variant from server/src/simulation/ generator.rs. The 7 abstract heritage roots were retired in favour of the corridor cultural system; these two stubs were the only remaining references. #878 (D-032 + cascade rule): Strips the entire CharacterArchetype (Smuggler/Detective) trace from the server. Per lead direction 2026-04-21 and the development cascade (CLAUDE.md), character/NPC/ verb-differentiation/monologue code is Phase 6 detail that should not exist in code yet. The running archetype trace was pre-cascade filler, not production — production is only the client's character- creation UI and insert screens (client follow-up in #882). Deleted: - CharacterArchetype enum + StartupMessage.character_archetype field - archetype_verb_label() + archetype branch of apply_phase2_verb_filter (D-057 character-verb differentiation — marked superseded) - MonologueState.character partitioning - Gauntlet archetype plumbing (setup_gauntlet no longer takes an archetype) - server/content/schemas/drama_module.schema.yaml (zero Rust consumers) - server/content/modules/tier1/smuggling_ring_v0_1.yaml - server/tests/archetype_monologue.rs (regression guard for the removed system) - server/tests/v01_integration_playthrough.rs (archetype-dependent) Decision updates: - decisions/content.md D-032 supersession rewritten to cite the cascade (v0.2 drop invalidated the prior D-117 framing). - decisions/content.md D-035 tag taxonomy: `character` enum footnote updated; field noted as unused, do not reintroduce without a confirmed Phase 6 design. - decisions/perception.md D-057: archetype-verb differentiation marked superseded. Also bundles the types.rs version-field removal from #874 since the file was already touched here. Full trace audit in docs/architecture/sprint-37-878-audit.md. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
577 lines
21 KiB
Rust
577 lines
21 KiB
Rust
//! 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<bool>;
|
||
/// 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<ReservationId>,
|
||
pub chunk_layout: ChunkLayout,
|
||
pub hosted_sites: Vec<SocialSiteId>,
|
||
pub era: Era,
|
||
pub era_modifications: Vec<EraModification>,
|
||
pub era_cause: Option<EraCause>,
|
||
/// 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<LandmarkSlot>,
|
||
}
|
||
|
||
/// 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<u8>,
|
||
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<PaletteModifier>,
|
||
}
|
||
|
||
/// 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<TriangleAssignment>,
|
||
pub role_slots: Vec<RoleSlot>,
|
||
pub active_phases: Vec<DayPhase>,
|
||
}
|
||
|
||
/// Triangle assignment within a social site.
|
||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||
pub struct TriangleAssignment {
|
||
pub template: TriangleTemplate,
|
||
pub purposes: Vec<TrianglePurpose>,
|
||
pub participants: Vec<RoleSlotId>,
|
||
/// 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<TileOverride>,
|
||
pub structural_changes: Vec<StructuralChange>,
|
||
pub placed_objects: Vec<PlacedObject>,
|
||
pub removed_objects: Vec<ObjectId>,
|
||
}
|
||
|
||
/// 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<FloorZone>,
|
||
/// 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<ZoneDefinition>,
|
||
/// Vertical connection specs (stairs, elevators, ladders).
|
||
pub vertical_corridors: Vec<VerticalCorridorSpec>,
|
||
pub hosted_sites: Vec<SocialSiteId>,
|
||
}
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// 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<MultiBlockReservation>,
|
||
/// Corridor spines connecting key access points.
|
||
pub corridors: Vec<CorridorSpine>,
|
||
/// Number of floor levels in this district (count, unsigned — D-110).
|
||
pub z_levels: u8,
|
||
|
||
// ── Social Structure ──────────────────────────────────
|
||
pub social_sites: Vec<SocialSitePlacement>,
|
||
pub access_points: Vec<AccessPoint>,
|
||
|
||
// ── Cultural / World Context ──────────────────────────
|
||
pub society_profile: SocietyProfileRef,
|
||
pub zone_palette: Vec<ZoneDefinition>,
|
||
|
||
// ── Boundary System ───────────────────────────────────
|
||
pub boundaries: DistrictBoundaries,
|
||
|
||
// ── Validation ────────────────────────────────────────
|
||
/// Guarantee audit result. `None` for Empty districts.
|
||
pub guarantee_audit: Option<GuaranteeAuditResult>,
|
||
}
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// 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");
|
||
}
|
||
}
|