From 27b6cc385dc8fa56c0fafbf862889457137383b3 Mon Sep 17 00:00:00 2001 From: Timothy Warren Date: Tue, 4 Jan 2022 15:34:07 -0500 Subject: [PATCH] Improve forest map generation --- src/gamesystem.rs | 2 +- src/map_builders/forest.rs | 121 +++++++++++++++++++++++++++++++++++-- 2 files changed, 117 insertions(+), 6 deletions(-) diff --git a/src/gamesystem.rs b/src/gamesystem.rs index 2821759..9632f81 100644 --- a/src/gamesystem.rs +++ b/src/gamesystem.rs @@ -9,7 +9,7 @@ pub fn player_hp_per_level(fitness: i32) -> i32 { } pub fn player_hp_at_level(fitness: i32, level: i32) -> i32 { - player_hp_per_level(fitness) * level + 10 + player_hp_per_level(fitness) * level } pub fn npc_hp(fitness: i32, level: i32) -> i32 { diff --git a/src/map_builders/forest.rs b/src/map_builders/forest.rs index b4ddc1f..858ec28 100644 --- a/src/map_builders/forest.rs +++ b/src/map_builders/forest.rs @@ -1,9 +1,11 @@ use ::rltk::RandomNumberGenerator; use super::{ - AreaStartingPosition, BuilderChain, CellularAutomataBuilder, CullUnreachable, DistantExit, + AreaStartingPosition, BuilderChain, CellularAutomataBuilder, CullUnreachable, MetaMapBuilder, VoronoiSpawning, XStart, YStart, }; +use crate::map_builders::BuilderMap; +use crate::{map, TileType}; pub fn forest_builder( new_depth: i32, @@ -17,10 +19,119 @@ pub fn forest_builder( .start_with(CellularAutomataBuilder::new()) .with(AreaStartingPosition::new(XStart::Center, YStart::Center)) .with(CullUnreachable::new()) - .with(AreaStartingPosition::new(XStart::Left, YStart::Center)); - - // Setup an exit and spawn mobs - chain.with(VoronoiSpawning::new()).with(DistantExit::new()); + .with(AreaStartingPosition::new(XStart::Left, YStart::Center)) + .with(VoronoiSpawning::new()) + .with(YellowBrickRoad::new()); chain } + +pub struct YellowBrickRoad {} + +impl MetaMapBuilder for YellowBrickRoad { + fn build_map(&mut self, rng: &mut RandomNumberGenerator, build_data: &mut BuilderMap) { + self.build(rng, build_data); + } +} + +impl YellowBrickRoad { + #[allow(dead_code)] + pub fn new() -> Box { + Box::new(YellowBrickRoad {}) + } + + fn find_exit(&self, build_data: &mut BuilderMap, seed_x: i32, seed_y: i32) -> (i32, i32) { + let mut available_floors: Vec<(usize, f32)> = Vec::new(); + for (idx, tiletype) in build_data.map.tiles.iter().enumerate() { + if map::tile_walkable(*tiletype) { + available_floors.push(( + idx, + rltk::DistanceAlg::PythagorasSquared.distance2d( + rltk::Point::new( + idx as i32 % build_data.map.width, + idx as i32 / build_data.map.width, + ), + rltk::Point::new(seed_x, seed_y), + ), + )); + } + } + + if available_floors.is_empty() { + panic!("No valid floors to start on"); + } + + available_floors.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap()); + + let end_x = available_floors[0].0 as i32 % build_data.map.width; + let end_y = available_floors[0].0 as i32 / build_data.map.width; + + (end_x, end_y) + } + + fn paint_road(&self, build_data: &mut BuilderMap, x: i32, y: i32) { + if x < 1 || x > build_data.map.width - 2 || y < 1 || y > build_data.map.height - 2 { + return; + } + + let idx = build_data.map.xy_idx(x, y); + if build_data.map.tiles[idx] != TileType::DownStairs { + build_data.map.tiles[idx] = TileType::Road; + } + } + + fn build(&mut self, rng: &mut RandomNumberGenerator, build_data: &mut BuilderMap) { + let starting_pos = build_data.starting_position.as_ref().unwrap().clone(); + let start_idx = build_data.map.xy_idx(starting_pos.x, starting_pos.y); + + let (end_x, end_y) = self.find_exit( + build_data, + build_data.map.width - 2, + build_data.map.height / 2, + ); + let end_idx = build_data.map.xy_idx(end_x, end_y); + + build_data.map.populate_blocked(); + let path = rltk::a_star_search(start_idx, end_idx, &mut build_data.map); + + for idx in path.steps.iter() { + let x = *idx as i32 % build_data.map.width; + let y = *idx as i32 / build_data.map.width; + self.paint_road(build_data, x, y); + self.paint_road(build_data, x - 1, y); + self.paint_road(build_data, x + 1, y); + self.paint_road(build_data, x, y - 1); + self.paint_road(build_data, x, y + 1); + } + build_data.map.tiles[end_idx] = TileType::DownStairs; + build_data.take_snapshot(); + + // Place exit + let exit_dir = rng.roll_dice(1, 2); + let (seed_x, seed_y, stream_startx, stream_starty) = if exit_dir == 1 { + (build_data.map.width - 1, 1, 0, build_data.height - 1) + } else { + ( + build_data.map.width - 1, + build_data.height - 1, + 1, + build_data.height - 1, + ) + }; + + let (stairs_x, stairs_y) = self.find_exit(build_data, seed_x, seed_y); + let stairs_idx = build_data.map.xy_idx(stairs_x, stairs_y); + build_data.take_snapshot(); + + let (stream_x, stream_y) = self.find_exit(build_data, stream_startx, stream_starty); + let stream_idx = build_data.map.xy_idx(stream_x, stream_y) as usize; + let stream = rltk::a_star_search(stairs_idx, stream_idx, &mut build_data.map); + for tile in stream.steps.iter() { + if build_data.map.tiles[*tile as usize] == TileType::Floor { + build_data.map.tiles[*tile as usize] = TileType::ShallowWater; + } + } + build_data.map.tiles[stairs_idx] = TileType::DownStairs; + build_data.take_snapshot(); + } +}