From 0af23af3d349460efcc0cb93f636ec782925a8c4 Mon Sep 17 00:00:00 2001 From: Kasper Juul Hermansen Date: Sat, 29 Jan 2022 16:35:10 +0100 Subject: [PATCH] Add basic map --- src/main.rs | 9 +++-- src/map.rs | 74 +--------------------------------- src/map_builders/common.rs | 31 ++++++++++++++ src/map_builders/mod.rs | 13 ++++++ src/map_builders/simple_map.rs | 61 ++++++++++++++++++++++++++++ src/spawner.rs | 7 +++- src/trigger_system.rs | 32 ++++++++++++--- src/visibility_system.rs | 24 +++++++++-- 8 files changed, 165 insertions(+), 86 deletions(-) create mode 100644 src/map_builders/common.rs create mode 100644 src/map_builders/mod.rs create mode 100644 src/map_builders/simple_map.rs diff --git a/src/main.rs b/src/main.rs index 11bee94..b1a83d3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -30,6 +30,7 @@ mod healing_system; mod hunger_system; mod inventory_system; mod map; +mod map_builders; mod map_indexing_system; mod melee_combat_system; mod monster_ai_system; @@ -40,8 +41,8 @@ mod rect; mod rex_assets; mod save_load_system; mod spawner; -mod visibility_system; mod trigger_system; +mod visibility_system; #[derive(PartialEq, Copy, Clone)] pub enum RunState { @@ -86,7 +87,7 @@ impl State { let worldmap; { let mut worldmap_resource = self.ecs.write_resource::(); - *worldmap_resource = Map::new_map_rooms_and_corridors(1); + *worldmap_resource = map_builders::build_random_map(1); worldmap = worldmap_resource.clone(); } @@ -169,7 +170,7 @@ impl State { { let mut worldmap_resource = self.ecs.write_resource::(); current_depth = worldmap_resource.depth; - *worldmap_resource = Map::new_map_rooms_and_corridors(current_depth + 1); + *worldmap_resource = map_builders::build_random_map(current_depth + 1); worldmap = worldmap_resource.clone(); } @@ -512,7 +513,7 @@ fn main() -> rltk::BError { gs.ecs.insert(SimpleMarkerAllocator::::new()); - let map = Map::new_map_rooms_and_corridors(1); + let map = map_builders::build_random_map(1); let (player_x, player_y) = map.rooms[0].center(); let player_entity = spawner::player(&mut gs.ecs, player_x, player_y); diff --git a/src/map.rs b/src/map.rs index 4c0020c..91b02a5 100644 --- a/src/map.rs +++ b/src/map.rs @@ -41,33 +41,6 @@ impl Map { (y as usize * self.width as usize) + x as usize } - fn apply_room_to_map(&mut self, room: &Rect) { - for y in room.y1 + 1..=room.y2 { - for x in room.x1 + 1..=room.x2 { - let idx = self.xy_idx(x, y); - self.tiles[idx] = TileType::Floor; - } - } - } - - fn apply_horizontal_tunnel(&mut self, x1: i32, x2: i32, y: i32) { - for x in min(x1, x2)..=max(x1, x2) { - let idx = self.xy_idx(x, y); - if idx > 0 && idx < self.width as usize * self.height as usize { - self.tiles[idx as usize] = TileType::Floor; - } - } - } - - fn apply_vertical_tunnel(&mut self, y1: i32, y2: i32, x: i32) { - for y in min(y1, y2)..=max(y1, y2) { - let idx = self.xy_idx(x, y); - if idx > 0 && idx < self.width as usize * self.height as usize { - self.tiles[idx as usize] = TileType::Floor; - } - } - } - fn is_exit_valid(&self, x: i32, y: i32) -> bool { if x < 1 || x > self.width - 1 || y < 1 || y > self.height - 1 { return false; @@ -88,8 +61,8 @@ impl Map { } } - pub fn new_map_rooms_and_corridors(new_depth: i32) -> Map { - let mut map = Map { + pub fn new(new_depth: i32) -> Map { + Map { tiles: vec![TileType::Wall; MAP_COUNT], rooms: Vec::new(), width: MAP_WIDTH as i32, @@ -100,50 +73,7 @@ impl Map { tile_content: vec![Vec::new(); MAP_COUNT], depth: new_depth, bloodstains: HashSet::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, map.width - w - 1) - 1; - let y = rng.roll_dice(1, map.height - h - 1) - 1; - let new_room = Rect::new(x, y, w, h); - let mut ok = true; - for other_room in map.rooms.iter() { - if new_room.intersect(other_room) { - ok = false - }; - } - if ok { - map.apply_room_to_map(&new_room); - - if !map.rooms.is_empty() { - let (new_x, new_y) = new_room.center(); - let (prev_x, prev_y) = map.rooms[map.rooms.len() - 1].center(); - if rng.range(0, 2) == 1 { - map.apply_horizontal_tunnel(prev_x, new_x, prev_y); - map.apply_vertical_tunnel(prev_y, new_y, new_x); - } else { - map.apply_vertical_tunnel(prev_y, new_y, prev_x); - map.apply_horizontal_tunnel(prev_x, new_x, new_y); - } - } - - map.rooms.push(new_room); - } } - - let stairs_position = map.rooms[map.rooms.len() - 1].center(); - let stairs_idx = map.xy_idx(stairs_position.0, stairs_position.1); - map.tiles[stairs_idx] = TileType::DownStairs; - - map } } diff --git a/src/map_builders/common.rs b/src/map_builders/common.rs new file mode 100644 index 0000000..15fe08c --- /dev/null +++ b/src/map_builders/common.rs @@ -0,0 +1,31 @@ +use std::cmp::{max, min}; + +use crate::rect::Rect; +use crate::{Map, TileType}; + +pub fn apply_room_to_map(map: &mut Map, room: &Rect) { + for y in room.y1 + 1..=room.y2 { + for x in room.x1 + 1..=room.x2 { + let idx = map.xy_idx(x, y); + map.tiles[idx] = TileType::Floor; + } + } +} + +pub fn apply_horizontal_tunnel(map: &mut Map, x1: i32, x2: i32, y: i32) { + for x in min(x1, x2)..=max(x1, x2) { + let idx = map.xy_idx(x, y); + if idx > 0 && idx < map.width as usize * map.height as usize { + map.tiles[idx as usize] = TileType::Floor; + } + } +} + +pub fn apply_vertical_tunnel(map: &mut Map, y1: i32, y2: i32, x: i32) { + for y in min(y1, y2)..=max(y1, y2) { + let idx = map.xy_idx(x, y); + if idx > 0 && idx < map.width as usize * map.height as usize { + map.tiles[idx as usize] = TileType::Floor; + } + } +} diff --git a/src/map_builders/mod.rs b/src/map_builders/mod.rs new file mode 100644 index 0000000..eaf89a5 --- /dev/null +++ b/src/map_builders/mod.rs @@ -0,0 +1,13 @@ +use crate::map_builders::simple_map::SimpleMapBuilder; +use crate::Map; + +mod common; +mod simple_map; + +trait MapBuilder { + fn build(new_depth: i32) -> Map; +} + +pub fn build_random_map(new_depth: i32) -> Map { + SimpleMapBuilder::build(new_depth) +} diff --git a/src/map_builders/simple_map.rs b/src/map_builders/simple_map.rs new file mode 100644 index 0000000..51f0d67 --- /dev/null +++ b/src/map_builders/simple_map.rs @@ -0,0 +1,61 @@ +use rltk::RandomNumberGenerator; +use specs::prelude::*; + +use crate::map_builders::{common, MapBuilder}; +use crate::rect::Rect; +use crate::{Map, TileType}; + +pub struct SimpleMapBuilder {} + +impl SimpleMapBuilder { + fn rooms_and_corridors(map: &mut Map) { + 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, map.width - w - 1) - 1; + let y = rng.roll_dice(1, map.height - h - 1) - 1; + let new_room = Rect::new(x, y, w, h); + let mut ok = true; + for other_room in map.rooms.iter() { + if new_room.intersect(other_room) { + ok = false + }; + } + if ok { + common::apply_room_to_map(map, &new_room); + + if !map.rooms.is_empty() { + let (new_x, new_y) = new_room.center(); + let (prev_x, prev_y) = map.rooms[map.rooms.len() - 1].center(); + if rng.range(0, 2) == 1 { + common::apply_horizontal_tunnel(map, prev_x, new_x, prev_y); + common::apply_vertical_tunnel(map, prev_y, new_y, new_x); + } else { + common::apply_vertical_tunnel(map, prev_y, new_y, prev_x); + common::apply_horizontal_tunnel(map, prev_x, new_x, new_y); + } + } + + map.rooms.push(new_room); + } + } + + let stairs_position = map.rooms[map.rooms.len() - 1].center(); + let stairs_idx = map.xy_idx(stairs_position.0, stairs_position.1); + map.tiles[stairs_idx] = TileType::DownStairs; + } +} + +impl MapBuilder for SimpleMapBuilder { + fn build(new_depth: i32) -> Map { + let mut map = Map::new(new_depth); + SimpleMapBuilder::rooms_and_corridors(&mut map); + map + } +} diff --git a/src/spawner.rs b/src/spawner.rs index 4a1a6d9..746995f 100644 --- a/src/spawner.rs +++ b/src/spawner.rs @@ -4,9 +4,14 @@ use rltk::{FontCharType, RandomNumberGenerator, RGB}; use specs::prelude::*; use specs::saveload::{MarkedBuilder, SimpleMarker}; -use crate::{AreaOfEffect, BlocksTile, CombatStats, Confusion, Consumable, DefenseBonus, EntryTrigger, EquipmentSlot, Equippable, Hidden, HungerClock, HungerState, InflictsDamage, Item, MagicMapper, MAP_WIDTH, MAX_MONSTER, MeleePowerBonus, Monster, Name, Player, Position, ProvidesFood, ProvidesHealing, Ranged, Renderable, SerializeMe, SingleActivation, Viewshed}; use crate::random_table::RandomTable; use crate::rect::Rect; +use crate::{ + AreaOfEffect, BlocksTile, CombatStats, Confusion, Consumable, DefenseBonus, EntryTrigger, + EquipmentSlot, Equippable, Hidden, HungerClock, HungerState, InflictsDamage, Item, MagicMapper, + MeleePowerBonus, Monster, Name, Player, Position, ProvidesFood, ProvidesHealing, Ranged, + Renderable, SerializeMe, SingleActivation, Viewshed, MAP_WIDTH, MAX_MONSTER, +}; pub fn player(ecs: &mut World, player_x: i32, player_y: i32) -> Entity { ecs.create_entity() diff --git a/src/trigger_system.rs b/src/trigger_system.rs index df72e72..630bce4 100644 --- a/src/trigger_system.rs +++ b/src/trigger_system.rs @@ -1,7 +1,10 @@ use specs::prelude::*; -use crate::{EntityMoved, EntryTrigger, GameLog, Hidden, InflictsDamage, Map, Name, Position, SingleActivation, SufferDamage}; use crate::particle_system::ParticleBuilder; +use crate::{ + EntityMoved, EntryTrigger, GameLog, Hidden, InflictsDamage, Map, Name, Position, + SingleActivation, SufferDamage, +}; pub struct TriggerSystem {} @@ -18,11 +21,24 @@ impl<'a> System<'a> for TriggerSystem { ReadStorage<'a, InflictsDamage>, WriteExpect<'a, ParticleBuilder>, WriteStorage<'a, SufferDamage>, - WriteStorage<'a, SingleActivation> + WriteStorage<'a, SingleActivation>, ); fn run(&mut self, data: Self::SystemData) { - let (map, mut entity_moved, position, entry_trigger, mut hidden, names, entities, mut game_log, inflicts_damage, mut particle_builder, mut inflict_damage, mut single_activation) = data; + let ( + map, + mut entity_moved, + position, + entry_trigger, + mut hidden, + names, + entities, + mut game_log, + inflicts_damage, + mut particle_builder, + mut inflict_damage, + mut single_activation, + ) = data; let mut remove_entities: Vec = Vec::new(); for (entity, mut _entity_moved, pos) in (&entities, &mut entity_moved, &position).join() { @@ -41,7 +57,14 @@ impl<'a> System<'a> for TriggerSystem { hidden.remove(*entity_id); if let Some(damage) = inflicts_damage.get(*entity_id) { - particle_builder.request(pos.x, pos.y, rltk::RGB::named(rltk::ORANGE), rltk::RGB::named(rltk::BLACK), rltk::to_cp437('‼'), 200.0); + particle_builder.request( + pos.x, + pos.y, + rltk::RGB::named(rltk::ORANGE), + rltk::RGB::named(rltk::BLACK), + rltk::to_cp437('‼'), + 200.0, + ); SufferDamage::new_damage(&mut inflict_damage, entity, damage.damage); } @@ -59,4 +82,3 @@ impl<'a> System<'a> for TriggerSystem { entity_moved.clear(); } } - diff --git a/src/visibility_system.rs b/src/visibility_system.rs index 6e989c0..9ce17ef 100644 --- a/src/visibility_system.rs +++ b/src/visibility_system.rs @@ -1,7 +1,11 @@ use rltk::{field_of_view, Point, RandomNumberGenerator}; use specs::prelude::*; -use crate::{components::{Player, Position, Viewshed}, GameLog, Hidden, map::Map, Name}; +use crate::{ + components::{Player, Position, Viewshed}, + map::Map, + GameLog, Hidden, Name, +}; pub struct VisibilitySystem {} @@ -15,11 +19,21 @@ impl<'a> System<'a> for VisibilitySystem { WriteStorage<'a, Hidden>, WriteExpect<'a, RandomNumberGenerator>, ReadStorage<'a, Name>, - WriteExpect<'a, GameLog> + WriteExpect<'a, GameLog>, ); fn run(&mut self, data: Self::SystemData) { - let (mut map, entities, mut viewshed, pos, player, mut hidden, mut rng, names, mut game_log) = data; + let ( + mut map, + entities, + mut viewshed, + pos, + player, + mut hidden, + mut rng, + names, + mut game_log, + ) = data; for (ent, viewshed, pos) in (&entities, &mut viewshed, &pos).join() { if !viewshed.dirty { continue; @@ -45,7 +59,9 @@ impl<'a> System<'a> for VisibilitySystem { if let Some(_maybe_hidden) = hidden.get(*e) { if rng.roll_dice(1, 24) == 1 { if let Some(name) = names.get(*e) { - game_log.entries.push(format!("You spotted a {}.", &name.name)); + game_log + .entries + .push(format!("You spotted a {}.", &name.name)); } hidden.remove(*e); }