diff --git a/raws/items.json b/raws/items.json index 8ec6e2c..0e5a017 100644 --- a/raws/items.json +++ b/raws/items.json @@ -3,6 +3,8 @@ "id": "potion_health", "name": { "name": "potion of health", "plural": "potions of health" }, "renderable": { "glyph": "!", "fg": "#FF00FF", "bg": "#000000", "order": 2 }, + "weight": 1, + "value": 50, "flags": ["CONSUMABLE", "DESTRUCTIBLE"], "effects": { "provides_healing": "12" } }, @@ -10,6 +12,8 @@ "id": "potion_health_weak", "name": { "name": "potion of lesser health", "plural": "potions of lesser health" }, "renderable": { "glyph": "!", "fg": "#FF00FF", "bg": "#000000", "order": 2 }, + "weight": 1, + "value": 25, "flags": ["CONSUMABLE", "DESTRUCTIBLE"], "effects": { "provides_healing": "6" } }, @@ -17,6 +21,8 @@ "id": "scroll_magicmissile", "name": { "name": "scroll of magic missile", "plural": "scrolls of magic missile" }, "renderable": { "glyph": "?", "fg": "#00FFFF", "bg": "#000000", "order": 2 }, + "weight": 0.5, + "value": 50, "flags": ["CONSUMABLE", "DESTRUCTIBLE"], "effects": { "ranged": "12", "damage": "10" } }, @@ -24,6 +30,8 @@ "id": "scroll_fireball", "name": { "name": "scroll of fireball", "plural": "scrolls of fireball" }, "renderable": { "glyph": "?", "fg": "#00FFFF", "bg": "#000000", "order": 2 }, + "weight": 0.5, + "value": 150, "flags": ["CONSUMABLE", "DESTRUCTIBLE"], "effects": { "ranged": "10", "damage": "15", "aoe": "3" } }, @@ -31,6 +39,8 @@ "id": "scroll_fireball_c", "name": { "name": "cursed scroll of fireball", "plural": "cursed scrolls of fireball" }, "renderable": { "glyph": "?", "fg": "#00FFFF", "bg": "#000000", "order": 2 }, + "weight": 0.5, + "value": 150, "flags": ["CONSUMABLE", "DESTRUCTIBLE", "CURSED"], "effects": { "ranged": "10", "damage": "15", "aoe": "3" } }, @@ -38,6 +48,8 @@ "id": "scroll_confusion", "name": { "name": "scroll of confusion", "plural": "scrolls of confusion" }, "renderable": { "glyph": "?", "fg": "#00FFFF", "bg": "#000000", "order": 2 }, + "weight": 0.5, + "value": 100, "flags": ["CONSUMABLE", "DESTRUCTIBLE"], "effects": { "ranged": "10", "confusion": "4" } }, @@ -45,6 +57,8 @@ "id": "scroll_magicmap", "name": { "name": "scroll of magic mapping", "plural": "scrolls of magic mapping" }, "renderable": { "glyph": "?", "fg": "#00FFFF", "bg": "#000000", "order": 2 }, + "weight": 0.5, + "value": 50, "flags": ["CONSUMABLE", "DESTRUCTIBLE"], "effects": { "magicmapper": "" } }, @@ -52,6 +66,8 @@ "id": "scroll_magicmap_c", "name": { "name": "cursed scroll of magic mapping", "plural": "cursed scrolls of magic mapping" }, "renderable": { "glyph": "?", "fg": "#00FFFF", "bg": "#000000", "order": 2 }, + "weight": 0.5, + "value": 50, "flags": ["CONSUMABLE", "DESTRUCTIBLE", "CURSED"], "effects": { "magicmapper": "" } }, @@ -59,20 +75,26 @@ "id": "equip_dagger", "name": { "name": "dagger", "plural": "daggers" }, "renderable": { "glyph": ")", "fg": "#808080", "bg": "#000000", "order": 2 }, + "weight": 130, + "value": 2, "flags": ["EQUIP_MELEE", "FINESSE"], - "effects": { "base_damage": "1d4"} + "effects": { "base_damage": "1d4" } }, { "id": "equip_shortsword", "name": { "name": "shortsword", "plural": "shortswords" }, "renderable": { "glyph": ")", "fg": "#C0C0C0", "bg": "#000000", "order": 2 }, + "weight": 2, + "value": 10, "flags": ["EQUIP_MELEE", "STRENGTH"], - "effects": { "base_damage": "1d6"} + "effects": { "base_damage": "1d6" } }, { "id": "equip_longsword", "name": { "name": "longsword", "plural": "longswords" }, "renderable": { "glyph": ")", "fg": "#FFF8DC", "bg": "#000000", "order": 2 }, + "weight": 3, + "value": 15, "flags": ["EQUIP_MELEE", "STRENGTH"], "effects": { "base_damage": "1d8" } }, @@ -80,6 +102,8 @@ "id": "equip_smallshield", "name": { "name": "buckler", "plural": "bucklers" }, "renderable": { "glyph": "[", "fg": "#808080", "bg": "#000000", "order": 2 }, + "weight": 2, + "value": 5, "flags": ["EQUIP_SHIELD"], "effects": { "ac": "1" } }, @@ -87,6 +111,8 @@ "id": "equip_mediumshield", "name": { "name": "medium shield", "plural": "medium shields" }, "renderable": { "glyph": "[", "fg": "#C0C0C0", "bg": "#000000", "order": 2 }, + "weight": 6, + "value": 10, "flags": ["EQUIP_SHIELD"], "effects": { "ac": "2", "melee_power_bonus": "-1" } }, @@ -94,6 +120,8 @@ "id": "equip_largeshield", "name": { "name": "large shield", "plural": "large shields" }, "renderable": { "glyph": "[", "fg": "#FFF8DC", "bg": "#000000", "order": 2 }, + "weight": 12, + "value": 35, "flags": ["EQUIP_SHIELD"], "effects": { "ac": "4", "melee_power_bonus": "-2" } }, @@ -101,6 +129,8 @@ "id": "equip_body_weakleather", "name": { "name": "leather jacket", "plural": "leather jackets" }, "renderable": { "glyph": "[", "fg": "#aa6000", "bg": "#000000", "order": 2 }, + "weight": 8, + "value": 5, "flags": ["EQUIP_BODY"], "effects": { "ac": "1" } }, @@ -108,6 +138,8 @@ "id": "equip_body_leather", "name": { "name": "leather chestpiece", "plural": "leather chestpiece" }, "renderable": { "glyph": "[", "fg": "#aa6000", "bg": "#000000", "order": 2 }, + "weight": 10, + "value": 10, "flags": ["EQUIP_BODY"], "effects": { "ac": "2" } }, @@ -115,6 +147,8 @@ "id": "equip_body_studdedleather", "name": { "name": "studded leather chestpiece", "plural": "studded leather chestpieces" }, "renderable": { "glyph": "[", "fg": "#aa6000", "bg": "#000000", "order": 2 }, + "weight": 13, + "value": 45, "flags": ["EQUIP_BODY"], "effects": { "ac": "3" } }, @@ -122,6 +156,8 @@ "id": "equip_body_ringmail_o", "name": { "name": "orcish ring mail", "plural": "orcish ring mail" }, "renderable": { "glyph": "[", "fg": "#aa6000", "bg": "#000000", "order": 2 }, + "weight": 45, + "value": 50, "flags": ["EQUIP_BODY"], "effects": { "ac": "3" } }, @@ -129,6 +165,8 @@ "id": "equip_body_ringmail", "name": { "name": "ring mail", "plural": "ring mail" }, "renderable": { "glyph": "[", "fg": "#aa6000", "bg": "#000000", "order": 2 }, + "weight": 45, + "value": 70, "flags": ["EQUIP_BODY"], "effects": { "ac": "4" } }, @@ -136,6 +174,8 @@ "id": "equip_head_leather", "name": { "name": "leather cap", "plural": "leather caps" }, "renderable": { "glyph": "[", "fg": "#aa6000", "bg": "#000000", "order": 2 }, + "weight": 2, + "value": 10, "flags": ["EQUIP_HEAD"], "effects": { "ac": "1" } }, @@ -143,6 +183,8 @@ "id": "equip_head_elvish", "name": { "name": "elvish leather helm", "plural": "elvish leather helms" }, "renderable": { "glyph": "[", "fg": "#aa6000", "bg": "#000000", "order": 2 }, + "weight": 2, + "value": 25, "flags": ["EQUIP_HEAD"], "effects": { "ac": "2" } }, @@ -150,6 +192,8 @@ "id": "equip_head_o", "name": { "name": "orcish helm", "plural": "orcish helm" }, "renderable": { "glyph": "[", "fg": "#aa6000", "bg": "#000000", "order": 2 }, + "weight": 6, + "value": 25, "flags": ["EQUIP_HEAD"], "effects": { "ac": "2" } }, @@ -157,6 +201,8 @@ "id": "equip_head_iron", "name": { "name": "iron helm", "plural": "iron helm" }, "renderable": { "glyph": "[", "fg": "#aa6000", "bg": "#000000", "order": 2 }, + "weight": 10, + "value": 45, "flags": ["EQUIP_HEAD"], "effects": { "ac": "3" } }, @@ -164,12 +210,16 @@ "id": "equip_feet_leather", "name": { "name": "leather shoes", "plural": "leather shoes" }, "renderable": { "glyph": "[", "fg": "#aa6000", "bg": "#000000", "order": 2 }, + "weight": 2, + "value": 10, "flags": ["EQUIP_FEET"] }, { "id": "equip_feet_elvish", "name": { "name": "elvish leather shoes", "plural": "elvish leather shoes" }, "renderable": { "glyph": "[", "fg": "#aa6000", "bg": "#000000", "order": 2 }, + "weight": 2, + "value": 25, "flags": ["EQUIP_FEET"], "effects": { "ac": "1" } }, @@ -177,6 +227,8 @@ "id": "equip_feet_o", "name": { "name": "orcish boots", "plural": "orcish boots" }, "renderable": { "glyph": "[", "fg": "#aa6000", "bg": "#000000", "order": 2 }, + "weight": 6, + "value": 25, "flags": ["EQUIP_FEET"], "effects": { "ac": "1" } }, @@ -184,6 +236,8 @@ "id": "equip_feet_iron", "name": { "name": "iron boots", "plural": "iron boots" }, "renderable": { "glyph": "[", "fg": "#aa6000", "bg": "#000000", "order": 2 }, + "weight": 10, + "value": 45, "flags": ["EQUIP_FEET"], "effects": { "ac": "2" } }, @@ -191,6 +245,8 @@ "id": "equip_neck_protection", "name": { "name": "amulet of protection", "plural": "amulets of protection" }, "renderable": { "glyph": "\"", "fg": "#aa6000", "bg": "#000000", "order": 2 }, + "weight": 1, + "value": 200, "flags": ["EQUIP_NECK"], "effects": { "ac": "1" } }, @@ -198,6 +254,8 @@ "id": "equip_back_protection", "name": { "name": "cloak of protection", "plural": "cloaks of protection" }, "renderable": { "glyph": "[", "fg": "#aa6000", "bg": "#000000", "order": 2 }, + "weight": 1, + "value": 200, "flags": ["EQUIP_BACK"], "effects": { "ac": "1" } }, @@ -205,6 +263,8 @@ "id": "wand_magicmissile", "name": { "name": "wand of magic missile", "plural": "wands of magic missile" }, "renderable": { "glyph": "/", "fg": "#00FFFF", "bg": "#000000", "order": 2 }, + "weight": 2, + "value": 100, "flags": ["WAND"], "effects": { "ranged": "12", "damage": "10" } }, @@ -212,6 +272,8 @@ "id": "wand_fireball", "name": { "name": "wand of fireball", "plural": "wands of fireball" }, "renderable": { "glyph": "/", "fg": "#00FFFF", "bg": "#000000", "order": 2 }, + "weight": 2, + "value": 300, "flags": ["WAND"], "effects": { "ranged": "10", "damage": "15", "aoe": "3" } }, @@ -219,6 +281,8 @@ "id": "wand_confusion", "name": { "name": "wand of confusion", "plural": "wands of confusion" }, "renderable": { "glyph": "/", "fg": "#00FFFF", "bg": "#000000", "order": 2 }, + "weight": 2, + "value": 200, "flags": ["WAND"], "effects": { "ranged": "10", "confusion": "4" } }, @@ -226,6 +290,8 @@ "id": "wand_digging", "name": { "name": "wand of digging", "plural": "wands of digging" }, "renderable": { "glyph": "/", "fg": "#00FFFF", "bg": "#000000", "order": 2 }, + "weight": 2, + "value": 300, "flags": ["WAND"], "effects": { "ranged": "10", "digger": "" } }, @@ -233,12 +299,16 @@ "id": "food_rations", "name": { "name": "rations", "plural": "rations" }, "renderable": { "glyph": "%", "fg": "#FFA07A", "bg": "#000000", "order": 2 }, + "weight": 1, + "value": 1, "flags": ["FOOD", "CONSUMABLE"] }, { "id": "food_apple", "name": { "name": "apple", "plural": "apples" }, "renderable": { "glyph": "%", "fg": "#00FF00", "bg": "#000000", "order": 2 }, + "weight": 0.5, + "value": 1, "flags": ["FOOD", "CONSUMABLE"] } ] diff --git a/raws/props.json b/raws/props.json index 91d244c..44162d9 100644 --- a/raws/props.json +++ b/raws/props.json @@ -23,6 +23,18 @@ "renderable": { "glyph": "-", "fg": "#AAAAAA", "bg": "#000000", "order": 2 }, "flags": ["PROP"] }, + { + "id": "prop_hay", + "name": "hay", + "renderable": { "glyph": "%", "fg": "#c7ad39", "bg": "#000000", "order": 2 }, + "flags": ["PROP"] + }, + { + "id": "prop_statue", + "name": "statue", + "renderable": { "glyph": "@", "fg": "#ffffff", "bg": "#000000", "order": 2 }, + "flags": ["PROP"] + }, { "id": "prop_bed", "name": "bed", diff --git a/src/ai/encumbrance_system.rs b/src/ai/encumbrance_system.rs new file mode 100644 index 0000000..5350614 --- /dev/null +++ b/src/ai/encumbrance_system.rs @@ -0,0 +1,84 @@ +use crate::{gamelog, Attributes, Burden, EquipmentChanged, Equipped, InBackpack, Item, Pools}; +use rltk::prelude::*; +use specs::prelude::*; +use std::collections::HashMap; + +pub struct EncumbranceSystem {} + +impl<'a> System<'a> for EncumbranceSystem { + #[allow(clippy::type_complexity)] + type SystemData = ( + WriteStorage<'a, EquipmentChanged>, + Entities<'a>, + ReadStorage<'a, Item>, + ReadStorage<'a, InBackpack>, + ReadStorage<'a, Equipped>, + WriteStorage<'a, Pools>, + ReadStorage<'a, Attributes>, + ReadExpect<'a, Entity>, + WriteStorage<'a, Burden>, + ); + + fn run(&mut self, data: Self::SystemData) { + let (mut equip_dirty, entities, items, backpacks, wielded, mut pools, attributes, player, mut burdened) = data; + if equip_dirty.is_empty() { + return; + } + // Build update map + let mut to_update: HashMap = HashMap::new(); + for (entity, _dirty) in (&entities, &equip_dirty).join() { + to_update.insert(entity, 0.0); + } + equip_dirty.clear(); + // Total up equipped items + for (item, equipped) in (&items, &wielded).join() { + if to_update.contains_key(&equipped.owner) { + let totals = to_update.get_mut(&equipped.owner).unwrap(); + *totals += item.weight; + } + } + // Total carried items + for (item, carried) in (&items, &backpacks).join() { + if to_update.contains_key(&carried.owner) { + let totals = to_update.get_mut(&carried.owner).unwrap(); + *totals += item.weight; + } + } + // Apply to pools + for (entity, weight) in to_update.iter() { + if let Some(pool) = pools.get_mut(*entity) { + pool.weight = *weight; + if let Some(attr) = attributes.get(*entity) { + let carry_capacity_lbs = (attr.strength.base + attr.strength.modifiers) * 10; + if pool.weight as i32 > 3 * carry_capacity_lbs { + // Overloaded + burdened + .insert(*entity, Burden { level: crate::BurdenLevel::Overloaded }) + .expect("Failed to insert Burden"); + if *entity == *player { + gamelog::Logger::new().append("You're overloaded!").log(); + } + } else if pool.weight as i32 > 2 * carry_capacity_lbs { + // Strained + burdened + .insert(*entity, Burden { level: crate::BurdenLevel::Strained }) + .expect("Failed to insert Burden"); + if *entity == *player { + gamelog::Logger::new().append("You're strained.").log(); + } + } else if pool.weight as i32 > carry_capacity_lbs { + // Burdened + burdened + .insert(*entity, Burden { level: crate::BurdenLevel::Burdened }) + .expect("Failed to insert Burden"); + if *entity == *player { + gamelog::Logger::new().append("You're burdened.").log(); + } + } else { + burdened.remove(*entity); + } + } + } + } + } +} diff --git a/src/ai/energy_system.rs b/src/ai/energy_system.rs index 0703aea..ef02d2d 100644 --- a/src/ai/energy_system.rs +++ b/src/ai/energy_system.rs @@ -1,4 +1,4 @@ -use crate::{Clock, Energy, Name, Position, RunState, TakingTurn, LOG_TICKS}; +use crate::{Burden, BurdenLevel, Clock, Energy, Name, Position, RunState, TakingTurn, LOG_TICKS}; use rltk::prelude::*; use specs::prelude::*; @@ -12,6 +12,7 @@ impl<'a> System<'a> for EnergySystem { type SystemData = ( ReadStorage<'a, Clock>, WriteStorage<'a, Energy>, + ReadStorage<'a, Burden>, ReadStorage<'a, Position>, WriteStorage<'a, TakingTurn>, Entities<'a>, @@ -22,7 +23,7 @@ impl<'a> System<'a> for EnergySystem { ); fn run(&mut self, data: Self::SystemData) { - let (clock, mut energies, positions, mut turns, entities, mut rng, mut runstate, player, names) = data; + let (clock, mut energies, burdens, positions, mut turns, entities, mut rng, mut runstate, player, names) = data; // If not ticking, do nothing. if *runstate != RunState::Ticking { return; @@ -44,8 +45,17 @@ impl<'a> System<'a> for EnergySystem { } // EVERYTHING ELSE for (entity, energy, _pos) in (&entities, &mut energies, &positions).join() { + let burden_modifier = if let Some(burden) = burdens.get(entity) { + match burden.level { + BurdenLevel::Burdened => 0.75, + BurdenLevel::Strained => 0.5, + BurdenLevel::Overloaded => 0.25, + } + } else { + 1.0 + }; // Every entity has a POTENTIAL equal to their speed. - let mut energy_potential: i32 = energy.speed; + let mut energy_potential: i32 = (energy.speed as f32 * burden_modifier) as i32; // Increment current energy by NORMAL_SPEED for every // whole number of NORMAL_SPEEDS in their POTENTIAL. while energy_potential >= NORMAL_SPEED { diff --git a/src/ai/mod.rs b/src/ai/mod.rs index b51dec3..b33f335 100644 --- a/src/ai/mod.rs +++ b/src/ai/mod.rs @@ -6,3 +6,5 @@ mod quip_system; pub use quip_system::QuipSystem; mod regen_system; pub use regen_system::RegenSystem; +mod encumbrance_system; +pub use encumbrance_system::EncumbranceSystem; diff --git a/src/components.rs b/src/components.rs index c4f7d9a..749d6e8 100644 --- a/src/components.rs +++ b/src/components.rs @@ -143,6 +143,7 @@ pub struct Pools { pub xp: i32, pub bac: i32, pub level: i32, + pub weight: f32, } #[derive(Debug, Serialize, Deserialize, Clone)] @@ -201,7 +202,25 @@ impl SufferDamage { } #[derive(Component, Debug, Serialize, Deserialize, Clone)] -pub struct Item {} +pub struct Item { + pub weight: f32, // in lbs + pub value: f32, // base +} + +#[derive(Component, Debug, Serialize, Deserialize, Clone)] +pub struct EquipmentChanged {} + +#[derive(PartialEq, Copy, Clone, Serialize, Deserialize)] +pub enum BurdenLevel { + Burdened, + Strained, + Overloaded, +} + +#[derive(Component, Serialize, Deserialize, Clone)] +pub struct Burden { + pub level: BurdenLevel, +} #[derive(PartialEq, Copy, Clone, Serialize, Deserialize)] pub enum EquipmentSlot { diff --git a/src/gui/mod.rs b/src/gui/mod.rs index 52b215b..0eb7215 100644 --- a/src/gui/mod.rs +++ b/src/gui/mod.rs @@ -1,7 +1,7 @@ use super::{ - camera, gamelog, gamesystem, rex_assets::RexAssets, ArmourClassBonus, Attributes, Equipped, Hidden, HungerClock, - HungerState, InBackpack, Map, Name, Player, Point, Pools, Position, Prop, Renderable, RunState, Skill, Skills, - State, Viewshed, + camera, gamelog, gamesystem, rex_assets::RexAssets, ArmourClassBonus, Attributes, Burden, Equipped, Hidden, + HungerClock, HungerState, InBackpack, Map, Name, Player, Point, Pools, Position, Prop, Renderable, RunState, Skill, + Skills, State, Viewshed, }; use rltk::{Rltk, VirtualKeyCode, RGB}; use specs::prelude::*; @@ -49,6 +49,7 @@ pub fn draw_ui(ecs: &World, ctx: &mut Rltk) { let attributes = ecs.read_storage::(); let players = ecs.read_storage::(); let hunger = ecs.read_storage::(); + let burden = ecs.read_storage::(); let skills = ecs.read_storage::(); for (_player, stats, attributes, hunger, skills) in (&players, &pools, &attributes, &hunger, &skills).join() { // Draw hp/mana bars @@ -56,7 +57,7 @@ pub fn draw_ui(ecs: &World, ctx: &mut Rltk) { ctx, 2, 53, - 26, + 22, stats.hit_points.current, stats.hit_points.max, RGB::from_u8(0, 255, 0), @@ -66,7 +67,7 @@ pub fn draw_ui(ecs: &World, ctx: &mut Rltk) { ctx, 2, 54, - 26, + 22, stats.mana.current, stats.mana.max, RGB::named(rltk::BLUE), @@ -84,11 +85,11 @@ pub fn draw_ui(ecs: &World, ctx: &mut Rltk) { } } let armour_class = stats.bac - attributes.dexterity.bonus - skill_ac_bonus - armour_ac_bonus; - ctx.print_color(30, 53, RGB::named(rltk::PINK), RGB::named(rltk::BLACK), "AC"); - ctx.print_color(32, 53, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK), armour_class); + ctx.print_color(26, 53, RGB::named(rltk::PINK), RGB::named(rltk::BLACK), "AC"); + ctx.print_color(28, 53, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK), armour_class); // Draw level ctx.print_color( - 30, + 26, 54, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK), @@ -124,25 +125,46 @@ pub fn draw_ui(ecs: &World, ctx: &mut Rltk) { ctx.print_color_right(70, 53, RGB::named(rltk::RED), RGB::named(rltk::BLACK), "Fainting") } } - + // Burden + if let Some(burden) = burden.get(*player_entity) { + match burden.level { + crate::BurdenLevel::Burdened => { + ctx.print_color_right(70, 50, RGB::named(rltk::BROWN1), RGB::named(rltk::BLACK), "Burdened") + } + crate::BurdenLevel::Strained => { + ctx.print_color_right(70, 50, RGB::named(rltk::ORANGE), RGB::named(rltk::BLACK), "Strained") + } + crate::BurdenLevel::Overloaded => { + ctx.print_color_right(70, 50, RGB::named(rltk::RED), RGB::named(rltk::BLACK), "Overloaded") + } + } + } // Draw equipment let names = ecs.read_storage::(); let mut equipment: Vec = Vec::new(); for (_equipped, name) in (&equipped, &names).join().filter(|item| item.0.owner == *player_entity) { - equipment.push(format!("- {} (worn)", &name.name)); + equipment.push(format!("{} (worn)", &name.name)); } let mut y = 1; if !equipment.is_empty() { ctx.print_color(72, y, RGB::named(rltk::BLACK), RGB::named(rltk::WHITE), "Equipment"); for item in equipment { y += 1; - ctx.print_color(72, y, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK), item); + ctx.print_color(72, y, RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK), "-"); + ctx.print_color(74, y, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK), item); } y += 2; } // Draw consumables ctx.print_color(72, y, RGB::named(rltk::BLACK), RGB::named(rltk::WHITE), "Backpack"); + ctx.print_color( + 81, + y, + RGB::named(rltk::WHITE), + RGB::named(rltk::BLACK), + &format!("[{:.1}/{} lbs]", stats.weight, (attributes.strength.base + attributes.strength.modifiers) * 10), + ); y += 1; let (player_inventory, _inventory_ids) = get_player_inventory(&ecs); y = print_options(player_inventory, 72, y, ctx).0; diff --git a/src/inventory_system.rs b/src/inventory_system.rs index 54788ca..1d34fc6 100644 --- a/src/inventory_system.rs +++ b/src/inventory_system.rs @@ -1,8 +1,9 @@ use super::{ - gamelog, Confusion, Consumable, Cursed, Destructible, Digger, Equippable, Equipped, HungerClock, HungerState, - InBackpack, InflictsDamage, MagicMapper, Map, Name, ParticleBuilder, Point, Pools, Position, ProvidesHealing, - ProvidesNutrition, RandomNumberGenerator, RunState, SufferDamage, TileType, Viewshed, Wand, WantsToDropItem, - WantsToPickupItem, WantsToRemoveItem, WantsToUseItem, AOE, DEFAULT_PARTICLE_LIFETIME, LONG_PARTICLE_LIFETIME, + gamelog, Confusion, Consumable, Cursed, Destructible, Digger, EquipmentChanged, Equippable, Equipped, HungerClock, + HungerState, InBackpack, InflictsDamage, MagicMapper, Map, Name, ParticleBuilder, Point, Pools, Position, + ProvidesHealing, ProvidesNutrition, RandomNumberGenerator, RunState, SufferDamage, TileType, Viewshed, Wand, + WantsToDropItem, WantsToPickupItem, WantsToRemoveItem, WantsToUseItem, AOE, DEFAULT_PARTICLE_LIFETIME, + LONG_PARTICLE_LIFETIME, }; use specs::prelude::*; @@ -16,14 +17,18 @@ impl<'a> System<'a> for ItemCollectionSystem { WriteStorage<'a, Position>, ReadStorage<'a, Name>, WriteStorage<'a, InBackpack>, + WriteStorage<'a, EquipmentChanged>, ); fn run(&mut self, data: Self::SystemData) { - let (player_entity, mut wants_pickup, mut positions, names, mut backpack) = data; + let (player_entity, mut wants_pickup, mut positions, names, mut backpack, mut equipment_changed) = data; for pickup in wants_pickup.join() { positions.remove(pickup.item); backpack.insert(pickup.item, InBackpack { owner: pickup.collected_by }).expect("Unable to pickup item."); + equipment_changed + .insert(pickup.collected_by, EquipmentChanged {}) + .expect("Unable to insert EquipmentChanged."); if pickup.collected_by == *player_entity { gamelog::Logger::new() @@ -41,7 +46,8 @@ impl<'a> System<'a> for ItemCollectionSystem { // Grouping together components because of type complexity issues - SystemData was too large. // This is a temporary solution that'll be fixed once inventory use is refactored into separate // systems. -type EquipComponents<'a> = (ReadStorage<'a, Equippable>, WriteStorage<'a, Equipped>); +type EquipComponents<'a> = + (ReadStorage<'a, Equippable>, WriteStorage<'a, Equipped>, WriteStorage<'a, EquipmentChanged>); pub struct ItemUseSystem {} impl<'a> System<'a> for ItemUseSystem { @@ -100,12 +106,15 @@ impl<'a> System<'a> for ItemUseSystem { mut confused, magic_mapper, mut runstate, - (equippable, mut equipped), + (equippable, mut equipped, mut equipment_changed), mut backpack, mut viewsheds, ) = data; for (entity, wants_to_use) in (&entities, &wants_to_use).join() { + // Could probably limit this insert only to if something is consumed/equipped/etc., but this is + // safer and items aren't used nearly frequently enough for this to cause performance issues. + equipment_changed.insert(entity, EquipmentChanged {}).expect("Unable to insert EquipmentChanged."); let mut verb = "use"; let mut used_item = true; let mut aoe_item = false; @@ -413,12 +422,14 @@ impl<'a> System<'a> for ItemDropSystem { ReadStorage<'a, Name>, WriteStorage<'a, Position>, WriteStorage<'a, InBackpack>, + WriteStorage<'a, EquipmentChanged>, ); fn run(&mut self, data: Self::SystemData) { - let (player_entity, entities, mut wants_drop, names, mut positions, mut backpack) = data; + let (player_entity, entities, mut wants_drop, names, mut positions, mut backpack, mut equipment_changed) = data; for (entity, to_drop) in (&entities, &wants_drop).join() { + equipment_changed.insert(entity, EquipmentChanged {}).expect("Unable to insert EquipmentChanged."); let mut dropper_pos: Position = Position { x: 0, y: 0 }; { let dropped_pos = positions.get(entity).unwrap(); diff --git a/src/main.rs b/src/main.rs index 9186653..a22baf4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -96,6 +96,7 @@ impl State { let mut vis = VisibilitySystem {}; let mut regen_system = ai::RegenSystem {}; let mut energy = ai::EnergySystem {}; + let mut encumbrance_system = ai::EncumbranceSystem {}; let mut turn_status_system = ai::TurnStatusSystem {}; let mut quip_system = ai::QuipSystem {}; let mut mob = MonsterAI {}; @@ -113,6 +114,7 @@ impl State { mapindex.run_now(&self.ecs); vis.run_now(&self.ecs); regen_system.run_now(&self.ecs); + encumbrance_system.run_now(&self.ecs); energy.run_now(&self.ecs); turn_status_system.run_now(&self.ecs); quip_system.run_now(&self.ecs); @@ -491,6 +493,7 @@ fn main() -> rltk::BError { gs.ecs.register::(); gs.ecs.register::(); gs.ecs.register::(); + gs.ecs.register::(); gs.ecs.register::(); gs.ecs.register::(); gs.ecs.register::(); @@ -516,6 +519,7 @@ fn main() -> rltk::BError { gs.ecs.register::(); gs.ecs.register::(); gs.ecs.register::(); + gs.ecs.register::(); gs.ecs.register::(); gs.ecs.register::(); gs.ecs.register::(); diff --git a/src/map_builders/town.rs b/src/map_builders/town.rs index c5ab5cb..9f6122d 100644 --- a/src/map_builders/town.rs +++ b/src/map_builders/town.rs @@ -127,14 +127,14 @@ impl TownBuilder { ) { for idx in available_building_tiles.iter() { if rng.roll_dice(1, 40) == 1 { - let roll = rng.roll_dice(1, 6); + let roll = rng.roll_dice(1, 7); match roll { 1 => build_data.spawn_list.push((*idx, "npc_fisher".to_string())), 2 => build_data.spawn_list.push((*idx, "npc_dockworker".to_string())), 3 => build_data.spawn_list.push((*idx, "npc_drunk".to_string())), 4 => build_data.spawn_list.push((*idx, "npc_townsperson".to_string())), 5 => build_data.spawn_list.push((*idx, "npc_guard".to_string())), - _ => { + 6 => { let animal_roll = rng.roll_dice(1, 3); match animal_roll { 1 => build_data.spawn_list.push((*idx, "chicken_little".to_string())), @@ -142,6 +142,14 @@ impl TownBuilder { _ => build_data.spawn_list.push((*idx, "dog_little".to_string())), } } + _ => { + let prop_roll = rng.roll_dice(1, 3); + match prop_roll { + 1 => build_data.spawn_list.push((*idx, "prop_hay".to_string())), + 2 => build_data.spawn_list.push((*idx, "prop_statue".to_string())), + _ => {} + } + } } } } diff --git a/src/raws/item_structs.rs b/src/raws/item_structs.rs index 1dbd5da..42a48f7 100644 --- a/src/raws/item_structs.rs +++ b/src/raws/item_structs.rs @@ -6,6 +6,8 @@ pub struct Item { pub id: String, pub name: Name, pub renderable: Option, + pub weight: Option, + pub value: Option, pub flags: Option>, pub effects: Option>, } diff --git a/src/raws/rawmaster.rs b/src/raws/rawmaster.rs index 4a310fd..8c2394d 100644 --- a/src/raws/rawmaster.rs +++ b/src/raws/rawmaster.rs @@ -114,7 +114,7 @@ pub fn spawn_named_item(raws: &RawMaster, ecs: &mut World, key: &str, pos: Spawn let mut eb = ecs.create_entity().marked::>(); eb = eb.with(Name { name: item_template.name.name.clone(), plural: item_template.name.plural.clone() }); - eb = eb.with(Item {}); + eb = eb.with(Item { weight: item_template.weight.unwrap_or(0.0), value: item_template.value.unwrap_or(0.0) }); eb = spawn_position(pos, eb, key, raws); if let Some(renderable) = &item_template.renderable { @@ -312,8 +312,10 @@ pub fn spawn_named_mob( bac: mob_bac, hit_points: Pool { current: mob_hp, max: mob_hp }, mana: Pool { current: mob_mana, max: mob_mana }, + weight: 0.0, }; eb = eb.with(pools); + eb = eb.with(EquipmentChanged {}); let mut skills = Skills { skills: HashMap::new() }; skills.skills.insert(Skill::Melee, 0); diff --git a/src/saveload_system.rs b/src/saveload_system.rs index def0feb..3b75d8a 100644 --- a/src/saveload_system.rs +++ b/src/saveload_system.rs @@ -55,6 +55,7 @@ pub fn save_game(ecs: &mut World) { Attributes, BlocksTile, BlocksVisibility, + Burden, Bystander, Clock, Confusion, @@ -66,6 +67,7 @@ pub fn save_game(ecs: &mut World) { Energy, EntityMoved, EntryTrigger, + EquipmentChanged, Equippable, Equipped, GrantsXP, @@ -165,6 +167,7 @@ pub fn load_game(ecs: &mut World) { Attributes, BlocksTile, BlocksVisibility, + Burden, Bystander, Clock, Confusion, @@ -176,6 +179,7 @@ pub fn load_game(ecs: &mut World) { Energy, EntityMoved, EntryTrigger, + EquipmentChanged, Equippable, Equipped, GrantsXP, diff --git a/src/spawner.rs b/src/spawner.rs index e35405f..3d80607 100644 --- a/src/spawner.rs +++ b/src/spawner.rs @@ -1,7 +1,7 @@ use super::{ ai::NORMAL_SPEED, gamesystem, gamesystem::attr_bonus, random_table::RandomTable, raws, Attribute, Attributes, - Clock, Energy, HungerClock, HungerState, Map, Name, Player, Pool, Pools, Position, Rect, Renderable, SerializeMe, - Skill, Skills, TileType, Viewshed, + Clock, Energy, EquipmentChanged, HungerClock, HungerState, Map, Name, Player, Pool, Pools, Position, Rect, + Renderable, SerializeMe, Skill, Skills, TileType, Viewshed, }; use rltk::{RandomNumberGenerator, RGB}; use specs::prelude::*; @@ -53,7 +53,9 @@ pub fn player(ecs: &mut World, player_x: i32, player_y: i32) -> Entity { xp: 0, level: 1, bac: 10, + weight: 0.0, }) + .with(EquipmentChanged {}) .with(skills) .with(Energy { current: 0, speed: NORMAL_SPEED }) .marked::>()