diff --git a/src/main.rs b/src/main.rs index 897616f..48b13b0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -315,6 +315,7 @@ impl GameState for State { while new_runstate == RunState::Ticking { self.run_systems(); self.ecs.maintain(); + try_spawn_interval(&mut self.ecs); match *self.ecs.fetch::() { RunState::AwaitingInput => new_runstate = RunState::AwaitingInput, RunState::MagicMapReveal { row, cursed } => { diff --git a/src/map/interval_spawning_system.rs b/src/map/interval_spawning_system.rs new file mode 100644 index 0000000..fd9afa3 --- /dev/null +++ b/src/map/interval_spawning_system.rs @@ -0,0 +1,60 @@ +use crate::{gamelog, raws, spawner, Clock, Map, RandomNumberGenerator, TakingTurn}; +use specs::prelude::*; + +const TRY_SPAWN_CHANCE: i32 = 70; + +pub fn try_spawn_interval(ecs: &mut World) { + let mut try_spawn = false; + // Scope for borrow checker (ECS) + { + let map = ecs.fetch::(); + // Difficulty 0 maps shouldn't have respawning hostile mobs. + if map.difficulty == 0 { + return; + } + let clock = ecs.read_storage::(); + let turns = ecs.read_storage::(); + let mut rng = ecs.write_resource::(); + for (_c, _t) in (&clock, &turns).join() { + if rng.roll_dice(1, TRY_SPAWN_CHANCE) == 1 { + rltk::console::log("Trying spawn."); + try_spawn = true; + } + } + } + if try_spawn { + spawn_random_mob_in_free_nonvisible_tile(ecs); + } +} + +fn spawn_random_mob_in_free_nonvisible_tile(ecs: &mut World) { + let map = ecs.fetch::(); + let available_tiles = populate_unblocked_nonvisible_tiles(&map); + let difficulty = (map.difficulty + gamelog::get_event_count("player_level")) / 2; + if available_tiles.len() == 0 { + return; + } + let mut rng = ecs.write_resource::(); + let idx = get_random_idx_from_possible_tiles(&mut rng, available_tiles); + let key = spawner::mob_table(difficulty).roll(&mut rng); + let x = idx as i32 % map.width; + let y = idx as i32 / map.width; + std::mem::drop(map); + std::mem::drop(rng); + raws::spawn_named_entity(&raws::RAWS.lock().unwrap(), ecs, &key, raws::SpawnType::AtPosition { x, y }, difficulty); +} + +fn populate_unblocked_nonvisible_tiles(map: &Map) -> Vec { + let mut tiles: Vec = Vec::new(); + for (i, _tile) in map.tiles.iter().enumerate() { + if !map.blocked[i] && !map.visible_tiles[i] { + tiles.push(i); + } + } + return tiles; +} + +fn get_random_idx_from_possible_tiles(rng: &mut rltk::RandomNumberGenerator, area: Vec) -> usize { + let idx = if area.len() == 1 { 0usize } else { (rng.roll_dice(1, area.len() as i32) - 1) as usize }; + return idx; +} diff --git a/src/map/mod.rs b/src/map/mod.rs index 2c7e1df..31fcd3c 100644 --- a/src/map/mod.rs +++ b/src/map/mod.rs @@ -6,6 +6,8 @@ pub mod colours; mod glyphs; mod tiletype; pub use tiletype::{tile_cost, tile_opaque, tile_walkable, TileType}; +mod interval_spawning_system; +pub use interval_spawning_system::try_spawn_interval; pub mod themes; // FIXME: If the map size gets too small, entities stop being rendered starting from the right. diff --git a/src/raws/rawmaster.rs b/src/raws/rawmaster.rs index 2e69c91..b9a456f 100644 --- a/src/raws/rawmaster.rs +++ b/src/raws/rawmaster.rs @@ -228,6 +228,8 @@ pub fn spawn_named_mob( "BLOCKS_TILE" => eb = eb.with(BlocksTile {}), "BYSTANDER" => eb = eb.with(Bystander {}), "MONSTER" => eb = eb.with(Monster {}), + "SMALL_GROUP" => {} // These flags are for region spawning, + "LARGE_GROUP" => {} // and don't matter here (yet)? "MULTIATTACK" => { eb = eb.with(MultiAttack {}); xp_value += 3; @@ -463,18 +465,17 @@ fn get_renderable_component(renderable: &super::item_structs::Renderable) -> cra } pub fn table_by_name(raws: &RawMaster, key: &str, difficulty: i32) -> RandomTable { + let upper_bound = difficulty; + let lower_bound = if key != "mobs" { 0 } else { difficulty / 6 }; if raws.table_index.contains_key(key) { let spawn_table = &raws.raws.spawn_tables[raws.table_index[key]]; use super::SpawnTableEntry; - let upper_bound = difficulty; - let lower_bound = if key != "mobs" { 0 } else { difficulty / 6 }; - let available_options: Vec<&SpawnTableEntry> = spawn_table .table .iter() - .filter(|entry| entry.difficulty > lower_bound && entry.difficulty <= upper_bound) + .filter(|entry| entry.difficulty >= lower_bound && entry.difficulty <= upper_bound) .collect(); let mut rt = RandomTable::new(); @@ -487,8 +488,8 @@ pub fn table_by_name(raws: &RawMaster, key: &str, difficulty: i32) -> RandomTabl } } rltk::console::log(format!( - "DEBUGINFO: Something went wrong when trying to spawn {} @ map difficulty {}. Returned debug entry.", - key, difficulty + "DEBUGINFO: Something went wrong when trying to spawn {} @ map difficulty {} [upper bound: {}, lower bound: {}]. Returned debug entry.", + key, difficulty, upper_bound, lower_bound )); return RandomTable::new().add("debug", 1); }