mp regeneration

using nethack calcs as a placeholder for now
This commit is contained in:
Llywelwyn 2023-08-22 19:07:50 +01:00
parent c46e302274
commit 4118783597
6 changed files with 64 additions and 11 deletions

View file

@ -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::*; use specs::prelude::*;
pub struct RegenSystem {} pub struct RegenSystem {}
@ -6,6 +8,12 @@ pub struct RegenSystem {}
const MONSTER_HP_REGEN_TURN: i32 = 20; const MONSTER_HP_REGEN_TURN: i32 = 20;
const MONSTER_HP_REGEN_PER_TICK: i32 = 1; 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 { impl<'a> System<'a> for RegenSystem {
#[allow(clippy::type_complexity)] #[allow(clippy::type_complexity)]
type SystemData = ( type SystemData = (
@ -15,10 +23,13 @@ impl<'a> System<'a> for RegenSystem {
WriteStorage<'a, Pools>, WriteStorage<'a, Pools>,
ReadStorage<'a, TakingTurn>, ReadStorage<'a, TakingTurn>,
ReadStorage<'a, Player>, ReadStorage<'a, Player>,
ReadStorage<'a, HasClass>,
ReadStorage<'a, Attributes>,
WriteExpect<'a, RandomNumberGenerator>,
); );
fn run(&mut self, data: Self::SystemData) { 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; let mut clock_turn = false;
for (_e, _c, _t) in (&entities, &clock, &turns).join() { for (_e, _c, _t) in (&entities, &clock, &turns).join() {
clock_turn = true; clock_turn = true;
@ -26,18 +37,30 @@ impl<'a> System<'a> for RegenSystem {
if !clock_turn { if !clock_turn {
return; return;
} }
// Monster HP regen
let current_turn = gamelog::get_event_count("turns"); let current_turn = gamelog::get_event_count("turns");
if current_turn % MONSTER_HP_REGEN_TURN == 0 { if current_turn % MONSTER_HP_REGEN_TURN == 0 {
for (_e, _p, pool, _player) in (&entities, &positions, &mut pools, !&player).join() { for (_e, _p, pool, _player) in (&entities, &positions, &mut pools, !&player).join() {
try_hp_regen_tick(pool, MONSTER_HP_REGEN_PER_TICK); try_hp_regen_tick(pool, MONSTER_HP_REGEN_PER_TICK);
} }
} }
// Player HP regen
let level = gamelog::get_event_count("player_level"); let level = gamelog::get_event_count("player_level");
if current_turn % get_player_hp_regen_turn(level) == 0 { if current_turn % get_player_hp_regen_turn(level) == 0 {
for (_e, _p, pool, _player) in (&entities, &positions, &mut pools, &player).join() { for (_e, _p, pool, _player) in (&entities, &positions, &mut pools, &player).join() {
try_hp_regen_tick(pool, get_player_hp_regen_per_tick(level)); 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) { 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); 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<Attributes>) -> 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);
}

View file

@ -190,6 +190,17 @@ pub struct Skills {
pub skills: HashMap<Skill, i32>, pub skills: HashMap<Skill, i32>,
} }
#[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<KnownSpell>,
}
#[derive(Component, Debug, Serialize, Deserialize, Clone)] #[derive(Component, Debug, Serialize, Deserialize, Clone)]
pub struct Attributes { pub struct Attributes {
pub strength: Attribute, pub strength: Attribute,

View file

@ -284,8 +284,8 @@ pub fn setup_player_class(ecs: &mut World, class: Class, ancestry: Ancestry) {
.insert( .insert(
player, player,
Pools { Pools {
hit_points: Pool { current: 10 + attr_bonus(con), max: 10 + attr_bonus(con) }, hit_points: Pool { current: 8 + attr_bonus(con), max: 8 + attr_bonus(con) },
mana: Pool { current: 2 + attr_bonus(int), max: 2 + attr_bonus(int) }, mana: Pool { current: 1 + attr_bonus(int), max: 1 + attr_bonus(int) },
xp: 0, xp: 0,
level: 1, level: 1,
bac: 10, bac: 10,

View file

@ -659,6 +659,7 @@ fn main() -> rltk::BError {
gs.ecs.register::<MultiAttack>(); gs.ecs.register::<MultiAttack>();
gs.ecs.register::<ProvidesRemoveCurse>(); gs.ecs.register::<ProvidesRemoveCurse>();
gs.ecs.register::<ProvidesIdentify>(); gs.ecs.register::<ProvidesIdentify>();
gs.ecs.register::<KnownSpells>();
gs.ecs.register::<ParticleLifetime>(); gs.ecs.register::<ParticleLifetime>();
gs.ecs.register::<SpawnParticleSimple>(); gs.ecs.register::<SpawnParticleSimple>();
gs.ecs.register::<SpawnParticleBurst>(); gs.ecs.register::<SpawnParticleBurst>();

View file

@ -82,6 +82,7 @@ pub fn save_game(ecs: &mut World) {
InBackpack, InBackpack,
InflictsDamage, InflictsDamage,
Item, Item,
KnownSpells,
LootTable, LootTable,
MagicItem, MagicItem,
MagicMapper, MagicMapper,
@ -209,6 +210,7 @@ pub fn load_game(ecs: &mut World) {
InBackpack, InBackpack,
InflictsDamage, InflictsDamage,
Item, Item,
KnownSpells,
LootTable, LootTable,
MagicItem, MagicItem,
MagicMapper, MagicMapper,

View file

@ -1,7 +1,9 @@
use super::{ use super::{
ai::NORMAL_SPEED, random_table::RandomTable, raws, Clock, Energy, EquipmentChanged, Faction, HungerClock, ai::NORMAL_SPEED, random_table::RandomTable, raws, Attribute, Attributes, Clock, Energy, EquipmentChanged, Faction,
HungerState, Map, Mind, Name, Player, Position, Rect, Renderable, SerializeMe, Skill, Skills, TileType, Viewshed, HungerClock, HungerState, Map, Mind, Name, Player, Pool, Pools, Position, Rect, Renderable, SerializeMe, Skill,
Skills, TileType, Viewshed,
}; };
use crate::gamesystem::*;
use rltk::{RandomNumberGenerator, RGB}; use rltk::{RandomNumberGenerator, RGB};
use specs::prelude::*; use specs::prelude::*;
use specs::saveload::{MarkedBuilder, SimpleMarker}; 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::Melee, 0);
skills.skills.insert(Skill::Defence, 0); skills.skills.insert(Skill::Defence, 0);
skills.skills.insert(Skill::Magic, 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. // 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(); ecs.create_entity().with(Clock {}).with(Energy { current: 0, speed: NORMAL_SPEED }).build();
let player = ecs 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(Viewshed { visible_tiles: Vec::new(), range: 12, dirty: true })
.with(Name { name: "you".to_string(), plural: "you".to_string() }) .with(Name { name: "you".to_string(), plural: "you".to_string() })
.with(HungerClock { state: HungerState::Satiated, duration: 1200 }) .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 }, strength: Attribute { base: 10, modifiers: 0, bonus: 0 },
dexterity: Attribute { base: 10, modifiers: 0, bonus: 0 }, dexterity: Attribute { base: 10, modifiers: 0, bonus: 0 },
constitution: 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 }, charisma: Attribute { base: 10, modifiers: 0, bonus: 0 },
}) })
.with(Pools { .with(Pools {
hit_points: Pool { current: 10 + attr_bonus(con), max: 10 + attr_bonus(con) }, // These are overwritten with chargen later -- placeholders.
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, xp: 0,
level: 1, level: 1,
bac: 10, bac: 10,
weight: 0.0, weight: 0.0,
god: false, god: false,
})*/ })
.with(EquipmentChanged {}) .with(EquipmentChanged {})
.with(skills) .with(skills)
.with(Energy { current: 0, speed: NORMAL_SPEED }) .with(Energy { current: 0, speed: NORMAL_SPEED })