Weight spawn odds, and simplify item spawning

This commit is contained in:
Timothy Warren 2021-11-12 14:06:55 -05:00
parent a51d63963f
commit 7079a7eedd
3 changed files with 111 additions and 83 deletions

View File

@ -13,6 +13,7 @@ mod map_indexing_system;
mod melee_combat_system;
mod monster_ai_system;
mod player;
pub mod random_table;
mod rect;
pub mod saveload_system;
mod spawner;

57
src/random_table.rs Normal file
View File

@ -0,0 +1,57 @@
use rltk::RandomNumberGenerator;
pub struct RandomEntry {
name: String,
weight: i32,
}
impl RandomEntry {
pub fn new<S: ToString>(name: S, weight: i32) -> RandomEntry {
RandomEntry {
name: name.to_string(),
weight,
}
}
}
#[derive(Default)]
pub struct RandomTable {
entries: Vec<RandomEntry>,
total_weight: i32,
}
impl RandomTable {
pub fn new() -> RandomTable {
RandomTable {
entries: Vec::new(),
total_weight: 0,
}
}
pub fn add<S: ToString>(mut self, name: S, weight: i32) -> Self {
self.total_weight += weight;
self.entries.push(RandomEntry::new(name, weight));
self
}
pub fn roll(&self, rng: &mut RandomNumberGenerator) -> String {
if self.total_weight == 0 {
return "None".to_string();
}
let mut roll = rng.roll_dice(1, self.total_weight) - 1;
let mut index: usize = 0;
while roll > 0 {
if roll < self.entries[index].weight {
return self.entries[index].name.clone();
}
roll -= self.entries[index].weight;
index += 1;
}
"None".to_string()
}
}

View File

