valence_entity/
active_status_effects.rs1use bevy_ecs::prelude::*;
2use indexmap::IndexMap;
3use valence_protocol::status_effects::StatusEffect;
4
5#[derive(Debug)]
7enum StatusEffectChange {
8 Apply(ActiveStatusEffect),
9 Replace(ActiveStatusEffect),
10 Remove(StatusEffect),
11 RemoveAll,
12 #[doc(hidden)]
14 Expire(StatusEffect),
15}
16
17pub enum DurationResult {
19 NoEffects,
21 Infinite,
23 Finite(i32),
26}
27
28#[derive(Component, Default, Debug)]
30pub struct ActiveStatusEffects {
31 current_effects: IndexMap<StatusEffect, Vec<ActiveStatusEffect>>,
34 changes: Vec<StatusEffectChange>,
35}
36
37impl ActiveStatusEffects {
39 pub fn apply(&mut self, effect: ActiveStatusEffect) {
51 self.changes.push(StatusEffectChange::Apply(effect));
52 }
53
54 pub fn replace(&mut self, effect: ActiveStatusEffect) {
56 self.changes.push(StatusEffectChange::Replace(effect));
57 }
58
59 pub fn remove(&mut self, effect: StatusEffect) {
61 self.changes.push(StatusEffectChange::Remove(effect));
62 }
63
64 pub fn remove_all(&mut self) {
66 self.changes.push(StatusEffectChange::RemoveAll);
67 }
68
69 pub fn no_effect(&self, effect: StatusEffect) -> bool {
71 self.current_effects
72 .get(&effect)
73 .is_none_or(|effects| effects.is_empty())
74 }
75
76 pub fn has_effect(&self, effect: StatusEffect) -> bool {
78 self.current_effects
79 .get(&effect)
80 .is_some_and(|effects| !effects.is_empty())
81 }
82
83 pub fn no_effects(&self) -> bool {
85 self.current_effects.is_empty()
86 }
87
88 pub fn has_effects(&self) -> bool {
90 !self.current_effects.is_empty()
91 }
92
93 pub fn max_duration(&self, effect: StatusEffect) -> DurationResult {
95 let effects = self.current_effects.get(&effect);
96
97 match effects {
98 None => DurationResult::NoEffects,
99 Some(effects) => {
100 if let Some(effect) = effects.last() {
101 match effect.remaining_duration() {
102 None => DurationResult::Infinite,
103 Some(duration) => DurationResult::Finite(duration),
104 }
105 } else {
106 DurationResult::NoEffects
107 }
108 }
109 }
110 }
111
112 pub fn get_current_effect(&self, effect: StatusEffect) -> Option<&ActiveStatusEffect> {
114 self.current_effects
115 .get(&effect)
116 .and_then(|effects| effects.first())
117 }
118
119 pub fn get_all_effect(&self, effect: StatusEffect) -> Option<&Vec<ActiveStatusEffect>> {
121 self.current_effects.get(&effect)
122 }
123
124 pub fn get_current_effects(&self) -> Vec<&ActiveStatusEffect> {
126 self.current_effects
127 .values()
128 .filter_map(|effects| effects.first())
129 .collect()
130 }
131
132 pub fn get_all_effects(&self) -> &IndexMap<StatusEffect, Vec<ActiveStatusEffect>> {
134 &self.current_effects
135 }
136}
137
138impl ActiveStatusEffects {
140 fn apply_effect(&mut self, effect: ActiveStatusEffect) -> bool {
147 let effects = self
148 .current_effects
149 .entry(effect.status_effect())
150 .or_default();
151
152 let duration = effect.remaining_duration();
153 let amplifier = effect.amplifier();
154
155 if let Some(index) = effects.iter().position(|e| e.amplifier() <= amplifier) {
156 let active_status_effect = &effects[index];
159
160 if active_status_effect.remaining_duration() < duration
161 || active_status_effect.amplifier() < amplifier
162 {
163 effects[index] = effect;
165
166 let mut remaining_effects = effects.split_off(index + 1);
169 remaining_effects.retain(|e| e.remaining_duration() >= duration);
170 effects.append(&mut remaining_effects);
171 true
172 } else if active_status_effect.remaining_duration() > duration
173 && active_status_effect.amplifier() < amplifier
174 {
175 effects.insert(index, effect);
178 true
179 } else {
180 false
183 }
184 } else {
185 if let Some(last) = effects.last() {
190 if last.remaining_duration() < effect.remaining_duration() {
192 effects.push(effect);
194 true
195 } else {
196 false
198 }
199 } else {
200 effects.push(effect);
202 true
203 }
204 }
205 }
206
207 fn replace_effect(&mut self, effect: ActiveStatusEffect) {
209 self.current_effects
210 .insert(effect.status_effect(), vec![effect]);
211 }
212
213 fn remove_effect(&mut self, effect: StatusEffect) {
215 self.current_effects.swap_remove(&effect);
216 }
217
218 fn remove_all_effects(&mut self) {
220 self.current_effects.clear();
221 }
222
223 fn remove_strongest_effect(&mut self, effect: StatusEffect) {
225 if let Some(effects) = self.current_effects.get_mut(&effect) {
226 effects.remove(0);
227 }
228 }
229
230 #[doc(hidden)]
234 pub fn increment_active_ticks(&mut self) {
235 for effects in self.current_effects.values_mut() {
236 for effect in effects.iter_mut() {
237 effect.increment_active_ticks();
238
239 if effect.expired() {
240 self.changes
241 .push(StatusEffectChange::Expire(effect.status_effect()));
242 }
243 }
244 }
245 }
246
247 #[doc(hidden)]
254 pub fn apply_changes(&mut self) -> IndexMap<StatusEffect, Option<ActiveStatusEffect>> {
255 let current = self.current_effects.clone();
256 let find_current = |effect: StatusEffect| {
257 current
258 .iter()
259 .find(|e| *e.0 == effect)
260 .map(|e| e.1.first().cloned())?
261 };
262 let mut updated_effects = IndexMap::new();
263
264 for change in std::mem::take(&mut self.changes) {
265 match change {
266 StatusEffectChange::Apply(effect) => {
267 let value = effect.status_effect();
268 if self.apply_effect(effect) {
269 updated_effects
270 .entry(value)
271 .or_insert_with(|| find_current(value));
272 }
273 }
274 StatusEffectChange::Replace(effect) => {
275 let value = effect.status_effect();
276 updated_effects
277 .entry(value)
278 .or_insert_with(|| find_current(value));
279 self.replace_effect(effect);
280 }
281 StatusEffectChange::Remove(effect) => {
282 self.remove_effect(effect);
283 updated_effects.insert(effect, find_current(effect));
284 }
285 StatusEffectChange::RemoveAll => {
286 self.remove_all_effects();
287 for (status, effects) in ¤t {
288 if let Some(effect) = effects.first() {
289 updated_effects.insert(*status, Some(effect.clone()));
290 }
291 }
292 }
293 StatusEffectChange::Expire(effect) => {
294 self.remove_strongest_effect(effect);
295 updated_effects.insert(effect, find_current(effect));
296 }
297 }
298 }
299
300 updated_effects
301 }
302}
303
304#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
306pub struct ActiveStatusEffect {
307 effect: StatusEffect,
308 amplifier: u8,
311 initial_duration: Option<i32>,
317 active_ticks: i32,
322 ambient: bool,
325 show_particles: bool,
328 show_icon: bool,
331}
332
333impl ActiveStatusEffect {
334 pub fn from_effect(effect: StatusEffect) -> Self {
336 Self {
337 effect,
338 amplifier: 0,
339 initial_duration: Some(600),
340 active_ticks: 0,
341 ambient: false,
342 show_particles: true,
343 show_icon: true,
344 }
345 }
346
347 pub fn with_amplifier(mut self, amplifier: u8) -> Self {
349 self.amplifier = amplifier;
350 self
351 }
352
353 pub fn with_duration(mut self, duration: i32) -> Self {
355 self.initial_duration = Some(duration);
356 self
357 }
358
359 pub fn with_duration_seconds(mut self, duration: f32) -> Self {
361 self.initial_duration = Some((duration * 20.0).round() as i32);
362 self
363 }
364
365 pub fn with_infinite(mut self) -> Self {
367 self.initial_duration = None;
368 self
369 }
370
371 pub fn with_ambient(mut self, ambient: bool) -> Self {
373 self.ambient = ambient;
374 self
375 }
376
377 pub fn with_show_particles(mut self, show_particles: bool) -> Self {
379 self.show_particles = show_particles;
380 self
381 }
382
383 pub fn with_show_icon(mut self, show_icon: bool) -> Self {
385 self.show_icon = show_icon;
386 self
387 }
388
389 pub fn increment_active_ticks(&mut self) {
391 self.active_ticks += 1;
392 }
393
394 pub fn status_effect(&self) -> StatusEffect {
396 self.effect
397 }
398
399 pub fn amplifier(&self) -> u8 {
401 self.amplifier
402 }
403
404 pub fn initial_duration(&self) -> Option<i32> {
407 self.initial_duration
408 }
409
410 pub fn remaining_duration(&self) -> Option<i32> {
413 self.initial_duration
414 .map(|duration| duration - self.active_ticks)
415 }
416
417 pub fn active_ticks(&self) -> i32 {
419 self.active_ticks
420 }
421
422 pub fn ambient(&self) -> bool {
424 self.ambient
425 }
426
427 pub fn show_particles(&self) -> bool {
429 self.show_particles
430 }
431
432 pub fn show_icon(&self) -> bool {
434 self.show_icon
435 }
436
437 pub fn expired(&self) -> bool {
440 self.status_effect().instant()
441 || self
442 .remaining_duration()
443 .is_some_and(|duration| duration <= 0)
444 }
445}
446
447#[cfg(test)]
448mod test {
449 use super::*;
450
451 #[test]
452 fn test_apply_effect() {
453 let mut effects = ActiveStatusEffects::default();
454
455 let effect = ActiveStatusEffect::from_effect(StatusEffect::Speed).with_amplifier(1);
456 let effect2 = ActiveStatusEffect::from_effect(StatusEffect::Speed).with_amplifier(2);
457
458 let effect3 = ActiveStatusEffect::from_effect(StatusEffect::Strength).with_amplifier(1);
459 let effect4 = ActiveStatusEffect::from_effect(StatusEffect::Strength).with_amplifier(2);
460
461 effects.apply(effect.clone());
462 effects.apply_changes();
463 assert_eq!(
464 effects.get_all_effect(StatusEffect::Speed),
465 Some(&vec![effect.clone()])
466 );
467
468 effects.apply(effect2.clone());
469 effects.apply_changes();
470 assert_eq!(
471 effects.get_all_effect(StatusEffect::Speed),
472 Some(&vec![effect2.clone()])
473 );
474
475 effects.apply(effect3.clone());
476 effects.apply_changes();
477 assert_eq!(
478 effects.get_all_effect(StatusEffect::Strength),
479 Some(&vec![effect3.clone()])
480 );
481
482 effects.apply(effect4.clone());
483 effects.apply_changes();
484 assert_eq!(
485 effects.get_all_effect(StatusEffect::Strength),
486 Some(&vec![effect4.clone()])
487 );
488 }
489
490 #[test]
491 fn test_apply_effect_duration() {
492 let mut effects = ActiveStatusEffects::default();
493
494 let effect = ActiveStatusEffect::from_effect(StatusEffect::Speed)
495 .with_amplifier(1)
496 .with_duration(100);
497 let effect2 = ActiveStatusEffect::from_effect(StatusEffect::Speed)
498 .with_amplifier(1)
499 .with_duration(200);
500 let effect3 = ActiveStatusEffect::from_effect(StatusEffect::Speed)
501 .with_amplifier(0)
502 .with_duration(300);
503
504 effects.apply(effect.clone());
505 effects.apply_changes();
506 assert_eq!(
507 effects.get_all_effect(StatusEffect::Speed),
508 Some(&vec![effect.clone()])
509 );
510
511 effects.apply(effect2.clone());
512 effects.apply_changes();
513 assert_eq!(
514 effects.get_all_effect(StatusEffect::Speed),
515 Some(&vec![effect2.clone()])
516 );
517
518 effects.apply(effect3.clone());
519 effects.apply_changes();
520 assert_eq!(
521 effects.get_all_effect(StatusEffect::Speed),
522 Some(&vec![effect2.clone(), effect3.clone()])
523 );
524 }
525}