From b7b20612289e764166f65fbd78f4952187b79275 Mon Sep 17 00:00:00 2001 From: Llywelwyn Date: Sun, 23 Jul 2023 15:50:56 +0100 Subject: [PATCH] fun with layers, and inventory display refactor --- src/gui.rs | 88 +++++++++---------------- src/main.rs | 7 +- src/map.rs | 2 +- src/map_builders/cellular_automata.rs | 95 +++++++++++++++------------ src/map_builders/dla.rs | 19 +++++- src/map_builders/drunkard.rs | 9 ++- src/map_builders/mod.rs | 31 ++------- 7 files changed, 122 insertions(+), 129 deletions(-) diff --git a/src/gui.rs b/src/gui.rs index e4017a1..8a3e850 100644 --- a/src/gui.rs +++ b/src/gui.rs @@ -136,32 +136,9 @@ pub enum ItemMenuResult { Selected, } -pub fn show_inventory(gs: &mut State, ctx: &mut Rltk) -> (ItemMenuResult, Option) { - let player_entity = gs.ecs.fetch::(); - let names = gs.ecs.read_storage::(); - let backpack = gs.ecs.read_storage::(); - let entities = gs.ecs.entities(); - - // FIXME: This is unwieldy. Having a separate data structure for (items, id) and (items, count) is not good. - // But it works, and this might get cut anyway as I get further along in the design, so leaving as is atm. - let mut inventory_ids: BTreeMap = BTreeMap::new(); - let mut player_inventory: BTreeMap<(String, String), i32> = BTreeMap::new(); - for (entity, _pack, name) in (&entities, &backpack, &names).join().filter(|item| item.1.owner == *player_entity) { - player_inventory - .entry((name.name.to_string(), name.plural.to_string())) - .and_modify(|count| *count += 1) - .or_insert(1); - inventory_ids.entry(name.name.to_string()).or_insert(entity); - } - - let count = player_inventory.len(); - let mut y = (25 - (count / 2)) as i32; - ctx.draw_box(15, y - 2, 45, (count + 3) as i32, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK)); - ctx.print_color(18, y - 2, RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK), "Inventory"); - ctx.print_color(18, y + count as i32 + 1, RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK), "ESC to cancel"); - +pub fn print_options(inventory: BTreeMap<(String, String), i32>, mut y: i32, ctx: &mut Rltk) { let mut j = 0; - for (name, item_count) in &player_inventory { + for (name, item_count) in &inventory { // Print the character required to access this item. i.e. (a) ctx.set(17, y, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK), rltk::to_cp437('(')); ctx.set(18, y, RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK), 97 + j as rltk::FontCharType); @@ -191,6 +168,33 @@ pub fn show_inventory(gs: &mut State, ctx: &mut Rltk) -> (ItemMenuResult, Option y += 1; j += 1; } +} + +pub fn show_inventory(gs: &mut State, ctx: &mut Rltk) -> (ItemMenuResult, Option) { + let player_entity = gs.ecs.fetch::(); + let names = gs.ecs.read_storage::(); + let backpack = gs.ecs.read_storage::(); + let entities = gs.ecs.entities(); + + // FIXME: This is unwieldy. Having a separate data structure for (name, id) and (name, count) is not good. + // But it works, and this might get cut anyway as I get further along in the design, so leaving as is atm. + let mut inventory_ids: BTreeMap = BTreeMap::new(); + let mut player_inventory: BTreeMap<(String, String), i32> = BTreeMap::new(); + for (entity, _pack, name) in (&entities, &backpack, &names).join().filter(|item| item.1.owner == *player_entity) { + player_inventory + .entry((name.name.to_string(), name.plural.to_string())) + .and_modify(|count| *count += 1) + .or_insert(1); + inventory_ids.entry(name.name.to_string()).or_insert(entity); + } + + let count = player_inventory.len(); + let y = (25 - (count / 2)) as i32; + ctx.draw_box(15, y - 2, 45, (count + 3) as i32, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK)); + ctx.print_color(18, y - 2, RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK), "Inventory"); + ctx.print_color(18, y + count as i32 + 1, RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK), "ESC to cancel"); + + print_options(player_inventory, y, ctx); match ctx.key { None => (ItemMenuResult::NoResponse, None), @@ -224,42 +228,12 @@ pub fn drop_item_menu(gs: &mut State, ctx: &mut Rltk) -> (ItemMenuResult, Option } let count = player_inventory.len(); - let mut y = (25 - (count / 2)) as i32; + let y = (25 - (count / 2)) as i32; ctx.draw_box(15, y - 2, 45, (count + 3) as i32, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK)); ctx.print_color(18, y - 2, RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK), "Drop what?"); ctx.print_color(18, y + count as i32 + 1, RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK), "ESC to cancel"); - let mut j = 0; - for (name, item_count) in &player_inventory { - // Print the character required to access this item. i.e. (a) - ctx.set(17, y, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK), rltk::to_cp437('(')); - ctx.set(18, y, RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK), 97 + j as rltk::FontCharType); - ctx.set(19, y, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK), rltk::to_cp437(')')); - - let mut x = 21; - if item_count > &1 { - // If more than one, print the number and pluralise - // i.e. (a) 3 daggers - ctx.print(x, y, item_count); - x += 2; - ctx.print(x, y, name.1.to_string()); - } else { - if ['a', 'e', 'i', 'o', 'u'].iter().any(|&v| name.0.starts_with(v)) { - // If one and starts with a vowel, print 'an' - // i.e. (a) an apple - ctx.print(x, y, "an"); - x += 3; - } else { - // If one and not a vowel, print 'a' - // i.e. (a) a dagger - ctx.print(x, y, "a"); - x += 2; - } - ctx.print(x, y, name.0.to_string()); - } - y += 1; - j += 1; - } + print_options(player_inventory, y, ctx); match ctx.key { None => (ItemMenuResult::NoResponse, None), diff --git a/src/main.rs b/src/main.rs index 485b8f5..2fa47b1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -411,13 +411,16 @@ impl GameState for State { gui::GameOverResult::NoSelection => {} gui::GameOverResult::QuitToMenu => { self.game_over_cleanup(); - new_runstate = RunState::MainMenu { menu_selection: gui::MainMenuSelection::NewGame }; + new_runstate = RunState::MapGeneration; + self.mapgen_next_state = + Some(RunState::MainMenu { menu_selection: gui::MainMenuSelection::NewGame }); } } } RunState::NextLevel => { self.goto_next_level(); - new_runstate = RunState::PreRun; + self.mapgen_next_state = Some(RunState::PreRun); + new_runstate = RunState::MapGeneration; } RunState::MagicMapReveal { row, cursed } => { let mut map = self.ecs.fetch_mut::(); diff --git a/src/map.rs b/src/map.rs index 39621fe..dffa42d 100644 --- a/src/map.rs +++ b/src/map.rs @@ -253,7 +253,7 @@ fn wall_glyph(map: &Map, x: i32, y: i32) -> rltk::FontCharType { } match mask { - 0 => 9, // Pillar because we can't see neighbors + 0 => 254, // ■ (254) square pillar; but maybe ○ (9) looks better 1 => 186, // Wall only to the north 2 => 186, // Wall only to the south 3 => 186, // Wall to the north and south diff --git a/src/map_builders/cellular_automata.rs b/src/map_builders/cellular_automata.rs index a7b5860..17dd869 100644 --- a/src/map_builders/cellular_automata.rs +++ b/src/map_builders/cellular_automata.rs @@ -1,4 +1,4 @@ -use super::{BuilderMap, InitialMapBuilder, TileType}; +use super::{BuilderMap, InitialMapBuilder, MetaMapBuilder, TileType}; use rltk::RandomNumberGenerator; pub struct CellularAutomataBuilder {} @@ -10,6 +10,13 @@ impl InitialMapBuilder for CellularAutomataBuilder { } } +impl MetaMapBuilder for CellularAutomataBuilder { + #[allow(dead_code)] + fn build_map(&mut self, _rng: &mut rltk::RandomNumberGenerator, build_data: &mut BuilderMap) { + self.apply_iteration(build_data); + } +} + impl CellularAutomataBuilder { #[allow(dead_code)] pub fn new() -> Box { @@ -34,47 +41,51 @@ impl CellularAutomataBuilder { // Now we iteratively apply cellular automata rules for _i in 0..15 { - let mut newtiles = build_data.map.tiles.clone(); - - for y in 1..build_data.map.height - 1 { - for x in 1..build_data.map.width - 1 { - let idx = build_data.map.xy_idx(x, y); - let mut neighbors = 0; - if build_data.map.tiles[idx - 1] == TileType::Wall { - neighbors += 1; - } - if build_data.map.tiles[idx + 1] == TileType::Wall { - neighbors += 1; - } - if build_data.map.tiles[idx - build_data.map.width as usize] == TileType::Wall { - neighbors += 1; - } - if build_data.map.tiles[idx + build_data.map.width as usize] == TileType::Wall { - neighbors += 1; - } - if build_data.map.tiles[idx - (build_data.map.width as usize - 1)] == TileType::Wall { - neighbors += 1; - } - if build_data.map.tiles[idx - (build_data.map.width as usize + 1)] == TileType::Wall { - neighbors += 1; - } - if build_data.map.tiles[idx + (build_data.map.width as usize - 1)] == TileType::Wall { - neighbors += 1; - } - if build_data.map.tiles[idx + (build_data.map.width as usize + 1)] == TileType::Wall { - neighbors += 1; - } - - if neighbors > 4 || neighbors == 0 { - newtiles[idx] = TileType::Wall; - } else { - newtiles[idx] = TileType::Floor; - } - } - } - - build_data.map.tiles = newtiles.clone(); - build_data.take_snapshot(); + self.apply_iteration(build_data); } } + + fn apply_iteration(&mut self, build_data: &mut BuilderMap) { + let mut newtiles = build_data.map.tiles.clone(); + + for y in 1..build_data.map.height - 1 { + for x in 1..build_data.map.width - 1 { + let idx = build_data.map.xy_idx(x, y); + let mut neighbors = 0; + if build_data.map.tiles[idx - 1] == TileType::Wall { + neighbors += 1; + } + if build_data.map.tiles[idx + 1] == TileType::Wall { + neighbors += 1; + } + if build_data.map.tiles[idx - build_data.map.width as usize] == TileType::Wall { + neighbors += 1; + } + if build_data.map.tiles[idx + build_data.map.width as usize] == TileType::Wall { + neighbors += 1; + } + if build_data.map.tiles[idx - (build_data.map.width as usize - 1)] == TileType::Wall { + neighbors += 1; + } + if build_data.map.tiles[idx - (build_data.map.width as usize + 1)] == TileType::Wall { + neighbors += 1; + } + if build_data.map.tiles[idx + (build_data.map.width as usize - 1)] == TileType::Wall { + neighbors += 1; + } + if build_data.map.tiles[idx + (build_data.map.width as usize + 1)] == TileType::Wall { + neighbors += 1; + } + + if neighbors > 4 || neighbors == 0 { + newtiles[idx] = TileType::Wall; + } else { + newtiles[idx] = TileType::Floor; + } + } + } + + build_data.map.tiles = newtiles.clone(); + build_data.take_snapshot(); + } } diff --git a/src/map_builders/dla.rs b/src/map_builders/dla.rs index 1f96b4a..2cb5fa3 100644 --- a/src/map_builders/dla.rs +++ b/src/map_builders/dla.rs @@ -1,4 +1,4 @@ -use super::{paint, BuilderMap, InitialMapBuilder, Position, Symmetry, TileType}; +use super::{paint, BuilderMap, InitialMapBuilder, MetaMapBuilder, Position, Symmetry, TileType}; use rltk::RandomNumberGenerator; #[derive(PartialEq, Copy, Clone)] @@ -23,6 +23,13 @@ impl InitialMapBuilder for DLABuilder { } } +impl MetaMapBuilder for DLABuilder { + #[allow(dead_code)] + fn build_map(&mut self, rng: &mut rltk::RandomNumberGenerator, build_data: &mut BuilderMap) { + self.build(rng, build_data); + } +} + impl DLABuilder { #[allow(dead_code)] pub fn new() -> Box { @@ -74,6 +81,16 @@ impl DLABuilder { }) } + #[allow(dead_code)] + pub fn heavy_erosion() -> Box { + Box::new(DLABuilder { + algorithm: DLAAlgorithm::WalkInwards, + brush_size: 2, + symmetry: Symmetry::None, + floor_percent: 0.35, + }) + } + #[allow(clippy::map_entry)] fn build(&mut self, rng: &mut RandomNumberGenerator, build_data: &mut BuilderMap) { // Carve a starting seed diff --git a/src/map_builders/drunkard.rs b/src/map_builders/drunkard.rs index 5057bc1..151e100 100644 --- a/src/map_builders/drunkard.rs +++ b/src/map_builders/drunkard.rs @@ -1,4 +1,4 @@ -use super::{paint, BuilderMap, InitialMapBuilder, Position, Symmetry, TileType}; +use super::{paint, BuilderMap, InitialMapBuilder, MetaMapBuilder, Position, Symmetry, TileType}; use rltk::RandomNumberGenerator; #[derive(PartialEq, Copy, Clone)] @@ -27,6 +27,13 @@ impl InitialMapBuilder for DrunkardsWalkBuilder { } } +impl MetaMapBuilder for DrunkardsWalkBuilder { + #[allow(dead_code)] + fn build_map(&mut self, rng: &mut rltk::RandomNumberGenerator, build_data: &mut BuilderMap) { + self.build(rng, build_data); + } +} + impl DrunkardsWalkBuilder { #[allow(dead_code)] pub fn new(settings: DrunkardSettings) -> DrunkardsWalkBuilder { diff --git a/src/map_builders/mod.rs b/src/map_builders/mod.rs index 56cfaf3..f855fb7 100644 --- a/src/map_builders/mod.rs +++ b/src/map_builders/mod.rs @@ -146,30 +146,11 @@ fn random_initial_builder(rng: &mut rltk::RandomNumberGenerator) -> (Box BuilderChain { let mut builder = BuilderChain::new(new_depth); - let (random_starter, has_rooms) = random_initial_builder(rng); - builder.start_with(random_starter); - if has_rooms { - builder.with(RoomBasedSpawner::new()); - builder.with(RoomBasedStairs::new()); - builder.with(RoomBasedStartingPosition::new()); - } else { - builder.with(AreaStartingPosition::new(XStart::CENTRE, YStart::CENTRE)); - builder.with(CullUnreachable::new()); - builder.with(VoronoiSpawning::new()); - builder.with(DistantExit::new()); - } - - if rng.roll_dice(1, 3) == 1 { - builder.with(WaveFunctionCollapseBuilder::new()); - builder.with(CullUnreachable::new()); - builder.with(VoronoiSpawning::new()); - } - - if rng.roll_dice(1, 20) == 1 { - builder.with(PrefabBuilder::sectional(prefab_builder::prefab_sections::UNDERGROUND_FORT)); - } - - builder.with(PrefabBuilder::vaults()); - + builder.start_with(simple_map::SimpleMapBuilder::new()); + builder.with(DLABuilder::heavy_erosion()); + builder.with(AreaStartingPosition::new(XStart::CENTRE, YStart::CENTRE)); + builder.with(CullUnreachable::new()); + builder.with(VoronoiSpawning::new()); + builder.with(DistantExit::new()); builder }