Browse Source

Added rooms to worldgen

master
Thomas Johnson 3 years ago
parent
commit
0fb26d876c
  1. 156
      src/world/gen.rs

156
src/world/gen.rs

@ -0,0 +1,156 @@
use rogue_util::coord::{R2i, V2i};
use rand::rngs::SmallRng;
use rand::SeedableRng;
use rand_distr::{Poisson, Distribution, weighted::WeightedIndex, Uniform};
use std::hash::{Hash, Hasher};
use std::collections::hash_map::DefaultHasher;
const GRID_SIZE: isize = 32;
const GRID_USIZE: usize = GRID_SIZE as usize;
const AVG_ROOMS_PER_CELL: f64 = 2.0;
const ROOM_TYPE_WEIGHTS: [usize; 2] = [1, 1];
const RECT_MIN_SIZE: isize = 3;
const RECT_MAX_SIZE: isize = 10;
const CIRC_MIN_RAD: isize = 3;
const CIRC_MAX_RAD: isize = 10;
#[derive(Clone, Debug)]
enum RoomShape
{
Rect(R2i),
Circ(V2i, isize),
}
impl RoomShape
{
fn offset_grid(self, grid_offset: V2i) -> RoomShape
{
let offset = grid_offset * V2i(GRID_SIZE, GRID_SIZE);
match self
{
Self::Rect(rect) => Self::Rect(rect.translate(offset)),
Self::Circ(center, radius) => Self::Circ(center + offset, radius),
}
}
fn get_bounding_rect(&self) -> R2i
{
match self
{
Self::Rect(rect) => *rect,
Self::Circ(center, radius) => R2i::origin_dim(*center - V2i(radius - 1, radius - 1), V2i(2 * radius, 2 * radius)),
}
}
}
fn get_random_point<T: rand::Rng>(rng: &mut T) -> V2i
{
let coorddist = Uniform::new(0, GRID_SIZE);
let x = coorddist.sample(rng);
let y = coorddist.sample(rng);
V2i(x, y)
}
fn get_room_shapes(seed: u64, coords: V2i) -> Vec<RoomShape>
{
let mut hasher = DefaultHasher::new();
seed.hash(&mut hasher);
coords.hash(&mut hasher);
let mut rg = SmallRng::seed_from_u64(hasher.finish());
let nrooms: u64 = Poisson::new(AVG_ROOMS_PER_CELL).unwrap().sample(&mut rg);
let mut rooms = Vec::new();
let chooser = WeightedIndex::new(&ROOM_TYPE_WEIGHTS).unwrap();
let rect_size_dist = Uniform::new_inclusive(RECT_MIN_SIZE, RECT_MAX_SIZE);
let circ_rad_dist = Uniform::new_inclusive(CIRC_MIN_RAD, CIRC_MAX_RAD);
for ii in 0 .. nrooms
{
let shape = match chooser.sample(&mut rg)
{
0 =>
{
RoomShape::Rect(R2i::origin_dim(get_random_point(&mut rg), V2i(rect_size_dist.sample(&mut rg), rect_size_dist.sample(&mut rg))))
},
1 =>
{
RoomShape::Circ(get_random_point(&mut rg), circ_rad_dist.sample(&mut rg))
},
_ => panic!("random value out of range in worldgen"),
};
rooms.push(shape);
}
rooms
}
fn get_nearby_room_shapes(seed: u64, coords: V2i) -> impl Iterator<Item=RoomShape>
{
R2i::origin_dim(V2i(-1, -1), V2i(2, 2)).iter()
.map(move |v| get_room_shapes(seed, v + coords).into_iter()
.map(move |shape| shape.offset_grid(v))).flatten()
}
fn carve_rooms<T: Iterator<Item=RoomShape>>(map: &mut [bool; GRID_USIZE * GRID_USIZE], shapes: T)
{
let gbox = R2i::origin_dim(V2i(0, 0), V2i(GRID_SIZE, GRID_SIZE));
for shape in shapes
{
let bbox = shape.get_bounding_rect().intersect(gbox);
if let Some(bbox) = bbox
{
for tile in bbox.iter()
{
if match shape
{
RoomShape::Rect(rect) => true,
RoomShape::Circ(c, r) => (tile - c).l2_sq() < r * r,
} {
map[(tile.0 * GRID_SIZE + tile.1) as usize] = true;
}
}
}
}
}
#[cfg(test)]
mod test
{
use super::*;
#[test]
fn list_rooms()
{
for ii in 0u64..20u64
{
let rooms = get_room_shapes(ii, V2i(0, 0));
println!("rooms: {:?}", rooms);
}
}
#[test]
fn show_rooms()
{
for seed in 0u64..20u64
{
let origin = V2i(0, 0);
let mut map = [false; GRID_USIZE * GRID_USIZE];
carve_rooms(&mut map, get_nearby_room_shapes(seed, origin));
for x in 0 .. GRID_USIZE
{
for y in 0 .. GRID_USIZE
{
print!("{}", if map[x * GRID_USIZE + y] { ' ' } else { '#' });
}
println!("");
}
println!("");
}
}
}
Loading…
Cancel
Save