From fae5608d919dc6367593c07a4af661a2500b0a37 Mon Sep 17 00:00:00 2001 From: Tyrel Souza <923113+tyrelsouza@users.noreply.github.com> Date: Tue, 31 Aug 2021 13:33:30 -0400 Subject: [PATCH] drunkard, and prefabs --- src/map_builder/drunkard.rs | 78 +++++++++++++++++++++++++++++++++++++ src/map_builder/mod.rs | 15 ++++++- src/map_builder/prefab.rs | 78 +++++++++++++++++++++++++++++++++++++ 3 files changed, 169 insertions(+), 2 deletions(-) create mode 100644 src/map_builder/drunkard.rs create mode 100644 src/map_builder/prefab.rs diff --git a/src/map_builder/drunkard.rs b/src/map_builder/drunkard.rs new file mode 100644 index 0000000..3a4a03a --- /dev/null +++ b/src/map_builder/drunkard.rs @@ -0,0 +1,78 @@ +use super::MapArchitect; +use crate::prelude::*; +pub struct DrunkardsWalkArchitect {} + +const STAGGER_DISTANCE: usize = 400; +const NUM_TILES: usize = (SCREEN_HEIGHT * SCREEN_WIDTH) as usize; +const DESIRED_FLOOR: usize = NUM_TILES / 3; + +impl MapArchitect for DrunkardsWalkArchitect { + fn new(&mut self, rng: &mut RandomNumberGenerator) -> MapBuilder { + let mut mb = MapBuilder { + map: Map::new(), + rooms: Vec::new(), + monster_spawns: Vec::new(), + player_start: Point::zero(), + amulet_start: Point::zero(), + }; + mb.fill(TileType::Wall); + let center = Point::new(SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2); + self.drunkard(¢er, rng, &mut mb.map); + while mb + .map + .tiles + .iter() + .filter(|t| **t == TileType::Floor) + .count() + < DESIRED_FLOOR + { + self.drunkard( + &Point::new(rng.range(0, SCREEN_WIDTH), rng.range(0, SCREEN_HEIGHT)), + rng, + &mut mb.map, + ); + let dijkstra_map = DijkstraMap::new( + SCREEN_WIDTH, + SCREEN_HEIGHT, + &vec![mb.map.point2d_to_index(center)], + &mb.map, + 1024.0, + ); + dijkstra_map + .map + .iter() + .enumerate() + .filter(|(_, distance)| *distance > &2000.0) + .for_each(|(idx, _)| mb.map.tiles[idx] = TileType::Wall); + } + + mb.monster_spawns = mb.spawn_monsters(¢er, rng); + mb.player_start = center; + mb.amulet_start = mb.find_most_distant(); + mb + } +} + +impl DrunkardsWalkArchitect { + fn drunkard(&mut self, start: &Point, rng: &mut RandomNumberGenerator, map: &mut Map) { + let mut drunkard_pos = start.clone(); + let mut distance_staggered = 0; + loop { + let drunk_idx = map.point2d_to_index(drunkard_pos); + map.tiles[drunk_idx] = TileType::Floor; + match rng.range(0, 4) { + 0 => drunkard_pos.x -= 1, + 1 => drunkard_pos.x += 1, + 2 => drunkard_pos.y -= 1, + _ => drunkard_pos.y += 1, + } + if !map.in_bounds(drunkard_pos) { + break; + } + distance_staggered += 1; + if distance_staggered > STAGGER_DISTANCE { + break; + } + } + } +} diff --git a/src/map_builder/mod.rs b/src/map_builder/mod.rs index b63df17..fd3a693 100644 --- a/src/map_builder/mod.rs +++ b/src/map_builder/mod.rs @@ -1,11 +1,15 @@ mod automata; +mod drunkard; mod empty; +mod prefab; mod rooms; use automata::AutomataArchitect; +use drunkard::DrunkardsWalkArchitect; use empty::EmptyArchitect; use rooms::RoomsArchitect; +use crate::map_builder::prefab::apply_prefab; use crate::prelude::*; trait MapArchitect { @@ -23,9 +27,16 @@ pub struct MapBuilder { impl MapBuilder { pub fn new(rng: &mut RandomNumberGenerator) -> Self { - let mut architecht = AutomataArchitect {}; + let mut architect: Box = match rng.range(0, 3) { + 0 => Box::new(DrunkardsWalkArchitect {}), + 1 => Box::new(RoomsArchitect {}), + _ => Box::new(AutomataArchitect {}), + }; - architecht.new(rng) + let mut mb = architect.new(rng); + apply_prefab(&mut mb, rng); + + mb } fn fill(&mut self, tile: TileType) { diff --git a/src/map_builder/prefab.rs b/src/map_builder/prefab.rs new file mode 100644 index 0000000..67e2826 --- /dev/null +++ b/src/map_builder/prefab.rs @@ -0,0 +1,78 @@ +use crate::prelude::*; + +const FORTRESS: (&str, i32, i32) = ( + " +------------ +---######--- +---#----#--- +---#-M--#--- +-###----###- +--M--------- +-###----###- +---#----#--- +---#----#--- +---######--- +------------ +", + 12, + 11, +); + +pub fn apply_prefab(mb: &mut MapBuilder, rng: &mut RandomNumberGenerator) { + let mut placement = None; + let dijkstra_map = DijkstraMap::new( + SCREEN_WIDTH, + SCREEN_HEIGHT, + &vec![mb.map.point2d_to_index(mb.player_start)], + &mb.map, + 1024.0, + ); + let mut attempts = 0; + while placement.is_none() && attempts < 10 { + let dimensions = Rect::with_size( + rng.range(0, SCREEN_WIDTH - FORTRESS.1), + rng.range(0, SCREEN_HEIGHT - FORTRESS.2), + FORTRESS.1, + FORTRESS.2, + ); + let mut can_place = false; + dimensions.for_each(|pt| { + let idx = mb.map.point2d_to_index(pt); + let distance = dijkstra_map.map[idx]; + if distance < 2000.0 && distance > 20.0 && mb.amulet_start != pt { + can_place = true; + } + }); + if can_place { + placement = Some(Point::new(dimensions.x1, dimensions.y1)); + let points = dimensions.point_set(); + mb.monster_spawns.retain(|pt| !points.contains(pt)); + } + attempts += 1; + } + + if let Some(placement) = placement { + let string_vec: Vec = FORTRESS + .0 + .chars() + .filter(|a| *a != '\r' && *a != '\n') + .collect(); + let mut i = 0; + for ty in placement.y..placement.y + FORTRESS.2 { + for tx in placement.x..placement.x + FORTRESS.1 { + let idx = map_idx(tx, ty); + let c = string_vec[i]; + match c { + 'M' => { + mb.map.tiles[idx] = TileType::Floor; + mb.monster_spawns.push(Point::new(tx, ty)); + } + '-' => mb.map.tiles[idx] = TileType::Floor, + '#' => mb.map.tiles[idx] = TileType::Wall, + _ => println!("No idea what to do with [{}]", c), + } + i += 1; + } + } + } +}