diff --git a/Cargo.lock b/Cargo.lock index 3eb2a911bd..156f052930 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2037,6 +2037,15 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "inline_tweak" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7033e97b20277cc0d043226d1940fa7719ff08d2305d1fc7421e53066d00eb4b" +dependencies = [ + "lazy_static", +] + [[package]] name = "inotify" version = "0.8.3" @@ -4957,6 +4966,7 @@ dependencies = [ "guillotiere", "hashbrown 0.7.2", "image", + "inline_tweak", "itertools", "native-dialog", "num 0.2.1", @@ -4988,6 +4998,7 @@ name = "veloren-voxygen-anim" version = "0.7.0" dependencies = [ "find_folder", + "inline_tweak", "lazy_static", "libloading 0.6.3", "notify", diff --git a/common/src/comp/buff.rs b/common/src/comp/buff.rs index c5dabe6c48..9f661199b1 100644 --- a/common/src/comp/buff.rs +++ b/common/src/comp/buff.rs @@ -2,37 +2,46 @@ use crate::sync::Uid; use serde::{Deserialize, Serialize}; use specs::{Component, FlaggedStorage}; use specs_idvs::IdvStorage; -use std::time::Duration; +use std::{collections::HashMap, time::Duration}; /// De/buff Kind. /// This is used to determine what effects a buff will have, as well as /// determine the strength and duration of the buff effects using the internal /// values -#[derive(Clone, Copy, PartialEq, Debug, Serialize, Deserialize)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)] pub enum BuffKind { /// Restores health/time for some period - Regeneration { - strength: f32, - duration: Option, - }, + Regeneration, /// Lowers health over time for some duration - Bleeding { - strength: f32, - duration: Option, - }, + Bleeding, /// Prefixes an entity's name with "Cursed" /// Currently placeholder buff to show other stuff is possible - Cursed { duration: Option }, + Cursed, +} + +impl BuffKind { + // Checks if buff is buff or debuff + pub fn is_buff(self) -> bool { + match self { + BuffKind::Regeneration { .. } => true, + BuffKind::Bleeding { .. } => false, + BuffKind::Cursed { .. } => false, + } + } +} + +// Struct used to store data relevant to a buff +#[derive(Clone, Copy, Debug, Serialize, Deserialize)] +pub struct BuffData { + pub strength: f32, + pub duration: Option, } /// De/buff category ID. /// Similar to `BuffKind`, but to mark a category (for more generic usage, like /// positive/negative buffs). #[derive(Clone, Copy, Eq, PartialEq, Debug, Serialize, Deserialize)] -pub enum BuffCategoryId { - // Buff and debuff get added in builder function based off of the buff kind - Debuff, - Buff, +pub enum BuffCategory { Natural, Physical, Magical, @@ -62,7 +71,8 @@ pub enum BuffEffect { #[derive(Clone, Debug, Serialize, Deserialize)] pub struct Buff { pub kind: BuffKind, - pub cat_ids: Vec, + pub data: BuffData, + pub cat_ids: Vec, pub time: Option, pub effects: Vec, pub source: BuffSource, @@ -70,27 +80,68 @@ pub struct Buff { /// Information about whether buff addition or removal was requested. /// This to implement "on_add" and "on_remove" hooks for constant buffs. -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug)] pub enum BuffChange { /// Adds this buff. Add(Buff), /// Removes all buffs with this ID. RemoveByKind(BuffKind), /// Removes all buffs with this ID, but not debuffs. - RemoveFromClient(BuffKind), + RemoveFromController(BuffKind), /// Removes buffs of these indices (first vec is for active buffs, second is /// for inactive buffs), should only be called when buffs expire - RemoveExpiredByIndex(Vec, Vec), + RemoveById(Vec), /// Removes buffs of these categories (first vec is of categories of which /// all are required, second vec is of categories of which at least one is /// required, third vec is of categories that will not be removed) RemoveByCategory { - required: Vec, - optional: Vec, - blacklisted: Vec, + all_required: Vec, + any_required: Vec, + none_required: Vec, }, } +impl Buff { + /// Builder function for buffs + pub fn new( + kind: BuffKind, + data: BuffData, + cat_ids: Vec, + source: BuffSource, + ) -> Self { + let (effects, time) = match kind { + BuffKind::Bleeding => ( + vec![BuffEffect::HealthChangeOverTime { + rate: -data.strength, + accumulated: 0.0, + }], + data.duration, + ), + BuffKind::Regeneration => ( + vec![BuffEffect::HealthChangeOverTime { + rate: data.strength, + accumulated: 0.0, + }], + data.duration, + ), + BuffKind::Cursed => ( + vec![BuffEffect::NameChange { + prefix: String::from("Cursed "), + }], + data.duration, + ), + }; + Buff { + kind, + data, + cat_ids, + time, + effects, + source, + } + } +} + /// Source of the de/buff #[derive(Clone, Copy, PartialEq, Debug, Serialize, Deserialize)] pub enum BuffSource { @@ -121,65 +172,84 @@ pub enum BuffSource { /// would be probably an undesired effect). #[derive(Clone, Debug, Serialize, Deserialize, Default)] pub struct Buffs { - /// Active de/buffs. - pub active_buffs: Vec, - /// Inactive de/buffs (used so that only 1 buff of a particular type is - /// active at any time) - pub inactive_buffs: Vec, + id_counter: u64, + pub kinds: HashMap>, + pub buffs: HashMap, } -impl Buff { - /// Builder function for buffs - pub fn new(kind: BuffKind, cat_ids: Vec, source: BuffSource) -> Self { - let mut cat_ids = cat_ids; - let (effects, time) = match kind { - BuffKind::Bleeding { strength, duration } => { - cat_ids.push(BuffCategoryId::Debuff); - ( - vec![BuffEffect::HealthChangeOverTime { - rate: -strength, - accumulated: 0.0, - }], - duration, - ) - }, - BuffKind::Regeneration { strength, duration } => { - cat_ids.push(BuffCategoryId::Buff); - ( - vec![BuffEffect::HealthChangeOverTime { - rate: strength, - accumulated: 0.0, - }], - duration, - ) - }, - BuffKind::Cursed { duration } => { - cat_ids.push(BuffCategoryId::Debuff); - ( - vec![BuffEffect::NameChange { - prefix: String::from("Cursed "), - }], - duration, - ) - }, - }; - assert_eq!( - cat_ids - .iter() - .any(|cat| *cat == BuffCategoryId::Buff || *cat == BuffCategoryId::Debuff), - true, - "Buff must have either buff or debuff category." - ); - Buff { - kind, - cat_ids, - time, - effects, - source, +impl Buffs { + fn sort_kind(&mut self, kind: BuffKind) { + if let Some(buff_order) = self.kinds.get_mut(&kind) { + if buff_order.len() == 0 { + self.kinds.remove(&kind); + } else { + let buffs = &self.buffs; + buff_order.sort_by(|a, b| { + buffs[&b] + .data + .strength + .partial_cmp(&buffs[&a].data.strength) + .unwrap() + }); + } } } + + pub fn remove_kind(&mut self, kind: BuffKind) { + if let Some(buff_ids) = self.kinds.get_mut(&kind) { + for id in buff_ids { + self.buffs.remove(id); + } + self.kinds.remove(&kind); + } + self.sort_kind(kind); + } + + pub fn force_insert(&mut self, id: BuffId, buff: Buff) -> BuffId { + let kind = buff.kind; + self.kinds.entry(kind).or_default().push(id); + self.buffs.insert(id, buff); + self.sort_kind(kind); + id + } + + pub fn insert(&mut self, buff: Buff) -> BuffId { + self.id_counter += 1; + self.force_insert(self.id_counter, buff) + } + + // Iterate through buffs of a given kind in effect order (most powerful first) + pub fn iter_kind(&self, kind: BuffKind) -> impl Iterator + '_ { + self.kinds + .get(&kind) + .map(|ids| ids.iter()) + .unwrap_or((&[]).iter()) + .map(move |id| (*id, &self.buffs[id])) + } + + // Iterates through all active buffs (the most powerful buff of each kind) + pub fn iter_active(&self) -> impl Iterator + '_ { + self.kinds + .values() + .map(move |ids| self.buffs.get(&ids[0])) + .filter(|buff| buff.is_some()) + .map(|buff| buff.unwrap()) + } + + // 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 + .get_mut(&kind) + .map(|ids| ids.retain(|id| *id != buff_id)); + self.sort_kind(kind); + } } +pub type BuffId = u64; + impl Component for Buffs { type Storage = FlaggedStorage>; } diff --git a/common/src/comp/mod.rs b/common/src/comp/mod.rs index 4357952d37..73a62193a1 100644 --- a/common/src/comp/mod.rs +++ b/common/src/comp/mod.rs @@ -32,7 +32,9 @@ pub use body::{ biped_large, bird_medium, bird_small, dragon, fish_medium, fish_small, golem, humanoid, object, quadruped_low, quadruped_medium, quadruped_small, theropod, AllBodies, Body, BodyData, }; -pub use buff::{Buff, BuffCategoryId, BuffChange, BuffEffect, BuffKind, BuffSource, Buffs}; +pub use buff::{ + Buff, BuffCategory, BuffChange, BuffData, BuffEffect, BuffId, BuffKind, BuffSource, Buffs, +}; pub use character_state::{Attacking, CharacterState, StateUpdate}; pub use chat::{ ChatMode, ChatMsg, ChatType, Faction, SpeechBubble, SpeechBubbleType, UnresolvedChatMsg, diff --git a/common/src/sys/buff.rs b/common/src/sys/buff.rs index 8d50ce15fb..b5243c0334 100644 --- a/common/src/sys/buff.rs +++ b/common/src/sys/buff.rs @@ -1,7 +1,7 @@ use crate::{ comp::{ - BuffCategoryId, BuffChange, BuffEffect, BuffSource, Buffs, HealthChange, HealthSource, - Stats, + BuffCategory, BuffChange, BuffEffect, BuffId, BuffSource, Buffs, HealthChange, + HealthSource, Stats, }, event::{EventBus, ServerEvent}, state::DeltaTime, @@ -25,108 +25,84 @@ impl<'a> System<'a> for Sys { let mut server_emitter = server_bus.emitter(); // Set to false to avoid spamming server buffs.set_event_emission(false); - for (buff_comp, uid) in (&mut buffs, &uids).join() { - let (mut active_buff_indices_for_removal, mut inactive_buff_indices_for_removal) = - (Vec::::new(), Vec::::new()); - for (i, active_buff) in buff_comp.active_buffs.iter_mut().enumerate() { + for (buff_comp, uid, stat) in (&mut buffs, &uids, &stats).join() { + 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 active_buff.time { - let new_duration = remaining_time.checked_sub(Duration::from_secs_f32(dt.0)); - if new_duration.is_some() { + if let Some(remaining_time) = &mut buff.time { + if let Some(new_duration) = + remaining_time.checked_sub(Duration::from_secs_f32(dt.0)) + { // The buff still continues. - *remaining_time -= Duration::from_secs_f32(dt.0); + *remaining_time = new_duration; } else { // The buff has expired. // Remove it. - active_buff_indices_for_removal.push(i); - active_buff.time = Some(Duration::default()); - }; + expired_buffs.push(*id); + } } } - for (i, inactive_buff) in buff_comp.inactive_buffs.iter_mut().enumerate() { - // Tick the buff and subtract delta from it - if let Some(remaining_time) = &mut inactive_buff.time { - let new_duration = remaining_time.checked_sub(Duration::from_secs_f32(dt.0)); - if new_duration.is_some() { - // The buff still continues. - *remaining_time -= Duration::from_secs_f32(dt.0); + for buff_ids in buff_comp.kinds.values() { + if let Some(buff) = buff_comp.buffs.get_mut(&buff_ids[0]) { + // Get buff owner + let buff_owner = if let BuffSource::Character { by: owner } = buff.source { + Some(owner) } else { - // The buff has expired. - // Remove it. - inactive_buff_indices_for_removal.push(i); - inactive_buff.time = Some(Duration::default()); + None }; + // Now, execute the buff, based on it's delta + for effect in &mut buff.effects { + match effect { + // Only add an effect here if it is continuous or it is not immediate + BuffEffect::HealthChangeOverTime { rate, accumulated } => { + *accumulated += *rate * dt.0; + // Apply damage only once a second (with a minimum of 1 damage), or + // when a buff is removed + if accumulated.abs() > rate.abs().max(10.0) + || buff.time.map_or(false, |dur| dur == Duration::default()) + { + let cause = if *accumulated > 0.0 { + HealthSource::Healing { by: buff_owner } + } else { + HealthSource::Buff { owner: buff_owner } + }; + server_emitter.emit(ServerEvent::Damage { + uid: *uid, + change: HealthChange { + amount: *accumulated as i32, + cause, + }, + }); + *accumulated = 0.0; + }; + }, + BuffEffect::NameChange { .. } => {}, + }; + } } } - if !active_buff_indices_for_removal.is_empty() - || !inactive_buff_indices_for_removal.is_empty() - { + // Remove buffs that expire + if !expired_buffs.is_empty() { server_emitter.emit(ServerEvent::Buff { uid: *uid, - buff_change: BuffChange::RemoveExpiredByIndex( - active_buff_indices_for_removal, - inactive_buff_indices_for_removal, - ), + buff_change: BuffChange::RemoveById(expired_buffs), }); } - } - // Set back to true after timer decrement - buffs.set_event_emission(true); - for (uid, stat, mut buffs) in (&uids, &stats, &mut buffs.restrict_mut()).join() { - let buff_comp = buffs.get_mut_unchecked(); - // Tick all de/buffs on a Buffs component. - for active_buff in buff_comp.active_buffs.iter_mut() { - // Get buff owner - let buff_owner = if let BuffSource::Character { by: owner } = active_buff.source { - Some(owner) - } else { - None - }; - // Now, execute the buff, based on it's delta - for effect in &mut active_buff.effects { - match effect { - // Only add an effect here if it is continuous or it is not immediate - BuffEffect::HealthChangeOverTime { rate, accumulated } => { - *accumulated += *rate * dt.0; - // Apply damage only once a second (with a minimum of 1 damage), or when - // a buff is removed - if accumulated.abs() > rate.abs().max(10.0) - || active_buff - .time - .map_or(false, |dur| dur == Duration::default()) - { - let cause = if *accumulated > 0.0 { - HealthSource::Healing { by: buff_owner } - } else { - HealthSource::Buff { owner: buff_owner } - }; - server_emitter.emit(ServerEvent::Damage { - uid: *uid, - change: HealthChange { - amount: *accumulated as i32, - cause, - }, - }); - *accumulated = 0.0; - }; - }, - BuffEffect::NameChange { .. } => {}, - }; - } - } + // Remove stats that don't persist on death if stat.is_dead { server_emitter.emit(ServerEvent::Buff { uid: *uid, buff_change: BuffChange::RemoveByCategory { - required: vec![], - optional: vec![], - blacklisted: vec![BuffCategoryId::PersistOnDeath], + all_required: vec![], + any_required: vec![], + none_required: vec![BuffCategory::PersistOnDeath], }, }); } } + buffs.set_event_emission(true); } } diff --git a/common/src/sys/combat.rs b/common/src/sys/combat.rs index 95665bf038..ef07258227 100644 --- a/common/src/sys/combat.rs +++ b/common/src/sys/combat.rs @@ -158,11 +158,12 @@ impl<'a> System<'a> for Sys { server_emitter.emit(ServerEvent::Buff { uid: *uid_b, buff_change: buff::BuffChange::Add(buff::Buff::new( - buff::BuffKind::Bleeding { + buff::BuffKind::Bleeding, + buff::BuffData { strength: attack.base_damage as f32 / 10.0, duration: Some(Duration::from_secs(10)), }, - vec![buff::BuffCategoryId::Physical], + vec![buff::BuffCategory::Physical], buff::BuffSource::Character { by: *uid }, )), }); diff --git a/common/src/sys/controller.rs b/common/src/sys/controller.rs index 68d40b6e41..df9a404d36 100644 --- a/common/src/sys/controller.rs +++ b/common/src/sys/controller.rs @@ -86,7 +86,7 @@ impl<'a> System<'a> for Sys { ControlEvent::RemoveBuff(buff_id) => { server_emitter.emit(ServerEvent::Buff { uid: *uid, - buff_change: BuffChange::RemoveFromClient(buff_id), + buff_change: BuffChange::RemoveFromController(buff_id), }); }, ControlEvent::Unmount => server_emitter.emit(ServerEvent::Unmount(entity)), diff --git a/server/src/events/entity_manipulation.rs b/server/src/events/entity_manipulation.rs index 9bca72ff09..4e4c2a1ad4 100644 --- a/server/src/events/entity_manipulation.rs +++ b/server/src/events/entity_manipulation.rs @@ -24,7 +24,6 @@ use common::{ use comp::item::Reagent; use rand::prelude::*; use specs::{join::Join, saveload::MarkerAllocator, Entity as EcsEntity, WorldExt}; -use std::mem::discriminant; use tracing::error; use vek::Vec3; @@ -706,105 +705,31 @@ pub fn handle_buff(server: &mut Server, uid: Uid, buff_change: buff::BuffChange) 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::(); - let (mut active_buff_indices_for_removal, mut inactive_buff_indices_for_removal) = - (Vec::new(), Vec::new()); use buff::BuffChange; match buff_change { BuffChange::Add(new_buff) => { - if buffs.active_buffs.is_empty() { - add_buff_effects(new_buff.clone(), stats.get_mut(entity)); - buffs.active_buffs.push(new_buff); - } else { - let mut duplicate_existed = false; - for i in 0..buffs.active_buffs.len() { - let active_buff = &buffs.active_buffs[i].clone(); - // Checks if new buff has the same id as an already active buff. If it - // doesn't, new buff added to active buffs. If it does, compares the new - // buff and the active buff, and decides to either add new buff to - // inactive buffs, or move active buff to - // inactive buffs and add new buff to active - // buffs. - if discriminant(&active_buff.kind) == discriminant(&new_buff.kind) { - duplicate_existed = true; - // Determines if active buff is weaker than newer buff - if determine_replace_active_buff( - active_buff.clone(), - new_buff.clone(), - ) { - active_buff_indices_for_removal.push(i); - add_buff_effects(new_buff.clone(), stats.get_mut(entity)); - buffs.active_buffs.push(new_buff.clone()); - // Sees if weaker active has longer duration than new buff - #[allow(clippy::blocks_in_if_conditions)] - if active_buff.time.map_or(true, |act_dur| { - new_buff.time.map_or(false, |new_dur| act_dur > new_dur) - }) { - buffs.inactive_buffs.push(active_buff.clone()); - } - // Sees if weaker new buff has longer duration - // than active buff - } else if let Some(active_dur) = active_buff.time { - if let Some(new_dur) = new_buff.time { - if new_dur > active_dur { - buffs.inactive_buffs.push(new_buff.clone()); - } - } else { - buffs.inactive_buffs.push(new_buff.clone()); - } - } - break; - } - } - if !duplicate_existed { - add_buff_effects(new_buff.clone(), stats.get_mut(entity)); - buffs.active_buffs.push(new_buff); - } - } + buffs.insert(new_buff); }, - BuffChange::RemoveExpiredByIndex(active_indices, inactive_indices) => { - active_buff_indices_for_removal = active_indices; - inactive_buff_indices_for_removal = inactive_indices; + BuffChange::RemoveById(ids) => { + for id in ids { + buffs.remove(id); + } }, BuffChange::RemoveByKind(kind) => { - for (i, buff) in buffs.active_buffs.iter().enumerate() { - if discriminant(&kind) == discriminant(&buff.kind) { - active_buff_indices_for_removal.push(i); - } - } - for (i, buff) in buffs.inactive_buffs.iter().enumerate() { - if discriminant(&kind) == discriminant(&buff.kind) { - inactive_buff_indices_for_removal.push(i); - } - } + buffs.remove_kind(kind); }, - BuffChange::RemoveFromClient(kind) => { - for (i, buff) in buffs.active_buffs.iter().enumerate() { - if discriminant(&kind) == discriminant(&buff.kind) - && buff - .cat_ids - .iter() - .any(|cat| *cat == buff::BuffCategoryId::Buff) - { - active_buff_indices_for_removal.push(i); - } - } - for (i, buff) in buffs.inactive_buffs.iter().enumerate() { - if discriminant(&kind) == discriminant(&buff.kind) - && buff - .cat_ids - .iter() - .any(|cat| *cat == buff::BuffCategoryId::Buff) - { - inactive_buff_indices_for_removal.push(i); - } + BuffChange::RemoveFromController(kind) => { + if kind.is_buff() { + buffs.remove_kind(kind); } }, BuffChange::RemoveByCategory { - required: all_required, - optional: any_required, - blacklisted: none_required, + all_required, + any_required, + none_required, } => { - for (i, buff) in buffs.active_buffs.iter().enumerate() { + let mut ids_to_remove = Vec::new(); + for (id, buff) in buffs.buffs.iter() { let mut required_met = true; for required in &all_required { if !buff.cat_ids.iter().any(|cat| cat == required) { @@ -814,7 +739,7 @@ pub fn handle_buff(server: &mut Server, uid: Uid, buff_change: buff::BuffChange) } let mut any_met = any_required.is_empty(); for any in &any_required { - if !buff.cat_ids.iter().any(|cat| cat == any) { + if buff.cat_ids.iter().any(|cat| cat == any) { any_met = true; break; } @@ -827,126 +752,18 @@ pub fn handle_buff(server: &mut Server, uid: Uid, buff_change: buff::BuffChange) } } if required_met && any_met && none_met { - active_buff_indices_for_removal.push(i); + ids_to_remove.push(*id); } } - for (i, buff) in buffs.inactive_buffs.iter().enumerate() { - let mut required_met = true; - for required in &all_required { - if !buff.cat_ids.iter().any(|cat| cat == required) { - required_met = false; - break; - } - } - let mut any_met = any_required.is_empty(); - for any in &any_required { - if !buff.cat_ids.iter().any(|cat| cat == any) { - any_met = true; - break; - } - } - if required_met && any_met { - inactive_buff_indices_for_removal.push(i); - } + for id in ids_to_remove { + buffs.remove(id); } }, } - let mut removed_active_buff_kinds = Vec::new(); - while !active_buff_indices_for_removal.is_empty() { - if let Some(i) = active_buff_indices_for_removal.pop() { - let buff = buffs.active_buffs.remove(i); - removed_active_buff_kinds.push(buff.kind); - remove_buff_effects(buff, stats.get_mut(entity)); - } - } - while !inactive_buff_indices_for_removal.is_empty() { - if let Some(i) = inactive_buff_indices_for_removal.pop() { - buffs.inactive_buffs.remove(i); - } - } - // Checks after buffs are removed so that it doesn't grab incorrect - // index - for buff_kind in removed_active_buff_kinds { - // Checks to verify that there are no active buffs with the same id - if buffs - .active_buffs - .iter() - .any(|buff| discriminant(&buff.kind) == discriminant(&buff_kind)) - { - continue; - } - let mut new_active_buff = None::; - let mut replacement_buff_index = 0; - for (i, inactive_buff) in buffs.inactive_buffs.iter().enumerate() { - if discriminant(&buff_kind) == discriminant(&inactive_buff.kind) { - if let Some(ref buff) = new_active_buff { - if determine_replace_active_buff(buff.clone(), inactive_buff.clone()) { - new_active_buff = Some(inactive_buff.clone()); - replacement_buff_index = i; - } - } else { - new_active_buff = Some(inactive_buff.clone()); - replacement_buff_index = i; - } - } - } - if new_active_buff.is_some() { - let buff = buffs.inactive_buffs.remove(replacement_buff_index); - add_buff_effects(buff.clone(), stats.get_mut(entity)); - buffs.active_buffs.push(buff.clone()); - } - } } } } -fn determine_replace_active_buff(active_buff: buff::Buff, new_buff: buff::Buff) -> bool { - use buff::BuffKind; - match new_buff.kind { - BuffKind::Bleeding { - strength: new_strength, - duration: new_duration, - } => { - if let BuffKind::Bleeding { - strength: active_strength, - duration: _, - } = active_buff.kind - { - new_strength > active_strength - || (new_strength >= active_strength - && new_duration.map_or(true, |new_dur| { - active_buff.time.map_or(false, |act_dur| new_dur > act_dur) - })) - } else { - false - } - }, - BuffKind::Regeneration { - strength: new_strength, - duration: new_duration, - } => { - if let BuffKind::Regeneration { - strength: active_strength, - duration: _, - } = active_buff.kind - { - new_strength > active_strength - || (new_strength >= active_strength - && new_duration.map_or(true, |new_dur| { - active_buff.time.map_or(false, |act_dur| new_dur > act_dur) - })) - } else { - false - } - }, - BuffKind::Cursed { - duration: new_duration, - } => new_duration.map_or(true, |new_dur| { - active_buff.time.map_or(false, |act_dur| new_dur > act_dur) - }), - } -} - fn add_buff_effects(buff: buff::Buff, mut stats: Option<&mut Stats>) { for effect in &buff.effects { use buff::BuffEffect; diff --git a/voxygen/Cargo.toml b/voxygen/Cargo.toml index 03d70d004b..439574c43f 100644 --- a/voxygen/Cargo.toml +++ b/voxygen/Cargo.toml @@ -74,6 +74,7 @@ treeculler = "0.1.0" uvth = "3.1.1" # vec_map = { version = "0.8.2" } const-tweaker = {version = "0.3.1", optional = true} +inline_tweak = "1.0.2" itertools = "0.9.0" # Logging diff --git a/voxygen/src/anim/Cargo.toml b/voxygen/src/anim/Cargo.toml index 7fae1fbe67..86545be0f6 100644 --- a/voxygen/src/anim/Cargo.toml +++ b/voxygen/src/anim/Cargo.toml @@ -20,6 +20,7 @@ default = ["be-dyn-lib", "simd"] [dependencies] common = {package = "veloren-common", path = "../../../common"} find_folder = {version = "0.3.0", optional = true} +inline_tweak = "1.0.2" lazy_static = {version = "1.4.0", optional = true} libloading = {version = "0.6.2", optional = true} notify = {version = "5.0.0-pre.2", optional = true} diff --git a/voxygen/src/hud/buffs.rs b/voxygen/src/hud/buffs.rs index 8572ce50d8..1bd91eabc3 100644 --- a/voxygen/src/hud/buffs.rs +++ b/voxygen/src/hud/buffs.rs @@ -90,8 +90,7 @@ impl<'a> Widget for BuffsBar<'a> { } } - #[allow(clippy::unused_unit)] // TODO: Pending review in #587 - fn style(&self) -> Self::Style { () } + fn style(&self) -> Self::Style {} fn update(self, args: widget::UpdateArgs) -> Self::Event { let widget::UpdateArgs { state, ui, .. } = args; @@ -132,7 +131,7 @@ impl<'a> Widget for BuffsBar<'a> { .set(state.ids.buffs_align, ui); // Buffs and Debuffs - let (buff_count, debuff_count) = buffs.active_buffs.iter().map(get_buff_info).fold( + let (buff_count, debuff_count) = buffs.iter_active().map(get_buff_info).fold( (0, 0), |(buff_count, debuff_count), info| { if info.is_buff { @@ -169,18 +168,13 @@ impl<'a> Widget for BuffsBar<'a> { .zip(state.ids.buff_timers.iter().copied()) .zip( buffs - .active_buffs - .iter() + .iter_active() .map(get_buff_info) .filter(|info| info.is_buff), ) .enumerate() .for_each(|(i, ((id, timer_id), buff))| { - let max_duration = match buff.kind { - BuffKind::Bleeding { duration, .. } => duration, - BuffKind::Regeneration { duration, .. } => duration, - BuffKind::Cursed { duration } => duration, - }; + let max_duration = buff.data.duration; let current_duration = buff.dur; let duration_percentage = current_duration.map_or(1000.0, |cur| { max_duration @@ -264,18 +258,13 @@ impl<'a> Widget for BuffsBar<'a> { .zip(state.ids.debuff_timers.iter().copied()) .zip( buffs - .active_buffs - .iter() + .iter_active() .map(get_buff_info) .filter(|info| !info.is_buff), ) .enumerate() .for_each(|(i, ((id, timer_id), debuff))| { - let max_duration = match debuff.kind { - BuffKind::Bleeding { duration, .. } => duration, - BuffKind::Regeneration { duration, .. } => duration, - BuffKind::Cursed { duration } => duration, - }; + let max_duration = debuff.data.duration; let current_duration = debuff.dur; let duration_percentage = current_duration.map_or(1000.0, |cur| { max_duration @@ -355,7 +344,7 @@ impl<'a> Widget for BuffsBar<'a> { .set(state.ids.align, ui); // Buffs and Debuffs - let buff_count = buffs.active_buffs.len().min(11); + let buff_count = buffs.kinds.len().min(11); // Limit displayed buffs let buff_count = buff_count.min(20); @@ -378,14 +367,10 @@ impl<'a> Widget for BuffsBar<'a> { .copied() .zip(state.ids.buff_timers.iter().copied()) .zip(state.ids.buff_txts.iter().copied()) - .zip(buffs.active_buffs.iter().map(get_buff_info)) + .zip(buffs.iter_active().map(get_buff_info)) .enumerate() .for_each(|(i, (((id, timer_id), txt_id), buff))| { - let max_duration = match buff.kind { - BuffKind::Bleeding { duration, .. } => duration, - BuffKind::Regeneration { duration, .. } => duration, - BuffKind::Cursed { duration } => duration, - }; + let max_duration = buff.data.duration; let current_duration = buff.dur; // Percentage to determine which frame of the timer overlay is displayed let duration_percentage = current_duration.map_or(1000.0, |cur| { diff --git a/voxygen/src/hud/group.rs b/voxygen/src/hud/group.rs index ab3239e099..1bfeaa1cb2 100644 --- a/voxygen/src/hud/group.rs +++ b/voxygen/src/hud/group.rs @@ -444,7 +444,7 @@ impl<'a> Widget for Group<'a> { } if let Some(buffs) = buffs { // Limit displayed buffs to 11 - let buff_count = buffs.active_buffs.len().min(11); + let buff_count = buffs.kinds.len().min(11); total_buff_count += buff_count; let gen = &mut ui.widget_id_generator(); if state.ids.buffs.len() < total_buff_count { @@ -464,13 +464,9 @@ impl<'a> Widget for Group<'a> { .copied() .zip(state.ids.buff_timers.iter().copied()) .skip(total_buff_count - buff_count) - .zip(buffs.active_buffs.iter().map(get_buff_info)) + .zip(buffs.iter_active().map(get_buff_info)) .for_each(|((id, timer_id), buff)| { - let max_duration = match buff.kind { - BuffKind::Bleeding { duration, .. } => duration, - BuffKind::Regeneration { duration, .. } => duration, - BuffKind::Cursed { duration } => duration, - }; + let max_duration = buff.data.duration; let pulsating_col = Color::Rgba(1.0, 1.0, 1.0, buff_ani); let norm_col = Color::Rgba(1.0, 1.0, 1.0, 1.0); let current_duration = buff.dur; diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index 34201c5021..907a04857f 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -275,6 +275,7 @@ widget_ids! { #[derive(Clone, Copy)] pub struct BuffInfo { kind: comp::BuffKind, + data: comp::BuffData, is_buff: bool, dur: Option, } @@ -2729,10 +2730,8 @@ pub fn get_quality_col(item: &I) -> Color { fn get_buff_info(buff: &comp::Buff) -> BuffInfo { BuffInfo { kind: buff.kind, - is_buff: buff - .cat_ids - .iter() - .any(|cat| *cat == comp::BuffCategoryId::Buff), + data: buff.data, + is_buff: buff.kind.is_buff(), dur: buff.time, } } diff --git a/voxygen/src/hud/overhead.rs b/voxygen/src/hud/overhead.rs index f50437d4f0..7ebfb29cfc 100644 --- a/voxygen/src/hud/overhead.rs +++ b/voxygen/src/hud/overhead.rs @@ -137,7 +137,7 @@ impl<'a> Ingameable for Overhead<'a> { self.info.map_or(0, |info| { 2 + 1 + if self.bubble.is_none() { - info.buffs.active_buffs.len().min(10) * 2 + info.buffs.kinds.len().min(10) * 2 } else { 0 } @@ -204,7 +204,7 @@ impl<'a> Widget for Overhead<'a> { }; // Buffs // Alignment - let buff_count = buffs.active_buffs.len().min(11); + let buff_count = buffs.kinds.len().min(11); Rectangle::fill_with([168.0, 100.0], color::TRANSPARENT) .x_y(-1.0, name_y + 60.0) .parent(id) @@ -229,15 +229,11 @@ impl<'a> Widget for Overhead<'a> { .iter() .copied() .zip(state.ids.buff_timers.iter().copied()) - .zip(buffs.active_buffs.iter().map(get_buff_info)) + .zip(buffs.iter_active().map(get_buff_info)) .enumerate() .for_each(|(i, ((id, timer_id), buff))| { // Limit displayed buffs - let max_duration = match buff.kind { - BuffKind::Bleeding { duration, .. } => duration, - BuffKind::Regeneration { duration, .. } => duration, - BuffKind::Cursed { duration } => duration, - }; + let max_duration = buff.data.duration; let current_duration = buff.dur; let duration_percentage = current_duration.map_or(1000.0, |cur| { max_duration.map_or(1000.0, |max| { diff --git a/voxygen/src/hud/skillbar.rs b/voxygen/src/hud/skillbar.rs index d73ff01b66..172671622e 100644 --- a/voxygen/src/hud/skillbar.rs +++ b/voxygen/src/hud/skillbar.rs @@ -1014,11 +1014,5 @@ impl<'a> Widget for Skillbar<'a> { .w_h(16.0, 18.0) .mid_bottom_with_margin_on(state.ids.m2_content, -11.0) .set(state.ids.m2_ico, ui); - - // Buffs - // Add debuff slots above the health bar - // Add buff slots above the mana bar - - // Debuffs } }