feat: courtyard layout generation for BSP 2
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
5a565844dd
commit
2fb4042fba
2 changed files with 151 additions and 5 deletions
|
|
@ -6,6 +6,7 @@ extern crate alloc;
|
||||||
use pxl8d::*;
|
use pxl8d::*;
|
||||||
use pxl8d::chunk::ChunkId;
|
use pxl8d::chunk::ChunkId;
|
||||||
use pxl8d::chunk::stream::ClientChunkState;
|
use pxl8d::chunk::stream::ClientChunkState;
|
||||||
|
use pxl8d::procgen::LayoutMode;
|
||||||
|
|
||||||
const TICK_RATE: u64 = 30;
|
const TICK_RATE: u64 = 30;
|
||||||
const TICK_NS: u64 = 1_000_000_000 / TICK_RATE;
|
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,
|
width: 20,
|
||||||
height: 20,
|
height: 20,
|
||||||
seed: 12345u32.wrapping_add(2),
|
seed: 12345u32.wrapping_add(2),
|
||||||
min_room_size: 14,
|
layout: LayoutMode::Courtyard,
|
||||||
max_room_size: 16,
|
..ProcgenParams::default()
|
||||||
num_rooms: 1,
|
|
||||||
}),
|
}),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,12 @@ pub struct LightSource {
|
||||||
pub radius: f32,
|
pub radius: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, PartialEq)]
|
||||||
|
pub enum LayoutMode {
|
||||||
|
Dungeon,
|
||||||
|
Courtyard,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub struct ProcgenParams {
|
pub struct ProcgenParams {
|
||||||
pub width: i32,
|
pub width: i32,
|
||||||
|
|
@ -27,6 +33,7 @@ pub struct ProcgenParams {
|
||||||
pub min_room_size: i32,
|
pub min_room_size: i32,
|
||||||
pub max_room_size: i32,
|
pub max_room_size: i32,
|
||||||
pub num_rooms: i32,
|
pub num_rooms: i32,
|
||||||
|
pub layout: LayoutMode,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ProcgenParams {
|
impl Default for ProcgenParams {
|
||||||
|
|
@ -38,6 +45,7 @@ impl Default for ProcgenParams {
|
||||||
min_room_size: 3,
|
min_room_size: 3,
|
||||||
max_room_size: 6,
|
max_room_size: 6,
|
||||||
num_rooms: 8,
|
num_rooms: 8,
|
||||||
|
layout: LayoutMode::Dungeon,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1074,6 +1082,144 @@ pub fn generate_rooms(params: &ProcgenParams) -> Bsp {
|
||||||
bsp.into()
|
bsp.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn generate(params: &ProcgenParams) -> Bsp {
|
pub fn generate_courtyard(params: &ProcgenParams) -> Bsp {
|
||||||
generate_rooms(params)
|
let mut rng = Rng::new(params.seed);
|
||||||
|
let mut grid = RoomGrid::new(params.width, params.height);
|
||||||
|
grid.fill(1);
|
||||||
|
|
||||||
|
let mut rooms: Vec<Bounds> = 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<LightSource> = 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),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue