Add clothing to player and NPCs

This commit is contained in:
Timothy Warren 2022-01-04 11:11:38 -05:00
parent d96d1ce003
commit 5b227115db
9 changed files with 459 additions and 72 deletions

View File

@ -184,6 +184,49 @@
} }
} }
}, },
{
"name": "Dried Sausage",
"renderable": {
"glyph": "%",
"fg": "#00FF00",
"bg": "#000000",
"order": 2
},
"consumable": {
"effects": {
"food": ""
}
}
},
{
"name": "Beer",
"renderable": {
"glyph": "!",
"fg": "#FF00FF",
"bg": "#000000",
"order": 2
},
"consumable": {
"effects": {
"provides_healing": "4"
}
}
},
{
"name": "Rusty Longsword",
"renderable": {
"glyph": "/",
"fg": "#BB77BB",
"bg": "#000000",
"order": 2
},
"weapon": {
"range": "melee",
"attribute": "Might",
"base_damage": "1d8-1",
"hit_bonus": -1
}
},
{ {
"name": "Dagger", "name": "Dagger",
"renderable": { "renderable": {
@ -194,7 +237,9 @@
}, },
"weapon": { "weapon": {
"range": "melee", "range": "melee",
"power_bonus": 2 "attribute": "Quickness",
"base_damage": "1d4",
"hit_bonus": 0
} }
}, },
{ {
@ -207,7 +252,9 @@
}, },
"weapon": { "weapon": {
"range": "melee", "range": "melee",
"power_bonus": 4 "attribute": "Might",
"base_damage": "1d8",
"hit_bonus": 0
} }
}, },
{ {
@ -220,7 +267,9 @@
}, },
"weapon": { "weapon": {
"range": "melee", "range": "melee",
"power_bonus": 5 "attribute": "Might",
"base_damage": "1d8+1",
"hit_bonus": 0
} }
}, },
{ {
@ -231,8 +280,9 @@
"bg": "#000000", "bg": "#000000",
"order": 2 "order": 2
}, },
"shield": { "wearable": {
"defense_bonus": 1 "slot": "Shield",
"armor_class": 1.0
} }
}, },
{ {
@ -243,8 +293,102 @@
"bg": "#000000", "bg": "#000000",
"order": 2 "order": 2
}, },
"shield": { "wearable": {
"defense_bonus": 3 "slot": "Shield",
"armor_class": 2.0
}
},
{
"name": "Stained Tunic",
"renderable": {
"glyph": "[",
"fg": "#00FF00",
"bg": "#000000",
"order": 2
},
"wearable": {
"slot": "Torso",
"armor_class": 0.1
}
},
{
"name": "Torn Trousers",
"renderable": {
"glyph": "[",
"fg": "#00FFFF",
"bg": "#000000",
"order": 2
},
"wearable": {
"slot": "Legs",
"armor_class": 0.1
}
},
{
"name": "Old Boots",
"renderable": {
"glyph": "[",
"fg": "#FF9999",
"bg": "#000000",
"order": 2
},
"wearable": {
"slot": "Legs",
"armor_class": 0.1
}
},
{
"name": "Cudgel",
"renderable": {
"glyph": "/",
"fg": "#A52A2A",
"bg": "#000000",
"order": 2
},
"weapon": {
"range": "melee",
"attribute": "Quickness",
"base_damage": "1d4",
"hit_bonus": 0
}
},
{
"name": "Cloth Tunic",
"renderable": {
"glyph": "[",
"fg": "#00FF00",
"bg": "#000000",
"order": 2
},
"wearable": {
"slot": "Torso",
"armor_class": 0.1
}
},
{
"name": "Cloth Pants",
"renderable": {
"glyph": "[",
"fg": "#00FFFF",
"bg": "#000000",
"order": 2
},
"wearable": {
"slot": "Legs",
"armor_class": 0.1
}
},
{
"name": "Slippers",
"renderable": {
"glyph": "[",
"fg": "#FF9999",
"bg": "#000000",
"order": 2
},
"wearable": {
"slot": "Legs",
"armor_class": 0.1
} }
} }
], ],
@ -265,7 +409,13 @@
}, },
"skills": { "skills": {
"Melee": 2 "Melee": 2
} },
"equipped": [
"Cudgel",
"Cloth Tunic",
"Cloth Pants",
"Slippers"
]
}, },
{ {
"name": "Shady Salesman", "name": "Shady Salesman",
@ -278,7 +428,13 @@
"blocks_tile": true, "blocks_tile": true,
"vision_range": 4, "vision_range": 4,
"ai": "vendor", "ai": "vendor",
"attributes": {} "attributes": {},
"equipped": [
"Cudgel",
"Cloth Tunic",
"Cloth Pants",
"Slippers"
]
}, },
{ {
"name": "Patron", "name": "Patron",
@ -296,7 +452,13 @@
"Oh my, I drank too much.", "Oh my, I drank too much.",
"Still saving the world, eh?" "Still saving the world, eh?"
], ],
"attributes": {} "attributes": {},
"equipped": [
"Cudgel",
"Cloth Tunic",
"Cloth Pants",
"Slippers"
]
}, },
{ {
"name": "Priest", "name": "Priest",
@ -309,7 +471,13 @@
"blocks_tile": true, "blocks_tile": true,
"vision_range": 4, "vision_range": 4,
"ai": "bystander", "ai": "bystander",
"attributes": {} "attributes": {},
"equipped": [
"Cudgel",
"Cloth Tunic",
"Cloth Pants",
"Slippers"
]
}, },
{ {
"name": "Parishioner", "name": "Parishioner",
@ -327,7 +495,13 @@
"I hear there's going to be a good sermon on tea", "I hear there's going to be a good sermon on tea",
"Want some cake?" "Want some cake?"
], ],
"attributes": {} "attributes": {},
"equipped": [
"Cudgel",
"Cloth Tunic",
"Cloth Pants",
"Slippers"
]
}, },
{ {
"name": "Blacksmith", "name": "Blacksmith",
@ -340,7 +514,13 @@
"blocks_tile": true, "blocks_tile": true,
"vision_range": 4, "vision_range": 4,
"ai": "vendor", "ai": "vendor",
"attributes": {} "attributes": {},
"equipped": [
"Cudgel",
"Cloth Tunic",
"Cloth Pants",
"Slippers"
]
}, },
{ {
"name": "Clothier", "name": "Clothier",
@ -353,7 +533,13 @@
"blocks_tile": true, "blocks_tile": true,
"vision_range": 4, "vision_range": 4,
"ai": "vendor", "ai": "vendor",
"attributes": {} "attributes": {},
"equipped": [
"Cudgel",
"Cloth Tunic",
"Cloth Pants",
"Slippers"
]
}, },
{ {
"name": "Alchemist", "name": "Alchemist",
@ -366,7 +552,13 @@
"blocks_tile": true, "blocks_tile": true,
"vision_range": 4, "vision_range": 4,
"ai": "vendor", "ai": "vendor",
"attributes": {} "attributes": {},
"equipped": [
"Cudgel",
"Cloth Tunic",
"Cloth Pants",
"Slippers"
]
}, },
{ {
"name": "Mom", "name": "Mom",
@ -385,7 +577,13 @@
"Be careful in the dungeon!", "Be careful in the dungeon!",
"Your father would be so proud, were he here." "Your father would be so proud, were he here."
], ],
"attributes": {} "attributes": {},
"equipped": [
"Cudgel",
"Cloth Tunic",
"Cloth Pants",
"Slippers"
]
}, },
{ {
"name": "Peasant", "name": "Peasant",
@ -401,7 +599,13 @@
"quips": [ "quips": [
"Why are you in my house?" "Why are you in my house?"
], ],
"attributes": {} "attributes": {},
"equipped": [
"Cudgel",
"Cloth Tunic",
"Cloth Pants",
"Slippers"
]
}, },
{ {
"name": "Dock Worker", "name": "Dock Worker",
@ -419,7 +623,13 @@
"Nice weather", "Nice weather",
"Hello" "Hello"
], ],
"attributes": {} "attributes": {},
"equipped": [
"Cudgel",
"Cloth Tunic",
"Cloth Pants",
"Slippers"
]
}, },
{ {
"name": "Fisher", "name": "Fisher",
@ -437,7 +647,13 @@
"I caught something, but it wasn't a fish!", "I caught something, but it wasn't a fish!",
"Looks like rain" "Looks like rain"
], ],
"attributes": {} "attributes": {},
"equipped": [
"Cudgel",
"Cloth Tunic",
"Cloth Pants",
"Slippers"
]
}, },
{ {
"name": "Wannabe Pirate", "name": "Wannabe Pirate",
@ -455,7 +671,13 @@
"Grog!", "Grog!",
"Booze!" "Booze!"
], ],
"attributes": {} "attributes": {},
"equipped": [
"Cudgel",
"Cloth Tunic",
"Cloth Pants",
"Slippers"
]
}, },
{ {
"name": "Drunk", "name": "Drunk",
@ -473,7 +695,13 @@
"Need... more... booze!", "Need... more... booze!",
"Spare a copper?" "Spare a copper?"
], ],
"attributes": {} "attributes": {},
"equipped": [
"Cudgel",
"Cloth Tunic",
"Cloth Pants",
"Slippers"
]
}, },
{ {
"name": "Rat", "name": "Rat",
@ -493,6 +721,16 @@
"skills": { "skills": {
"Melee": -1, "Melee": -1,
"Defense": -1 "Defense": -1
},
"natural": {
"armor_class": 11,
"attacks": [
{
"name": "bite",
"hit_bonus": 0,
"damage": "1d4"
}
]
} }
}, },
{ {

View File

@ -165,6 +165,11 @@ pub struct WantsToRemoveItem {
pub enum EquipmentSlot { pub enum EquipmentSlot {
Melee, Melee,
Shield, Shield,
Head,
Torso,
Legs,
Feet,
Hands,
} }
#[derive(Component, Serialize, Deserialize, Clone)] #[derive(Component, Serialize, Deserialize, Clone)]
@ -194,8 +199,9 @@ pub struct MeleeWeapon {
} }
#[derive(Component, ConvertSaveload, Clone)] #[derive(Component, ConvertSaveload, Clone)]
pub struct DefenseBonus { pub struct Wearable {
pub defense: i32, pub armor_class: f32,
pub slot: EquipmentSlot,
} }
#[derive(Component, Serialize, Deserialize, Clone)] #[derive(Component, Serialize, Deserialize, Clone)]

View File

@ -519,7 +519,7 @@ fn main() -> ::rltk::BError {
Equippable, Equippable,
Equipped, Equipped,
MeleeWeapon, MeleeWeapon,
DefenseBonus, Wearable,
WantsToRemoveItem, WantsToRemoveItem,
ParticleLifetime, ParticleLifetime,
HungerClock, HungerClock,

View File

@ -2,12 +2,13 @@ use ::rltk::{RandomNumberGenerator, RGB};
use ::specs::prelude::*; use ::specs::prelude::*;
use crate::components::{ use crate::components::{
Attributes, HungerClock, HungerState, Name, Pools, Skill, Skills, SufferDamage, WantsToMelee, Attributes, Equipped, HungerClock, HungerState, MeleeWeapon, Name, Pools, Skill, Skills,
SufferDamage, WantsToMelee, Wearable,
}; };
use crate::game_log::GameLog; use crate::game_log::GameLog;
use crate::gamesystem::skill_bonus; use crate::gamesystem::skill_bonus;
use crate::particle_system::ParticleBuilder; use crate::particle_system::ParticleBuilder;
use crate::Position; use crate::{EquipmentSlot, Position, WeaponAttribute};
pub struct MeleeCombatSystem {} pub struct MeleeCombatSystem {}
@ -26,6 +27,9 @@ impl<'a> System<'a> for MeleeCombatSystem {
ReadStorage<'a, HungerClock>, ReadStorage<'a, HungerClock>,
ReadStorage<'a, Pools>, ReadStorage<'a, Pools>,
WriteExpect<'a, RandomNumberGenerator>, WriteExpect<'a, RandomNumberGenerator>,
ReadStorage<'a, Equipped>,
ReadStorage<'a, MeleeWeapon>,
ReadStorage<'a, Wearable>,
); );
fn run(&mut self, data: Self::SystemData) { fn run(&mut self, data: Self::SystemData) {
@ -42,6 +46,9 @@ impl<'a> System<'a> for MeleeCombatSystem {
hunger_clock, hunger_clock,
pools, pools,
mut rng, mut rng,
equipped_items,
meleeweapons,
wearables,
) = data; ) = data;
for (entity, wants_melee, name, attacker_attributes, attacker_skills, attacker_pools) in ( for (entity, wants_melee, name, attacker_attributes, attacker_skills, attacker_pools) in (
@ -61,10 +68,28 @@ impl<'a> System<'a> for MeleeCombatSystem {
if attacker_pools.hit_points.current > 0 && target_pools.hit_points.current > 0 { if attacker_pools.hit_points.current > 0 && target_pools.hit_points.current > 0 {
let target_name = names.get(wants_melee.target).unwrap(); let target_name = names.get(wants_melee.target).unwrap();
let mut weapon_info = MeleeWeapon {
attribute: WeaponAttribute::Might,
hit_bonus: 0,
damage_n_dice: 1,
damage_die_type: 4,
damage_bonus: 0,
};
for (wielded, melee) in (&equipped_items, &meleeweapons).join() {
if wielded.owner == entity && wielded.slot == EquipmentSlot::Melee {
weapon_info = melee.clone();
}
}
let natural_roll = rng.roll_dice(1, 20); let natural_roll = rng.roll_dice(1, 20);
let attribute_hit_bonus = attacker_attributes.might.bonus; let attribute_hit_bonus = if weapon_info.attribute == WeaponAttribute::Might {
attacker_attributes.might.bonus
} else {
attacker_attributes.quickness.bonus
};
let skill_hit_bonus = skill_bonus(Skill::Melee, &*attacker_skills); let skill_hit_bonus = skill_bonus(Skill::Melee, &*attacker_skills);
let weapon_hit_bonus = 0; // TODO: Once weapons support this let weapon_hit_bonus = weapon_info.hit_bonus;
let mut status_hit_bonus = 0; let mut status_hit_bonus = 0;
if let Some(hc) = hunger_clock.get(entity) { if let Some(hc) = hunger_clock.get(entity) {
// Well-Fed grants +1 // Well-Fed grants +1
@ -78,10 +103,21 @@ impl<'a> System<'a> for MeleeCombatSystem {
+ weapon_hit_bonus + weapon_hit_bonus
+ status_hit_bonus; + status_hit_bonus;
let mut armor_item_bonus_f = 0.0;
for (wielded, armor) in (&equipped_items, &wearables).join() {
if wielded.owner == wants_melee.target {
armor_item_bonus_f += armor.armor_class;
}
}
// let base_armor_class = match natural.get(wants_melee.target) {
// None => 10,
// Some(nat) = nat.armor_class.unwrap_or(10);
// };
let base_armor_class = 10; let base_armor_class = 10;
let armor_quickness_bonus = target_attributes.quickness.bonus; let armor_quickness_bonus = target_attributes.quickness.bonus;
let armor_skill_bonus = skill_bonus(Skill::Defense, &*target_skills); let armor_skill_bonus = skill_bonus(Skill::Defense, &*target_skills);
let armor_item_bonus = 0; // TODO: Once armor supports this let armor_item_bonus = armor_item_bonus_f as i32;
let armor_class = let armor_class =
base_armor_class + armor_quickness_bonus + armor_skill_bonus + armor_item_bonus; base_armor_class + armor_quickness_bonus + armor_skill_bonus + armor_item_bonus;
@ -90,7 +126,7 @@ impl<'a> System<'a> for MeleeCombatSystem {
let base_damage = rng.roll_dice(1, 4); let base_damage = rng.roll_dice(1, 4);
let attr_damage_bonus = attacker_attributes.might.bonus; let attr_damage_bonus = attacker_attributes.might.bonus;
let skill_damage_bonus = skill_bonus(Skill::Melee, &*attacker_skills); let skill_damage_bonus = skill_bonus(Skill::Melee, &*attacker_skills);
let weapon_damage_bonus = 0; let weapon_damage_bonus = weapon_info.damage_bonus;
let damage = i32::max( let damage = i32::max(
0, 0,

View File

@ -8,7 +8,7 @@ pub struct Item {
pub renderable: Option<Renderable>, pub renderable: Option<Renderable>,
pub consumable: Option<Consumable>, pub consumable: Option<Consumable>,
pub weapon: Option<Weapon>, pub weapon: Option<Weapon>,
pub shield: Option<Shield>, pub wearable: Option<Wearable>,
} }
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
@ -33,6 +33,7 @@ pub struct Weapon {
} }
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
pub struct Shield { pub struct Wearable {
pub defense_bonus: i32, pub armor_class: f32,
pub slot: String,
} }

View File

@ -17,6 +17,7 @@ pub struct Mob {
pub level: Option<i32>, pub level: Option<i32>,
pub hp: Option<i32>, pub hp: Option<i32>,
pub mana: Option<i32>, pub mana: Option<i32>,
pub equipped: Option<Vec<String>>,
} }
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]

View File

@ -2,6 +2,7 @@ use std::collections::{HashMap, HashSet};
use ::regex::Regex; use ::regex::Regex;
use ::specs::prelude::*; use ::specs::prelude::*;
use ::specs::saveload::{MarkedBuilder, SimpleMarker};
use crate::components::*; use crate::components::*;
use crate::gamesystem::{mana_at_level, npc_hp}; use crate::gamesystem::{mana_at_level, npc_hp};
@ -16,7 +17,7 @@ pub fn parse_dice_string(dice: &str) -> (i32, i32, i32) {
let mut die_type = 4; let mut die_type = 4;
let mut die_bonus = 0; let mut die_bonus = 0;
for cap in DIC_RE.captures_iter(dice) { for cap in DICE_RE.captures_iter(dice) {
if let Some(group) = cap.get(1) { if let Some(group) = cap.get(1) {
n_dice = group.as_str().parse::<i32>().expect("Not a digit"); n_dice = group.as_str().parse::<i32>().expect("Not a digit");
} }
@ -33,6 +34,8 @@ pub fn parse_dice_string(dice: &str) -> (i32, i32, i32) {
pub enum SpawnType { pub enum SpawnType {
AtPosition { x: i32, y: i32 }, AtPosition { x: i32, y: i32 },
Equipped { by: Entity },
Carried { by: Entity },
} }
pub struct RawMaster { pub struct RawMaster {
@ -103,17 +106,40 @@ impl RawMaster {
} }
} }
fn spawn_position(pos: SpawnType, new_entity: EntityBuilder) -> EntityBuilder { fn find_slot_for_equippable_item(tag: &str, raws: &RawMaster) -> EquipmentSlot {
let mut eb = new_entity; if !raws.item_index.contains_key(tag) {
panic!("Trying to equip an unknown item: {}", tag);
}
let item_index = raws.item_index[tag];
let item = &raws.raws.items[item_index];
if item.weapon.is_some() {
return EquipmentSlot::Melee;
} else if let Some(wearable) = &item.wearable {
return string_to_slot(&wearable.slot);
}
panic!("Trying to equip {}, but it has not slot tag", tag);
}
fn spawn_position<'a>(
pos: SpawnType,
new_entity: EntityBuilder<'a>,
tag: &str,
raws: &RawMaster,
) -> EntityBuilder<'a> {
let eb = new_entity;
// Spawn in the specified location // Spawn in the specified location
match pos { match pos {
SpawnType::AtPosition { x, y } => { SpawnType::AtPosition { x, y } => eb.with(Position { x, y }),
eb = eb.with(Position { x, y }); SpawnType::Carried { by } => eb.with(InBackpack { owner: by }),
SpawnType::Equipped { by } => {
let slot = find_slot_for_equippable_item(tag, raws);
eb.with(Equipped { owner: by, slot })
} }
} }
eb
} }
fn get_renderable_component( fn get_renderable_component(
@ -127,19 +153,36 @@ fn get_renderable_component(
} }
} }
pub fn string_to_slot(slot: &str) -> EquipmentSlot {
match slot {
"Shield" => EquipmentSlot::Shield,
"Head" => EquipmentSlot::Head,
"Torso" => EquipmentSlot::Torso,
"Legs" => EquipmentSlot::Legs,
"Feet" => EquipmentSlot::Feet,
"Hands" => EquipmentSlot::Hands,
"Melee" => EquipmentSlot::Melee,
_ => {
rltk::console::log(format!("Warning: unknown equipment slot type [{}]", slot));
EquipmentSlot::Melee
}
}
}
pub fn spawn_named_item( pub fn spawn_named_item(
raws: &RawMaster, raws: &RawMaster,
new_entity: EntityBuilder, ecs: &mut World,
key: &str, key: &str,
pos: SpawnType, pos: SpawnType,
) -> Option<Entity> { ) -> Option<Entity> {
if raws.item_index.contains_key(key) { if raws.item_index.contains_key(key) {
let item_template = &raws.raws.items[raws.item_index[key]]; let item_template = &raws.raws.items[raws.item_index[key]];
let mut eb = new_entity; let mut eb = ecs.create_entity().marked::<SimpleMarker<SerializeMe>>();
// Spawn in the specified location // Spawn in the specified location
eb = spawn_position(pos, eb); eb = spawn_position(pos, eb, key, raws);
// Renderable // Renderable
if let Some(renderable) = &item_template.renderable { if let Some(renderable) = &item_template.renderable {
@ -196,17 +239,29 @@ pub fn spawn_named_item(
eb = eb.with(Equippable { eb = eb.with(Equippable {
slot: EquipmentSlot::Melee, slot: EquipmentSlot::Melee,
}); });
eb = eb.with(MeleePowerBonus { let (n_dice, die_type, bonus) = parse_dice_string(&weapon.base_damage);
power: weapon.power_bonus, let mut wpn = MeleeWeapon {
}); attribute: WeaponAttribute::Might,
damage_n_dice: n_dice,
damage_die_type: die_type,
damage_bonus: bonus,
hit_bonus: weapon.hit_bonus,
};
wpn.attribute = match weapon.attribute.as_str() {
"Quickness" => WeaponAttribute::Quickness,
_ => WeaponAttribute::Might,
};
eb = eb.with(wpn);
} }
if let Some(shield) = &item_template.shield { if let Some(wearable) = &item_template.wearable {
eb = eb.with(Equippable { let slot = string_to_slot(&wearable.slot);
slot: EquipmentSlot::Shield, eb = eb.with(Equippable { slot });
}); eb = eb.with(Wearable {
eb = eb.with(DefenseBonus { slot,
defense: shield.defense_bonus, armor_class: wearable.armor_class,
}); });
} }
@ -218,17 +273,17 @@ pub fn spawn_named_item(
pub fn spawn_named_mob( pub fn spawn_named_mob(
raws: &RawMaster, raws: &RawMaster,
new_entity: EntityBuilder, ecs: &mut World,
key: &str, key: &str,
pos: SpawnType, pos: SpawnType,
) -> Option<Entity> { ) -> Option<Entity> {
if raws.mob_index.contains_key(key) { if raws.mob_index.contains_key(key) {
let mob_template = &raws.raws.mobs[raws.mob_index[key]]; let mob_template = &raws.raws.mobs[raws.mob_index[key]];
let mut eb = new_entity; let mut eb = ecs.create_entity().marked::<SimpleMarker<SerializeMe>>();
// Spawn in the specified location // Spawn in the specified location
eb = spawn_position(pos, eb); eb = spawn_position(pos, eb, key, raws);
// Renderable // Renderable
if let Some(renderable) = &mob_template.renderable { if let Some(renderable) = &mob_template.renderable {
@ -251,6 +306,10 @@ pub fn spawn_named_mob(
}); });
} }
if mob_template.blocks_tile {
eb = eb.with(BlocksTile {});
}
let mut mob_fitness = 11; let mut mob_fitness = 11;
let mut mob_int = 11; let mut mob_int = 11;
let mut attr = Attributes { let mut attr = Attributes {
@ -318,17 +377,22 @@ pub fn spawn_named_mob(
} }
eb = eb.with(skills); eb = eb.with(skills);
if mob_template.blocks_tile {
eb = eb.with(BlocksTile {});
}
eb = eb.with(Viewshed { eb = eb.with(Viewshed {
visible_tiles: Vec::new(), visible_tiles: Vec::new(),
range: mob_template.vision_range, range: mob_template.vision_range,
dirty: true, dirty: true,
}); });
return Some(eb.build()); let new_mob = eb.build();
// Are they weilding anything?
if let Some(wielding) = &mob_template.equipped {
for tag in wielding.iter() {
spawn_named_entity(raws, ecs, tag, SpawnType::Equipped { by: new_mob });
}
}
return Some(new_mob);
} }
None None
@ -336,17 +400,17 @@ pub fn spawn_named_mob(
pub fn spawn_named_prop( pub fn spawn_named_prop(
raws: &RawMaster, raws: &RawMaster,
new_entity: EntityBuilder, ecs: &mut World,
key: &str, key: &str,
pos: SpawnType, pos: SpawnType,
) -> Option<Entity> { ) -> Option<Entity> {
if raws.prop_index.contains_key(key) { if raws.prop_index.contains_key(key) {
let prop_template = &raws.raws.props[raws.prop_index[key]]; let prop_template = &raws.raws.props[raws.prop_index[key]];
let mut eb = new_entity; let mut eb = ecs.create_entity().marked::<SimpleMarker<SerializeMe>>();
// Spawn in the specified location // Spawn in the specified location
eb = spawn_position(pos, eb); eb = spawn_position(pos, eb, key, raws);
// Renderable // Renderable
if let Some(renderable) = &prop_template.renderable { if let Some(renderable) = &prop_template.renderable {
@ -396,16 +460,16 @@ pub fn spawn_named_prop(
pub fn spawn_named_entity( pub fn spawn_named_entity(
raws: &RawMaster, raws: &RawMaster,
new_entity: EntityBuilder, ecs: &mut World,
key: &str, key: &str,
pos: SpawnType, pos: SpawnType,
) -> Option<Entity> { ) -> Option<Entity> {
if raws.item_index.contains_key(key) { if raws.item_index.contains_key(key) {
return spawn_named_item(raws, new_entity, key, pos); return spawn_named_item(raws, ecs, key, pos);
} else if raws.mob_index.contains_key(key) { } else if raws.mob_index.contains_key(key) {
return spawn_named_mob(raws, new_entity, key, pos); return spawn_named_mob(raws, ecs, key, pos);
} else if raws.prop_index.contains_key(key) { } else if raws.prop_index.contains_key(key) {
return spawn_named_prop(raws, new_entity, key, pos); return spawn_named_prop(raws, ecs, key, pos);
} }
None None

View File

@ -76,7 +76,7 @@ pub fn save_game(ecs: &mut World) {
Equippable, Equippable,
Equipped, Equipped,
MeleeWeapon, MeleeWeapon,
DefenseBonus, Wearable,
WantsToRemoveItem, WantsToRemoveItem,
ParticleLifetime, ParticleLifetime,
HungerClock, HungerClock,
@ -172,7 +172,7 @@ pub fn load_game(ecs: &mut World) {
Equippable, Equippable,
Equipped, Equipped,
MeleeWeapon, MeleeWeapon,
DefenseBonus, Wearable,
WantsToRemoveItem, WantsToRemoveItem,
ParticleLifetime, ParticleLifetime,
HungerClock, HungerClock,

View File

@ -12,7 +12,8 @@ use crate::{Map, Rect, TileType};
/// Spawns the player and returns their entity object /// Spawns the player and returns their entity object
pub fn player(ecs: &mut World, player_x: i32, player_y: i32) -> Entity { pub fn player(ecs: &mut World, player_x: i32, player_y: i32) -> Entity {
ecs.create_entity() let player = ecs
.create_entity()
.with(Position { .with(Position {
x: player_x, x: player_x,
y: player_y, y: player_y,
@ -50,7 +51,47 @@ pub fn player(ecs: &mut World, player_x: i32, player_y: i32) -> Entity {
level: 1, level: 1,
}) })
.marked::<SimpleMarker<SerializeMe>>() .marked::<SimpleMarker<SerializeMe>>()
.build() .build();
// Starting equipment
spawn_named_entity(
&RAWS.lock().unwrap(),
ecs,
"Rusty Longsword",
SpawnType::Equipped { by: player },
);
spawn_named_entity(
&RAWS.lock().unwrap(),
ecs,
"Dried Sausage",
SpawnType::Carried { by: player },
);
spawn_named_entity(
&RAWS.lock().unwrap(),
ecs,
"Beer",
SpawnType::Carried { by: player },
);
spawn_named_entity(
&RAWS.lock().unwrap(),
ecs,
"Stained Tunic",
SpawnType::Equipped { by: player },
);
spawn_named_entity(
&RAWS.lock().unwrap(),
ecs,
"Torn Trousers",
SpawnType::Equipped { by: player },
);
spawn_named_entity(
&RAWS.lock().unwrap(),
ecs,
"Old Boots",
SpawnType::Equipped { by: player },
);
player
} }
const MAX_MONSTERS: i32 = 4; const MAX_MONSTERS: i32 = 4;
@ -136,7 +177,7 @@ pub fn spawn_entity(ecs: &mut World, spawn: &(&usize, &String)) {
let item_result = spawn_named_entity( let item_result = spawn_named_entity(
&RAWS.lock().unwrap(), &RAWS.lock().unwrap(),
ecs.create_entity(), ecs,
spawn.1, spawn.1,
SpawnType::AtPosition { x, y }, SpawnType::AtPosition { x, y },
); );