//! hexwork-worldgen: seeded layouts (docs/04 §4.2, docs/03 §3.8). //! Hand-designed anchor rooms + systemic fill. Every floor guarantees //! 3+ vertical connectors, an airflow network, and a water path. pub mod deep; pub mod runtime; pub use runtime::{ is_supported_standing, route_exists, route_exists_with_policy, validate_deep_links, ActorRole, ActorSpawnPlan, AirflowEdge, AirflowPlan, Bounds, Cell, ConnectorPlan, FloorPlan, InteractionAnchor, InteractionKind, ItemSpawnPlan, Job, MechanicHook, MovementPolicy, NamedAnchor, PlanDestination, PlanValidationError, PressureZone, RoadPlan, SpawnZone, TransitionDirection, VoxelWrite, WorldPlan, }; use hexwork_core::connector::{Connector, ConnectorKind}; use hexwork_core::rng::{domains, Rng}; /// Which stratum a floor belongs to: Shallows 1-3, Works 4-8, Deep 9-12. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Stratum { Shallows, Works, Deep, } /// A generated floor: anchor rooms plus connectors. Voxel fill arrives in Phase 2+. #[derive(Clone, Debug)] pub struct Floor { pub depth: u8, pub stratum: Stratum, pub seed: u64, pub anchors: Vec, pub connectors: Vec, } pub fn stratum_for(depth: u8) -> Stratum { match depth { 1..=3 => Stratum::Shallows, 4..=8 => Stratum::Works, _ => Stratum::Deep, } } /// Generate a floor deterministically from run seed + depth. pub fn generate_floor(master_seed: u64, depth: u8) -> Floor { let mut rng = Rng::from_seed(master_seed ^ (depth as u64).wrapping_mul(0x9E3779B97F4A7C15)); let mut rng = rng.fork(domains::WORLDGEN); let stratum = stratum_for(depth); let anchors = anchor_set(stratum, &mut rng); // Guarantee: 3+ vertical connectors per floor (docs/03 §3.8). let kinds = [ ConnectorKind::Stairwell, ConnectorKind::Shaft, ConnectorKind::Vent, ]; let mut connectors = Vec::new(); for (i, kind) in kinds.iter().enumerate() { let z_top = -(depth as f32) * 10.0; connectors.push(Connector { id: (depth as u64) * 100 + i as u64, kind: *kind, from: [i as f32 * 8.0, 0.0, z_top - 10.0], to: [i as f32 * 8.0, 0.0, z_top], length: 12.0, anchor_quality: 60 + rng.below(41) as u8, load_kg: 0.0, blocked: false, }); } Floor { depth, stratum, seed: master_seed ^ (depth as u64), anchors, connectors, } } /// Generate an apply-ready Milthollow plan without mutating runtime state. #[must_use] pub fn milthollow(seed: u64) -> WorldPlan { runtime::build_milthollow(seed) } /// Generate one apply-ready Deep floor for a depth in `1..=12`. /// /// # Panics /// Panics when `depth` is outside the twelve playable floors. #[must_use] pub fn playable_floor(master_seed: u64, depth: u8) -> FloorPlan { runtime::build_playable_floor(master_seed, depth) } /// Generate the complete twelve-floor Deep in ascending depth order. #[must_use] pub fn playable_deep(master_seed: u64) -> Vec { let floors: Vec<_> = (1..=12) .map(|depth| playable_floor(master_seed, depth)) .collect(); assert!( validate_deep_links(&milthollow(master_seed), &floors).is_ok(), "generated Deep transition graph must validate" ); floors } fn anchor_set(stratum: Stratum, rng: &mut Rng) -> Vec { let pool: &[&str] = match stratum { Stratum::Shallows => &[ "root-cellar relay", "sewer chapel", "collapsed stair", "rat warren", "tinker cache", ], Stratum::Works => &[ "elevator hall", "audit vault", "flooded shaft", "server shrine", "counterweight gallery", ], Stratum::Deep => &[ "dream chapel", "sideways fall", "saint throne", "choir loft", "idle gate", ], }; let n = 3 + rng.below(3) as usize; let mut out = Vec::new(); for _ in 0..n { out.push(pool[rng.below(pool.len() as u64) as usize].to_string()); } out } #[cfg(test)] mod tests { use super::*; #[test] fn three_connectors_minimum() { for depth in 1..=12 { let f = generate_floor(99, depth); assert!(f.connectors.len() >= 3, "floor {depth} needs 3+ connectors"); } } #[test] fn deterministic_floors() { assert_eq!( format!("{:?}", generate_floor(5, 2).anchors), format!("{:?}", generate_floor(5, 2).anchors) ); } }