diff --git a/src/ai/regen_system.rs b/src/ai/regen_system.rs index c831808..67d12f0 100644 --- a/src/ai/regen_system.rs +++ b/src/ai/regen_system.rs @@ -1,4 +1,6 @@ -use crate::{gamelog, Clock, Player, Pools, Position, TakingTurn}; +use crate::{ + gamelog, gui::Class, Attributes, Clock, HasClass, Player, Pools, Position, RandomNumberGenerator, TakingTurn, +}; use specs::prelude::*; pub struct RegenSystem {} @@ -6,6 +8,12 @@ pub struct RegenSystem {} const MONSTER_HP_REGEN_TURN: i32 = 20; const MONSTER_HP_REGEN_PER_TICK: i32 = 1; +const WIZARD_MP_REGEN_MOD: i32 = 3; +const NONWIZARD_MP_REGEN_MOD: i32 = 4; +const MP_REGEN_BASE: i32 = 38; +const MP_REGEN_DIVISOR: i32 = 6; +const MIN_MP_REGEN_PER_TURN: i32 = 1; + impl<'a> System<'a> for RegenSystem { #[allow(clippy::type_complexity)] type SystemData = ( @@ -15,10 +23,13 @@ impl<'a> System<'a> for RegenSystem { WriteStorage<'a, Pools>, ReadStorage<'a, TakingTurn>, ReadStorage<'a, Player>, + ReadStorage<'a, HasClass>, + ReadStorage<'a, Attributes>, + WriteExpect<'a, RandomNumberGenerator>, ); fn run(&mut self, data: Self::SystemData) { - let (clock, entities, positions, mut pools, turns, player) = data; + let (clock, entities, positions, mut pools, turns, player, classes, attributes, mut rng) = data; let mut clock_turn = false; for (_e, _c, _t) in (&entities, &clock, &turns).join() { clock_turn = true; @@ -26,18 +37,30 @@ impl<'a> System<'a> for RegenSystem { if !clock_turn { return; } + // Monster HP regen let current_turn = gamelog::get_event_count("turns"); if current_turn % MONSTER_HP_REGEN_TURN == 0 { for (_e, _p, pool, _player) in (&entities, &positions, &mut pools, !&player).join() { try_hp_regen_tick(pool, MONSTER_HP_REGEN_PER_TICK); } } + // Player HP regen let level = gamelog::get_event_count("player_level"); if current_turn % get_player_hp_regen_turn(level) == 0 { for (_e, _p, pool, _player) in (&entities, &positions, &mut pools, &player).join() { try_hp_regen_tick(pool, get_player_hp_regen_per_tick(level)); } } + // Both MP regen + for (e, _p, pool) in (&entities, &positions, &mut pools).join() { + let is_wizard = if let Some(class) = classes.get(e) { class.name == Class::Wizard } else { false }; + let numerator = if is_wizard { WIZARD_MP_REGEN_MOD } else { NONWIZARD_MP_REGEN_MOD }; + let multiplier: f32 = numerator as f32 / MP_REGEN_DIVISOR as f32; + let mp_regen_tick = ((MP_REGEN_BASE - pool.level) as f32 * multiplier) as i32; + if current_turn % mp_regen_tick == 0 { + try_mana_regen_tick(pool, rng.roll_dice(1, get_mana_regen_per_tick(e, &attributes))); + } + } } } @@ -60,3 +83,15 @@ fn get_player_hp_regen_per_tick(level: i32) -> i32 { fn try_hp_regen_tick(pool: &mut Pools, amount: i32) { pool.hit_points.current = i32::min(pool.hit_points.current + amount, pool.hit_points.max); } + +fn get_mana_regen_per_tick(e: Entity, attributes: &ReadStorage) -> i32 { + return if let Some(attributes) = attributes.get(e) { + ((attributes.intelligence.bonus + attributes.wisdom.bonus) / 2) + MIN_MP_REGEN_PER_TURN + } else { + MIN_MP_REGEN_PER_TURN + }; +} + +fn try_mana_regen_tick(pool: &mut Pools, amount: i32) { + pool.mana.current = i32::min(pool.mana.current + amount, pool.mana.max); +} diff --git a/src/components.rs b/src/components.rs index d608adb..88cbb33 100644 --- a/src/components.rs +++ b/src/components.rs @@ -190,6 +190,17 @@ pub struct Skills { pub skills: HashMap, } +#[derive(Debug, Serialize, Deserialize, Clone)] +pub struct KnownSpell { + pub display_name: String, + pub mana_cost: i32, +} + +#[derive(Component, Debug, Serialize, Deserialize, Clone)] +pub struct KnownSpells { + pub spells: Vec, +} + #[derive(Component, Debug, Serialize, Deserialize, Clone)] pub struct Attributes { pub strength: Attribute, diff --git a/src/gui/character_creation.rs b/src/gui/character_creation.rs index 91773ff..7661c10 100644 --- a/src/gui/character_creation.rs +++ b/src/gui/character_creation.rs @@ -284,8 +284,8 @@ pub fn setup_player_class(ecs: &mut World, class: Class, ancestry: Ancestry) { .insert( player, Pools { - hit_points: Pool { current: 10 + attr_bonus(con), max: 10 + attr_bonus(con) }, - mana: Pool { current: 2 + attr_bonus(int), max: 2 + attr_bonus(int) }, + hit_points: Pool { current: 8 + attr_bonus(con), max: 8 + attr_bonus(con) }, + mana: Pool { current: 1 + attr_bonus(int), max: 1 + attr_bonus(int) }, xp: 0, level: 1, bac: 10, diff --git a/src/main.rs b/src/main.rs index cf3b7ef..435d4bc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -659,6 +659,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/saveload_system.rs b/src/saveload_system.rs index 08aed29..1738dc8 100644 --- a/src/saveload_system.rs +++ b/src/saveload_system.rs @@ -82,6 +82,7 @@ pub fn save_game(ecs: &mut World) { InBackpack, InflictsDamage, Item, + KnownSpells, LootTable, MagicItem, MagicMapper, @@ -209,6 +210,7 @@ pub fn load_game(ecs: &mut World) { InBackpack, InflictsDamage, Item, + KnownSpells, LootTable, MagicItem, MagicMapper, diff --git a/src/spawner.rs b/src/spawner.rs index 39c35ae..a5a8097 100644 --- a/src/spawner.rs +++ b/src/spawner.rs @@ -1,7 +1,9 @@ use super::{ - ai::NORMAL_SPEED, random_table::RandomTable, raws, Clock, Energy, EquipmentChanged, Faction, HungerClock, - HungerState, Map, Mind, Name, Player, Position, Rect, Renderable, SerializeMe, Skill, Skills, TileType, Viewshed, + ai::NORMAL_SPEED, random_table::RandomTable, raws, Attribute, Attributes, Clock, Energy, EquipmentChanged, Faction, + HungerClock, HungerState, Map, Mind, Name, Player, Pool, Pools, Position, Rect, Renderable, SerializeMe, Skill, + Skills, TileType, Viewshed, }; +use crate::gamesystem::*; use rltk::{RandomNumberGenerator, RGB}; use specs::prelude::*; use specs::saveload::{MarkedBuilder, SimpleMarker}; @@ -13,7 +15,7 @@ pub fn player(ecs: &mut World, player_x: i32, player_y: i32) -> Entity { skills.skills.insert(Skill::Melee, 0); skills.skills.insert(Skill::Defence, 0); skills.skills.insert(Skill::Magic, 0); - + let (int, con) = (10, 10); // We only create the player once, so create the Clock here for counting turns too. ecs.create_entity().with(Clock {}).with(Energy { current: 0, speed: NORMAL_SPEED }).build(); let player = ecs @@ -31,7 +33,8 @@ pub fn player(ecs: &mut World, player_x: i32, player_y: i32) -> Entity { .with(Viewshed { visible_tiles: Vec::new(), range: 12, dirty: true }) .with(Name { name: "you".to_string(), plural: "you".to_string() }) .with(HungerClock { state: HungerState::Satiated, duration: 1200 }) - /*.with(Attributes { + .with(Attributes { + // These are overwritten with chargen later -- placeholders. strength: Attribute { base: 10, modifiers: 0, bonus: 0 }, dexterity: Attribute { base: 10, modifiers: 0, bonus: 0 }, constitution: Attribute { base: 10, modifiers: 0, bonus: 0 }, @@ -40,14 +43,15 @@ pub fn player(ecs: &mut World, player_x: i32, player_y: i32) -> Entity { charisma: Attribute { base: 10, modifiers: 0, bonus: 0 }, }) .with(Pools { - hit_points: Pool { current: 10 + attr_bonus(con), max: 10 + attr_bonus(con) }, - mana: Pool { current: 2 + attr_bonus(int), max: 2 + attr_bonus(int) }, + // These are overwritten with chargen later -- placeholders. + hit_points: Pool { current: 8 + attr_bonus(con), max: 8 + attr_bonus(con) }, + mana: Pool { current: 1 + attr_bonus(int), max: 1 + attr_bonus(int) }, xp: 0, level: 1, bac: 10, weight: 0.0, god: false, - })*/ + }) .with(EquipmentChanged {}) .with(skills) .with(Energy { current: 0, speed: NORMAL_SPEED })