Add hunger
This commit is contained in:
parent
cb17a9c356
commit
74c05f97af
@ -109,6 +109,25 @@ pub struct ProvidesHealing {
|
||||
pub heal_amount: i32,
|
||||
}
|
||||
|
||||
#[derive(Component, Debug, ConvertSaveload, Clone)]
|
||||
pub struct Heals {
|
||||
pub amount: Vec<i32>,
|
||||
}
|
||||
|
||||
impl Heals {
|
||||
pub fn new_healing(store: &mut WriteStorage<Heals>, target: Entity, amount: i32) {
|
||||
if let Some(healing) = store.get_mut(target) {
|
||||
healing.amount.push(amount);
|
||||
} else {
|
||||
let heal = Heals {
|
||||
amount: vec![amount],
|
||||
};
|
||||
store.insert(target, heal).expect("Unable to insert heal");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Component, Debug, Clone, ConvertSaveload)]
|
||||
pub struct Ranged {
|
||||
pub range: i32,
|
||||
@ -178,3 +197,21 @@ pub struct WantsToRemoveItem {
|
||||
pub struct ParticleLifetime {
|
||||
pub lifetime_ms: f32,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Serialize, Deserialize, Clone, Copy)]
|
||||
pub enum HungerState {
|
||||
WellFed,
|
||||
Normal,
|
||||
Hungry,
|
||||
Starving,
|
||||
}
|
||||
|
||||
#[derive(Component, Serialize, Deserialize, Clone)]
|
||||
pub struct HungerClock {
|
||||
pub state: HungerState,
|
||||
pub duration: i32,
|
||||
}
|
||||
|
||||
|
||||
#[derive(Component, Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct ProvidesFood {}
|
||||
|
23
src/gui.rs
23
src/gui.rs
@ -1,14 +1,14 @@
|
||||
use rltk::Rltk;
|
||||
use rltk::RGB;
|
||||
use rltk::{Point, VirtualKeyCode};
|
||||
use rltk::RGB;
|
||||
use rltk::Rltk;
|
||||
use specs::prelude::*;
|
||||
|
||||
use crate::gamelog::GameLog;
|
||||
use crate::Name;
|
||||
use crate::Player;
|
||||
use crate::Position;
|
||||
use crate::{HungerClock, HungerState, HungerSystem, Name};
|
||||
use crate::{CombatStats, InBackpack, RunState, State, Viewshed};
|
||||
use crate::{Equipped, Map};
|
||||
use crate::gamelog::GameLog;
|
||||
use crate::Player;
|
||||
use crate::Position;
|
||||
|
||||
pub fn draw_ui(ecs: &World, ctx: &mut Rltk) {
|
||||
ctx.draw_box(
|
||||
@ -22,9 +22,10 @@ pub fn draw_ui(ecs: &World, ctx: &mut Rltk) {
|
||||
|
||||
let combat_stats = ecs.read_storage::<CombatStats>();
|
||||
let players = ecs.read_storage::<Player>();
|
||||
let hunger = ecs.read_storage::<HungerClock>();
|
||||
let log = ecs.fetch::<GameLog>();
|
||||
|
||||
for (_player, stats) in (&players, &combat_stats).join() {
|
||||
for (_player, stats, hc) in (&players, &combat_stats, &hunger).join() {
|
||||
let health = format!(" HP: {} / {}", stats.hp, stats.max_hp);
|
||||
ctx.print_color(
|
||||
12,
|
||||
@ -43,7 +44,15 @@ pub fn draw_ui(ecs: &World, ctx: &mut Rltk) {
|
||||
RGB::named(rltk::RED),
|
||||
RGB::named(rltk::BLACK),
|
||||
);
|
||||
|
||||
match hc.state {
|
||||
HungerState::WellFed => ctx.print_color(71, 42, RGB::named(rltk::GREEN), RGB::named(rltk::BLACK), "Well Fed"),
|
||||
HungerState::Normal => {}
|
||||
HungerState::Hungry => ctx.print_color(71, 42, RGB::named(rltk::ORANGE), RGB::named(rltk::BLACK), "Hungry"),
|
||||
HungerState::Starving => ctx.print_color(71, 42, RGB::named(rltk::RED), RGB::named(rltk::BLACK), "Starving"),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
let mut y = 44;
|
||||
for s in log.entries.iter().rev() {
|
||||
|
22
src/healing_system.rs
Normal file
22
src/healing_system.rs
Normal file
@ -0,0 +1,22 @@
|
||||
use specs::prelude::*;
|
||||
|
||||
use crate::{CombatStats, GameLog, Heals};
|
||||
|
||||
pub struct HealingSystem {}
|
||||
|
||||
impl<'a> System<'a> for HealingSystem {
|
||||
type SystemData = (
|
||||
WriteStorage<'a, Heals>,
|
||||
WriteStorage<'a, CombatStats>,
|
||||
);
|
||||
|
||||
fn run(&mut self, data: Self::SystemData) {
|
||||
let (mut heals, mut stats) = data;
|
||||
|
||||
for (heal, mut stats) in (&heals, &mut stats).join() {
|
||||
stats.hp = i32::min(stats.max_hp, stats.hp + heal.amount.iter().sum::<i32>());
|
||||
}
|
||||
|
||||
heals.clear();
|
||||
}
|
||||
}
|
71
src/hunger_system.rs
Normal file
71
src/hunger_system.rs
Normal file
@ -0,0 +1,71 @@
|
||||
use specs::prelude::*;
|
||||
|
||||
use crate::{GameLog, Heals, HungerClock, HungerState, ProvidesHealing, RunState, SufferDamage};
|
||||
use crate::spawner::player;
|
||||
|
||||
pub struct HungerSystem {}
|
||||
|
||||
impl<'a> System<'a> for HungerSystem {
|
||||
type SystemData = (
|
||||
Entities<'a>,
|
||||
WriteStorage<'a, HungerClock>,
|
||||
ReadExpect<'a, Entity>,
|
||||
ReadExpect<'a, RunState>,
|
||||
WriteStorage<'a, SufferDamage>,
|
||||
WriteExpect<'a, GameLog>,
|
||||
WriteStorage<'a, Heals>
|
||||
);
|
||||
|
||||
fn run(&mut self, data: Self::SystemData) {
|
||||
let (entities, mut hunger_clock, player_entity, run_state, mut suffer_damage, mut game_log, mut heals) = data;
|
||||
for (entity, mut clock) in (&entities, &mut hunger_clock).join() {
|
||||
let mut proceed = false;
|
||||
|
||||
match *run_state {
|
||||
RunState::PlayerTurn => {
|
||||
if entity == *player_entity { proceed = true; } }
|
||||
RunState::MonsterTurn => { if entity != *player_entity { proceed = true; } }
|
||||
_ => { proceed = false; }
|
||||
}
|
||||
|
||||
if !proceed {
|
||||
continue;
|
||||
}
|
||||
|
||||
clock.duration -= 1;
|
||||
if clock.duration > 0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
match clock.state {
|
||||
HungerState::WellFed => {
|
||||
clock.state = HungerState::Normal;
|
||||
clock.duration = 200;
|
||||
if entity == *player_entity {
|
||||
game_log.entries.push("You are no longer well-feed".to_string())
|
||||
}
|
||||
}
|
||||
HungerState::Normal => {
|
||||
clock.state = HungerState::Hungry;
|
||||
clock.duration = 200;
|
||||
if entity == *player_entity {
|
||||
game_log.entries.push("You are hungry".to_string())
|
||||
}
|
||||
}
|
||||
HungerState::Hungry => {
|
||||
clock.state = HungerState::Starving;
|
||||
clock.duration = 200;
|
||||
if entity == *player_entity {
|
||||
game_log.entries.push("You are Starving!".to_string())
|
||||
}
|
||||
}
|
||||
HungerState::Starving => {
|
||||
if entity == *player_entity {
|
||||
game_log.entries.push("Your hunger pangs are getting painful".to_string())
|
||||
}
|
||||
SufferDamage::new_damage(&mut suffer_damage, entity, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,12 +1,8 @@
|
||||
use specs::prelude::*;
|
||||
|
||||
use crate::{AreaOfEffect, CombatStats, Confusion, Consumable, Equippable, Equipped, HungerClock, HungerState, InBackpack, InflictsDamage, Map, Name, Position, ProvidesFood, ProvidesHealing, Ranged, SufferDamage, WantsToDropItem, WantsToPickupItem, WantsToRemoveItem, WantsToUseItem};
|
||||
use crate::gamelog::GameLog;
|
||||
use crate::particle_system::ParticleBuilder;
|
||||
use crate::{
|
||||
AreaOfEffect, CombatStats, Confusion, Consumable, Equippable, Equipped, InBackpack,
|
||||
InflictsDamage, Map, Name, Position, ProvidesHealing, Ranged, SufferDamage, WantsToDropItem,
|
||||
WantsToPickupItem, WantsToRemoveItem, WantsToUseItem,
|
||||
};
|
||||
|
||||
pub struct ItemCollectionSystem {}
|
||||
|
||||
@ -72,6 +68,8 @@ impl<'a> System<'a> for ItemUseSystem {
|
||||
WriteStorage<'a, InBackpack>,
|
||||
WriteExpect<'a, ParticleBuilder>,
|
||||
ReadStorage<'a, Position>,
|
||||
ReadStorage<'a, ProvidesFood>,
|
||||
WriteStorage<'a, HungerClock>,
|
||||
);
|
||||
|
||||
fn run(&mut self, data: Self::SystemData) {
|
||||
@ -95,6 +93,8 @@ impl<'a> System<'a> for ItemUseSystem {
|
||||
mut backpack,
|
||||
mut particle_builder,
|
||||
positions,
|
||||
provides_food,
|
||||
mut hunger_clocks
|
||||
) = data;
|
||||
|
||||
for (entity, use_item) in (&entities, &wants_use).join() {
|
||||
@ -133,10 +133,20 @@ impl<'a> System<'a> for ItemUseSystem {
|
||||
200.,
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(item_edible) = provides_food.get(use_item.item) {
|
||||
used_item = true;
|
||||
let target = targets[0];
|
||||
if let Some(hc) = hunger_clocks.get_mut(target) {
|
||||
hc.state = HungerState::WellFed;
|
||||
hc.duration = 20;
|
||||
game_log.entries.push(format!("You eat the {}.", names.get(use_item.item).unwrap().name));
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(item_equippable) = equippable.get(use_item.item) {
|
||||
let target_slot = item_equippable.slot;
|
||||
|
13
src/main.rs
13
src/main.rs
@ -14,6 +14,8 @@ use player::*;
|
||||
use visibility_system::*;
|
||||
|
||||
use crate::gamelog::GameLog;
|
||||
use crate::healing_system::HealingSystem;
|
||||
use crate::hunger_system::HungerSystem;
|
||||
use crate::inventory_system::{
|
||||
ItemCollectionSystem, ItemDropSystem, ItemRemoveSystem, ItemUseSystem,
|
||||
};
|
||||
@ -35,6 +37,8 @@ mod rect;
|
||||
mod save_load_system;
|
||||
mod spawner;
|
||||
mod visibility_system;
|
||||
mod hunger_system;
|
||||
mod healing_system;
|
||||
|
||||
#[derive(PartialEq, Copy, Clone)]
|
||||
pub enum RunState {
|
||||
@ -213,6 +217,9 @@ impl State {
|
||||
let mut damage_system = DamageSystem {};
|
||||
damage_system.run_now(&self.ecs);
|
||||
|
||||
let mut healing_system = HealingSystem {};
|
||||
healing_system.run_now(&self.ecs);
|
||||
|
||||
let mut inventory = ItemCollectionSystem {};
|
||||
inventory.run_now(&self.ecs);
|
||||
|
||||
@ -225,6 +232,9 @@ impl State {
|
||||
let mut remove_items = ItemRemoveSystem {};
|
||||
remove_items.run_now(&self.ecs);
|
||||
|
||||
let mut hunger = HungerSystem {};
|
||||
hunger.run_now(&self.ecs);
|
||||
|
||||
let mut particle_spawn = ParticleSpawnSystem {};
|
||||
particle_spawn.run_now(&self.ecs);
|
||||
|
||||
@ -458,6 +468,9 @@ fn main() -> rltk::BError {
|
||||
gs.ecs.register::<DefenseBonus>();
|
||||
gs.ecs.register::<WantsToRemoveItem>();
|
||||
gs.ecs.register::<ParticleLifetime>();
|
||||
gs.ecs.register::<HungerClock>();
|
||||
gs.ecs.register::<Heals>();
|
||||
gs.ecs.register::<ProvidesFood>();
|
||||
|
||||
gs.ecs.insert(SimpleMarkerAllocator::<SerializeMe>::new());
|
||||
|
||||
|
@ -1,9 +1,9 @@
|
||||
use specs::prelude::*;
|
||||
|
||||
use crate::{DefenseBonus, Equipped, HungerClock, HungerState, MeleePowerBonus, Position};
|
||||
use crate::components::{CombatStats, Name, SufferDamage, WantsToMelee};
|
||||
use crate::gamelog::GameLog;
|
||||
use crate::particle_system::ParticleBuilder;
|
||||
use crate::{DefenseBonus, Equipped, MeleePowerBonus, Position};
|
||||
|
||||
pub struct MeleeCombatSystem {}
|
||||
|
||||
@ -21,6 +21,7 @@ impl<'a> System<'a> for MeleeCombatSystem {
|
||||
ReadStorage<'a, Equipped>,
|
||||
WriteExpect<'a, ParticleBuilder>,
|
||||
ReadStorage<'a, Position>,
|
||||
ReadStorage<'a, HungerClock>
|
||||
);
|
||||
|
||||
fn run(&mut self, data: Self::SystemData) {
|
||||
@ -36,12 +37,16 @@ impl<'a> System<'a> for MeleeCombatSystem {
|
||||
equipped,
|
||||
mut particle_builder,
|
||||
positions,
|
||||
hunger_clock
|
||||
) = data;
|
||||
|
||||
for (entity, wants_melee, name, stats) in
|
||||
(&entities, &wants_melee, &names, &combat_stats).join()
|
||||
{
|
||||
if stats.hp > 0 {
|
||||
if stats.hp <= 0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut offensive_bonus = 0;
|
||||
for (_item_entity, power_bonus, equipped_by) in
|
||||
(&entities, &melee_bonus, &equipped).join()
|
||||
@ -51,6 +56,12 @@ impl<'a> System<'a> for MeleeCombatSystem {
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(hc) = hunger_clock.get(entity) {
|
||||
if hc.state == HungerState::WellFed {
|
||||
offensive_bonus += 1;
|
||||
}
|
||||
}
|
||||
|
||||
let target_stats = combat_stats.get(wants_melee.target).unwrap();
|
||||
if target_stats.hp > 0 {
|
||||
let target_name = names.get(wants_melee.target).unwrap();
|
||||
@ -94,7 +105,6 @@ impl<'a> System<'a> for MeleeCombatSystem {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wants_melee.clear();
|
||||
}
|
||||
|
@ -3,13 +3,9 @@ use std::cmp::{max, min};
|
||||
use rltk::{Point, Rltk, VirtualKeyCode};
|
||||
use specs::prelude::*;
|
||||
|
||||
use crate::{components::{CombatStats, Player, Position, Viewshed, WantsToMelee}, HungerClock, HungerState, Item, map::Map, Monster, RunState, State, TileType, WantsToPickupItem};
|
||||
use crate::gamelog::GameLog;
|
||||
use crate::spawner::player;
|
||||
use crate::{
|
||||
components::{CombatStats, Player, Position, Viewshed, WantsToMelee},
|
||||
map::Map,
|
||||
Item, Monster, RunState, State, TileType, WantsToPickupItem,
|
||||
};
|
||||
|
||||
pub fn try_move_player(delta_x: i32, delta_y: i32, ecs: &mut World) {
|
||||
let mut positions = ecs.write_storage::<Position>();
|
||||
@ -139,10 +135,20 @@ fn skip_turn(ecs: &mut World) -> RunState {
|
||||
}
|
||||
}
|
||||
|
||||
let hunger_clock = ecs.read_storage::<HungerClock>();
|
||||
let mut heal_bonus = 0;
|
||||
if let Some(hc) = hunger_clock.get(*player_entity) {
|
||||
match hc.state {
|
||||
HungerState::Hungry | HungerState::Starving => {can_heal = false;}
|
||||
HungerState::WellFed => {heal_bonus = 1;}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
if can_heal {
|
||||
let mut health_component = ecs.write_storage::<CombatStats>();
|
||||
let player_hp = health_component.get_mut(*player_entity).unwrap();
|
||||
player_hp.hp = i32::min(player_hp.hp + 1, player_hp.max_hp);
|
||||
player_hp.hp = i32::min(player_hp.hp + 1 + heal_bonus, player_hp.max_hp);
|
||||
}
|
||||
|
||||
RunState::PlayerTurn
|
||||
|
@ -77,7 +77,10 @@ pub fn save_game(ecs: &mut World) {
|
||||
MeleePowerBonus,
|
||||
DefenseBonus,
|
||||
WantsToRemoveItem,
|
||||
ParticleLifetime
|
||||
ParticleLifetime,
|
||||
HungerClock,
|
||||
Heals,
|
||||
ProvidesFood
|
||||
);
|
||||
}
|
||||
|
||||
@ -157,7 +160,10 @@ pub fn load_game(ecs: &mut World) {
|
||||
MeleePowerBonus,
|
||||
DefenseBonus,
|
||||
WantsToRemoveItem,
|
||||
ParticleLifetime
|
||||
ParticleLifetime,
|
||||
HungerClock,
|
||||
Heals,
|
||||
ProvidesFood
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -4,13 +4,9 @@ use rltk::{FontCharType, RandomNumberGenerator, RGB};
|
||||
use specs::prelude::*;
|
||||
use specs::saveload::{MarkedBuilder, SimpleMarker};
|
||||
|
||||
use crate::{AreaOfEffect, BlocksTile, CombatStats, Confusion, Consumable, DefenseBonus, EquipmentSlot, Equippable, HungerClock, HungerState, InflictsDamage, Item, MAP_WIDTH, MAX_MONSTER, MeleePowerBonus, Monster, Name, Player, Position, ProvidesFood, ProvidesHealing, Ranged, Renderable, SerializeMe, Viewshed};
|
||||
use crate::random_table::RandomTable;
|
||||
use crate::rect::Rect;
|
||||
use crate::{
|
||||
AreaOfEffect, BlocksTile, CombatStats, Confusion, Consumable, DefenseBonus, EquipmentSlot,
|
||||
Equippable, InflictsDamage, Item, MeleePowerBonus, Monster, Name, Player, Position,
|
||||
ProvidesHealing, Ranged, Renderable, SerializeMe, Viewshed, MAP_WIDTH, MAX_MONSTER,
|
||||
};
|
||||
|
||||
pub fn player(ecs: &mut World, player_x: i32, player_y: i32) -> Entity {
|
||||
ecs.create_entity()
|
||||
@ -39,6 +35,10 @@ pub fn player(ecs: &mut World, player_x: i32, player_y: i32) -> Entity {
|
||||
defense: 2,
|
||||
power: 5,
|
||||
})
|
||||
.with(HungerClock {
|
||||
duration: 20,
|
||||
state: HungerState::WellFed,
|
||||
})
|
||||
.marked::<SimpleMarker<SerializeMe>>()
|
||||
.build()
|
||||
}
|
||||
@ -125,6 +125,7 @@ pub fn spawn_room(ecs: &mut World, room: &Rect, map_depth: i32) {
|
||||
"Breastplate" => breastplate(ecs, x, y),
|
||||
"Leggings" => leggings(ecs, x, y),
|
||||
"Sabatons" => sabatons(ecs, x, y),
|
||||
"Rations" => rations(ecs, x, y),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
@ -226,6 +227,7 @@ pub fn room_table(map_depth: i32) -> RandomTable {
|
||||
.add("Breastplate", map_depth - 3)
|
||||
.add("Leggings", map_depth - 4)
|
||||
.add("Sabatons", map_depth - 4)
|
||||
.add("Rations", 10)
|
||||
}
|
||||
|
||||
fn dagger(ecs: &mut World, x: i32, y: i32) {
|
||||
@ -397,3 +399,23 @@ fn sabatons(ecs: &mut World, x: i32, y: i32) {
|
||||
.marked::<SimpleMarker<SerializeMe>>()
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
fn rations(ecs: &mut World, x: i32, y: i32) {
|
||||
ecs.create_entity()
|
||||
.with(Position { x, y })
|
||||
.with(Renderable {
|
||||
glyph: rltk::to_cp437('%'),
|
||||
render_order: 2,
|
||||
fg: RGB::named(rltk::GREEN),
|
||||
bg: RGB::named(rltk::BLACK),
|
||||
})
|
||||
.with(Item {})
|
||||
.with(Name {
|
||||
name: "Rations".to_string(),
|
||||
})
|
||||
.with(Consumable{})
|
||||
.with(ProvidesFood{})
|
||||
.marked::<SimpleMarker<SerializeMe>>()
|
||||
.build();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user