diff --git a/CHANGELOG.md b/CHANGELOG.md index b5ab250558..38dad4e0ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,7 +31,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Basic NPC interaction - Lights in dungeons - Trading system (bound to the `R` key by default, currently only works with players) +- Support for dual wielding (not accessible as animations still needed) - Support for modular weapons. +- Saturation buff (healing from food) now queues ### Changed diff --git a/common/src/comp/buff.rs b/common/src/comp/buff.rs index 8059930b2b..8aab7fcd43 100644 --- a/common/src/comp/buff.rs +++ b/common/src/comp/buff.rs @@ -29,19 +29,22 @@ pub enum BuffKind { } impl BuffKind { - // Checks if buff is buff or debuff + /// Checks if buff is buff or debuff pub fn is_buff(self) -> bool { match self { - BuffKind::Regeneration { .. } => true, - BuffKind::Saturation { .. } => true, - BuffKind::Bleeding { .. } => false, - BuffKind::Cursed { .. } => false, - BuffKind::Potion { .. } => true, - BuffKind::CampfireHeal { .. } => true, - BuffKind::IncreaseMaxEnergy { .. } => true, - BuffKind::IncreaseMaxHealth { .. } => true, + BuffKind::Regeneration => true, + BuffKind::Saturation => true, + BuffKind::Bleeding => false, + BuffKind::Cursed => false, + BuffKind::Potion => true, + BuffKind::CampfireHeal => true, + BuffKind::IncreaseMaxEnergy => true, + BuffKind::IncreaseMaxHealth => true, } } + + /// Checks if buff should queue + pub fn queues(self) -> bool { matches!(self, BuffKind::Saturation) } } // Struct used to store data relevant to a buff @@ -321,7 +324,6 @@ impl Buffs { // Gets most powerful buff of a given kind // pub fn get_active_kind(&self, kind: BuffKind) -> Buff - pub fn remove(&mut self, buff_id: BuffId) { let kind = self.buffs.remove(&buff_id).unwrap().kind; self.kinds @@ -329,6 +331,12 @@ impl Buffs { .map(|ids| ids.retain(|id| *id != buff_id)); self.sort_kind(kind); } + + /// Returns an immutable reference to the buff kinds on an entity, and a + /// mutable reference to the buffs + pub fn parts(&mut self) -> (&HashMap>, &mut HashMap) { + (&self.kinds, &mut self.buffs) + } } pub type BuffId = u64; diff --git a/common/sys/src/buff.rs b/common/sys/src/buff.rs index b7a12fbfef..c7ec01b504 100644 --- a/common/sys/src/buff.rs +++ b/common/sys/src/buff.rs @@ -1,6 +1,6 @@ use common::{ comp::{ - BuffCategory, BuffChange, BuffEffect, BuffId, BuffSource, Buffs, Energy, Health, + Buff, BuffCategory, BuffChange, BuffEffect, BuffId, BuffSource, Buffs, Energy, Health, HealthChange, HealthSource, Inventory, ModifierKind, }, event::{EventBus, ServerEvent}, @@ -35,22 +35,24 @@ impl<'a> System<'a> for Sys { for (entity, mut buff_comp, mut health, mut energy) in (&entities, &mut buffs, &mut healths, &mut energies).join() { + let (buff_comp_kinds, buff_comp_buffs) = buff_comp.parts(); let mut expired_buffs = Vec::::new(); - for (id, buff) in buff_comp.buffs.iter_mut() { - // Tick the buff and subtract delta from it - if let Some(remaining_time) = &mut buff.time { - if let Some(new_duration) = - remaining_time.checked_sub(Duration::from_secs_f32(dt.0)) + // For each buff kind present on entity, if the buff kind queues, only ticks + // duration of strongest buff of that kind, else it ticks durations of all buffs + // of that kind. Any buffs whose durations expire are marked expired. + for (kind, ids) in buff_comp_kinds.iter() { + if kind.queues() { + if let Some((Some(buff), id)) = + ids.get(0).map(|id| (buff_comp_buffs.get_mut(id), id)) { - // 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); + tick_buff(*id, buff, dt.0, |id| expired_buffs.push(id)); + } + } else { + for (id, buff) in buff_comp_buffs + .iter_mut() + .filter(|(i, _)| ids.iter().any(|id| id == *i)) + { + tick_buff(*id, buff, dt.0, |id| expired_buffs.push(id)); } } } @@ -172,3 +174,19 @@ impl<'a> System<'a> for Sys { energies.set_event_emission(true); } } + +fn tick_buff(id: u64, buff: &mut Buff, dt: f32, mut expire_buff: impl FnMut(u64)) { + if let Some(remaining_time) = &mut buff.time { + if let Some(new_duration) = remaining_time.checked_sub(Duration::from_secs_f32(dt)) { + // 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. + expire_buff(id); + } + } +}