From 75e17f235d8c8da76cb1a2ea506febb7e912b05c Mon Sep 17 00:00:00 2001 From: Llywelwyn Date: Wed, 2 Aug 2023 00:37:56 +0100 Subject: [PATCH] IntervalSpawnSystem{} now works with mob groupsize flags --- src/main.rs | 2 +- src/map/interval_spawning_system.rs | 42 ++++++++++++++++++++++------- src/raws/rawmaster.rs | 33 ++++++++++++++++++++++- src/spawner.rs | 26 ++---------------- 4 files changed, 67 insertions(+), 36 deletions(-) diff --git a/src/main.rs b/src/main.rs index 8e7d1de..795fb4d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -45,7 +45,7 @@ extern crate lazy_static; //Consts pub const SHOW_MAPGEN: bool = false; -pub const LOG_SPAWNING: bool = false; +pub const LOG_SPAWNING: bool = true; pub const LOG_TICKS: bool = false; #[derive(PartialEq, Copy, Clone)] diff --git a/src/map/interval_spawning_system.rs b/src/map/interval_spawning_system.rs index 5fbb0f1..3ff2e36 100644 --- a/src/map/interval_spawning_system.rs +++ b/src/map/interval_spawning_system.rs @@ -31,28 +31,48 @@ pub fn try_spawn_interval(ecs: &mut World) { 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; + let mut available_tiles = populate_unblocked_nonvisible(&map); + let player_level = gamelog::get_event_count("player_level"); + rltk::console::log(player_level); + let difficulty = (map.difficulty + player_level) / 2; if available_tiles.len() == 0 { if LOG_SPAWNING { rltk::console::log("SPAWNINFO: No free tiles; not spawning anything.."); } return; } + let mut spawn_locations: Vec<(i32, i32)> = Vec::new(); let mut rng = ecs.write_resource::(); - let idx = get_random_idx_from_possible_tiles(&mut rng, available_tiles); + // Get mob type let key = spawner::mob_table(difficulty).roll(&mut rng); - let x = idx as i32 % map.width; - let y = idx as i32 / map.width; + // Check if it spawns in a group, and roll for how many to spawn accordingly. + let spawn_type = raws::get_mob_spawn_type(&raws::RAWS.lock().unwrap(), &key); + let roll = raws::get_mob_spawn_amount(&mut rng, &spawn_type, player_level); + // Get that many idxs, and push them to the spawn list. + for _i in 0..roll { + let idx = get_random_idx_from_tiles(&mut rng, &mut available_tiles); + spawn_locations.push((idx as i32 % map.width, idx as i32 / map.width)); + } + // Dropping resources for borrow-checker. std::mem::drop(map); std::mem::drop(rng); - if LOG_SPAWNING { - rltk::console::log(format!("SPAWNINFO: Spawning {} at {}, {}.", key, x, y)); + // For every idx in the spawn list, spawn mob. + for idx in spawn_locations { + if LOG_SPAWNING { + rltk::console::log(format!("SPAWNINFO: Spawning {} at {}, {}.", key, idx.0, idx.1)); + } + raws::spawn_named_entity( + &raws::RAWS.lock().unwrap(), + ecs, + &key, + raws::SpawnType::AtPosition { x: idx.0, y: idx.1 }, + difficulty, + ); } - raws::spawn_named_entity(&raws::RAWS.lock().unwrap(), ecs, &key, raws::SpawnType::AtPosition { x, y }, difficulty); } -fn populate_unblocked_nonvisible_tiles(map: &Map) -> Vec { +/// Returns a Vec of every tile that is not blocked, and is not currently in the player's view. +fn populate_unblocked_nonvisible(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] { @@ -62,7 +82,9 @@ fn populate_unblocked_nonvisible_tiles(map: &Map) -> Vec { return tiles; } -fn get_random_idx_from_possible_tiles(rng: &mut rltk::RandomNumberGenerator, area: Vec) -> usize { +/// Picks a random index from a vector of indexes, and removes it from the vector. +fn get_random_idx_from_tiles(rng: &mut rltk::RandomNumberGenerator, area: &mut Vec) -> usize { let idx = if area.len() == 1 { 0usize } else { (rng.roll_dice(1, area.len() as i32) - 1) as usize }; + area.remove(idx); return area[idx]; } diff --git a/src/raws/rawmaster.rs b/src/raws/rawmaster.rs index c763202..4a310fd 100644 --- a/src/raws/rawmaster.rs +++ b/src/raws/rawmaster.rs @@ -572,7 +572,7 @@ pub enum SpawnsAs { LargeGroup, } -pub fn check_if_mob_spawns_in_group(raws: &RawMaster, key: &str) -> SpawnsAs { +pub fn get_mob_spawn_type(raws: &RawMaster, key: &str) -> SpawnsAs { if raws.mob_index.contains_key(key) { let mob_template = &raws.raws.mobs[raws.mob_index[key]]; if let Some(flags) = &mob_template.flags { @@ -587,3 +587,34 @@ pub fn check_if_mob_spawns_in_group(raws: &RawMaster, key: &str) -> SpawnsAs { } return SpawnsAs::Single; } + +pub fn get_mob_spawn_amount(rng: &mut RandomNumberGenerator, spawn_type: &SpawnsAs, player_level: i32) -> i32 { + let n = match spawn_type { + // Single mobs always spawn alone. + SpawnsAs::Single => 1, + // Small groups either spawn alone or as a small group (2-4). + SpawnsAs::SmallGroup => { + if rng.roll_dice(1, 2) == 1 { + 1 + } else { + 4 + } + } + // Large groups either spawn in a small group or as a large group (2-11). + SpawnsAs::LargeGroup => { + if rng.roll_dice(1, 2) == 1 { + 4 + } else { + 11 + } + } + }; + let roll = if n == 1 { 1 } else { rng.roll_dice(2, n) }; + // We want to constrain group sizes depending on player's level, so + // we don't get large groups of mobs when the player is unequipped. + match player_level { + 0..=2 => return i32::max(1, roll / 4), + 3..=4 => return i32::max(1, roll / 2), + _ => return roll, + }; +} diff --git a/src/spawner.rs b/src/spawner.rs index 033bad8..e35405f 100644 --- a/src/spawner.rs +++ b/src/spawner.rs @@ -132,30 +132,8 @@ pub fn spawn_region( // Roll on each table, getting an entity + spawn point if spawn_mob { let key = mob_table(difficulty).roll(rng); - let spawn_type = raws::check_if_mob_spawns_in_group(&raws::RAWS.lock().unwrap(), &key); - let n = match spawn_type { - raws::SpawnsAs::Single => 1, - raws::SpawnsAs::SmallGroup => { - if rng.roll_dice(1, 2) == 1 { - 1 - } else { - 4 - } - } - raws::SpawnsAs::LargeGroup => { - if rng.roll_dice(1, 2) == 1 { - 4 - } else { - 11 - } - } - }; - let mut roll = if n == 1 { 1 } else { rng.roll_dice(2, n) }; - roll = match player_level { - 0..=2 => i32::min(1, roll / 4), - 3..=4 => i32::min(1, roll / 2), - _ => roll, - }; + let spawn_type = raws::get_mob_spawn_type(&raws::RAWS.lock().unwrap(), &key); + let roll = raws::get_mob_spawn_amount(rng, &spawn_type, player_level); for _i in 0..roll { entity_to_spawn_list(rng, &mut areas, key.clone(), &mut spawn_points); }