diff --git a/pxl8d/src/main.rs b/pxl8d/src/main.rs index e67ec8a..e626355 100644 --- a/pxl8d/src/main.rs +++ b/pxl8d/src/main.rs @@ -6,6 +6,7 @@ extern crate alloc; use pxl8d::*; use pxl8d::chunk::ChunkId; use pxl8d::chunk::stream::ClientChunkState; +use pxl8d::procgen::LayoutMode; const TICK_RATE: u64 = 30; const TICK_NS: u64 = 1_000_000_000 / TICK_RATE; @@ -126,9 +127,8 @@ pub extern "C" fn main(_argc: i32, _argv: *const *const u8) -> i32 { width: 20, height: 20, seed: 12345u32.wrapping_add(2), - min_room_size: 14, - max_room_size: 16, - num_rooms: 1, + layout: LayoutMode::Courtyard, + ..ProcgenParams::default() }), _ => None, }; diff --git a/pxl8d/src/procgen.rs b/pxl8d/src/procgen.rs index 5818198..0f0f611 100644 --- a/pxl8d/src/procgen.rs +++ b/pxl8d/src/procgen.rs @@ -19,6 +19,12 @@ pub struct LightSource { pub radius: f32, } +#[derive(Clone, Copy, PartialEq)] +pub enum LayoutMode { + Dungeon, + Courtyard, +} + #[derive(Clone, Copy)] pub struct ProcgenParams { pub width: i32, @@ -27,6 +33,7 @@ pub struct ProcgenParams { pub min_room_size: i32, pub max_room_size: i32, pub num_rooms: i32, + pub layout: LayoutMode, } impl Default for ProcgenParams { @@ -38,6 +45,7 @@ impl Default for ProcgenParams { min_room_size: 3, max_room_size: 6, num_rooms: 8, + layout: LayoutMode::Dungeon, } } } @@ -1074,6 +1082,144 @@ pub fn generate_rooms(params: &ProcgenParams) -> Bsp { bsp.into() } -pub fn generate(params: &ProcgenParams) -> Bsp { - generate_rooms(params) +pub fn generate_courtyard(params: &ProcgenParams) -> Bsp { + let mut rng = Rng::new(params.seed); + let mut grid = RoomGrid::new(params.width, params.height); + grid.fill(1); + + let mut rooms: Vec = Vec::new(); + + let cx = params.width / 2; + let cy = params.height / 2; + let court_half = 4; + let court_x = cx - court_half; + let court_y = cy - court_half; + let court_w = court_half * 2; + let court_h = court_half * 2; + + for ry in court_y..(court_y + court_h) { + for rx in court_x..(court_x + court_w) { + grid.set(rx, ry, 0); + } + } + rooms.push(Bounds { x: court_x, y: court_y, w: court_w, h: court_h }); + + let wing_dirs: [(i32, i32); 4] = [(0, -1), (0, 1), (-1, 0), (1, 0)]; + + for &(dx, dy) in &wing_dirs { + let room_w = 3 + (rng.next() % 3) as i32; + let room_h = 3 + (rng.next() % 3) as i32; + + let (rx, ry) = if dx != 0 { + let rx = if dx > 0 { court_x + court_w + 2 } else { court_x - room_w - 2 }; + let ry = cy - room_h / 2; + (rx, ry) + } else { + let rx = cx - room_w / 2; + let ry = if dy > 0 { court_y + court_h + 2 } else { court_y - room_h - 2 }; + (rx, ry) + }; + + let rx = rx.max(1).min(params.width - room_w - 1); + let ry = ry.max(1).min(params.height - room_h - 1); + + for y in ry..(ry + room_h) { + for x in rx..(rx + room_w) { + grid.set(x, y, 0); + } + } + + let room_cx = rx + room_w / 2; + let room_cy = ry + room_h / 2; + if dx != 0 { + carve_corridor_h(&mut grid, cx, room_cx, room_cy); + } else { + carve_corridor_v(&mut grid, cy, room_cy, room_cx); + } + + let primary = Bounds { x: rx, y: ry, w: room_w, h: room_h }; + rooms.push(primary); + + let extra_count = 1 + (rng.next() % 2) as i32; + for _ in 0..extra_count { + let ew = 2 + (rng.next() % 3) as i32; + let eh = 2 + (rng.next() % 3) as i32; + + let (ex, ey) = if dx != 0 { + let ex = if dx > 0 { rx + room_w + 1 } else { rx - ew - 1 }; + let ey = ry + (rng.next() % room_h.max(1) as u32) as i32 - eh / 2; + (ex, ey) + } else { + let ex = rx + (rng.next() % room_w.max(1) as u32) as i32 - ew / 2; + let ey = if dy > 0 { ry + room_h + 1 } else { ry - eh - 1 }; + (ex, ey) + }; + + let ex = ex.max(1).min(params.width - ew - 1); + let ey = ey.max(1).min(params.height - eh - 1); + + for y in ey..(ey + eh) { + for x in ex..(ex + ew) { + grid.set(x, y, 0); + } + } + + let ecx = ex + ew / 2; + let ecy = ey + eh / 2; + if rng.next() % 2 == 0 { + carve_corridor_h(&mut grid, room_cx, ecx, room_cy); + carve_corridor_v(&mut grid, room_cy, ecy, ecx); + } else { + carve_corridor_v(&mut grid, room_cy, ecy, room_cx); + carve_corridor_h(&mut grid, room_cx, ecx, ecy); + } + + rooms.push(Bounds { x: ex, y: ey, w: ew, h: eh }); + } + } + + let mut bsp = BspBuilder::new(); + grid_to_bsp(&mut bsp, &grid); + + let light_height = 80.0; + let fireball_pos = Vec3::new(384.0, light_height, 324.0); + let fireball_exclusion_radius = 150.0; + + let lights: Vec = rooms.iter().filter_map(|room| { + let room_cx = (room.x as f32 + room.w as f32 / 2.0) * CELL_SIZE; + let room_cz = (room.y as f32 + room.h as f32 / 2.0) * CELL_SIZE; + + let ddx = room_cx - fireball_pos.x; + let ddz = room_cz - fireball_pos.z; + if ddx * ddx + ddz * ddz < fireball_exclusion_radius * fireball_exclusion_radius { + return None; + } + + let intensity = if room.w >= 6 && room.h >= 6 { 2.0 } else { 1.5 }; + let radius = if room.w >= 6 && room.h >= 6 { 250.0 } else { 160.0 }; + + Some(LightSource { + position: Vec3::new(room_cx, light_height, room_cz), + intensity, + radius, + }) + }).collect(); + + let mut lights = lights; + lights.push(LightSource { + position: Vec3::new(860.0, light_height, 416.0), + intensity: 1.2, + radius: 120.0, + }); + + compute_bsp_vertex_lighting(&mut bsp, &lights); + + bsp.into() +} + +pub fn generate(params: &ProcgenParams) -> Bsp { + match params.layout { + LayoutMode::Dungeon => generate_rooms(params), + LayoutMode::Courtyard => generate_courtyard(params), + } }