diff --git a/src/main.rs b/src/main.rs index 4ecfae1..a2f23d8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,11 @@ use rltk::{Console, GameState, Rltk, RGB, VirtualKeyCode}; use specs::prelude::*; use std::cmp::{max, min}; - +mod rect; +pub use rect::*; +mod map; +pub use map::*; + #[macro_use] extern crate specs_derive; @@ -25,70 +29,6 @@ struct State { #[derive(Component)] struct LeftMover {} - -#[derive(PartialEq, Copy, Clone)] -enum TileType { - Wall, Floor -} - -pub fn xy_idx(x: i32, y: i32) -> usize { - (y as usize * 80) + x as usize -} - -fn new_map() -> Vec { - let mut map = vec![TileType::Floor; 80*50]; - - // Make the boundaries walls - for x in 0..80 { - map[xy_idx(x, 0)] = TileType::Wall; - map[xy_idx(x, 49)] = TileType::Wall; - } - for y in 0..50 { - map[xy_idx(0, y)] = TileType::Wall; - map[xy_idx(79, y)] = TileType::Wall; - } - - // Now we'll randomly splat a bunch of walls. It won't be pretty, but it's a decent illustration. - // First, obtain the thread-local RNG: - let mut rng = rltk::RandomNumberGenerator::new(); - - for _i in 0..400 { - let x = rng.roll_dice(1, 79); - let y = rng.roll_dice(1, 49); - let idx = xy_idx(x, y); - if idx != xy_idx(40, 25) { - map[idx] = TileType::Wall; - } - } - - map -} - - -fn draw_map(map: &[TileType], ctx : &mut Rltk) { - let mut y = 0; - let mut x = 0; - for tile in map.iter() { - // Render a tile depending upon the tile type - match tile { - TileType::Floor => { - ctx.set(x, y, RGB::from_f32(0.5, 0.5, 0.5), RGB::from_f32(0., 0., 0.), rltk::to_cp437('.')); - } - TileType::Wall => { - ctx.set(x, y, RGB::from_f32(0.0, 1.0, 0.0), RGB::from_f32(0., 0., 0.), rltk::to_cp437('#')); - } - } - - // Move the coordinates - x += 1; - if x > 79 { - x = 0; - y += 1; - } - } -} - - #[derive(Component, Debug)] struct Player {} @@ -197,7 +137,7 @@ fn main() { .build(); } - gs.ecs.insert(new_map()); + gs.ecs.insert(new_map_rooms_and_corridors()); rltk::main_loop(context, gs); } diff --git a/src/map.rs b/src/map.rs new file mode 100644 index 0000000..af071fa --- /dev/null +++ b/src/map.rs @@ -0,0 +1,149 @@ +use super::rect::*; +use rltk::{Console, RandomNumberGenerator, Rltk, RGB}; +use std::cmp::{max, min}; + +#[derive(PartialEq, Copy, Clone)] +pub enum TileType { + Wall, + Floor, +} + +pub fn xy_idx(x: i32, y: i32) -> usize { + (y as usize * 80) + x as usize +} + +fn new_map_test() -> Vec { + let mut map = vec![TileType::Floor; 80 * 50]; + + // Make the boundaries walls + for x in 0..80 { + map[xy_idx(x, 0)] = TileType::Wall; + map[xy_idx(x, 49)] = TileType::Wall; + } + for y in 0..50 { + map[xy_idx(0, y)] = TileType::Wall; + map[xy_idx(79, y)] = TileType::Wall; + } + + // Now we'll randomly splat a bunch of walls. It won't be pretty, but it's a decent illustration. + // First, obtain the thread-local RNG: + let mut rng = rltk::RandomNumberGenerator::new(); + + for _i in 0..400 { + let x = rng.roll_dice(1, 79); + let y = rng.roll_dice(1, 49); + let idx = xy_idx(x, y); + if idx != xy_idx(40, 25) { + map[idx] = TileType::Wall; + } + } + + map +} + +fn apply_room_to_map(room: &Rect, map: &mut Vec) { + for x in room.x1..room.x2 { + for y in room.y1..room.y2 { + map[xy_idx(x, y)] = TileType::Floor + } + } +} + +fn apply_horizontal_tunnel(map: &mut Vec, x1: i32, x2: i32, y: i32) { + for x in min(x1, x2)..=max(x1, x2) { + map[xy_idx(x, y)] = TileType::Floor + } +} + +fn apply_vertical_tunnel(map: &mut Vec, x: i32, y1: i32, y2: i32) { + for y in min(y1, y2)..=max(y1, y2) { + map[xy_idx(x, y)] = TileType::Floor + } +} + +pub fn new_map_rooms_and_corridors() -> Vec { + let mut map = vec![TileType::Wall; 80 * 50]; + + let mut rooms: Vec = Vec::new(); + const MAX_ROOMS: i32 = 30; + const MIN_SIZE: i32 = 6; + const MAX_SIZE: i32 = 10; + + let mut rng = RandomNumberGenerator::new(); + + for _ in 0..MAX_ROOMS { + let w = rng.range(MIN_SIZE, MAX_SIZE); + let h = rng.range(MIN_SIZE, MAX_SIZE); + let x = rng.roll_dice(1, 80 - w - 1) - 1; + let y = rng.roll_dice(1, 50 - h - 1) - 1; + let new_room = Rect::new(x, y, w, h); + let mut ok = true; + for other_room in rooms.iter() { + if new_room.intersect(other_room) { + ok = false + } + } + if ok { + apply_room_to_map(&new_room, &mut map); + if rooms.len() > 0 { + let r1_center = new_room.center(); + let r2_center = rooms[rooms.len() - 1].center(); + if rng.range(0, 2) == 1 { + apply_horizontal_tunnel(&mut map, r1_center.0, r2_center.0, r1_center.1); + apply_vertical_tunnel( + &mut map, + max(r1_center.0, r2_center.0), + r1_center.1, + r2_center.1, + ); + } else { + apply_vertical_tunnel(&mut map, r1_center.0, r1_center.1, r2_center.1); + apply_horizontal_tunnel( + &mut map, + r1_center.0, + r2_center.0, + max(r1_center.1, r2_center.1), + ); + } + } + rooms.push(new_room); + } + } + + map +} + +pub fn draw_map(map: &[TileType], ctx: &mut Rltk) { + let mut y = 0; + let mut x = 0; + for tile in map.iter() { + // Render a tile depending upon the tile type + match tile { + TileType::Floor => { + ctx.set( + x, + y, + RGB::from_f32(0.5, 0.5, 0.5), + RGB::from_f32(0., 0., 0.), + rltk::to_cp437('.'), + ); + } + TileType::Wall => { + ctx.set( + x, + y, + RGB::from_f32(0.0, 1.0, 0.0), + RGB::from_f32(0., 0., 0.), + rltk::to_cp437('#'), + ); + } + } + + // Move the coordinates + x += 1; + if x > 79 { + x = 0; + y += 1; + } + } +} diff --git a/src/rect.rs b/src/rect.rs new file mode 100644 index 0000000..bff317e --- /dev/null +++ b/src/rect.rs @@ -0,0 +1,25 @@ +pub struct Rect { + pub x1: i32, + pub x2: i32, + pub y1: i32, + pub y2: i32, +} + +impl Rect { + pub fn new(x: i32, y: i32, w: i32, h: i32) -> Rect { + Rect { + x1: x, + y1: y, + x2: x + w, + y2: y + h, + } + } + + pub fn intersect(&self, other: &Rect) -> bool { + self.x1 <= other.x2 && self.x2 >= other.x1 && self.y1 <= other.y2 && self.y2 >= other.y1 + } + + pub fn center(&self) -> (i32, i32) { + ((self.x1 + self.x2) / 2, (self.y1 + self.y2) / 2) + } +}