@ -2,13 +2,11 @@ use crate::components::{
AreaOfEffect, BlocksTile, CombatStats, Confusion, Consumable, InflictsDamage, Item, Monster,
Name, Player, Position, ProvidesHealing, Ranged, Renderable, SerializeMe, Viewshed,
};
use crate::{Rect, MAP_WIDTH};
use crate::{random_table::RandomTable, Rect, MAP_WIDTH};
use rltk::{RandomNumberGenerator, RGB};
use specs::prelude::*;
use specs::saveload::{MarkedBuilder, SimpleMarker};
const MAX_MONSTERS: i32 = 4;
const MAX_ITEMS: i32 = 2;
use std::collections::HashMap;
/// Spawns the player and returns their entity object
pub fn player(ecs: &mut World, player_x: i32, player_y: i32) -> Entity {
@ -36,18 +34,62 @@ pub fn player(ecs: &mut World, player_x: i32, player_y: i32) -> Entity {
.build()
}
/// Spawns a random monster at a given location
pub fn random_monster(ecs: &mut World, x: i32, y: i32) {
let roll: i32;
const MAX_MONSTERS: i32 = 4;
fn room_table() -> RandomTable {
RandomTable::new()
.add("Goblin", 10)
.add("Orc", 1)
.add("Health Potion", 7)
.add("Fireball Scroll", 2)
.add("Confusion Scroll", 2)
.add("Magic Missile Scroll", 4)
}
/// fills a room with stuff!
#[allow(clippy::map_entry)]
pub fn spawn_room(ecs: &mut World, room: &Rect) {
let spawn_table = room_table();
let mut spawn_points: HashMap<usize, String> = HashMap::new();
// Scope to keep the borrow checker happy
{
let mut rng = ecs.write_resource::<RandomNumberGenerator>();
roll = rng.roll_dice(1, 2);
let num_spawns = rng.roll_dice(1, MAX_MONSTERS + 3) - 3;
for _i in 0..num_spawns {
let mut added = false;
let mut tries = 0;
while !added && tries < 20 {
let x = (room.x1 + rng.roll_dice(1, i32::abs(room.x2 - room.x1))) as usize;
let y = (room.y1 + rng.roll_dice(1, i32::abs(room.y2 - room.y1))) as usize;
let idx = (y * MAP_WIDTH) + x;
if !spawn_points.contains_key(&idx) {
spawn_points.insert(idx, spawn_table.roll(&mut rng));
added = true;
} else {
tries += 1;
}
}
}
}
match roll {
1 => orc(ecs, x, y),
_ => goblin(ecs, x, y),
// Actually spawn stuff
for spawn in spawn_points.iter() {
let x = (*spawn.0 % MAP_WIDTH) as i32;
let y = (*spawn.0 / MAP_WIDTH) as i32;
match spawn.1.as_ref() {
"Goblin" => goblin(ecs, x, y),
"Orc" => orc(ecs, x, y),
"Health Potion" => health_potion(ecs, x, y),
"Fireball Scroll" => fireball_scroll(ecs, x, y),
"Confusion Scroll" => confusion_scroll(ecs, x, y),
"Magic Missile Scroll" => magic_missile_scroll(ecs, x, y),
_ => {}
}
}
}
@ -82,63 +124,6 @@ fn monster<S: ToString>(ecs: &mut World, x: i32, y: i32, glyph: rltk::FontCharTy
.build();
}
/// fills a room with stuff!
pub fn spawn_room(ecs: &mut World, room: &Rect) {
let mut monster_spawn_points: Vec<usize> = Vec::new();
let mut item_spawn_points: Vec<usize> = Vec::new();
// Scope to keep the borrow checker happy
{
let mut rng = ecs.write_resource::<RandomNumberGenerator>();
let num_monsters = rng.roll_dice(1, MAX_MONSTERS + 2) - 3;
let num_items = rng.roll_dice(1, MAX_ITEMS + 2) - 3;
for _i in 0..num_monsters {
let mut added = false;
while !added {
let x = (room.x1 + rng.roll_dice(1, i32::abs(room.x2 - room.x1))) as usize;
let y = (room.y1 + rng.roll_dice(1, i32::abs(room.y2 - room.y1))) as usize;
let idx = (y * MAP_WIDTH) + x;
if !monster_spawn_points.contains(&idx) {
monster_spawn_points.push(idx);
added = true;
}
}
}
for _i in 0..num_items {
let mut added = false;
while !added {
let x = (room.x1 + rng.roll_dice(1, i32::abs(room.x2 - room.x1))) as usize;
let y = (room.y1 + rng.roll_dice(1, i32::abs(room.y2 - room.y1))) as usize;
let idx = (y * MAP_WIDTH) + x;
if !item_spawn_points.contains(&idx) {
item_spawn_points.push(idx);
added = true;
}
}
}
}
// Actually spawn the monsters
for idx in monster_spawn_points.iter() {
let x = *idx % MAP_WIDTH;
let y = *idx / MAP_WIDTH;
random_monster(ecs, x as i32, y as i32);
}
// Actually spawn the items
for idx in item_spawn_points.iter() {
let x = *idx % MAP_WIDTH;
let y = *idx / MAP_WIDTH;
random_item(ecs, x as i32, y as i32);
}
}
fn health_potion(ecs: &mut World, x: i32, y: i32) {
ecs.create_entity()
.with(Position { x, y })
@ -173,21 +158,6 @@ fn magic_missile_scroll(ecs: &mut World, x: i32, y: i32) {
.build();
}
fn random_item(ecs: &mut World, x: i32, y: i32) {
let roll: i32;
{
let mut rng = ecs.write_resource::<RandomNumberGenerator>();
roll = rng.roll_dice(1, 4);
}
match roll {
1 => health_potion(ecs, x, y),
2 => fireball_scroll(ecs, x, y),
3 => confusion_scroll(ecs, x, y),
_ => magic_missile_scroll(ecs, x, y),
}
}
fn fireball_scroll(ecs: &mut World, x: i32, y: i32) {
ecs.create_entity()
.with(Position { x, y })