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, pub connection_right: Option, } 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 { // self.offset_x..self.offset_x + self.width // } // pub fn get_y_range(&self) -> Range { // 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 )); }