chunkedge_entity/
active_status_effects.rs1use bevy_ecs::prelude::*;
2use chunkedge_protocol::status_effects::StatusEffect;
3use indexmap::IndexMap;
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: i32,
311 initial_duration: Option<i32>,
318 active_ticks: i32,
323 ambient: bool,
326 show_particles: bool,
329 show_icon: bool,
332}
333
334impl ActiveStatusEffect {
335 pub fn from_effect(effect: StatusEffect) -> Self {
337 Self {
338 effect,
339 amplifier: 0,
340 initial_duration: Some(600),
341 active_ticks: 0,
342 ambient: false,
343 show_particles: true,
344 show_icon: true,
345 }
346 }
347
348 pub fn with_amplifier(mut self, amplifier: i32) -> Self {
350 self.amplifier = amplifier;
351 self
352 }
353
354 pub fn with_duration(mut self, duration: i32) -> Self {
356 self.initial_duration = Some(duration);
357 self
358 }
359
360 pub fn with_duration_seconds(mut self, duration: f32) -> Self {
362 self.initial_duration = Some((duration * 20.0).round() as i32);
363 self
364 }
365
366 pub fn with_infinite(mut self) -> Self {
368 self.initial_duration = None;
369 self
370 }
371
372 pub fn with_ambient(mut self, ambient: bool) -> Self {
374 self.ambient = ambient;
375 self
376 }
377
378 pub fn with_show_particles(mut self, show_particles: bool) -> Self {
380 self.show_particles = show_particles;
381 self
382 }
383
384 pub fn with_show_icon(mut self, show_icon: bool) -> Self {
386 self.show_icon = show_icon;
387 self
388 }
389
390 pub fn increment_active_ticks(&mut self) {
392 self.active_ticks += 1;
393 }
394
395 pub fn status_effect(&self) -> StatusEffect {
397 self.effect
398 }
399
400 pub fn amplifier(&self) -> i32 {
402 self.amplifier
403 }
404
405 pub fn initial_duration(&self) -> Option<i32> {
408 self.initial_duration
409 }
410
411 pub fn remaining_duration(&self) -> Option<i32> {
414 self.initial_duration
415 .map(|duration| duration - self.active_ticks)
416 }
417
418 pub fn active_ticks(&self) -> i32 {
420 self.active_ticks
421 }
422
423 pub fn ambient(&self) -> bool {
425 self.ambient
426 }
427
428 pub fn show_particles(&self) -> bool {
430 self.show_particles
431 }
432
433 pub fn show_icon(&self) -> bool {
435 self.show_icon
436 }
437
438 pub fn expired(&self) -> bool {
441 self.status_effect().instant()
442 || self
443 .remaining_duration()
444 .is_some_and(|duration| duration <= 0)
445 }
446}
447
448#[cfg(test)]
449mod test {
450 use super::*;
451
452 #[test]
453 fn test_apply_effect() {
454 let mut effects = ActiveStatusEffects::default();
455
456 let effect = ActiveStatusEffect::from_effect(StatusEffect::Speed).with_amplifier(1);
457 let effect2 = ActiveStatusEffect::from_effect(StatusEffect::Speed).with_amplifier(2);
458
459 let effect3 = ActiveStatusEffect::from_effect(StatusEffect::Strength).with_amplifier(1);
460 let effect4 = ActiveStatusEffect::from_effect(StatusEffect::Strength).with_amplifier(2);
461
462 effects.apply(effect.clone());
463 effects.apply_changes();
464 assert_eq!(
465 effects.get_all_effect(StatusEffect::Speed),
466 Some(&vec![effect.clone()])
467 );
468
469 effects.apply(effect2.clone());
470 effects.apply_changes();
471 assert_eq!(
472 effects.get_all_effect(StatusEffect::Speed),
473 Some(&vec![effect2.clone()])
474 );
475
476 effects.apply(effect3.clone());
477 effects.apply_changes();
478 assert_eq!(
479 effects.get_all_effect(StatusEffect::Strength),
480 Some(&vec![effect3.clone()])
481 );
482
483 effects.apply(effect4.clone());
484 effects.apply_changes();
485 assert_eq!(
486 effects.get_all_effect(StatusEffect::Strength),
487 Some(&vec![effect4.clone()])
488 );
489 }
490
491 #[test]
492 fn test_apply_effect_duration() {
493 let mut effects = ActiveStatusEffects::default();
494
495 let effect = ActiveStatusEffect::from_effect(StatusEffect::Speed)
496 .with_amplifier(1)
497 .with_duration(100);
498 let effect2 = ActiveStatusEffect::from_effect(StatusEffect::Speed)
499 .with_amplifier(1)
500 .with_duration(200);
501 let effect3 = ActiveStatusEffect::from_effect(StatusEffect::Speed)
502 .with_amplifier(0)
503 .with_duration(300);
504
505 effects.apply(effect.clone());
506 effects.apply_changes();
507 assert_eq!(
508 effects.get_all_effect(StatusEffect::Speed),
509 Some(&vec![effect.clone()])
510 );
511
512 effects.apply(effect2.clone());
513 effects.apply_changes();
514 assert_eq!(
515 effects.get_all_effect(StatusEffect::Speed),
516 Some(&vec![effect2.clone()])
517 );
518
519 effects.apply(effect3.clone());
520 effects.apply_changes();
521 assert_eq!(
522 effects.get_all_effect(StatusEffect::Speed),
523 Some(&vec![effect2.clone(), effect3.clone()])
524 );
525 }
526}