diff --git a/src/main.rs b/src/main.rs index a862186..9c62c33 100644 --- a/src/main.rs +++ b/src/main.rs @@ -457,17 +457,16 @@ impl GameState for State { RunState::MapGeneration => { if !SHOW_MAPGEN { new_runstate = self.mapgen_next_state.unwrap(); - } else { - ctx.cls(); - draw_map(&self.mapgen_history[self.mapgen_index], ctx); + } + ctx.cls(); + draw_map(&self.mapgen_history[self.mapgen_index], ctx); - self.mapgen_timer += ctx.frame_time_ms; - if self.mapgen_timer > 300.0 { - self.mapgen_timer = 0.0; - self.mapgen_index += 1; - if self.mapgen_index >= self.mapgen_history.len() { - new_runstate = self.mapgen_next_state.unwrap(); - } + self.mapgen_timer += ctx.frame_time_ms; + if self.mapgen_timer > 300.0 { + self.mapgen_timer = 0.0; + self.mapgen_index += 1; + if self.mapgen_index >= self.mapgen_history.len() { + new_runstate = self.mapgen_next_state.unwrap(); } } } diff --git a/src/map_builders/bsp_dungeon.rs b/src/map_builders/bsp_dungeon.rs index 2784238..da12718 100644 --- a/src/map_builders/bsp_dungeon.rs +++ b/src/map_builders/bsp_dungeon.rs @@ -1,6 +1,5 @@ use super::{apply_room_to_map, spawner, Map, MapBuilder, Position, Rect, TileType, SHOW_MAPGEN}; use rltk::RandomNumberGenerator; -use specs::prelude::*; pub struct BspDungeonBuilder { map: Map, @@ -42,6 +41,7 @@ impl MapBuilder for BspDungeonBuilder { } impl BspDungeonBuilder { + #[allow(dead_code)] pub fn new(new_depth: i32) -> BspDungeonBuilder { BspDungeonBuilder { map: Map::new(new_depth), diff --git a/src/map_builders/bsp_interior.rs b/src/map_builders/bsp_interior.rs index 595af5a..c8855bc 100644 --- a/src/map_builders/bsp_interior.rs +++ b/src/map_builders/bsp_interior.rs @@ -1,6 +1,5 @@ use super::{spawner, Map, MapBuilder, Position, Rect, TileType, SHOW_MAPGEN}; use rltk::RandomNumberGenerator; -use specs::prelude::*; pub struct BspInteriorBuilder { map: Map, @@ -42,6 +41,7 @@ impl MapBuilder for BspInteriorBuilder { } impl BspInteriorBuilder { + #[allow(dead_code)] pub fn new(new_depth: i32) -> BspInteriorBuilder { BspInteriorBuilder { map: Map::new(new_depth), diff --git a/src/map_builders/cellular_automata.rs b/src/map_builders/cellular_automata.rs index 98c312e..4d34e3b 100644 --- a/src/map_builders/cellular_automata.rs +++ b/src/map_builders/cellular_automata.rs @@ -3,7 +3,6 @@ use super::{ Position, TileType, SHOW_MAPGEN, }; use rltk::RandomNumberGenerator; -use specs::prelude::*; use std::collections::HashMap; const PASSES: i32 = 15; diff --git a/src/map_builders/dla.rs b/src/map_builders/dla.rs index 45616fc..699dc15 100644 --- a/src/map_builders/dla.rs +++ b/src/map_builders/dla.rs @@ -3,9 +3,9 @@ use super::{ Map, MapBuilder, Position, TileType, SHOW_MAPGEN, }; use rltk::RandomNumberGenerator; -use specs::prelude::*; use std::collections::HashMap; +#[allow(dead_code)] #[derive(PartialEq, Copy, Clone)] pub enum DLAAlgorithm { WalkInwards, @@ -55,6 +55,7 @@ impl MapBuilder for DLABuilder { } } +#[allow(dead_code)] impl DLABuilder { pub fn walk_inwards(new_depth: i32) -> DLABuilder { DLABuilder { diff --git a/src/map_builders/drunkard.rs b/src/map_builders/drunkard.rs index ed1567a..dd56eac 100644 --- a/src/map_builders/drunkard.rs +++ b/src/map_builders/drunkard.rs @@ -3,7 +3,6 @@ use super::{ Position, Symmetry, TileType, SHOW_MAPGEN, }; use rltk::RandomNumberGenerator; -use specs::prelude::*; use std::collections::HashMap; #[derive(PartialEq, Copy, Clone)] @@ -59,6 +58,7 @@ impl MapBuilder for DrunkardsWalkBuilder { } } +#[allow(dead_code)] impl DrunkardsWalkBuilder { pub fn open_area(new_depth: i32) -> DrunkardsWalkBuilder { DrunkardsWalkBuilder { diff --git a/src/map_builders/maze.rs b/src/map_builders/maze.rs index 4f5341f..a06e44e 100644 --- a/src/map_builders/maze.rs +++ b/src/map_builders/maze.rs @@ -3,7 +3,6 @@ use super::{ Position, TileType, SHOW_MAPGEN, }; use rltk::RandomNumberGenerator; -use specs::prelude::*; use std::collections::HashMap; pub struct MazeBuilder { @@ -45,6 +44,7 @@ impl MapBuilder for MazeBuilder { } impl MazeBuilder { + #[allow(dead_code)] pub fn new(new_depth: i32) -> MazeBuilder { MazeBuilder { map: Map::new(new_depth), diff --git a/src/map_builders/mod.rs b/src/map_builders/mod.rs index 5789b6b..9d348ce 100644 --- a/src/map_builders/mod.rs +++ b/src/map_builders/mod.rs @@ -39,41 +39,41 @@ pub trait MapBuilder { #[rustfmt::skip] pub fn random_builder(new_depth: i32) -> Box { - /*let mut rng = rltk::RandomNumberGenerator::new(); - let builder = rng.roll_dice(1, 16); - let mut result : Box; - match builder { - 1 => { result = Box::new(BspDungeonBuilder::new(new_depth)); } - 2 => { result = Box::new(BspInteriorBuilder::new(new_depth)); } - 3 => { result = Box::new(CellularAutomataBuilder::new(new_depth)); } - 4 => { result = Box::new(DrunkardsWalkBuilder::open_area(new_depth)); } - 5 => { result = Box::new(DrunkardsWalkBuilder::open_halls(new_depth)); } - 6 => { result = Box::new(DrunkardsWalkBuilder::winding_passages(new_depth)); } - 7 => { result = Box::new(DrunkardsWalkBuilder::fat_passages(new_depth)); } - 8 => { result = Box::new(DrunkardsWalkBuilder::fearful_symmetry(new_depth)); } - 9 => { result = Box::new(MazeBuilder::new(new_depth)); } - 10 => { result = Box::new(DLABuilder::walk_inwards(new_depth)); } - 11 => { result = Box::new(DLABuilder::walk_outwards(new_depth)); } - 12 => { result = Box::new(DLABuilder::central_attractor(new_depth)); } - 13 => { result = Box::new(DLABuilder::insectoid(new_depth)); } - 14 => { result = Box::new(VoronoiBuilder::pythagoras(new_depth)); } - 15 => { result = Box::new(VoronoiBuilder::manhattan(new_depth)); } - _ => { result = Box::new(simple_map::SimpleMapBuilder::new(new_depth)); } + let mut rng = rltk::RandomNumberGenerator::new(); + let builder = rng.roll_dice(1, 17); + let mut result : Box; + rltk::console::log("Picking procgen type ->"); + match builder { + 1 => { result = Box::new(BspDungeonBuilder::new(new_depth)); } + 2 => { result = Box::new(BspInteriorBuilder::new(new_depth)); } + 3 => { result = Box::new(CellularAutomataBuilder::new(new_depth)); } + 4 => { result = Box::new(DrunkardsWalkBuilder::open_area(new_depth)); } + 5 => { result = Box::new(DrunkardsWalkBuilder::open_halls(new_depth)); } + 6 => { result = Box::new(DrunkardsWalkBuilder::winding_passages(new_depth)); } + 7 => { result = Box::new(DrunkardsWalkBuilder::fat_passages(new_depth)); } + 8 => { result = Box::new(DrunkardsWalkBuilder::fearful_symmetry(new_depth)); } + 9 => { result = Box::new(MazeBuilder::new(new_depth)); } + 10 => { result = Box::new(DLABuilder::walk_inwards(new_depth)); } + 11 => { result = Box::new(DLABuilder::walk_outwards(new_depth)); } + 12 => { result = Box::new(DLABuilder::central_attractor(new_depth)); } + 13 => { result = Box::new(DLABuilder::insectoid(new_depth)); } + 14 => { result = Box::new(VoronoiBuilder::pythagoras(new_depth)); } + 15 => { result = Box::new(VoronoiBuilder::manhattan(new_depth)); } + 16 => { result = Box::new(PrefabBuilder::constant(new_depth, prefab_builder::prefab_levels::WFC_POPULATED)) }, + _ => { result = Box::new(simple_map::SimpleMapBuilder::new(new_depth)); } + } + + if rng.roll_dice(1, 3)==1 { + result = Box::new(WaveFunctionCollapseBuilder::derived_map(new_depth, result)); + rltk::console::log("-> wfc"); + } + + if rng.roll_dice(1, 20)==1 { + result = Box::new(PrefabBuilder::sectional(new_depth, prefab_builder::prefab_sections::UNDERGROUND_FORT ,result)); + rltk::console::log("-> sectional"); + } + + result = Box::new(PrefabBuilder::vaults(new_depth, result)); + + result } - - if rng.roll_dice(1, 3)==1 { - result = Box::new(wfc::WaveFunctionCollapseBuilder::derived_map(new_depth, result)); - } - - result*/ - Box::new( - PrefabBuilder::new( - new_depth, - Some( - Box::new( - CellularAutomataBuilder::new(new_depth) - ) - ) - ) - ) -} diff --git a/src/map_builders/prefab_builder/mod.rs b/src/map_builders/prefab_builder/mod.rs index 4456652..6f23403 100644 --- a/src/map_builders/prefab_builder/mod.rs +++ b/src/map_builders/prefab_builder/mod.rs @@ -1,10 +1,9 @@ -use super::{ - remove_unreachable_areas_returning_most_distant, spawner, Map, MapBuilder, Position, TileType, SHOW_MAPGEN, -}; +use super::{remove_unreachable_areas_returning_most_distant, Map, MapBuilder, Position, TileType, SHOW_MAPGEN}; use rltk::RandomNumberGenerator; -use specs::prelude::*; -mod prefab_levels; -mod prefab_sections; +pub mod prefab_levels; +pub mod prefab_sections; +pub mod prefab_vaults; +use std::collections::HashSet; #[allow(dead_code)] #[derive(PartialEq, Clone)] @@ -12,6 +11,7 @@ pub enum PrefabMode { RexLevel { template: &'static str }, Constant { level: prefab_levels::PrefabLevel }, Sectional { section: prefab_sections::PrefabSection }, + Vaults, } pub struct PrefabBuilder { @@ -53,17 +53,54 @@ impl MapBuilder for PrefabBuilder { } } +#[allow(dead_code)] impl PrefabBuilder { - #[allow(dead_code)] - pub fn new(new_depth: i32, previous_builder: Option>) -> PrefabBuilder { + pub fn rex_level(new_depth: i32, template: &'static str) -> PrefabBuilder { PrefabBuilder { map: Map::new(new_depth), starting_position: Position { x: 0, y: 0 }, depth: new_depth, history: Vec::new(), - mode: PrefabMode::Sectional { section: prefab_sections::UNDERGROUND_FORT }, + mode: PrefabMode::RexLevel { template }, spawn_list: Vec::new(), - previous_builder, + previous_builder: None, + } + } + pub fn constant(new_depth: i32, level: prefab_levels::PrefabLevel) -> PrefabBuilder { + PrefabBuilder { + map: Map::new(new_depth), + starting_position: Position { x: 0, y: 0 }, + depth: new_depth, + history: Vec::new(), + mode: PrefabMode::Constant { level }, + spawn_list: Vec::new(), + previous_builder: None, + } + } + pub fn sectional( + new_depth: i32, + section: prefab_sections::PrefabSection, + previous_builder: Box, + ) -> PrefabBuilder { + PrefabBuilder { + map: Map::new(new_depth), + starting_position: Position { x: 0, y: 0 }, + depth: new_depth, + history: Vec::new(), + mode: PrefabMode::Sectional { section }, + spawn_list: Vec::new(), + previous_builder: Some(previous_builder), + } + } + pub fn vaults(new_depth: i32, previous_builder: Box) -> PrefabBuilder { + PrefabBuilder { + map: Map::new(new_depth), + starting_position: Position { x: 0, y: 0 }, + depth: new_depth, + history: Vec::new(), + mode: PrefabMode::Vaults, + spawn_list: Vec::new(), + previous_builder: Some(previous_builder), } } @@ -72,6 +109,7 @@ impl PrefabBuilder { PrefabMode::RexLevel { template } => self.load_rex_map(&template), PrefabMode::Constant { level } => self.load_ascii_map(&level), PrefabMode::Sectional { section } => self.apply_sectional(§ion, rng), + PrefabMode::Vaults => self.apply_room_vaults(rng), } self.take_snapshot(); @@ -95,6 +133,134 @@ impl PrefabBuilder { } } + fn apply_previous_iteration(&mut self, rng: &mut RandomNumberGenerator, mut filter: F) + where + F: FnMut(i32, i32, &(usize, String)) -> bool, + { + // Build the map + let prev_builder = self.previous_builder.as_mut().unwrap(); + prev_builder.build_map(rng); + self.starting_position = prev_builder.get_starting_pos(); + self.map = prev_builder.get_map().clone(); + self.history = prev_builder.get_snapshot_history(); + for e in prev_builder.get_spawn_list().iter() { + let idx = e.0; + let x = idx as i32 % self.map.width; + let y = idx as i32 / self.map.width; + if filter(x, y, e) { + self.spawn_list.push((idx, e.1.to_string())) + } + } + self.take_snapshot(); + } + + fn apply_room_vaults(&mut self, rng: &mut RandomNumberGenerator) { + use prefab_vaults::*; + + // Apply prev builder, and keep all entities + self.apply_previous_iteration(rng, |_x, _y, _e| true); + + // Roll for a vault + let vault_roll = rng.roll_dice(1, 6); + if vault_roll < 4 { + return; + } + + // Get all vaults + let master_vault_list = vec![GOBLINS_4X4, GOBLINS2_4X4, CLASSIC_TRAP_5X5]; + // Filter out vaults from outside the current depth + let mut possible_vaults: Vec<&PrefabVault> = + master_vault_list.iter().filter(|v| self.depth >= v.first_depth && self.depth <= v.last_depth).collect(); + // Return if there's no possible vaults + if possible_vaults.is_empty() { + return; + } + + // Pick number of vaults + let n_vaults = i32::min(rng.roll_dice(1, 3), possible_vaults.len() as i32); + let mut used_tiles: HashSet = HashSet::new(); + + for _i in 0..n_vaults { + // Select a vault + let vault_idx = if possible_vaults.len() == 1 { + 0 + } else { + (rng.roll_dice(1, possible_vaults.len() as i32) - 1) as usize + }; + let vault = possible_vaults[vault_idx]; + + // Make a list of all places the vault can fit + let mut vault_positions: Vec = Vec::new(); + let mut idx = 0usize; + loop { + let x = (idx % self.map.width as usize) as i32; + let y = (idx / self.map.width as usize) as i32; + // Check for overflow + if x > 1 + && (x + vault.width as i32) < self.map.width - 2 + && y > 1 + && (y + vault.height as i32) < self.map.height - 2 + { + let mut possible = true; + for tile_y in 0..vault.height as i32 { + for tile_x in 0..vault.width as i32 { + let idx = self.map.xy_idx(tile_x + x, tile_y + y); + if self.map.tiles[idx] != TileType::Floor { + possible = false; + } + if used_tiles.contains(&idx) { + possible = false; + } + } + } + // If we find a position that works, push it + if possible { + vault_positions.push(Position { x, y }); + } + } + // Once we reach the end of the map, break + idx += 1; + if idx >= self.map.tiles.len() - 1 { + break; + } + } + + // If we have a position, make the vault + if !vault_positions.is_empty() { + let pos_idx = if vault_positions.len() == 1 { + 0 + } else { + (rng.roll_dice(1, vault_positions.len() as i32) - 1) as usize + }; + let pos = &vault_positions[pos_idx]; + let chunk_x = pos.x; + let chunk_y = pos.y; + // Filter out entities from our spawn list that would have spawned inside this vault + let width = self.map.width; // For borrow checker. + let height = self.map.height; // As above. + self.spawn_list.retain(|e| { + let idx = e.0 as i32; + let x = idx % width; + let y = idx / height; + x < chunk_x || x > chunk_x + vault.width as i32 || y < chunk_y || y > chunk_y + vault.height as i32 + }); + let string_vec = PrefabBuilder::read_ascii_to_vec(vault.template); + let mut i = 0; + for tile_y in 0..vault.height { + for tile_x in 0..vault.width { + let idx = self.map.xy_idx(tile_x as i32 + chunk_x, tile_y as i32 + chunk_y); + self.char_to_map(string_vec[i], idx); + used_tiles.insert(idx); + i += 1; + } + } + rltk::console::log("-> adding vault"); + self.take_snapshot(); + possible_vaults.remove(vault_idx); + } + } + } + pub fn apply_sectional(&mut self, section: &prefab_sections::PrefabSection, rng: &mut RandomNumberGenerator) { use prefab_sections::*; let string_vec = PrefabBuilder::read_ascii_to_vec(section.template); @@ -115,24 +281,9 @@ impl PrefabBuilder { } // Build the map - let prev_builder = self.previous_builder.as_mut().unwrap(); - prev_builder.build_map(rng); - self.starting_position = prev_builder.get_starting_pos(); - self.map = prev_builder.get_map().clone(); - // Iterate previous spawn list, culling entities within new section - for entity in prev_builder.get_spawn_list().iter() { - let idx = entity.0; - let x = idx as i32 % self.map.width; - let y = idx as i32 / self.map.width; - if x < chunk_x - || x > (chunk_x + section.width as i32) - || y < chunk_y - || y > (chunk_y + section.height as i32) - { - self.spawn_list.push((idx, entity.1.to_string())); - } - } - self.take_snapshot(); + self.apply_previous_iteration(rng, |x, y, _e| { + x < chunk_x || x > (chunk_x + section.width as i32) || y < chunk_y || y > (chunk_y + section.height as i32) + }); let mut i = 0; for ty in 0..section.height { @@ -162,6 +313,10 @@ impl PrefabBuilder { self.map.tiles[idx] = TileType::Floor; self.spawn_list.push((idx, "goblin".to_string())); } + 'G' => { + self.map.tiles[idx] = TileType::Floor; + self.spawn_list.push((idx, "goblin chieftain".to_string())); + } 'o' => { self.map.tiles[idx] = TileType::Floor; self.spawn_list.push((idx, "orc".to_string())); diff --git a/src/map_builders/prefab_builder/prefab_levels.rs b/src/map_builders/prefab_builder/prefab_levels.rs index c1f56c5..23d1d90 100644 --- a/src/map_builders/prefab_builder/prefab_levels.rs +++ b/src/map_builders/prefab_builder/prefab_levels.rs @@ -5,8 +5,10 @@ pub struct PrefabLevel { pub height: usize, } +#[allow(dead_code)] pub const WFC_POPULATED: PrefabLevel = PrefabLevel { template: LEVEL_MAP, width: 80, height: 43 }; +#[allow(dead_code)] const LEVEL_MAP: &str = " ################################################################################ #          ########################################################    ######### diff --git a/src/map_builders/prefab_builder/prefab_vaults.rs b/src/map_builders/prefab_builder/prefab_vaults.rs new file mode 100644 index 0000000..b561a82 --- /dev/null +++ b/src/map_builders/prefab_builder/prefab_vaults.rs @@ -0,0 +1,41 @@ +#[allow(dead_code)] +#[derive(PartialEq, Copy, Clone)] +pub struct PrefabVault { + pub template: &'static str, + pub width: usize, + pub height: usize, + pub first_depth: i32, + pub last_depth: i32, +} + +#[allow(dead_code)] +pub const CLASSIC_TRAP_5X5: PrefabVault = + PrefabVault { template: CLASSIC_TRAP_5X5_V, width: 5, height: 5, first_depth: 0, last_depth: 100 }; +#[allow(dead_code)] +const CLASSIC_TRAP_5X5_V: &str = " + + ^^^ + ^!^ + ^^^ + +"; + +#[allow(dead_code)] +pub const GOBLINS_4X4: PrefabVault = + PrefabVault { template: GOBLINS_4X4_V, width: 4, height: 4, first_depth: 0, last_depth: 100 }; +const GOBLINS_4X4_V: &str = " +#^   + #G# +#g#  + ^g^ +"; + +#[allow(dead_code)] +pub const GOBLINS2_4X4: PrefabVault = + PrefabVault { template: GOBLINS2_4X4_V, width: 4, height: 4, first_depth: 0, last_depth: 100 }; +const GOBLINS2_4X4_V: &str = " +#^#g +G# # + g#  +# g^ +"; diff --git a/src/map_builders/simple_map.rs b/src/map_builders/simple_map.rs index 3294804..fa67750 100644 --- a/src/map_builders/simple_map.rs +++ b/src/map_builders/simple_map.rs @@ -2,8 +2,7 @@ use super::{ apply_horizontal_tunnel, apply_room_to_map, apply_vertical_tunnel, spawner, Map, MapBuilder, Position, Rect, TileType, SHOW_MAPGEN, }; -use rltk::{console, RandomNumberGenerator}; -use specs::prelude::*; +use rltk::RandomNumberGenerator; pub struct SimpleMapBuilder { map: Map, @@ -44,6 +43,7 @@ impl MapBuilder for SimpleMapBuilder { } impl SimpleMapBuilder { + #[allow(dead_code)] pub fn new(new_depth: i32) -> SimpleMapBuilder { SimpleMapBuilder { map: Map::new(new_depth), @@ -88,7 +88,6 @@ impl SimpleMapBuilder { } self.rooms.push(new_room); - console::log("pushed new room"); self.take_snapshot(); } } diff --git a/src/map_builders/voronoi.rs b/src/map_builders/voronoi.rs index 71f87ce..b1d7491 100644 --- a/src/map_builders/voronoi.rs +++ b/src/map_builders/voronoi.rs @@ -3,9 +3,9 @@ use super::{ Position, TileType, SHOW_MAPGEN, }; use rltk::RandomNumberGenerator; -use specs::prelude::*; use std::collections::HashMap; +#[allow(dead_code)] #[derive(PartialEq, Copy, Clone)] pub enum DistanceAlgorithm { Pythagoras, @@ -53,6 +53,7 @@ impl MapBuilder for VoronoiBuilder { } } +#[allow(dead_code)] impl VoronoiBuilder { pub fn pythagoras(new_depth: i32) -> VoronoiBuilder { VoronoiBuilder { diff --git a/src/map_builders/wfc/mod.rs b/src/map_builders/wfc/mod.rs index 9d6db4e..ee42828 100644 --- a/src/map_builders/wfc/mod.rs +++ b/src/map_builders/wfc/mod.rs @@ -9,7 +9,6 @@ mod constraints; mod solver; use rltk::RandomNumberGenerator; use solver::Solver; -use specs::prelude::*; use std::collections::HashMap; pub struct WaveFunctionCollapseBuilder { @@ -51,6 +50,7 @@ impl MapBuilder for WaveFunctionCollapseBuilder { } } +#[allow(dead_code)] impl WaveFunctionCollapseBuilder { pub fn new(new_depth: i32, derive_from: Option>) -> WaveFunctionCollapseBuilder { WaveFunctionCollapseBuilder { @@ -74,6 +74,7 @@ impl WaveFunctionCollapseBuilder { let prebuilder = &mut self.derive_from.as_mut().unwrap(); prebuilder.build_map(rng); self.map = prebuilder.get_map(); + self.history = prebuilder.get_snapshot_history(); for t in self.map.tiles.iter_mut() { if *t == TileType::DownStair { *t = TileType::Floor; diff --git a/src/spawner.rs b/src/spawner.rs index 295fb8d..8b01cb8 100644 --- a/src/spawner.rs +++ b/src/spawner.rs @@ -96,7 +96,7 @@ pub fn spawn_room( } pub fn spawn_region( - map: &Map, + _map: &Map, rng: &mut RandomNumberGenerator, area: &[usize], map_depth: i32, diff --git a/src/visibility_system.rs b/src/visibility_system.rs index 9161fc9..626ef3f 100644 --- a/src/visibility_system.rs +++ b/src/visibility_system.rs @@ -52,7 +52,7 @@ impl<'a> System<'a> for VisibilitySystem { for thing in map.tile_content[idx].iter() { let is_hidden = hidden.get(*thing); if let Some(_is_hidden) = is_hidden { - if rng.roll_dice(1, 20) == 1 { + if rng.roll_dice(1, 12) == 1 { let name = names.get(*thing); if let Some(name) = name { gamelog::Logger::new()