Changed buffs from using hash maps to an enum map and a slot map.

This commit is contained in:
Sam 2023-11-12 12:37:28 -05:00
parent d7b07ee250
commit 3f19c61426
12 changed files with 137 additions and 112 deletions

View File

@ -5,58 +5,65 @@ ItemDef(
kind: Drink, kind: Drink,
effects: Any([ effects: Any([
Buff(( Buff((
kind: Polymorphed(QuadrupedSmall(( species: Frog, body_type: Female ))), kind: Polymorphed,
data: ( data: (
strength: 0.0, strength: 0.0,
duration: Some(60), duration: Some(60),
misc_data: Some(Body(QuadrupedSmall(( species: Frog, body_type: Female )))),
), ),
cat_ids: [Natural], cat_ids: [Natural],
)), )),
Buff(( Buff((
kind: Polymorphed(QuadrupedSmall(( species: Rabbit, body_type: Female ))), kind: Polymorphed,
data: ( data: (
strength: 0.0, strength: 0.0,
duration: Some(60), duration: Some(60),
misc_data: Some(Body(QuadrupedSmall(( species: Rabbit, body_type: Female )))),
), ),
cat_ids: [Natural], cat_ids: [Natural],
)), )),
Buff(( Buff((
kind: Polymorphed(QuadrupedSmall(( species: Rat, body_type: Female ))), kind: Polymorphed,
data: ( data: (
strength: 0.0, strength: 0.0,
duration: Some(60), duration: Some(60),
misc_data: Some(Body(QuadrupedSmall(( species: Rat, body_type: Female )))),
), ),
cat_ids: [Natural], cat_ids: [Natural],
)), )),
Buff(( Buff((
kind: Polymorphed(QuadrupedSmall(( species: Squirrel, body_type: Female ))), kind: Polymorphed,
data: ( data: (
strength: 0.0, strength: 0.0,
duration: Some(60), duration: Some(60),
misc_data: Some(Body(QuadrupedSmall(( species: Squirrel, body_type: Female )))),
), ),
cat_ids: [Natural], cat_ids: [Natural],
)), )),
Buff(( Buff((
kind: Polymorphed(QuadrupedSmall(( species: Cat, body_type: Female ))), kind: Polymorphed,
data: ( data: (
strength: 0.0, strength: 0.0,
duration: Some(60), duration: Some(60),
misc_data: Some(Body(QuadrupedSmall(( species: Cat, body_type: Female )))),
), ),
cat_ids: [Natural], cat_ids: [Natural],
)), )),
Buff(( Buff((
kind: Polymorphed(QuadrupedSmall(( species: Fungome, body_type: Female ))), kind: Polymorphed,
data: ( data: (
strength: 0.0, strength: 0.0,
duration: Some(60), duration: Some(60),
misc_data: Some(Body(QuadrupedSmall(( species: Fungome, body_type: Female )))),
), ),
cat_ids: [Natural], cat_ids: [Natural],
)), )),
Buff(( Buff((
kind: Polymorphed(QuadrupedSmall(( species: Pig, body_type: Female ))), kind: Polymorphed,
data: ( data: (
strength: 0.0, strength: 0.0,
duration: Some(60), duration: Some(60),
misc_data: Some(Body(QuadrupedSmall(( species: Pig, body_type: Female )))),
), ),
cat_ids: [Natural], cat_ids: [Natural],
)), )),

View File

@ -172,7 +172,7 @@ lazy_static! {
BuffKind::Parried => "parried", BuffKind::Parried => "parried",
BuffKind::PotionSickness => "potion_sickness", BuffKind::PotionSickness => "potion_sickness",
BuffKind::Reckless => "reckless", BuffKind::Reckless => "reckless",
BuffKind::Polymorphed(_) => "polymorphed", BuffKind::Polymorphed => "polymorphed",
BuffKind::Flame => "flame", BuffKind::Flame => "flame",
BuffKind::Frigid => "frigid", BuffKind::Frigid => "frigid",
BuffKind::Lifesteal => "lifesteal", BuffKind::Lifesteal => "lifesteal",

View File

@ -10,18 +10,21 @@ use crate::{
}; };
use core::cmp::Ordering; use core::cmp::Ordering;
use hashbrown::HashMap; use enum_map::{Enum, EnumMap};
use itertools::Either; use itertools::Either;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use slotmap::{new_key_type, SlotMap};
use specs::{Component, DerefFlaggedStorage, VecStorage}; use specs::{Component, DerefFlaggedStorage, VecStorage};
use strum::EnumIter; use strum::EnumIter;
use super::Body; use super::Body;
new_key_type! { pub struct BuffKey; }
/// De/buff Kind. /// De/buff Kind.
/// This is used to determine what effects a buff will have /// This is used to determine what effects a buff will have
#[derive( #[derive(
Clone, Copy, PartialEq, Eq, Hash, Debug, Serialize, Deserialize, PartialOrd, Ord, EnumIter, Clone, Copy, PartialEq, Eq, Hash, Debug, Serialize, Deserialize, PartialOrd, Ord, EnumIter, Enum,
)] )]
pub enum BuffKind { pub enum BuffKind {
// Buffs // Buffs
@ -157,7 +160,7 @@ pub enum BuffKind {
/// Decreases the health gained from subsequent potions. /// Decreases the health gained from subsequent potions.
PotionSickness, PotionSickness,
/// Changed into another body. /// Changed into another body.
Polymorphed(Body), Polymorphed,
} }
impl BuffKind { impl BuffKind {
@ -197,7 +200,7 @@ impl BuffKind {
| BuffKind::Poisoned | BuffKind::Poisoned
| BuffKind::Parried | BuffKind::Parried
| BuffKind::PotionSickness | BuffKind::PotionSickness
| BuffKind::Polymorphed(_) => false, | BuffKind::Polymorphed => false,
} }
} }
@ -348,7 +351,13 @@ impl BuffKind {
BuffEffect::DamageReduction(-data.strength), BuffEffect::DamageReduction(-data.strength),
BuffEffect::AttackDamage(1.0 + data.strength), BuffEffect::AttackDamage(1.0 + data.strength),
], ],
BuffKind::Polymorphed(body) => vec![BuffEffect::BodyChange(*body)], BuffKind::Polymorphed => {
let mut effects = Vec::new();
if let Some(MiscBuffData::Body(body)) = data.misc_data {
effects.push(BuffEffect::BodyChange(body));
}
effects
},
BuffKind::Flame => vec![BuffEffect::AttackEffect(AttackEffect::new( BuffKind::Flame => vec![BuffEffect::AttackEffect(AttackEffect::new(
None, None,
CombatEffect::Buff(CombatBuff { CombatEffect::Buff(CombatBuff {
@ -433,6 +442,13 @@ pub struct BuffData {
pub force_immediate: bool, pub force_immediate: bool,
/// Used for buffs that have rider buffs (e.g. Flame, Frigid) /// Used for buffs that have rider buffs (e.g. Flame, Frigid)
pub secondary_duration: Option<Secs>, pub secondary_duration: Option<Secs>,
/// Used to add random data to buffs if needed (e.g. polymorphed)
pub misc_data: Option<MiscBuffData>,
}
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
pub enum MiscBuffData {
Body(Body),
} }
impl BuffData { impl BuffData {
@ -443,6 +459,7 @@ impl BuffData {
force_immediate: false, force_immediate: false,
delay: None, delay: None,
secondary_duration: None, secondary_duration: None,
misc_data: None,
} }
} }
@ -461,6 +478,11 @@ impl BuffData {
self.force_immediate = force_immediate; self.force_immediate = force_immediate;
self self
} }
pub fn with_misc_data(mut self, misc_data: MiscBuffData) -> Self {
self.misc_data = Some(misc_data);
self
}
} }
/// De/buff category ID. /// De/buff category ID.
@ -588,9 +610,8 @@ pub enum BuffChange {
RemoveByKind(BuffKind), RemoveByKind(BuffKind),
/// Removes all buffs with this ID, but not debuffs. /// Removes all buffs with this ID, but not debuffs.
RemoveFromController(BuffKind), RemoveFromController(BuffKind),
/// Removes buffs of these indices (first vec is for active buffs, second is /// Removes buffs of these indices, should only be called when buffs expire
/// for inactive buffs), should only be called when buffs expire RemoveByKey(Vec<BuffKey>),
RemoveById(Vec<BuffId>),
/// Removes buffs of these categories (first vec is of categories of which /// 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 /// 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) /// required, third vec is of categories that will not be removed)
@ -704,93 +725,88 @@ pub enum BuffSource {
/// would be probably an undesired effect). /// would be probably an undesired effect).
#[derive(Clone, Debug, Serialize, Deserialize, Default)] #[derive(Clone, Debug, Serialize, Deserialize, Default)]
pub struct Buffs { pub struct Buffs {
/// Uid used for synchronization /// Maps kinds of buff to currently applied buffs of that kind and
id_counter: u64,
/// Maps Kinds of buff to Id's of currently applied buffs of that kind and
/// the time that the first buff was added (time gets reset if entity no /// the time that the first buff was added (time gets reset if entity no
/// longer has buffs of that kind) /// longer has buffs of that kind)
pub kinds: HashMap<BuffKind, (Vec<BuffId>, Time)>, pub kinds: EnumMap<BuffKind, Option<(Vec<BuffKey>, Time)>>,
// All currently applied buffs stored by Id // All buffs currently present on an entity
pub buffs: HashMap<BuffId, Buff>, pub buffs: SlotMap<BuffKey, Buff>,
} }
impl Buffs { impl Buffs {
fn sort_kind(&mut self, kind: BuffKind) { fn sort_kind(&mut self, kind: BuffKind) {
if let Some(buff_order) = self.kinds.get_mut(&kind) { if let Some(buff_order) = self.kinds[kind].as_mut() {
if buff_order.0.is_empty() { if buff_order.0.is_empty() {
self.kinds.remove(&kind); self.kinds[kind] = None;
} else { } else {
let buffs = &self.buffs; let buffs = &self.buffs;
// Intentionally sorted in reverse so that the strongest buffs are earlier in // Intentionally sorted in reverse so that the strongest buffs are earlier in
// the vector // the vector
buff_order buff_order
.0 .0
.sort_by(|a, b| buffs[b].partial_cmp(&buffs[a]).unwrap_or(Ordering::Equal)); .sort_by(|a, b| buffs[*b].partial_cmp(&buffs[*a]).unwrap_or(Ordering::Equal));
} }
} }
} }
pub fn remove_kind(&mut self, kind: BuffKind) { pub fn remove_kind(&mut self, kind: BuffKind) {
if let Some(buff_ids) = self.kinds.get_mut(&kind) { if let Some((buff_keys, _)) = self.kinds[kind].as_ref() {
for id in &buff_ids.0 { for key in buff_keys {
self.buffs.remove(id); self.buffs.remove(*key);
} }
self.kinds.remove(&kind); self.kinds[kind] = None;
} }
} }
fn force_insert(&mut self, id: BuffId, buff: Buff, current_time: Time) -> BuffId { pub fn insert(&mut self, buff: Buff, current_time: Time) -> BuffKey {
let kind = buff.kind; let kind = buff.kind;
self.kinds let key = self.buffs.insert(buff);
.entry(kind) self.kinds[kind]
.or_insert((Vec::new(), current_time)) .get_or_insert_with(|| (Vec::new(), current_time))
.0 .0
.push(id); .push(key);
self.buffs.insert(id, buff);
self.sort_kind(kind); self.sort_kind(kind);
if kind.queues() { if kind.queues() {
self.delay_queueable_buffs(kind, current_time); self.delay_queueable_buffs(kind, current_time);
} }
id key
} }
pub fn insert(&mut self, buff: Buff, current_time: Time) -> BuffId { pub fn contains(&self, kind: BuffKind) -> bool { self.kinds[kind].is_some() }
self.id_counter += 1;
self.force_insert(self.id_counter, buff, current_time)
}
pub fn contains(&self, kind: BuffKind) -> bool { self.kinds.contains_key(&kind) }
// Iterate through buffs of a given kind in effect order (most powerful first) // Iterate through buffs of a given kind in effect order (most powerful first)
pub fn iter_kind(&self, kind: BuffKind) -> impl Iterator<Item = (BuffId, &Buff)> + '_ { pub fn iter_kind(&self, kind: BuffKind) -> impl Iterator<Item = (BuffKey, &Buff)> + '_ {
self.kinds self.kinds[kind]
.get(&kind) .as_ref()
.map(|ids| ids.0.iter()) .map(|keys| keys.0.iter())
.unwrap_or_else(|| [].iter()) .unwrap_or_else(|| [].iter())
.map(move |id| (*id, &self.buffs[id])) .map(move |&key| (key, &self.buffs[key]))
} }
// Iterates through all active buffs (the most powerful buff of each // Iterates through all active buffs (the most powerful buff of each
// non-stacking kind, and all of the stacking ones) // non-stacking kind, and all of the stacking ones)
pub fn iter_active(&self) -> impl Iterator<Item = impl Iterator<Item = &Buff>> + '_ { pub fn iter_active(&self) -> impl Iterator<Item = impl Iterator<Item = &Buff>> + '_ {
self.kinds.iter().map(move |(kind, ids)| { self.kinds
if kind.stacks() { .iter()
// Iterate stackable buffs in reverse order to show the timer of the soonest one .filter_map(|(kind, keys)| keys.as_ref().map(|keys| (kind, keys)))
// to expire .map(move |(kind, keys)| {
Either::Left(ids.0.iter().filter_map(|id| self.buffs.get(id)).rev()) if kind.stacks() {
} else { // Iterate stackable buffs in reverse order to show the timer of the soonest one
Either::Right(self.buffs.get(&ids.0[0]).into_iter()) // to expire
} Either::Left(keys.0.iter().filter_map(|key| self.buffs.get(*key)).rev())
}) } else {
Either::Right(self.buffs.get(keys.0[0]).into_iter())
}
})
} }
// Gets most powerful buff of a given kind // Gets most powerful buff of a given kind
pub fn remove(&mut self, buff_id: BuffId) { pub fn remove(&mut self, buff_key: BuffKey) {
if let Some(kind) = self.buffs.remove(&buff_id) { if let Some(buff) = self.buffs.remove(buff_key) {
let kind = kind.kind; let kind = buff.kind;
self.kinds self.kinds[kind]
.get_mut(&kind) .as_mut()
.map(|ids| ids.0.retain(|id| *id != buff_id)); .map(|keys| keys.0.retain(|key| *key != buff_key));
self.sort_kind(kind); self.sort_kind(kind);
} }
} }
@ -798,9 +814,9 @@ impl Buffs {
fn delay_queueable_buffs(&mut self, kind: BuffKind, current_time: Time) { fn delay_queueable_buffs(&mut self, kind: BuffKind, current_time: Time) {
let mut next_start_time: Option<Time> = None; let mut next_start_time: Option<Time> = None;
debug_assert!(kind.queues()); debug_assert!(kind.queues());
if let Some(buffs) = self.kinds.get(&kind) { if let Some(buffs) = self.kinds[kind].as_mut() {
buffs.0.iter().for_each(|id| { buffs.0.iter().for_each(|key| {
if let Some(buff) = self.buffs.get_mut(id) { if let Some(buff) = self.buffs.get_mut(*key) {
// End time only being updated when there is some next_start_time will // End time only being updated when there is some next_start_time will
// technically cause buffs to "end early" if they have a weaker strength than a // technically cause buffs to "end early" if they have a weaker strength than a
// buff with an infinite duration, but this is fine since those buffs wouldn't // buff with an infinite duration, but this is fine since those buffs wouldn't
@ -826,8 +842,6 @@ impl Buffs {
} }
} }
pub type BuffId = u64;
impl Component for Buffs { impl Component for Buffs {
type Storage = DerefFlaggedStorage<Self, VecStorage<Self>>; type Storage = DerefFlaggedStorage<Self, VecStorage<Self>>;
} }

View File

@ -57,7 +57,7 @@ pub use self::{
quadruped_small, ship, theropod, AllBodies, Body, BodyData, quadruped_small, ship, theropod, AllBodies, Body, BodyData,
}, },
buff::{ buff::{
Buff, BuffCategory, BuffChange, BuffData, BuffEffect, BuffId, BuffKind, BuffSource, Buffs, Buff, BuffCategory, BuffChange, BuffData, BuffEffect, BuffKey, BuffKind, BuffSource, Buffs,
ModifierKind, ModifierKind,
}, },
character_state::{CharacterActivity, CharacterState, StateUpdate}, character_state::{CharacterActivity, CharacterState, StateUpdate},

View File

@ -4,7 +4,7 @@ use common::{
aura::Auras, aura::Auras,
body::{object, Body}, body::{object, Body},
buff::{ buff::{
Buff, BuffCategory, BuffChange, BuffData, BuffEffect, BuffId, BuffKind, BuffSource, Buff, BuffCategory, BuffChange, BuffData, BuffEffect, BuffKey, BuffKind, BuffSource,
Buffs, Buffs,
}, },
fluid_dynamics::{Fluid, LiquidKind}, fluid_dynamics::{Fluid, LiquidKind},
@ -281,7 +281,7 @@ impl<'a> System<'a> for Sys {
kind: LiquidKind::Water, kind: LiquidKind::Water,
.. ..
}) })
) && buff_comp.kinds.contains_key(&BuffKind::Burning) ) && buff_comp.kinds[BuffKind::Burning].is_some()
{ {
// If in water fluid and currently burning, remove burning debuffs // If in water fluid and currently burning, remove burning debuffs
server_emitter.emit(ServerEvent::Buff { server_emitter.emit(ServerEvent::Buff {
@ -291,14 +291,14 @@ impl<'a> System<'a> for Sys {
} }
} }
let mut expired_buffs = Vec::<BuffId>::new(); let mut expired_buffs = Vec::<BuffKey>::new();
// Replace buffs from an active aura with a normal buff when out of range of the // Replace buffs from an active aura with a normal buff when out of range of the
// aura // aura
buff_comp buff_comp
.buffs .buffs
.iter() .iter()
.filter_map(|(id, buff)| { .filter_map(|(buff_key, buff)| {
if let Some((uid, aura_key)) = buff.cat_ids.iter().find_map(|cat_id| { if let Some((uid, aura_key)) = buff.cat_ids.iter().find_map(|cat_id| {
if let BuffCategory::FromActiveAura(uid, aura_key) = cat_id { if let BuffCategory::FromActiveAura(uid, aura_key) = cat_id {
Some((uid, aura_key)) Some((uid, aura_key))
@ -306,12 +306,12 @@ impl<'a> System<'a> for Sys {
None None
} }
}) { }) {
Some((id, buff, uid, aura_key)) Some((buff_key, buff, uid, aura_key))
} else { } else {
None None
} }
}) })
.for_each(|(buff_id, buff, uid, aura_key)| { .for_each(|(buff_key, buff, uid, aura_key)| {
let replace = if let Some(aura_entity) = read_data.id_maps.uid_entity(*uid) { let replace = if let Some(aura_entity) = read_data.id_maps.uid_entity(*uid) {
if let Some(aura) = read_data if let Some(aura) = read_data
.auras .auras
@ -333,7 +333,7 @@ impl<'a> System<'a> for Sys {
true true
}; };
if replace { if replace {
expired_buffs.push(*buff_id); expired_buffs.push(buff_key);
server_emitter.emit(ServerEvent::Buff { server_emitter.emit(ServerEvent::Buff {
entity, entity,
buff_change: BuffChange::Add(Buff::new( buff_change: BuffChange::Add(Buff::new(
@ -355,9 +355,9 @@ impl<'a> System<'a> for Sys {
} }
}); });
buff_comp.buffs.iter().for_each(|(id, buff)| { buff_comp.buffs.iter().for_each(|(buff_key, buff)| {
if buff.end_time.map_or(false, |end| end.0 < read_data.time.0) { if buff.end_time.map_or(false, |end| end.0 < read_data.time.0) {
expired_buffs.push(*id) expired_buffs.push(buff_key)
} }
}); });
@ -370,9 +370,9 @@ impl<'a> System<'a> for Sys {
.abs() .abs()
< f32::EPSILON; < f32::EPSILON;
if infinite_damage_reduction { if infinite_damage_reduction {
for (id, buff) in buff_comp.buffs.iter() { for (key, buff) in buff_comp.buffs.iter() {
if !buff.kind.is_buff() { if !buff.kind.is_buff() {
expired_buffs.push(*id); expired_buffs.push(key);
} }
} }
} }
@ -386,24 +386,24 @@ impl<'a> System<'a> for Sys {
let mut buff_kinds = buff_comp let mut buff_kinds = buff_comp
.kinds .kinds
.iter() .iter()
.map(|(kind, ids)| (*kind, ids.clone())) .filter_map(|(kind, keys)| keys.as_ref().map(|keys| (kind, keys.clone())))
.collect::<Vec<(BuffKind, (Vec<BuffId>, Time))>>(); .collect::<Vec<(BuffKind, (Vec<BuffKey>, Time))>>();
buff_kinds.sort_by_key(|(kind, _)| !kind.affects_subsequent_buffs()); buff_kinds.sort_by_key(|(kind, _)| !kind.affects_subsequent_buffs());
for (buff_kind, (buff_ids, kind_start_time)) in buff_kinds.into_iter() { for (buff_kind, (buff_keys, kind_start_time)) in buff_kinds.into_iter() {
let mut active_buff_ids = Vec::new(); let mut active_buff_keys = Vec::new();
if infinite_damage_reduction && !buff_kind.is_buff() { if infinite_damage_reduction && !buff_kind.is_buff() {
continue; continue;
} }
if buff_kind.stacks() { if buff_kind.stacks() {
// Process all the buffs of this kind // Process all the buffs of this kind
active_buff_ids = buff_ids; active_buff_keys = buff_keys;
} else { } else {
// Only process the strongest of this buff kind // Only process the strongest of this buff kind
active_buff_ids.push(buff_ids[0]); active_buff_keys.push(buff_keys[0]);
} }
for buff_id in active_buff_ids.into_iter() { for buff_key in active_buff_keys.into_iter() {
if let Some(buff) = buff_comp.buffs.get(&buff_id) { if let Some(buff) = buff_comp.buffs.get(buff_key) {
// Skip the effect of buffs whose start delay hasn't expired. // Skip the effect of buffs whose start delay hasn't expired.
if buff.start_time.0 > read_data.time.0 { if buff.start_time.0 > read_data.time.0 {
continue; continue;
@ -434,7 +434,7 @@ impl<'a> System<'a> for Sys {
&mut server_emitter, &mut server_emitter,
dt, dt,
*read_data.time, *read_data.time,
expired_buffs.contains(&buff_id), expired_buffs.contains(&buff_key),
buff_comp, buff_comp,
); );
} }
@ -452,7 +452,7 @@ impl<'a> System<'a> for Sys {
if !expired_buffs.is_empty() { if !expired_buffs.is_empty() {
server_emitter.emit(ServerEvent::Buff { server_emitter.emit(ServerEvent::Buff {
entity, entity,
buff_change: BuffChange::RemoveById(expired_buffs), buff_change: BuffChange::RemoveByKey(expired_buffs),
}); });
} }

View File

@ -32,7 +32,7 @@ pub fn is_dead(entity: EcsEntity, read_data: &ReadData) -> bool {
pub fn is_invulnerable(entity: EcsEntity, read_data: &ReadData) -> bool { pub fn is_invulnerable(entity: EcsEntity, read_data: &ReadData) -> bool {
let buffs = read_data.buffs.get(entity); let buffs = read_data.buffs.get(entity);
buffs.map_or(false, |b| b.kinds.contains_key(&BuffKind::Invulnerability)) buffs.map_or(false, |b| b.kinds[BuffKind::Invulnerability].is_some())
} }
/// Gets alignment of owner if alignment given is `Owned`. /// Gets alignment of owner if alignment given is `Owned`.
@ -204,7 +204,7 @@ impl<'a> AgentData<'a> {
read_data read_data
.buffs .buffs
.get(*self.entity) .get(*self.entity)
.map_or(false, |b| b.kinds.contains_key(&buff)) .map_or(false, |b| b.kinds[buff].is_some())
} }
pub fn extract_ability(&self, input: AbilityInput) -> Option<AbilityData> { pub fn extract_ability(&self, input: AbilityInput) -> Option<AbilityData> {

View File

@ -1226,9 +1226,9 @@ pub fn handle_buff(server: &mut Server, entity: EcsEntity, buff_change: buff::Bu
buffs.insert(new_buff, *time); buffs.insert(new_buff, *time);
} }
}, },
BuffChange::RemoveById(ids) => { BuffChange::RemoveByKey(keys) => {
for id in ids { for key in keys {
buffs.remove(id); buffs.remove(key);
} }
}, },
BuffChange::RemoveByKind(kind) => { BuffChange::RemoveByKind(kind) => {
@ -1244,8 +1244,8 @@ pub fn handle_buff(server: &mut Server, entity: EcsEntity, buff_change: buff::Bu
any_required, any_required,
none_required, none_required,
} => { } => {
let mut ids_to_remove = Vec::new(); let mut keys_to_remove = Vec::new();
for (id, buff) in buffs.buffs.iter() { for (key, buff) in buffs.buffs.iter() {
let mut required_met = true; let mut required_met = true;
for required in &all_required { for required in &all_required {
if !buff.cat_ids.iter().any(|cat| cat == required) { if !buff.cat_ids.iter().any(|cat| cat == required) {
@ -1268,11 +1268,11 @@ pub fn handle_buff(server: &mut Server, entity: EcsEntity, buff_change: buff::Bu
} }
} }
if required_met && any_met && none_met { if required_met && any_met && none_met {
ids_to_remove.push(*id); keys_to_remove.push(key);
} }
} }
for id in ids_to_remove { for key in keys_to_remove {
buffs.remove(id); buffs.remove(key);
} }
}, },
BuffChange::Refresh(kind) => { BuffChange::Refresh(kind) => {

View File

@ -213,7 +213,7 @@ fn react_if_on_fire(bdata: &mut BehaviorData) -> bool {
.read_data .read_data
.buffs .buffs
.get(*bdata.agent_data.entity) .get(*bdata.agent_data.entity)
.map_or(false, |b| b.kinds.contains_key(&BuffKind::Burning)); .map_or(false, |b| b.kinds[BuffKind::Burning].is_some());
if is_on_fire if is_on_fire
&& bdata.agent_data.body.map_or(false, |b| b.is_humanoid()) && bdata.agent_data.body.map_or(false, |b| b.is_humanoid())

View File

@ -137,7 +137,7 @@ pub fn localize_chat_message(
| BuffKind::Poisoned | BuffKind::Poisoned
| BuffKind::Parried | BuffKind::Parried
| BuffKind::PotionSickness | BuffKind::PotionSickness
| BuffKind::Polymorphed(_) => { | BuffKind::Polymorphed => {
tracing::error!("Player was killed by a debuff that doesn't do damage!"); tracing::error!("Player was killed by a debuff that doesn't do damage!");
"mysterious" "mysterious"
}, },

View File

@ -5191,7 +5191,7 @@ pub fn get_buff_image(buff: BuffKind, imgs: &Imgs) -> conrod_core::image::Id {
BuffKind::Poisoned => imgs.debuff_poisoned_0, BuffKind::Poisoned => imgs.debuff_poisoned_0,
BuffKind::Parried => imgs.debuff_parried_0, BuffKind::Parried => imgs.debuff_parried_0,
BuffKind::PotionSickness => imgs.debuff_potionsickness_0, BuffKind::PotionSickness => imgs.debuff_potionsickness_0,
BuffKind::Polymorphed(_) => imgs.debuff_polymorphed, BuffKind::Polymorphed => imgs.debuff_polymorphed,
} }
} }
@ -5232,7 +5232,7 @@ pub fn get_buff_title(buff: BuffKind, localized_strings: &Localization) -> Cow<s
BuffKind::Poisoned => localized_strings.get_msg("buff-title-poisoned"), BuffKind::Poisoned => localized_strings.get_msg("buff-title-poisoned"),
BuffKind::Parried => localized_strings.get_msg("buff-title-parried"), BuffKind::Parried => localized_strings.get_msg("buff-title-parried"),
BuffKind::PotionSickness => localized_strings.get_msg("buff-title-potionsickness"), BuffKind::PotionSickness => localized_strings.get_msg("buff-title-potionsickness"),
BuffKind::Polymorphed(_) => localized_strings.get_msg("buff-title-polymorphed"), BuffKind::Polymorphed => localized_strings.get_msg("buff-title-polymorphed"),
} }
} }
@ -5277,7 +5277,7 @@ pub fn get_buff_desc(buff: BuffKind, data: BuffData, localized_strings: &Localiz
BuffKind::Poisoned => localized_strings.get_msg("buff-desc-poisoned"), BuffKind::Poisoned => localized_strings.get_msg("buff-desc-poisoned"),
BuffKind::Parried => localized_strings.get_msg("buff-desc-parried"), BuffKind::Parried => localized_strings.get_msg("buff-desc-parried"),
BuffKind::PotionSickness => localized_strings.get_msg("buff-desc-potionsickness"), BuffKind::PotionSickness => localized_strings.get_msg("buff-desc-potionsickness"),
BuffKind::Polymorphed(_) => localized_strings.get_msg("buff-desc-polymorphed"), BuffKind::Polymorphed => localized_strings.get_msg("buff-desc-polymorphed"),
} }
} }

View File

@ -205,7 +205,7 @@ pub fn consumable_desc(effects: &Effects, i18n: &Localization) -> Vec<String> {
| BuffKind::Fortitude | BuffKind::Fortitude
| BuffKind::Parried | BuffKind::Parried
| BuffKind::Reckless | BuffKind::Reckless
| BuffKind::Polymorphed(_) | BuffKind::Polymorphed
| BuffKind::Flame | BuffKind::Flame
| BuffKind::Frigid | BuffKind::Frigid
| BuffKind::Lifesteal | BuffKind::Lifesteal
@ -233,7 +233,7 @@ pub fn consumable_desc(effects: &Effects, i18n: &Localization) -> Vec<String> {
| BuffKind::IncreaseMaxHealth | BuffKind::IncreaseMaxHealth
| BuffKind::Invulnerability | BuffKind::Invulnerability
| BuffKind::PotionSickness | BuffKind::PotionSickness
| BuffKind::Polymorphed(_) => { | BuffKind::Polymorphed => {
i18n.get_msg_ctx("buff-text-for_seconds", &i18n::fluent_args! { i18n.get_msg_ctx("buff-text-for_seconds", &i18n::fluent_args! {
"dur_secs" => dur_secs "dur_secs" => dur_secs
}) })

View File

@ -1482,7 +1482,11 @@ impl ParticleMgr {
{ {
let pos = interp.map_or(pos.0, |i| i.pos); let pos = interp.map_or(pos.0, |i| i.pos);
for (buff_kind, buff_ids) in buffs.kinds.iter() { for (buff_kind, buff_keys) in buffs
.kinds
.iter()
.filter_map(|(kind, keys)| keys.as_ref().map(|keys| (kind, keys)))
{
use buff::BuffKind; use buff::BuffKind;
match buff_kind { match buff_kind {
BuffKind::Cursed | BuffKind::Burning => { BuffKind::Cursed | BuffKind::Burning => {
@ -1519,9 +1523,9 @@ impl ParticleMgr {
let mut multiplicity = 0; let mut multiplicity = 0;
// Only show particles for potion sickness at the beginning, after the // Only show particles for potion sickness at the beginning, after the
// drinking animation finishes // drinking animation finishes
if buff_ids.0 if buff_keys.0
.iter() .iter()
.filter_map(|id| buffs.buffs.get(id)) .filter_map(|key| buffs.buffs.get(*key))
.any(|buff| { .any(|buff| {
matches!(buff.elapsed(Time(time)), dur if (1.0..=1.5).contains(&dur.0)) matches!(buff.elapsed(Time(time)), dur if (1.0..=1.5).contains(&dur.0))
}) })
@ -1583,13 +1587,13 @@ impl ParticleMgr {
}, },
); );
}, },
BuffKind::Polymorphed(_) => { BuffKind::Polymorphed => {
let mut multiplicity = 0; let mut multiplicity = 0;
// Only show particles for polymorph at the beginning, after the // Only show particles for polymorph at the beginning, after the
// drinking animation finishes // drinking animation finishes
if buff_ids.0 if buff_keys.0
.iter() .iter()
.filter_map(|id| buffs.buffs.get(id)) .filter_map(|key| buffs.buffs.get(*key))
.any(|buff| { .any(|buff| {
matches!(buff.elapsed(Time(time)), dur if (0.1..=0.3).contains(&dur.0)) matches!(buff.elapsed(Time(time)), dur if (0.1..=0.3).contains(&dur.0))
}) })