data driven items
This commit is contained in:
parent
cb97acba42
commit
488e73ea46
27
Cargo.lock
generated
27
Cargo.lock
generated
@ -60,6 +60,12 @@ version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff"
|
||||
|
||||
[[package]]
|
||||
name = "bit-set"
|
||||
version = "0.5.2"
|
||||
@ -556,6 +562,8 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"bracket-lib",
|
||||
"legion",
|
||||
"ron",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1432,6 +1440,17 @@ version = "0.6.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
|
||||
|
||||
[[package]]
|
||||
name = "ron"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2a9d94cee22c8a4b5e2a1c7f9a20fdc315668ee8a75835949bb40d7456934634"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"bitflags",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rusttype"
|
||||
version = "0.9.2"
|
||||
@ -1480,18 +1499,18 @@ checksum = "a0eddf2e8f50ced781f288c19f18621fa72a3779e3cb58dbf23b07469b0abeb4"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.129"
|
||||
version = "1.0.115"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d1f72836d2aa753853178eda473a3b9d8e4eefdaf20523b919677e6de489f8f1"
|
||||
checksum = "e54c9a88f2da7238af84b5101443f0c0d0a3bbdc455e34a5c9497b1903ed55d5"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.129"
|
||||
version = "1.0.115"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e57ae87ad533d9a56427558b516d0adac283614e347abf85b0dc0cbbf0a249f3"
|
||||
checksum = "609feed1d0a73cc36a0182a840a9b37b4a82f0b1150369f0536a9e3f2a31dc48"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -8,3 +8,5 @@ edition = "2018"
|
||||
[dependencies]
|
||||
bracket-lib = "~0.8.1"
|
||||
legion = "~0.3.1"
|
||||
serde = { version = "=1.0.115"}
|
||||
ron = "=0.6.1"
|
64
resources/template.ron
Normal file
64
resources/template.ron
Normal file
@ -0,0 +1,64 @@
|
||||
Templates(
|
||||
entities: [
|
||||
Template(
|
||||
entity_type: Item,
|
||||
name: "Healing Potion",
|
||||
glyph: '!',
|
||||
levels : [0,1,2],
|
||||
provides: Some([("Healing", 6)]),
|
||||
frequency: 2
|
||||
),
|
||||
Template(
|
||||
entity_type: Item,
|
||||
name: "Dungoen Map",
|
||||
glyph: '{',
|
||||
levels : [0, 1, 2],
|
||||
provides: Some([("MagicMap", 0)]),
|
||||
frequency: 1
|
||||
),
|
||||
Template(
|
||||
entity_type: Enemy,
|
||||
name: "Goblin",
|
||||
glyph: 'g',
|
||||
levels : [0, 1, 2],
|
||||
hp: Some(1),
|
||||
frequency: 3,
|
||||
base_damage: Some(1)
|
||||
),
|
||||
Template(
|
||||
entity_type: Enemy,
|
||||
name: "Orc",
|
||||
glyph: 'o',
|
||||
levels : [0, 1, 2],
|
||||
hp: Some(2),
|
||||
frequency: 2,
|
||||
base_damage: Some(1)
|
||||
),
|
||||
Template(
|
||||
entity_type: Enemy,
|
||||
name: "Ogre",
|
||||
glyph: 'O',
|
||||
levels : [1, 2],
|
||||
hp: Some(5),
|
||||
frequency: 1,
|
||||
base_damage: Some(2)
|
||||
),
|
||||
Template(
|
||||
entity_type: Enemy,
|
||||
name: "Ettin",
|
||||
glyph: 'E',
|
||||
levels : [2],
|
||||
hp: Some(10),
|
||||
frequency: 2,
|
||||
base_damage: Some(3)
|
||||
),
|
||||
Template(
|
||||
entity_type: Item,
|
||||
name : "Rusty Sword",
|
||||
glyph: '/',
|
||||
levels : [0,1,2],
|
||||
frequency: 2,
|
||||
base_damage: Some(2)
|
||||
)
|
||||
]
|
||||
)
|
@ -87,3 +87,9 @@ pub struct ActivateItem {
|
||||
pub used_by: Entity,
|
||||
pub item: Entity,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub struct Damage(pub i32);
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub struct Weapon;
|
||||
|
22
src/main.rs
22
src/main.rs
@ -79,12 +79,12 @@ 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,
|
||||
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);
|
||||
@ -101,10 +101,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));
|
||||
resources.insert(TurnState::AwaitingInput);
|
||||
@ -153,10 +150,7 @@ impl State {
|
||||
//spawn_amulet_of_yala(&mut self.ecs, 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
|
||||
.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));
|
||||
self.resources.insert(TurnState::AwaitingInput);
|
||||
|
@ -1,93 +0,0 @@
|
||||
pub 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,
|
||||
},
|
||||
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),
|
||||
));
|
||||
}
|
||||
|
||||
fn goblin() -> (i32, String, FontCharType) {
|
||||
(1, "Goblin".to_string(), to_cp437('g'))
|
||||
}
|
||||
fn orc() -> (i32, String, FontCharType) {
|
||||
(2, "orc".to_string(), to_cp437('o'))
|
||||
}
|
||||
|
||||
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("Magic 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),
|
||||
}
|
||||
}
|
44
src/spawners/mod.rs
Normal file
44
src/spawners/mod.rs
Normal file
@ -0,0 +1,44 @@
|
||||
mod template;
|
||||
use template::*;
|
||||
|
||||
pub use crate::prelude::*;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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,
|
||||
},
|
||||
FieldOfView::new(8),
|
||||
Damage(1),
|
||||
));
|
||||
}
|
||||
|
||||
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()),
|
||||
));
|
||||
}
|
107
src/spawners/template.rs
Normal file
107
src/spawners/template.rs
Normal file
@ -0,0 +1,107 @@
|
||||
pub 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>,
|
||||
pub base_damage: 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 to open template 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 legion::systems::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!("We don't know how to provide: {}", provides)
|
||||
}
|
||||
});
|
||||
}
|
||||
if let Some(damage) = &template.base_damage {
|
||||
commands.add_component(entity, Damage(*damage));
|
||||
if template.entity_type == EntityType::Item {
|
||||
commands.add_component(entity, Weapon {});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -4,29 +4,42 @@ use crate::prelude::*;
|
||||
#[read_component(WantsToAttack)]
|
||||
#[read_component(Player)]
|
||||
#[write_component(Health)]
|
||||
#[read_component(Damage)]
|
||||
#[read_component(Carried)]
|
||||
pub fn combat(ecs: &mut SubWorld, commands: &mut CommandBuffer) {
|
||||
let mut attackers = <(Entity, &WantsToAttack)>::query();
|
||||
let victims: Vec<(Entity, Entity)> = attackers
|
||||
let victims: Vec<(Entity, Entity, Entity)> = attackers
|
||||
.iter(ecs)
|
||||
.map(|(entity, attack)| (*entity, attack.victim))
|
||||
.map(|(entity, attack)| (*entity, attack.attacker, attack.victim))
|
||||
.collect();
|
||||
victims.iter().for_each(|(message, victim)| {
|
||||
victims.iter().for_each(|(message, attacker, victim)| {
|
||||
let is_player = ecs
|
||||
.entry_ref(*victim)
|
||||
.unwrap()
|
||||
.get_component::<Player>()
|
||||
.is_ok();
|
||||
if let Ok(mut health) = ecs
|
||||
.entry_mut(*victim)
|
||||
.unwrap()
|
||||
.get_component_mut::<Health>()
|
||||
{
|
||||
println!("Health before attack: {}", health.current);
|
||||
health.current -= 1;
|
||||
let base_damage = if let Ok(v) = ecs.entry_ref(*attacker) {
|
||||
if let Ok(dmg) = v.get_component::<Damage>() {
|
||||
dmg.0
|
||||
} else {
|
||||
0
|
||||
}
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
let weapon_damage: i32 = <(&Carried, &Damage)>::query()
|
||||
.iter(ecs)
|
||||
.filter(|(carried, _)| carried.0 == *attacker)
|
||||
.map(|(_, dmg)| dmg.0)
|
||||
.sum();
|
||||
let final_damage = base_damage + weapon_damage;
|
||||
|
||||
if let Ok(mut health) = ecs.entry_mut(*victim).unwrap().get_component::<Health>() {
|
||||
health.current -= final_damage;
|
||||
if health.current < 1 && !is_player {
|
||||
commands.remove(*victim);
|
||||
}
|
||||
println!("Health after attack: {}", health.current);
|
||||
}
|
||||
commands.remove(*message);
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user