Replace individual spawners with data-driven ones
This commit is contained in:
parent
269cf501cf
commit
e92160244a
src
21
src/main.rs
21
src/main.rs
@ -47,10 +47,7 @@ impl State {
|
|||||||
let exit_idx = map_builder.map.point2d_to_index(map_builder.amulet_start);
|
let exit_idx = map_builder.map.point2d_to_index(map_builder.amulet_start);
|
||||||
map_builder.map.tiles[exit_idx] = TileType::Exit;
|
map_builder.map.tiles[exit_idx] = TileType::Exit;
|
||||||
|
|
||||||
map_builder
|
spawn_level(&mut ecs, &mut rng, 0, &map_builder.monster_spawns);
|
||||||
.monster_spawns
|
|
||||||
.iter()
|
|
||||||
.for_each(|pos| spawn_entity(&mut ecs, &mut rng, *pos));
|
|
||||||
|
|
||||||
resources.insert(map_builder.map);
|
resources.insert(map_builder.map);
|
||||||
resources.insert(Camera::new(map_builder.player_start));
|
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);
|
let exit_idx = map_builder.map.point2d_to_index(map_builder.amulet_start);
|
||||||
map_builder.map.tiles[exit_idx] = TileType::Exit;
|
map_builder.map.tiles[exit_idx] = TileType::Exit;
|
||||||
|
|
||||||
map_builder
|
spawn_level(&mut self.ecs, &mut rng, 0, &map_builder.monster_spawns);
|
||||||
.monster_spawns
|
|
||||||
.iter()
|
|
||||||
.for_each(|pos| spawn_entity(&mut self.ecs, &mut rng, *pos));
|
|
||||||
|
|
||||||
self.resources.insert(map_builder.map);
|
self.resources.insert(map_builder.map);
|
||||||
self.resources.insert(Camera::new(map_builder.player_start));
|
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.map.tiles[exit_idx] = TileType::Exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
map_builder
|
spawn_level(
|
||||||
.monster_spawns
|
&mut self.ecs,
|
||||||
.iter()
|
&mut rng,
|
||||||
.for_each(|pos| spawn_entity(&mut self.ecs, &mut rng, *pos));
|
map_level as usize,
|
||||||
|
&map_builder.monster_spawns,
|
||||||
|
);
|
||||||
|
|
||||||
self.resources.insert(map_builder.map);
|
self.resources.insert(map_builder.map);
|
||||||
self.resources.insert(Camera::new(map_builder.player_start));
|
self.resources.insert(Camera::new(map_builder.player_start));
|
||||||
self.resources.insert(TurnState::AwaitingInput);
|
self.resources.insert(TurnState::AwaitingInput);
|
||||||
|
@ -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
44
src/spawner/mod.rs
Normal 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
99
src/spawner/template.rs
Normal 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),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user