add maxhealthmodifier

oops variable
This commit is contained in:
Adam Whitehurst 2020-10-24 16:07:38 -07:00 committed by Sam
parent 8aad7adab8
commit f759895d63
6 changed files with 76 additions and 52 deletions

View File

@ -49,13 +49,19 @@ pub enum BuffCategory {
PersistOnDeath,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum ModifierKind {
Additive,
Multiplicative,
}
/// Data indicating and configuring behaviour of a de/buff.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum BuffEffect {
/// Periodically damages or heals entity
HealthChangeOverTime { rate: f32, accumulated: f32 },
/// Changes name on_add/on_remove
NameChange { prefix: String },
/// Changes maximum health by a certain amount
MaxHealthModifier { value: f32, kind: ModifierKind },
}
/// Actual de/buff.
@ -125,8 +131,9 @@ impl Buff {
data.duration,
),
BuffKind::Cursed => (
vec![BuffEffect::NameChange {
prefix: String::from("Cursed "),
vec![BuffEffect::MaxHealthModifier {
value: -100.,
kind: ModifierKind::Additive,
}],
data.duration,
),

View File

@ -34,6 +34,7 @@ pub use body::{
};
pub use buff::{
Buff, BuffCategory, BuffChange, BuffData, BuffEffect, BuffId, BuffKind, BuffSource, Buffs,
ModifierKind,
};
pub use character_state::{Attacking, CharacterState, StateUpdate};
pub use chat::{

View File

@ -8,6 +8,7 @@ use specs::{Component, FlaggedStorage};
use specs_idvs::IdvStorage;
use std::{error::Error, fmt};
/// Specifies what and how much changed current health
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)]
pub struct HealthChange {
pub amount: i32,
@ -33,6 +34,7 @@ pub enum HealthSource {
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
pub struct Health {
base_max: u32,
current: u32,
maximum: u32,
pub last_change: (f64, HealthChange),
@ -69,10 +71,18 @@ impl Health {
}
// This is private because max hp is based on the level
fn set_maximum(&mut self, amount: u32) {
pub fn set_maximum(&mut self, amount: u32) {
self.maximum = amount;
self.current = self.current.min(self.maximum);
}
// This is private because max hp is based on the level
fn set_base_max(&mut self, amount: u32) {
self.base_max = amount;
self.current = self.current.min(self.maximum);
}
pub fn reset_max(&mut self) { self.maximum = self.base_max; }
}
#[derive(Debug)]
pub enum StatChangeError {
@ -149,6 +159,8 @@ impl Stats {
// TODO: Delete this once stat points will be a thing
pub fn update_max_hp(&mut self, body: Body) {
self.health
.set_base_max(body.base_health() + body.base_health_increase() * self.level.amount);
self.health
.set_maximum(body.base_health() + body.base_health_increase() * self.level.amount);
}
@ -180,6 +192,7 @@ impl Stats {
health: Health {
current: 0,
maximum: 0,
base_max: 0,
last_change: (0.0, HealthChange {
amount: 0,
cause: HealthSource::Revive,
@ -199,6 +212,7 @@ impl Stats {
};
stats.update_max_hp(body);
stats
.health
.set_to(stats.health.maximum(), HealthSource::Revive);
@ -214,6 +228,7 @@ impl Stats {
health: Health {
current: 0,
maximum: 0,
base_max: 0,
last_change: (0.0, HealthChange {
amount: 0,
cause: HealthSource::Revive,

View File

@ -1,7 +1,7 @@
use crate::{
comp::{
BuffCategory, BuffChange, BuffEffect, BuffId, BuffSource, Buffs, HealthChange,
HealthSource, Stats,
HealthSource, ModifierKind, Stats,
},
event::{EventBus, ServerEvent},
state::DeltaTime,
@ -17,15 +17,15 @@ impl<'a> System<'a> for Sys {
Read<'a, DeltaTime>,
Read<'a, EventBus<ServerEvent>>,
ReadStorage<'a, Uid>,
ReadStorage<'a, Stats>,
WriteStorage<'a, Stats>,
WriteStorage<'a, Buffs>,
);
fn run(&mut self, (dt, server_bus, uids, stats, mut buffs): Self::SystemData) {
fn run(&mut self, (dt, server_bus, uids, mut stats, mut buffs): Self::SystemData) {
let mut server_emitter = server_bus.emitter();
// Set to false to avoid spamming server
buffs.set_event_emission(false);
for (buff_comp, uid, stat) in (&mut buffs, &uids, &stats).join() {
// buffs.set_event_emission(false);
for (buff_comp, uid, stat) in (&mut buffs, &uids, &mut stats).join() {
let mut expired_buffs = Vec::<BuffId>::new();
for (id, buff) in buff_comp.buffs.iter_mut() {
// Tick the buff and subtract delta from it
@ -36,6 +36,9 @@ impl<'a> System<'a> for Sys {
// The buff still continues.
*remaining_time = new_duration;
} else {
// checked_sub returns None when remaining time
// went below 0, so set to 0
*remaining_time = Duration::default();
// The buff has expired.
// Remove it.
expired_buffs.push(*id);
@ -43,14 +46,20 @@ impl<'a> System<'a> for Sys {
}
}
// Call to reset stats to base values
stat.health.reset_max();
// Iterator over the lists of buffs by kind
for buff_ids in buff_comp.kinds.values() {
// Get the strongest of this buff kind
if let Some(buff) = buff_comp.buffs.get_mut(&buff_ids[0]) {
// Get buff owner
// Get buff owner?
let buff_owner = if let BuffSource::Character { by: owner } = buff.source {
Some(owner)
} else {
None
};
// Now, execute the buff, based on it's delta
for effect in &mut buff.effects {
match effect {
@ -77,7 +86,18 @@ impl<'a> System<'a> for Sys {
*accumulated = 0.0;
};
},
BuffEffect::NameChange { .. } => {},
BuffEffect::MaxHealthModifier { value, kind } => match kind {
ModifierKind::Multiplicative => {
stat.health.set_maximum(
(stat.health.maximum() as f32 * *value) as u32,
);
},
ModifierKind::Additive => {
stat.health.set_maximum(
(stat.health.maximum() as f32 + *value) as u32,
);
},
},
};
}
}
@ -103,6 +123,6 @@ impl<'a> System<'a> for Sys {
});
}
}
buffs.set_event_emission(true);
// buffs.set_event_emission(true);
}
}

View File

@ -152,19 +152,34 @@ impl<'a> System<'a> for Sys {
cause,
},
});
use buff::*;
server_emitter.emit(ServerEvent::Buff {
uid: *uid_b,
buff_change: BuffChange::Add(Buff::new(
BuffKind::Cursed,
BuffData {
strength: attack.base_damage as f32 / 10.0,
duration: Some(Duration::from_secs(10)),
},
vec![BuffCategory::Physical],
BuffSource::Character { by: *uid },
)),
});
// Apply bleeding buff on melee hits with 10% chance
// TODO: Don't have buff uniformly applied on all melee attacks
if thread_rng().gen::<f32>() < 0.1 {
server_emitter.emit(ServerEvent::Buff {
uid: *uid_b,
buff_change: buff::BuffChange::Add(buff::Buff::new(
buff::BuffKind::Bleeding,
buff::BuffData {
buff_change: BuffChange::Add(Buff::new(
BuffKind::Bleeding,
BuffData {
strength: attack.base_damage as f32 / 10.0,
duration: Some(Duration::from_secs(10)),
},
vec![buff::BuffCategory::Physical],
buff::BuffSource::Character { by: *uid },
vec![BuffCategory::Physical],
BuffSource::Character { by: *uid },
)),
});
}

View File

@ -704,7 +704,6 @@ pub fn handle_buff(server: &mut Server, uid: Uid, buff_change: buff::BuffChange)
let mut buffs_all = ecs.write_storage::<comp::Buffs>();
if let Some(entity) = ecs.entity_from_uid(uid.into()) {
if let Some(buffs) = buffs_all.get_mut(entity) {
let mut stats = ecs.write_storage::<comp::Stats>();
use buff::BuffChange;
match buff_change {
BuffChange::Add(new_buff) => {
@ -763,36 +762,3 @@ pub fn handle_buff(server: &mut Server, uid: Uid, buff_change: buff::BuffChange)
}
}
}
fn add_buff_effects(buff: buff::Buff, mut stats: Option<&mut Stats>) {
for effect in &buff.effects {
use buff::BuffEffect;
match effect {
// Only add an effect here if it is immediate and is not continuous
BuffEffect::NameChange { prefix } => {
if let Some(ref mut stats) = stats {
let mut pref = String::from(prefix);
pref.push_str(&stats.name);
stats.name = pref;
}
},
BuffEffect::HealthChangeOverTime { .. } => {},
}
}
}
fn remove_buff_effects(buff: buff::Buff, mut stats: Option<&mut Stats>) {
for effect in &buff.effects {
#[allow(clippy::single_match)] // Remove clippy when there are more buff effects here
match effect {
// Only remove an effect here if its effect was not continuously
// applied
buff::BuffEffect::NameChange { prefix } => {
if let Some(ref mut stats) = stats {
stats.name = stats.name.replacen(prefix, "", 1);
}
},
_ => {},
}
}
}