hunger system

This commit is contained in:
Llywelwyn 2023-07-12 08:17:50 +01:00
parent b6a29df222
commit 73bd07c1b8
11 changed files with 337 additions and 97 deletions

2
.gitignore vendored
View file

@ -1,6 +1,6 @@
/target /target
.rustfmt.toml .rustfmt.toml
.vscode/* .vscode/*
.gifs/* docs/gifs/*
.savegames/* .savegames/*
savegame.json savegame.json

33
docs/components-list.txt Normal file
View file

@ -0,0 +1,33 @@
AOE { radius: i32 }
BlocksTile {}
CombatStats { max_hp: i32, hp: i32, defence: i32, power: i32 }
Confusion { turns: i32 }
Consumable {}
Cursed {}
DefenceBonus { amount: i32 }
Destructible {}
Equippable { slot: EquipmentSlot }
Equipped { owner: Entity, slot: EquipmentSlot }
HungerClock { state: HungerState, duration: i32 }
InBackpack { owner: Entity }
InflictsDamage { amount: i32 }
Item {}
MagicMapper {}
MeleePowerBonus { amount: i32 }
Mind {}
Monster {}
Name { name: String }
ParticleLifetime { lifetime_ms: f32 }
Player {}
Position { x: i32, y: i32}
ProvidesHealing { amount: i32 }
Ranged { range: i32 }
Renderable { glyph: rltk::FontCharType, fg: RGB, bg: RGB, render_order: i32 }
SufferDamage { amount: i32 }
Telepath { telepath_tiles: Vec<rltk::Point>, range: i32, dirty: bool }
Viewshed { visible_tiles: Vec<rltk::Point>, range: i32, dirty: bool }
WantsToDropItem { item: Entity }
WantsToMelee { target: Entity }
WantsToPickupItem { collected_by: Entity, item: Entity }
WantsToRemoveItem { item: Entity }
WantsToUseItem { item: Entity, target: Option<rltk::Point }

View file

@ -62,6 +62,24 @@ pub struct Name {
#[derive(Component, Debug, Serialize, Deserialize, Clone)] #[derive(Component, Debug, Serialize, Deserialize, Clone)]
pub struct BlocksTile {} pub struct BlocksTile {}
#[derive(Serialize, Deserialize, Copy, Clone, PartialEq)]
pub enum HungerState {
Satiated,
Normal,
Hungry,
Weak,
Fainting,
}
#[derive(Component, Serialize, Deserialize, Clone)]
pub struct HungerClock {
pub state: HungerState,
pub duration: i32,
}
#[derive(Component, Debug, Serialize, Deserialize, Clone)]
pub struct ProvidesNutrition {}
#[derive(Component, Debug, ConvertSaveload, Clone)] #[derive(Component, Debug, ConvertSaveload, Clone)]
pub struct CombatStats { pub struct CombatStats {
pub max_hp: i32, pub max_hp: i32,

View file

@ -1,6 +1,6 @@
use super::{ use super::{
gamelog, rex_assets::RexAssets, CombatStats, Equipped, InBackpack, Map, Name, Player, Point, Position, RunState, gamelog, rex_assets::RexAssets, CombatStats, Equipped, HungerClock, HungerState, InBackpack, Map, Name, Player,
State, Viewshed, Point, Position, RunState, State, Viewshed,
}; };
use rltk::{Rltk, VirtualKeyCode, RGB}; use rltk::{Rltk, VirtualKeyCode, RGB};
use specs::prelude::*; use specs::prelude::*;
@ -11,10 +11,26 @@ pub fn draw_ui(ecs: &World, ctx: &mut Rltk) {
// Render stats // Render stats
let combat_stats = ecs.read_storage::<CombatStats>(); let combat_stats = ecs.read_storage::<CombatStats>();
let players = ecs.read_storage::<Player>(); let players = ecs.read_storage::<Player>();
for (_player, stats) in (&players, &combat_stats).join() { let hunger = ecs.read_storage::<HungerClock>();
for (_player, stats, hunger) in (&players, &combat_stats, &hunger).join() {
let health = format!(" HP {}/{} ", stats.hp, stats.max_hp); let health = format!(" HP {}/{} ", stats.hp, stats.max_hp);
ctx.print_color_right(36, 43, RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK), &health); ctx.print_color_right(36, 43, RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK), &health);
ctx.draw_bar_horizontal(38, 43, 34, stats.hp, stats.max_hp, RGB::named(rltk::RED), RGB::named(rltk::BLACK)); ctx.draw_bar_horizontal(38, 43, 34, stats.hp, stats.max_hp, RGB::named(rltk::RED), RGB::named(rltk::BLACK));
match hunger.state {
HungerState::Satiated => {
ctx.print_color_right(72, 42, RGB::named(rltk::GREEN), RGB::named(rltk::BLACK), "Satiated")
}
HungerState::Normal => {}
HungerState::Hungry => {
ctx.print_color_right(72, 42, RGB::named(rltk::BROWN1), RGB::named(rltk::BLACK), "Hungry")
}
HungerState::Weak => {
ctx.print_color_right(72, 42, RGB::named(rltk::ORANGE), RGB::named(rltk::BLACK), "Weak")
}
HungerState::Fainting => {
ctx.print_color_right(72, 42, RGB::named(rltk::RED), RGB::named(rltk::BLACK), "Fainting")
}
}
} }
// Render the message log at [1, 46], descending, with 6 lines. // Render the message log at [1, 46], descending, with 6 lines.
@ -36,7 +52,7 @@ pub fn draw_ui(ecs: &World, ctx: &mut Rltk) {
// Render mouse cursor // Render mouse cursor
let mouse_pos = ctx.mouse_pos(); let mouse_pos = ctx.mouse_pos();
ctx.set_bg(mouse_pos.0, mouse_pos.1, RGB::named(rltk::MAGENTA)); ctx.set_bg(mouse_pos.0, mouse_pos.1, RGB::named(rltk::YELLOW));
draw_tooltips(ecs, ctx); draw_tooltips(ecs, ctx);
} }

82
src/hunger_system.rs Normal file
View file

@ -0,0 +1,82 @@
use super::{gamelog, HungerClock, HungerState, RunState, SufferDamage};
use specs::prelude::*;
pub struct HungerSystem {}
impl<'a> System<'a> for HungerSystem {
#[allow(clippy::type_complexity)]
type SystemData = (
Entities<'a>,
WriteStorage<'a, HungerClock>,
ReadExpect<'a, Entity>,
ReadExpect<'a, RunState>,
WriteStorage<'a, SufferDamage>,
);
fn run(&mut self, data: Self::SystemData) {
let (entities, mut hunger_clock, player_entity, runstate, mut inflict_damage) = data;
for (entity, mut clock) in (&entities, &mut hunger_clock).join() {
let mut proceed = false;
match *runstate {
RunState::PlayerTurn => {
if entity == *player_entity {
proceed = true;
}
}
RunState::MonsterTurn => {
if entity != *player_entity {
proceed = true;
}
}
_ => proceed = false,
}
if !proceed {
return;
}
clock.duration -= 1;
if clock.duration > 0 {
return;
}
match clock.state {
HungerState::Satiated => {
clock.state = HungerState::Normal;
clock.duration = 300;
if entity == *player_entity {
gamelog::Logger::new().append("You are no longer satiated.").log();
}
}
HungerState::Normal => {
clock.state = HungerState::Hungry;
clock.duration = 100;
if entity == *player_entity {
gamelog::Logger::new().colour(rltk::RED).append("You feel hungry.").log();
}
}
HungerState::Hungry => {
clock.state = HungerState::Weak;
clock.duration = 50;
if entity == *player_entity {
gamelog::Logger::new().colour(rltk::RED).append("You feel weak with hunger.").log();
}
}
HungerState::Weak => {
clock.state = HungerState::Fainting;
clock.duration = 50;
if entity == *player_entity {
gamelog::Logger::new().colour(rltk::RED).append("You feel hungry enough to faint.").log();
}
}
HungerState::Fainting => {
SufferDamage::new_damage(&mut inflict_damage, entity, 1);
if entity == *player_entity {
gamelog::Logger::new().colour(rltk::RED).append("You can't go on without food...").log();
}
}
}
}
}
}

View file

@ -1,8 +1,8 @@
use super::{ use super::{
gamelog, CombatStats, Confusion, Consumable, Cursed, Destructible, Equippable, Equipped, InBackpack, gamelog, CombatStats, Confusion, Consumable, Cursed, Destructible, Equippable, Equipped, HungerClock, HungerState,
InflictsDamage, MagicMapper, Map, Name, ParticleBuilder, Point, Position, ProvidesHealing, RunState, SufferDamage, InBackpack, InflictsDamage, MagicMapper, Map, Name, ParticleBuilder, Point, Position, ProvidesHealing,
WantsToDropItem, WantsToPickupItem, WantsToRemoveItem, WantsToUseItem, AOE, DEFAULT_PARTICLE_LIFETIME, ProvidesNutrition, RunState, SufferDamage, WantsToDropItem, WantsToPickupItem, WantsToRemoveItem, WantsToUseItem,
LONG_PARTICLE_LIFETIME, AOE, DEFAULT_PARTICLE_LIFETIME, LONG_PARTICLE_LIFETIME,
}; };
use specs::prelude::*; use specs::prelude::*;
@ -51,6 +51,8 @@ impl<'a> System<'a> for ItemUseSystem {
ReadStorage<'a, Destructible>, ReadStorage<'a, Destructible>,
ReadStorage<'a, Cursed>, ReadStorage<'a, Cursed>,
ReadStorage<'a, ProvidesHealing>, ReadStorage<'a, ProvidesHealing>,
ReadStorage<'a, ProvidesNutrition>,
WriteStorage<'a, HungerClock>,
WriteStorage<'a, CombatStats>, WriteStorage<'a, CombatStats>,
WriteStorage<'a, SufferDamage>, WriteStorage<'a, SufferDamage>,
WriteExpect<'a, ParticleBuilder>, WriteExpect<'a, ParticleBuilder>,
@ -76,6 +78,8 @@ impl<'a> System<'a> for ItemUseSystem {
destructibles, destructibles,
cursed_items, cursed_items,
provides_healing, provides_healing,
provides_nutrition,
mut hunger_clock,
mut combat_stats, mut combat_stats,
mut suffer_damage, mut suffer_damage,
mut particle_builder, mut particle_builder,
@ -97,8 +101,19 @@ impl<'a> System<'a> for ItemUseSystem {
let is_cursed = cursed_items.get(wants_to_use.item); let is_cursed = cursed_items.get(wants_to_use.item);
let mut verb = "use";
let is_edible = provides_nutrition.get(wants_to_use.item);
if let Some(_) = is_edible {
verb = "eat";
}
let item_equippable = equippable.get(wants_to_use.item);
if let Some(_) = item_equippable {
verb = "equip"
}
gamelog::Logger::new() gamelog::Logger::new()
.append("You use the") .append(format!("You {} the", verb))
.item_name_n(format!("{}", &item_being_used.name)) .item_name_n(format!("{}", &item_being_used.name))
.period() .period()
.log(); .log();
@ -133,8 +148,7 @@ impl<'a> System<'a> for ItemUseSystem {
.append("The") .append("The")
.item_name(&item_being_used.name) .item_name(&item_being_used.name)
.colour(rltk::WHITE) .colour(rltk::WHITE)
.append("disobeys!") .append("disobeys!");
.log();
} }
} }
// AOE // AOE
@ -160,15 +174,28 @@ impl<'a> System<'a> for ItemUseSystem {
} }
} }
// EDIBLE
match is_edible {
None => {}
Some(_) => {
let target = targets[0];
let hc = hunger_clock.get_mut(target);
if let Some(hc) = hc {
hc.state = HungerState::Satiated;
hc.duration = 50;
gamelog::Logger::new().append("You eat the").item_name_n(&item_being_used.name).period().log();
}
}
}
// EQUIPMENT // EQUIPMENT
let item_equippable = equippable.get(wants_to_use.item);
match item_equippable { match item_equippable {
None => {} None => {}
Some(can_equip) => { Some(can_equip) => {
let target_slot = can_equip.slot; let target_slot = can_equip.slot;
let target = targets[0]; let target = targets[0];
// Room any items target has in item's slot // Remove any items target has in item's slot
let mut to_unequip: Vec<Entity> = Vec::new(); let mut to_unequip: Vec<Entity> = Vec::new();
for (item_entity, already_equipped, _name) in (&entities, &equipped, &names).join() { for (item_entity, already_equipped, _name) in (&entities, &equipped, &names).join() {
if already_equipped.owner == target && already_equipped.slot == target_slot { if already_equipped.owner == target && already_equipped.slot == target_slot {
@ -177,8 +204,7 @@ impl<'a> System<'a> for ItemUseSystem {
gamelog::Logger::new() gamelog::Logger::new()
.append("You unequip the") .append("You unequip the")
.item_name_n(&item_being_used.name) .item_name_n(&item_being_used.name)
.period() .period();
.log();
} }
} }
} }
@ -192,13 +218,6 @@ impl<'a> System<'a> for ItemUseSystem {
.insert(wants_to_use.item, Equipped { owner: target, slot: target_slot }) .insert(wants_to_use.item, Equipped { owner: target, slot: target_slot })
.expect("Unable to insert equipped component"); .expect("Unable to insert equipped component");
backpack.remove(wants_to_use.item); backpack.remove(wants_to_use.item);
if target == *player_entity {
gamelog::Logger::new()
.append("You equip the")
.item_name_n(&item_being_used.name)
.period()
.log();
}
} }
} }

View file

@ -24,6 +24,7 @@ mod map_indexing_system;
use map_indexing_system::MapIndexingSystem; use map_indexing_system::MapIndexingSystem;
mod damage_system; mod damage_system;
use damage_system::*; use damage_system::*;
mod hunger_system;
mod melee_combat_system; mod melee_combat_system;
use melee_combat_system::MeleeCombatSystem; use melee_combat_system::MeleeCombatSystem;
mod inventory_system; mod inventory_system;
@ -81,6 +82,8 @@ impl State {
melee_system.run_now(&self.ecs); melee_system.run_now(&self.ecs);
let mut damage_system = DamageSystem {}; let mut damage_system = DamageSystem {};
damage_system.run_now(&self.ecs); damage_system.run_now(&self.ecs);
let mut hunger_clock = hunger_system::HungerSystem {};
hunger_clock.run_now(&self.ecs);
let mut particle_system = particle_system::ParticleSpawnSystem {}; let mut particle_system = particle_system::ParticleSpawnSystem {};
particle_system.run_now(&self.ecs); particle_system.run_now(&self.ecs);
self.ecs.maintain(); self.ecs.maintain();
@ -484,6 +487,7 @@ fn main() -> rltk::BError {
gs.ecs.register::<Name>(); gs.ecs.register::<Name>();
gs.ecs.register::<BlocksTile>(); gs.ecs.register::<BlocksTile>();
gs.ecs.register::<CombatStats>(); gs.ecs.register::<CombatStats>();
gs.ecs.register::<HungerClock>();
gs.ecs.register::<WantsToMelee>(); gs.ecs.register::<WantsToMelee>();
gs.ecs.register::<SufferDamage>(); gs.ecs.register::<SufferDamage>();
gs.ecs.register::<Item>(); gs.ecs.register::<Item>();
@ -504,6 +508,7 @@ fn main() -> rltk::BError {
gs.ecs.register::<WantsToRemoveItem>(); gs.ecs.register::<WantsToRemoveItem>();
gs.ecs.register::<WantsToUseItem>(); gs.ecs.register::<WantsToUseItem>();
gs.ecs.register::<Consumable>(); gs.ecs.register::<Consumable>();
gs.ecs.register::<ProvidesNutrition>();
gs.ecs.register::<Destructible>(); gs.ecs.register::<Destructible>();
gs.ecs.register::<ParticleLifetime>(); gs.ecs.register::<ParticleLifetime>();
gs.ecs.register::<SimpleMarker<SerializeMe>>(); gs.ecs.register::<SimpleMarker<SerializeMe>>();

View file

@ -1,6 +1,6 @@
use super::{ use super::{
gamelog, CombatStats, DefenceBonus, Equipped, MeleePowerBonus, Name, ParticleBuilder, Position, SufferDamage, gamelog, CombatStats, DefenceBonus, Equipped, HungerClock, HungerState, MeleePowerBonus, Name, ParticleBuilder,
WantsToMelee, Position, SufferDamage, WantsToMelee,
}; };
use specs::prelude::*; use specs::prelude::*;
@ -19,6 +19,7 @@ impl<'a> System<'a> for MeleeCombatSystem {
ReadStorage<'a, Equipped>, ReadStorage<'a, Equipped>,
ReadStorage<'a, DefenceBonus>, ReadStorage<'a, DefenceBonus>,
ReadStorage<'a, MeleePowerBonus>, ReadStorage<'a, MeleePowerBonus>,
ReadStorage<'a, HungerClock>,
); );
fn run(&mut self, data: Self::SystemData) { fn run(&mut self, data: Self::SystemData) {
@ -34,6 +35,7 @@ impl<'a> System<'a> for MeleeCombatSystem {
equipped, equipped,
defence_bonuses, defence_bonuses,
melee_power_bonuses, melee_power_bonuses,
hunger_clock,
) = data; ) = data;
for (entity, wants_melee, name, stats) in (&entities, &wants_melee, &names, &combat_stats).join() { for (entity, wants_melee, name, stats) in (&entities, &wants_melee, &names, &combat_stats).join() {
@ -59,6 +61,22 @@ impl<'a> System<'a> for MeleeCombatSystem {
defensive_bonus += defence_bonus.amount; defensive_bonus += defence_bonus.amount;
} }
} }
let hc = hunger_clock.get(entity);
if let Some(hc) = hc {
match hc.state {
HungerState::Satiated => {
offensive_bonus += 1;
}
HungerState::Weak => {
offensive_bonus -= 1;
}
HungerState::Fainting => {
offensive_bonus -= 1;
defensive_bonus -= 1;
}
_ => {}
}
}
let damage = i32::max(0, (stats.power + offensive_bonus) - (target_stats.defence + defensive_bonus)); let damage = i32::max(0, (stats.power + offensive_bonus) - (target_stats.defence + defensive_bonus));
if damage == 0 { if damage == 0 {

View file

@ -1,6 +1,6 @@
use super::{ use super::{
gamelog, CombatStats, Item, Map, Monster, Name, Player, Position, RunState, State, Telepath, TileType, Viewshed, gamelog, CombatStats, HungerClock, HungerState, Item, Map, Monster, Name, Player, Position, RunState, State,
WantsToMelee, WantsToPickupItem, MAPHEIGHT, MAPWIDTH, Telepath, TileType, Viewshed, WantsToMelee, WantsToPickupItem, MAPHEIGHT, MAPWIDTH,
}; };
use rltk::{Point, RandomNumberGenerator, Rltk, VirtualKeyCode}; use rltk::{Point, RandomNumberGenerator, Rltk, VirtualKeyCode};
use specs::prelude::*; use specs::prelude::*;
@ -172,8 +172,12 @@ fn skip_turn(ecs: &mut World) -> RunState {
let viewshed_components = ecs.read_storage::<Viewshed>(); let viewshed_components = ecs.read_storage::<Viewshed>();
let monsters = ecs.read_storage::<Monster>(); let monsters = ecs.read_storage::<Monster>();
let worldmap_resource = ecs.fetch::<Map>(); let worldmap_resource = ecs.fetch::<Map>();
let hunger_clocks = ecs.read_storage::<HungerClock>();
// Default to being able to heal by waiting.
let mut can_heal = true; let mut can_heal = true;
// Check viewshed for monsters nearby. If we can see a monster, we can't heal.
let viewshed = viewshed_components.get(*player_entity).unwrap(); let viewshed = viewshed_components.get(*player_entity).unwrap();
for tile in viewshed.visible_tiles.iter() { for tile in viewshed.visible_tiles.iter() {
let idx = worldmap_resource.xy_idx(tile.x, tile.y); let idx = worldmap_resource.xy_idx(tile.x, tile.y);
@ -188,6 +192,17 @@ fn skip_turn(ecs: &mut World) -> RunState {
} }
} }
// Check player's hunger state - if we're hungry or worse, we can't heal.
let player_hunger_clock = hunger_clocks.get(*player_entity);
if let Some(clock) = player_hunger_clock {
match clock.state {
HungerState::Hungry => can_heal = false,
HungerState::Weak => can_heal = false,
HungerState::Fainting => can_heal = false,
_ => {}
}
}
let mut did_heal = false; let mut did_heal = false;
if can_heal { if can_heal {
let mut health_components = ecs.write_storage::<CombatStats>(); let mut health_components = ecs.write_storage::<CombatStats>();

View file

@ -47,37 +47,40 @@ pub fn save_game(ecs: &mut World) {
ecs, ecs,
serializer, serializer,
data, data,
Position, AOE,
Renderable,
Player,
Viewshed,
Telepath,
Monster,
Mind,
Name,
BlocksTile, BlocksTile,
CombatStats, CombatStats,
SufferDamage, Confusion,
WantsToMelee, Consumable,
Item, Cursed,
DefenceBonus,
Destructible,
Equippable, Equippable,
Equipped, Equipped,
MeleePowerBonus, HungerClock,
DefenceBonus,
Cursed,
Consumable,
Destructible,
Ranged,
InflictsDamage,
AOE,
Confusion,
MagicMapper,
ProvidesHealing,
InBackpack, InBackpack,
WantsToPickupItem, InflictsDamage,
WantsToUseItem, Item,
MagicMapper,
MeleePowerBonus,
Mind,
Monster,
Name,
ParticleLifetime,
Player,
Position,
ProvidesHealing,
ProvidesNutrition,
Ranged,
Renderable,
SufferDamage,
Telepath,
Viewshed,
WantsToDropItem, WantsToDropItem,
WantsToMelee,
WantsToPickupItem,
WantsToRemoveItem, WantsToRemoveItem,
WantsToUseItem,
SerializationHelper SerializationHelper
); );
} }
@ -131,37 +134,40 @@ pub fn load_game(ecs: &mut World) {
ecs, ecs,
de, de,
d, d,
Position, AOE,
Renderable,
Player,
Viewshed,
Telepath,
Monster,
Mind,
Name,
BlocksTile, BlocksTile,
CombatStats, CombatStats,
SufferDamage, Confusion,
WantsToMelee, Consumable,
Item, Cursed,
DefenceBonus,
Destructible,
Equippable, Equippable,
Equipped, Equipped,
MeleePowerBonus, HungerClock,
DefenceBonus,
Cursed,
Consumable,
Destructible,
Ranged,
InflictsDamage,
AOE,
Confusion,
MagicMapper,
ProvidesHealing,
InBackpack, InBackpack,
WantsToPickupItem, InflictsDamage,
WantsToUseItem, Item,
MagicMapper,
MeleePowerBonus,
Mind,
Monster,
Name,
ParticleLifetime,
Player,
Position,
ProvidesHealing,
ProvidesNutrition,
Ranged,
Renderable,
SufferDamage,
Telepath,
Viewshed,
WantsToDropItem, WantsToDropItem,
WantsToMelee,
WantsToPickupItem,
WantsToRemoveItem, WantsToRemoveItem,
WantsToUseItem,
SerializationHelper SerializationHelper
); );
} }

View file

@ -1,7 +1,8 @@
use super::{ use super::{
random_table::RandomTable, BlocksTile, CombatStats, Confusion, Consumable, Cursed, DefenceBonus, Destructible, random_table::RandomTable, BlocksTile, CombatStats, Confusion, Consumable, Cursed, DefenceBonus, Destructible,
EquipmentSlot, Equippable, InflictsDamage, Item, MagicMapper, MeleePowerBonus, Mind, Monster, Name, Player, EquipmentSlot, Equippable, HungerClock, HungerState, InflictsDamage, Item, MagicMapper, MeleePowerBonus, Mind,
Position, ProvidesHealing, Ranged, Rect, Renderable, SerializeMe, Telepath, Viewshed, AOE, MAPWIDTH, Monster, Name, Player, Position, ProvidesHealing, ProvidesNutrition, Ranged, Rect, Renderable, SerializeMe,
Viewshed, AOE, MAPWIDTH,
}; };
use rltk::{console, RandomNumberGenerator, RGB}; use rltk::{console, RandomNumberGenerator, RGB};
use specs::prelude::*; use specs::prelude::*;
@ -23,6 +24,7 @@ pub fn player(ecs: &mut World, player_x: i32, player_y: i32, player_name: String
.with(Viewshed { visible_tiles: Vec::new(), range: 12, dirty: true }) .with(Viewshed { visible_tiles: Vec::new(), range: 12, dirty: true })
.with(Name { name: player_name }) .with(Name { name: player_name })
.with(CombatStats { max_hp: 8, hp: 8, defence: 0, power: 4 }) .with(CombatStats { max_hp: 8, hp: 8, defence: 0, power: 4 })
.with(HungerClock { state: HungerState::Satiated, duration: 50 })
.marked::<SimpleMarker<SerializeMe>>() .marked::<SimpleMarker<SerializeMe>>()
.build() .build()
} }
@ -96,6 +98,7 @@ pub fn spawn_room(ecs: &mut World, room: &Rect, map_depth: i32) {
match category.as_ref() { match category.as_ref() {
"mob" => spawn_table = mob_table(map_depth), "mob" => spawn_table = mob_table(map_depth),
"item" => spawn_table = item_table(map_depth), "item" => spawn_table = item_table(map_depth),
"food" => spawn_table = food_table(map_depth),
_ => spawn_table = debug_table(), _ => spawn_table = debug_table(),
} }
spawn_points.insert(idx, spawn_table.roll(&mut rng)); spawn_points.insert(idx, spawn_table.roll(&mut rng));
@ -132,14 +135,16 @@ pub fn spawn_room(ecs: &mut World, room: &Rect, map_depth: i32) {
"magic missile scroll" => magic_missile_scroll(ecs, x, y), "magic missile scroll" => magic_missile_scroll(ecs, x, y),
"magic map scroll" => magic_map_scroll(ecs, x, y), "magic map scroll" => magic_map_scroll(ecs, x, y),
"cursed magic map scroll" => cursed_magic_map_scroll(ecs, x, y), "cursed magic map scroll" => cursed_magic_map_scroll(ecs, x, y),
// Food
"rations" => rations(ecs, x, y),
_ => console::log("Tried to spawn nothing. Bugfix needed!"), _ => console::log("Tried to spawn nothing. Bugfix needed!"),
} }
} }
} }
// 3 mobs : 1 item // 10 mobs : 3 items : 1 food
fn category_table() -> RandomTable { fn category_table() -> RandomTable {
return RandomTable::new().add("mob", 3).add("item", 1); return RandomTable::new().add("mob", 9).add("item", 3).add("food", 1);
} }
fn debug_table() -> RandomTable { fn debug_table() -> RandomTable {
@ -156,18 +161,18 @@ fn mob_table(map_depth: i32) -> RandomTable {
} }
// 6 equipment : 10 potions : 10 scrolls : 2 cursed scrolls // 6 equipment : 10 potions : 10 scrolls : 2 cursed scrolls
fn item_table(map_depth: i32) -> RandomTable { fn item_table(_map_depth: i32) -> RandomTable {
return RandomTable::new() return RandomTable::new()
// Equipment // Equipment
.add("dagger", 2) .add("dagger", 2)
.add("shortsword", map_depth - 1) .add("shortsword", 2)
.add("buckler", 2) .add("buckler", 2)
.add("shield", 1) .add("shield", 1)
// Potions // Potions
.add("weak health potion", 7) .add("weak health potion", 7)
.add("health potion", 3) .add("health potion", 3)
// Scrolls // Scrolls
.add("fireball scroll", map_depth - 1) .add("fireball scroll", 1)
.add("cursed fireball scroll", 1) .add("cursed fireball scroll", 1)
.add("confusion scroll", 2) .add("confusion scroll", 2)
.add("magic missile scroll", 5) .add("magic missile scroll", 5)
@ -175,12 +180,16 @@ fn item_table(map_depth: i32) -> RandomTable {
.add("cursed magic map scroll", 1); .add("cursed magic map scroll", 1);
} }
fn food_table(_map_depth: i32) -> RandomTable {
return RandomTable::new().add("rations", 1);
}
fn health_potion(ecs: &mut World, x: i32, y: i32) { fn health_potion(ecs: &mut World, x: i32, y: i32) {
ecs.create_entity() ecs.create_entity()
.with(Position { x, y }) .with(Position { x, y })
.with(Renderable { .with(Renderable {
glyph: rltk::to_cp437('I'), glyph: rltk::to_cp437('!'),
fg: RGB::named(rltk::MAGENTA), fg: RGB::named(rltk::MAGENTA2),
bg: RGB::named(rltk::BLACK), bg: RGB::named(rltk::BLACK),
render_order: 2, render_order: 2,
}) })
@ -197,7 +206,7 @@ fn weak_health_potion(ecs: &mut World, x: i32, y: i32) {
ecs.create_entity() ecs.create_entity()
.with(Position { x, y }) .with(Position { x, y })
.with(Renderable { .with(Renderable {
glyph: rltk::to_cp437('i'), glyph: rltk::to_cp437('!'),
fg: RGB::named(rltk::MAGENTA), fg: RGB::named(rltk::MAGENTA),
bg: RGB::named(rltk::BLACK), bg: RGB::named(rltk::BLACK),
render_order: 2, render_order: 2,
@ -237,7 +246,7 @@ fn magic_missile_scroll(ecs: &mut World, x: i32, y: i32) {
ecs.create_entity() ecs.create_entity()
.with(Position { x, y }) .with(Position { x, y })
.with(Renderable { .with(Renderable {
glyph: rltk::to_cp437(')'), glyph: rltk::to_cp437('?'),
fg: RGB::named(rltk::BLUE), fg: RGB::named(rltk::BLUE),
bg: RGB::named(rltk::BLACK), bg: RGB::named(rltk::BLACK),
render_order: 2, render_order: 2,
@ -256,7 +265,7 @@ fn fireball_scroll(ecs: &mut World, x: i32, y: i32) {
ecs.create_entity() ecs.create_entity()
.with(Position { x, y }) .with(Position { x, y })
.with(Renderable { .with(Renderable {
glyph: rltk::to_cp437(')'), glyph: rltk::to_cp437('?'),
fg: RGB::named(rltk::ORANGE), fg: RGB::named(rltk::ORANGE),
bg: RGB::named(rltk::BLACK), bg: RGB::named(rltk::BLACK),
render_order: 2, render_order: 2,
@ -276,7 +285,7 @@ fn cursed_fireball_scroll(ecs: &mut World, x: i32, y: i32) {
ecs.create_entity() ecs.create_entity()
.with(Position { x, y }) .with(Position { x, y })
.with(Renderable { .with(Renderable {
glyph: rltk::to_cp437(')'), glyph: rltk::to_cp437('?'),
fg: RGB::named(rltk::ORANGE), fg: RGB::named(rltk::ORANGE),
bg: RGB::named(rltk::BLACK), bg: RGB::named(rltk::BLACK),
render_order: 2, render_order: 2,
@ -297,7 +306,7 @@ fn confusion_scroll(ecs: &mut World, x: i32, y: i32) {
ecs.create_entity() ecs.create_entity()
.with(Position { x, y }) .with(Position { x, y })
.with(Renderable { .with(Renderable {
glyph: rltk::to_cp437(')'), glyph: rltk::to_cp437('?'),
fg: RGB::named(rltk::PURPLE), fg: RGB::named(rltk::PURPLE),
bg: RGB::named(rltk::BLACK), bg: RGB::named(rltk::BLACK),
render_order: 2, render_order: 2,
@ -316,7 +325,7 @@ fn magic_map_scroll(ecs: &mut World, x: i32, y: i32) {
ecs.create_entity() ecs.create_entity()
.with(Position { x, y }) .with(Position { x, y })
.with(Renderable { .with(Renderable {
glyph: rltk::to_cp437(')'), glyph: rltk::to_cp437('?'),
fg: RGB::named(rltk::ROYALBLUE), fg: RGB::named(rltk::ROYALBLUE),
bg: RGB::named(rltk::BLACK), bg: RGB::named(rltk::BLACK),
render_order: 2, render_order: 2,
@ -334,7 +343,7 @@ fn cursed_magic_map_scroll(ecs: &mut World, x: i32, y: i32) {
ecs.create_entity() ecs.create_entity()
.with(Position { x, y }) .with(Position { x, y })
.with(Renderable { .with(Renderable {
glyph: rltk::to_cp437(')'), glyph: rltk::to_cp437('?'),
fg: RGB::named(rltk::ROYALBLUE), fg: RGB::named(rltk::ROYALBLUE),
bg: RGB::named(rltk::BLACK), bg: RGB::named(rltk::BLACK),
render_order: 2, render_order: 2,
@ -354,7 +363,7 @@ fn dagger(ecs: &mut World, x: i32, y: i32) {
ecs.create_entity() ecs.create_entity()
.with(Position { x, y }) .with(Position { x, y })
.with(Renderable { .with(Renderable {
glyph: rltk::to_cp437('/'), glyph: rltk::to_cp437(')'),
fg: RGB::named(rltk::GREY), fg: RGB::named(rltk::GREY),
bg: RGB::named(rltk::BLACK), bg: RGB::named(rltk::BLACK),
render_order: 2, render_order: 2,
@ -370,8 +379,8 @@ fn shortsword(ecs: &mut World, x: i32, y: i32) {
ecs.create_entity() ecs.create_entity()
.with(Position { x, y }) .with(Position { x, y })
.with(Renderable { .with(Renderable {
glyph: rltk::to_cp437('/'), glyph: rltk::to_cp437(')'),
fg: RGB::named(rltk::GREY), fg: RGB::named(rltk::LIGHTGREY),
bg: RGB::named(rltk::BLACK), bg: RGB::named(rltk::BLACK),
render_order: 2, render_order: 2,
}) })
@ -387,7 +396,7 @@ fn buckler(ecs: &mut World, x: i32, y: i32) {
ecs.create_entity() ecs.create_entity()
.with(Position { x, y }) .with(Position { x, y })
.with(Renderable { .with(Renderable {
glyph: rltk::to_cp437('('), glyph: rltk::to_cp437('['),
fg: RGB::named(rltk::GREY), fg: RGB::named(rltk::GREY),
bg: RGB::named(rltk::BLACK), bg: RGB::named(rltk::BLACK),
render_order: 2, render_order: 2,
@ -404,8 +413,8 @@ fn shield(ecs: &mut World, x: i32, y: i32) {
ecs.create_entity() ecs.create_entity()
.with(Position { x, y }) .with(Position { x, y })
.with(Renderable { .with(Renderable {
glyph: rltk::to_cp437('('), glyph: rltk::to_cp437('['),
fg: RGB::named(rltk::GREY), fg: RGB::named(rltk::LIGHTGREY),
bg: RGB::named(rltk::BLACK), bg: RGB::named(rltk::BLACK),
render_order: 2, render_order: 2,
}) })
@ -417,3 +426,22 @@ fn shield(ecs: &mut World, x: i32, y: i32) {
.marked::<SimpleMarker<SerializeMe>>() .marked::<SimpleMarker<SerializeMe>>()
.build(); .build();
} }
// FOOD
fn rations(ecs: &mut World, x: i32, y: i32) {
ecs.create_entity()
.with(Position { x, y })
.with(Renderable {
glyph: rltk::to_cp437('%'),
fg: RGB::named(rltk::LIGHT_SALMON),
bg: RGB::named(rltk::BLACK),
render_order: 2,
})
.with(Name { name: "rations".to_string() })
.with(Item {})
.with(ProvidesNutrition {})
.with(Consumable {})
.marked::<SimpleMarker<SerializeMe>>()
.build();
}