Replace individual spawners with data-driven ones

This commit is contained in:
Daniel Lynn 2021-07-11 11:03:41 -05:00
parent 269cf501cf
commit e92160244a
4 changed files with 152 additions and 109 deletions

View File

@ -47,10 +47,7 @@ impl State {
let exit_idx = map_builder.map.point2d_to_index(map_builder.amulet_start);
map_builder.map.tiles[exit_idx] = TileType::Exit;
map_builder
.monster_spawns
.iter()
.for_each(|pos| spawn_entity(&mut ecs, &mut rng, *pos));
spawn_level(&mut ecs, &mut rng, 0, &map_builder.monster_spawns);
resources.insert(map_builder.map);
resources.insert(Camera::new(map_builder.player_start));
@ -78,10 +75,7 @@ impl State {
let exit_idx = map_builder.map.point2d_to_index(map_builder.amulet_start);
map_builder.map.tiles[exit_idx] = TileType::Exit;
map_builder
.monster_spawns
.iter()
.for_each(|pos| spawn_entity(&mut self.ecs, &mut rng, *pos));
spawn_level(&mut self.ecs, &mut rng, 0, &map_builder.monster_spawns);
self.resources.insert(map_builder.map);
self.resources.insert(Camera::new(map_builder.player_start));
@ -189,10 +183,13 @@ impl State {
map_builder.map.tiles[exit_idx] = TileType::Exit;
}
map_builder
.monster_spawns
.iter()
.for_each(|pos| spawn_entity(&mut self.ecs, &mut rng, *pos));
spawn_level(
&mut self.ecs,
&mut rng,
map_level as usize,
&map_builder.monster_spawns,
);
self.resources.insert(map_builder.map);
self.resources.insert(Camera::new(map_builder.player_start));
self.resources.insert(TurnState::AwaitingInput);

View File

@ -1,97 +0,0 @@
use crate::prelude::*;
pub fn spawn_player(ecs: &mut World, pos: Point) {
ecs.push((
Player { map_level: 0 },
pos,
Render {
color: ColorPair::new(WHITE, BLACK),
glyph: to_cp437('@'),
},
Health {
current: 10,
max: 10,
},
Name("Player 1".to_string()),
FieldOfView::new(8),
));
}
pub fn spawn_monster(ecs: &mut World, rng: &mut RandomNumberGenerator, pos: Point) {
let (hp, name, glyph) = match rng.roll_dice(1, 10) {
1..=8 => goblin(),
_ => orc(),
};
ecs.push((
Enemy,
pos,
Render {
color: ColorPair::new(WHITE, BLACK),
glyph,
},
ChasingPlayer,
Health {
current: hp,
max: hp,
},
Name(name),
FieldOfView::new(6),
));
}
pub fn spawn_amulet_of_yala(ecs: &mut World, pos: Point) {
ecs.push((
Item,
AmuletOfYala,
pos,
Render {
color: ColorPair::new(WHITE, BLACK),
glyph: to_cp437('|'),
},
Name("Amulet of Yala".to_string()),
));
}
pub fn spawn_healing_potion(ecs: &mut World, pos: Point) {
ecs.push((
Item,
pos,
Render {
color: ColorPair::new(WHITE, BLACK),
glyph: to_cp437('!'),
},
Name("Healing Potion".to_string()),
ProvidesHealing { amount: 6 },
));
}
pub fn spawn_magic_mapper(ecs: &mut World, pos: Point) {
ecs.push((
Item,
pos,
Render {
color: ColorPair::new(WHITE, BLACK),
glyph: to_cp437('{'),
},
Name("Dungeon Map".to_string()),
ProvidesDungeonMap,
));
}
pub fn spawn_entity(ecs: &mut World, rng: &mut RandomNumberGenerator, pos: Point) {
let roll = rng.roll_dice(1, 6);
match roll {
1 => spawn_healing_potion(ecs, pos),
2 => spawn_magic_mapper(ecs, pos),
_ => spawn_monster(ecs, rng, pos),
}
}
fn goblin() -> (i32, String, FontCharType) {
(1, "Goblin".to_string(), to_cp437('g'))
}
fn orc() -> (i32, String, FontCharType) {
(2, "Orc".to_string(), to_cp437('o'))
}

44
src/spawner/mod.rs Normal file
View File

@ -0,0 +1,44 @@
mod template;
use crate::prelude::*;
use template::Templates;
pub fn spawn_player(ecs: &mut World, pos: Point) {
ecs.push((
Player { map_level: 0 },
pos,
Render {
color: ColorPair::new(WHITE, BLACK),
glyph: to_cp437('@'),
},
Health {
current: 10,
max: 10,
},
Name("Player 1".to_string()),
FieldOfView::new(8),
));
}
pub fn spawn_amulet_of_yala(ecs: &mut World, pos: Point) {
ecs.push((
Item,
AmuletOfYala,
pos,
Render {
color: ColorPair::new(WHITE, BLACK),
glyph: to_cp437('|'),
},
Name("Amulet of Yala".to_string()),
));
}
pub fn spawn_level(
ecs: &mut World,
rng: &mut RandomNumberGenerator,
level: usize,
spawn_points: &[Point],
) {
let template = Templates::load();
template.spawn_entities(ecs, rng, level, spawn_points);
}

99
src/spawner/template.rs Normal file
View File

@ -0,0 +1,99 @@
use crate::prelude::*;
use legion::systems::CommandBuffer;
use ron::de::from_reader;
use serde::Deserialize;
use std::collections::HashSet;
use std::fs::File;
#[derive(Clone, Deserialize, Debug)]
pub struct Template {
pub entity_type: EntityType,
pub levels: HashSet<usize>,
pub frequency: i32,
pub name: String,
pub glyph: char,
pub provides: Option<Vec<(String, i32)>>,
pub hp: Option<i32>,
}
#[derive(Clone, Deserialize, Debug, PartialEq)]
pub enum EntityType {
Enemy,
Item,
}
#[derive(Clone, Deserialize, Debug)]
pub struct Templates {
pub entities: Vec<Template>,
}
impl Templates {
pub fn load() -> Self {
let file = File::open("resources/template.ron").expect("Failed opening file");
from_reader(file).expect("Unable to load templates")
}
pub fn spawn_entities(
&self,
ecs: &mut World,
rng: &mut RandomNumberGenerator,
level: usize,
spawn_points: &[Point],
) {
let mut available_entities = Vec::new();
self.entities
.iter()
.filter(|e| e.levels.contains(&level))
.for_each(|t| {
for _ in 0..t.frequency {
available_entities.push(t);
}
});
let mut commands = CommandBuffer::new(ecs);
spawn_points.iter().for_each(|pt| {
if let Some(entity) = rng.random_slice_entry(&available_entities) {
self.spawn_entity(pt, entity, &mut commands);
}
});
commands.flush(ecs);
}
fn spawn_entity(&self, pt: &Point, template: &Template, commands: &mut CommandBuffer) {
let entity = commands.push((
pt.clone(),
Render {
color: ColorPair::new(WHITE, BLACK),
glyph: to_cp437(template.glyph),
},
Name(template.name.clone()),
));
match template.entity_type {
EntityType::Item => commands.add_component(entity, Item),
EntityType::Enemy => {
commands.add_component(entity, Enemy);
commands.add_component(entity, FieldOfView::new(6));
commands.add_component(entity, ChasingPlayer);
commands.add_component(
entity,
Health {
current: template.hp.unwrap(),
max: template.hp.unwrap(),
},
);
}
}
if let Some(effects) = &template.provides {
effects
.iter()
.for_each(|(provides, n)| match provides.as_str() {
"Healing" => commands.add_component(entity, ProvidesHealing { amount: *n }),
"MagicMap" => commands.add_component(entity, ProvidesDungeonMap),
_ => println!("Warning: we don't know how to provide {}", provides),
});
}
}
}