272 lines
8.2 KiB
Rust
272 lines
8.2 KiB
Rust
use std::cmp::{max, min};
|
|
|
|
use crate::{
|
|
constants::{LEVEL_HEIGHT, LEVEL_WIDTH, ROOM_HEIGHT, ROOM_WIDTH},
|
|
level::StructureElement,
|
|
};
|
|
use rand::rngs::ThreadRng;
|
|
use rand::Rng;
|
|
|
|
#[derive(PartialEq, Copy, Clone, Eq, Hash, Debug)]
|
|
pub enum RoomType {
|
|
Start,
|
|
End,
|
|
StairUp,
|
|
StairDown,
|
|
BasicRoom,
|
|
ArtifactRoom,
|
|
MonsterRoom,
|
|
EmptyRoom,
|
|
}
|
|
|
|
#[derive(Copy, Clone, Debug)]
|
|
pub struct Connection {
|
|
pub start_pos: (usize, usize),
|
|
pub end_pos: (usize, usize),
|
|
}
|
|
|
|
impl Connection {
|
|
pub fn render(&self, tgt: &mut [[StructureElement; LEVEL_HEIGHT]; LEVEL_WIDTH]) {
|
|
// the tuples are (col, row)
|
|
let abs_d_col =
|
|
max(self.start_pos.0, self.end_pos.0) - min(self.start_pos.0, self.end_pos.0);
|
|
|
|
let abs_d_row =
|
|
max(self.start_pos.1, self.end_pos.1) - min(self.start_pos.1, self.end_pos.1);
|
|
|
|
if abs_d_col == 0 {
|
|
let range = if self.end_pos.1 > self.start_pos.1 {
|
|
self.start_pos.1..=self.end_pos.1
|
|
} else {
|
|
self.end_pos.1..=self.start_pos.1
|
|
};
|
|
|
|
for row in range {
|
|
tgt[self.end_pos.0][row] = StructureElement::Floor;
|
|
}
|
|
} else if abs_d_row == 0 {
|
|
let range = if self.end_pos.0 > self.start_pos.0 {
|
|
self.start_pos.0..=self.end_pos.0
|
|
} else {
|
|
self.end_pos.0..=self.start_pos.0
|
|
};
|
|
for col in range {
|
|
tgt[col][self.end_pos.1] = StructureElement::Floor;
|
|
}
|
|
} else {
|
|
let d_row = self.end_pos.1 as i32 - self.start_pos.1 as i32;
|
|
if d_row > 0 {
|
|
// more up/down
|
|
let d_row = self.end_pos.1 - self.start_pos.1;
|
|
for r in self.start_pos.1..=(self.start_pos.1 + d_row / 2) {
|
|
tgt[self.start_pos.0][r] = StructureElement::Floor;
|
|
}
|
|
let range = if self.end_pos.0 > self.start_pos.0 {
|
|
self.start_pos.0..=self.end_pos.0
|
|
} else {
|
|
self.end_pos.0..=self.start_pos.0
|
|
};
|
|
for c in range {
|
|
tgt[c][self.start_pos.1 + d_row / 2] = StructureElement::Floor;
|
|
}
|
|
for r in (self.start_pos.1 + d_row / 2)..=self.end_pos.1 {
|
|
tgt[self.end_pos.0][r] = StructureElement::Floor;
|
|
}
|
|
} else {
|
|
// more left/right
|
|
let d_col = self.end_pos.0 - self.start_pos.0;
|
|
for tgt_col in tgt.iter_mut().skip(self.start_pos.0).take(d_col / 2 + 1) {
|
|
tgt_col[self.start_pos.1] = StructureElement::Floor;
|
|
}
|
|
let range = if self.end_pos.1 > self.start_pos.1 {
|
|
self.start_pos.1..=self.end_pos.1
|
|
} else {
|
|
self.end_pos.1..=self.start_pos.1
|
|
};
|
|
for r in range {
|
|
tgt[self.start_pos.0 + d_col / 2][r] = StructureElement::Floor;
|
|
}
|
|
for tgt_col in tgt
|
|
.iter_mut()
|
|
.take(self.end_pos.0 + 1)
|
|
.skip(self.start_pos.0 + d_col / 2)
|
|
{
|
|
tgt_col[self.end_pos.1] = StructureElement::Floor;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Copy, Clone, Debug)]
|
|
pub struct Room {
|
|
pub kind: RoomType,
|
|
pub offset_x: usize,
|
|
pub offset_y: usize,
|
|
pub width: usize,
|
|
pub height: usize,
|
|
pub special: (usize, usize),
|
|
pub connection_down: Option<Connection>,
|
|
pub connection_right: Option<Connection>,
|
|
}
|
|
|
|
impl Room {
|
|
pub fn new(rng: &mut ThreadRng) -> Self {
|
|
let width = rng.gen_range(3..ROOM_WIDTH);
|
|
let height = rng.gen_range(3..ROOM_HEIGHT);
|
|
let offset_x = rng.gen_range(0..(ROOM_WIDTH - width));
|
|
let offset_y = rng.gen_range(0..(ROOM_HEIGHT - height));
|
|
let sx = offset_x + rng.gen_range(1..width - 1);
|
|
let sy = offset_y + rng.gen_range(1..height - 1);
|
|
|
|
Self {
|
|
kind: RoomType::EmptyRoom,
|
|
offset_x,
|
|
offset_y,
|
|
width,
|
|
height,
|
|
special: (sx, sy),
|
|
connection_down: None,
|
|
connection_right: None,
|
|
}
|
|
}
|
|
|
|
// pub fn get_x_range(&self) -> Range<usize> {
|
|
// self.offset_x..self.offset_x + self.width
|
|
// }
|
|
|
|
// pub fn get_y_range(&self) -> Range<usize> {
|
|
// self.offset_y..self.offset_y + self.height
|
|
// }
|
|
|
|
pub fn render(
|
|
&self,
|
|
tgt: &mut [[StructureElement; LEVEL_HEIGHT]; LEVEL_WIDTH],
|
|
col: usize,
|
|
row: usize,
|
|
) -> (usize, usize) {
|
|
let top = 1 + row * ROOM_HEIGHT;
|
|
let left = 1 + col * ROOM_WIDTH;
|
|
for col in tgt.iter_mut().skip(left).take(ROOM_WIDTH) {
|
|
//left..left + ROOM_WIDTH {
|
|
for row_element in col.iter_mut().skip(top).take(ROOM_HEIGHT) {
|
|
// top..top + ROOM_HEIGHT {
|
|
*row_element = StructureElement::Wall;
|
|
}
|
|
}
|
|
if self.kind == RoomType::EmptyRoom {
|
|
return (0, 0);
|
|
}
|
|
// render floor elements
|
|
for x in 0..self.width {
|
|
for y in 0..self.height {
|
|
tgt[left + self.offset_x + x][top + self.offset_y + y] = StructureElement::Floor;
|
|
}
|
|
}
|
|
|
|
match self.kind {
|
|
RoomType::Start => {
|
|
tgt[left + self.special.0][top + self.special.1] = StructureElement::Start;
|
|
}
|
|
RoomType::End => {
|
|
tgt[left + self.special.0][top + self.special.1] = StructureElement::End;
|
|
}
|
|
RoomType::StairUp => {
|
|
tgt[left + self.special.0][top + self.special.1] = StructureElement::StairUp;
|
|
}
|
|
RoomType::StairDown => {
|
|
tgt[left + self.special.0][top + self.special.1] = StructureElement::StairDown;
|
|
}
|
|
_ => {}
|
|
};
|
|
(left + self.special.0, top + self.special.1)
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_room_creation() {
|
|
let mut rng = rand::thread_rng();
|
|
let room = Room::new(&mut rng);
|
|
assert_eq!(room.kind, RoomType::EmptyRoom);
|
|
for _ in 0..1000 {
|
|
let room = Room::new(&mut rng);
|
|
assert!((3..=ROOM_WIDTH).contains(&room.width));
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
fn all_wall(
|
|
tgt: &mut [[StructureElement; LEVEL_HEIGHT]; LEVEL_WIDTH],
|
|
col: usize,
|
|
row: usize,
|
|
) -> bool {
|
|
let top = 1 + row * ROOM_HEIGHT;
|
|
let left = 1 + col * ROOM_WIDTH;
|
|
for x in left..left + ROOM_WIDTH {
|
|
for y in top..top + ROOM_HEIGHT {
|
|
if tgt[x][y] != StructureElement::Wall {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
true
|
|
}
|
|
|
|
#[cfg(test)]
|
|
fn has_structure_element(
|
|
tgt: &mut [[StructureElement; LEVEL_HEIGHT]; LEVEL_WIDTH],
|
|
col: usize,
|
|
row: usize,
|
|
element: StructureElement,
|
|
) -> bool {
|
|
let top = 1 + row * ROOM_HEIGHT;
|
|
let left = 1 + col * ROOM_WIDTH;
|
|
for x in left..left + ROOM_WIDTH {
|
|
for y in top..top + ROOM_HEIGHT {
|
|
if tgt[x][y] == element {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
false
|
|
}
|
|
|
|
#[test]
|
|
fn test_room_render_empty() {
|
|
let mut rng = rand::thread_rng();
|
|
let room = Room::new(&mut rng);
|
|
assert_eq!(room.kind, RoomType::EmptyRoom);
|
|
let mut structure = [[StructureElement::Floor; LEVEL_HEIGHT]; LEVEL_WIDTH];
|
|
|
|
room.render(&mut structure, 0, 0);
|
|
assert!(all_wall(&mut structure, 0, 0));
|
|
}
|
|
|
|
#[test]
|
|
fn test_room_render_basic() {
|
|
let mut rng = rand::thread_rng();
|
|
let mut room = Room::new(&mut rng);
|
|
room.kind = RoomType::BasicRoom;
|
|
let mut structure = [[StructureElement::Floor; LEVEL_HEIGHT]; LEVEL_WIDTH];
|
|
|
|
room.render(&mut structure, 1, 3);
|
|
assert!(!all_wall(&mut structure, 1, 3));
|
|
}
|
|
|
|
#[test]
|
|
fn test_room_render_start() {
|
|
let mut rng = rand::thread_rng();
|
|
let mut room = Room::new(&mut rng);
|
|
room.kind = RoomType::Start;
|
|
let mut structure = [[StructureElement::Floor; LEVEL_HEIGHT]; LEVEL_WIDTH];
|
|
|
|
room.render(&mut structure, 1, 3);
|
|
assert!(!all_wall(&mut structure, 1, 3));
|
|
assert!(has_structure_element(
|
|
&mut structure,
|
|
1,
|
|
3,
|
|
StructureElement::Start
|
|
));
|
|
}
|