weasel/
status.rs

1//! Module for long lasting status effects.
2
3use crate::battle::{Battle, BattleRules};
4use crate::character::{verify_get_character, CharacterRules};
5use crate::entity::EntityId;
6use crate::error::{WeaselError, WeaselResult};
7use crate::event::{
8    Event, EventId, EventKind, EventProcessor, EventQueue, EventTrigger, LinkedQueue,
9};
10use crate::fight::FightRules;
11use crate::util::Id;
12#[cfg(feature = "serialization")]
13use serde::{Deserialize, Serialize};
14use std::any::Any;
15use std::fmt::{Debug, Formatter, Result};
16
17/// A long lasting effect altering an entity's condition.
18///
19/// Statuses are used to represent anything that changes at least one property of an entity,
20/// for a given amount of turns. DoTs (damage over time) are one example.\
21/// A status can alter an entity just once or at every turn.
22pub type Status<R> = <<R as BattleRules>::CR as CharacterRules<R>>::Status;
23
24/// Alias for `Status<R>::Id`.
25pub type StatusId<R> = <Status<R> as Id>::Id;
26
27/// Represents the intensity of a status.
28pub type Potency<R> = <<R as BattleRules>::FR as FightRules<R>>::Potency;
29
30/// Contains the changes to apply an alteration to one or more statuses.
31pub type StatusesAlteration<R> = <<R as BattleRules>::CR as CharacterRules<R>>::StatusesAlteration;
32
33/// Type for duration of statuses (in number of turns).
34pub type StatusDuration = EventId;
35
36/// Stores a `Status` and additional information about it.
37pub struct AppliedStatus<R: BattleRules> {
38    /// The status.
39    status: Status<R>,
40    /// An optional link to the origin event.
41    origin: Option<EventId>,
42    /// How long this status have been running.
43    duration: StatusDuration,
44}
45
46impl<R: BattleRules> AppliedStatus<R> {
47    /// Creates a new `AppliedStatus` without any origin.
48    pub fn new(status: Status<R>) -> Self {
49        Self {
50            status,
51            origin: None,
52            duration: 0,
53        }
54    }
55
56    /// Creates a new `AppliedStatus` with an origin.
57    pub fn with_origin(status: Status<R>, origin: EventId) -> Self {
58        Self {
59            status,
60            origin: Some(origin),
61            duration: 0,
62        }
63    }
64
65    /// Returns a reference to the status.
66    pub fn status(&self) -> &Status<R> {
67        &self.status
68    }
69
70    /// Returns a mutable reference to the status.
71    pub fn status_mut(&mut self) -> &mut Status<R> {
72        &mut self.status
73    }
74
75    /// Returns the origin event's id of this status.
76    pub fn origin(&self) -> Option<EventId> {
77        self.origin
78    }
79
80    /// Returns for how many turns the status has been in place.\
81    /// Duration is increased at every turn start.
82    pub fn duration(&self) -> StatusDuration {
83        self.duration
84    }
85
86    /// Increases the duration by one.
87    pub(crate) fn update(&mut self) {
88        self.duration += 1;
89    }
90}
91
92impl<R: BattleRules> std::ops::Deref for AppliedStatus<R> {
93    type Target = Status<R>;
94
95    fn deref(&self) -> &Self::Target {
96        &self.status
97    }
98}
99
100impl<R: BattleRules> std::ops::DerefMut for AppliedStatus<R> {
101    fn deref_mut(&mut self) -> &mut Self::Target {
102        &mut self.status
103    }
104}
105
106/// Alias of `Status<R>`, used for new incoming statuses.
107pub type NewStatus<R> = AppliedStatus<R>;
108
109/// Alias of `Status<R>`, used for replaced statuses.
110pub type OldStatus<R> = AppliedStatus<R>;
111
112/// Represents the application of a new status effect.
113pub enum Application<'a, R: BattleRules> {
114    /// A completely new status is applied.
115    New(&'a NewStatus<R>),
116    /// An existing status is replaced by a new one.
117    Replacement(&'a OldStatus<R>, &'a NewStatus<R>),
118}
119
120/// Updates all statuses of a entity.
121/// Returns an error if the entity doesn't exist or if it isn't a character.
122pub(crate) fn update_statuses<R: BattleRules + 'static>(
123    id: &EntityId<R>,
124    battle: &mut Battle<R>,
125    event_queue: &mut Option<EventQueue<R>>,
126) -> WeaselResult<(), R> {
127    // Update the duration of all statuses.
128    let character = battle
129        .state
130        .entities
131        .character_mut(id)
132        .ok_or_else(|| WeaselError::EntityNotFound(id.clone()))?;
133    for status in character.statuses_mut() {
134        status.update();
135    }
136    // Apply the effects of all statuses.
137    let character = battle
138        .state
139        .entities
140        .character(id)
141        .ok_or_else(|| WeaselError::EntityNotFound(id.clone()))?;
142    for status in character.statuses() {
143        let terminated = battle.rules.fight_rules().update_status(
144            &battle.state,
145            character,
146            status,
147            // Set the origin of all events caused by the status' update to the status own origin.
148            &mut event_queue
149                .as_mut()
150                .map(|queue| LinkedQueue::new(queue, status.origin())),
151            &mut battle.entropy,
152            &mut battle.metrics.write_handle(),
153        );
154        if terminated {
155            // Remove the status.
156            ClearStatus::trigger(
157                event_queue,
158                character.entity_id().clone(),
159                status.id().clone(),
160            )
161            .fire();
162        }
163    }
164    Ok(())
165}
166
167/// Event to inflict a status effect on a character.
168///
169/// A status may apply side effects upon activation and each time the creature takes an action.
170///
171/// # Examples
172/// ```
173/// use weasel::{
174///     battle_rules, rules::empty::*, Battle, BattleController, BattleRules, CreateCreature,
175///     CreateTeam, EntityId, EventKind, EventTrigger, InflictStatus, Server,
176/// };
177///
178/// battle_rules! {}
179///
180/// let battle = Battle::builder(CustomRules::new()).build();
181/// let mut server = Server::builder(battle).build();
182///
183/// let team_id = 1;
184/// CreateTeam::trigger(&mut server, team_id).fire().unwrap();
185/// let creature_id = 1;
186/// let position = ();
187/// CreateCreature::trigger(&mut server, creature_id, team_id, position)
188///     .fire()
189///     .unwrap();
190///
191/// let status_id = 1;
192/// InflictStatus::trigger(&mut server, EntityId::Creature(creature_id), status_id)
193///     .fire()
194///     .unwrap();
195/// assert_eq!(
196///     server.battle().history().events().iter().last().unwrap().kind(),
197///     EventKind::InflictStatus
198/// );
199/// ```
200#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
201pub struct InflictStatus<R: BattleRules> {
202    #[cfg_attr(
203        feature = "serialization",
204        serde(bound(
205            serialize = "EntityId<R>: Serialize",
206            deserialize = "EntityId<R>: Deserialize<'de>"
207        ))
208    )]
209    entity_id: EntityId<R>,
210
211    #[cfg_attr(
212        feature = "serialization",
213        serde(bound(
214            serialize = "StatusId<R>: Serialize",
215            deserialize = "StatusId<R>: Deserialize<'de>"
216        ))
217    )]
218    status_id: StatusId<R>,
219
220    #[cfg_attr(
221        feature = "serialization",
222        serde(bound(
223            serialize = "Option<Potency<R>>: Serialize",
224            deserialize = "Option<Potency<R>>: Deserialize<'de>"
225        ))
226    )]
227    potency: Option<Potency<R>>,
228}
229
230impl<R: BattleRules> InflictStatus<R> {
231    /// Returns a trigger for this event.
232    pub fn trigger<'a, P: EventProcessor<R>>(
233        processor: &'a mut P,
234        entity_id: EntityId<R>,
235        status_id: StatusId<R>,
236    ) -> InflictStatusTrigger<'a, R, P> {
237        InflictStatusTrigger {
238            processor,
239            entity_id,
240            status_id,
241            potency: None,
242        }
243    }
244
245    /// Returns the id of the entity target of this status.
246    pub fn entity_id(&self) -> &EntityId<R> {
247        &self.entity_id
248    }
249
250    /// Returns the id of the status to be inflicted.
251    pub fn status_id(&self) -> &StatusId<R> {
252        &self.status_id
253    }
254
255    /// Returns the status' potency.
256    pub fn potency(&self) -> &Option<Potency<R>> {
257        &self.potency
258    }
259}
260
261impl<R: BattleRules> Debug for InflictStatus<R> {
262    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
263        write!(
264            f,
265            "InflictStatus {{ entity_id: {:?}, status_id: {:?}, potency: {:?} }}",
266            self.entity_id, self.status_id, self.potency
267        )
268    }
269}
270
271impl<R: BattleRules> Clone for InflictStatus<R> {
272    fn clone(&self) -> Self {
273        Self {
274            entity_id: self.entity_id.clone(),
275            status_id: self.status_id.clone(),
276            potency: self.potency.clone(),
277        }
278    }
279}
280
281impl<R: BattleRules + 'static> Event<R> for InflictStatus<R> {
282    fn verify(&self, battle: &Battle<R>) -> WeaselResult<(), R> {
283        verify_get_character(battle.entities(), &self.entity_id).map(|_| ())
284    }
285
286    fn apply(&self, battle: &mut Battle<R>, event_queue: &mut Option<EventQueue<R>>) {
287        // Retrieve the character.
288        let character = battle
289            .state
290            .entities
291            .character_mut(&self.entity_id)
292            .unwrap_or_else(|| {
293                panic!(
294                    "constraint violated: character {:?} not found",
295                    self.entity_id
296                )
297            });
298        // Generate the status.
299        let status = battle.rules.character_rules().generate_status(
300            character,
301            &self.status_id,
302            &self.potency,
303            &mut battle.entropy,
304            &mut battle.metrics.write_handle(),
305        );
306        if let Some(status) = status {
307            // We can assume that the id of this event will be equal to history's next_id(),
308            // because the id will be assigned just after this function returns.
309            let origin = battle.history.next_id();
310            // Add the status to the character.
311            let old_status = character.add_status(AppliedStatus::with_origin(status, origin));
312            // Retrieve the character again, but this time immutably borrowing battle.state.
313            let character = battle
314                .state
315                .entities
316                .character(&self.entity_id)
317                .unwrap_or_else(|| {
318                    panic!(
319                        "constraint violated: character {:?} not found",
320                        self.entity_id
321                    )
322                });
323            // Apply the status' side effects.
324            let status = character.status(&self.status_id).unwrap_or_else(|| {
325                panic!(
326                    "constraint violated: status {:?} not found in {:?}",
327                    self.status_id, self.entity_id
328                )
329            });
330            let application = if let Some(old) = old_status.as_ref() {
331                Application::Replacement(old, status)
332            } else {
333                Application::New(status)
334            };
335            battle.rules.fight_rules().apply_status(
336                &battle.state,
337                character,
338                application,
339                event_queue,
340                &mut battle.entropy,
341                &mut battle.metrics.write_handle(),
342            );
343        }
344    }
345
346    fn kind(&self) -> EventKind {
347        EventKind::InflictStatus
348    }
349
350    fn box_clone(&self) -> Box<dyn Event<R> + Send> {
351        Box::new(self.clone())
352    }
353
354    fn as_any(&self) -> &dyn Any {
355        self
356    }
357}
358
359/// Trigger to build and fire an `InflictStatus` event.
360pub struct InflictStatusTrigger<'a, R, P>
361where
362    R: BattleRules,
363    P: EventProcessor<R>,
364{
365    processor: &'a mut P,
366    entity_id: EntityId<R>,
367    status_id: StatusId<R>,
368    potency: Option<Potency<R>>,
369}
370
371impl<'a, R, P> InflictStatusTrigger<'a, R, P>
372where
373    R: BattleRules + 'static,
374    P: EventProcessor<R>,
375{
376    /// Specify the potency of the status.
377    pub fn potency(&'a mut self, potency: Potency<R>) -> &'a mut Self {
378        self.potency = Some(potency);
379        self
380    }
381}
382
383impl<'a, R, P> EventTrigger<'a, R, P> for InflictStatusTrigger<'a, R, P>
384where
385    R: BattleRules + 'static,
386    P: EventProcessor<R>,
387{
388    fn processor(&'a mut self) -> &'a mut P {
389        self.processor
390    }
391
392    /// Returns an `InflictStatus` event.
393    fn event(&self) -> Box<dyn Event<R> + Send> {
394        Box::new(InflictStatus {
395            entity_id: self.entity_id.clone(),
396            status_id: self.status_id.clone(),
397            potency: self.potency.clone(),
398        })
399    }
400}
401
402/// Event to erase a status effect and its side effects from a character.
403///
404/// # Examples
405/// ```
406/// use weasel::{
407///     battle_rules, rules::empty::*, Battle, BattleRules, ClearStatus, CreateCreature,
408///     CreateTeam, EntityId, EventKind, EventTrigger, Server,
409/// };
410///
411/// battle_rules! {}
412///
413/// let battle = Battle::builder(CustomRules::new()).build();
414/// let mut server = Server::builder(battle).build();
415///
416/// let team_id = 1;
417/// CreateTeam::trigger(&mut server, team_id).fire().unwrap();
418/// let creature_id = 1;
419/// let position = ();
420/// CreateCreature::trigger(&mut server, creature_id, team_id, position)
421///     .fire()
422///     .unwrap();
423///
424/// let status_id = 1;
425/// let result =
426///     ClearStatus::trigger(&mut server, EntityId::Creature(creature_id), status_id).fire();
427/// // In this case the event should return an error because the creature is not afflicted
428/// // by the status.
429/// assert!(result.is_err());
430/// ```
431#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
432pub struct ClearStatus<R: BattleRules> {
433    #[cfg_attr(
434        feature = "serialization",
435        serde(bound(
436            serialize = "EntityId<R>: Serialize",
437            deserialize = "EntityId<R>: Deserialize<'de>"
438        ))
439    )]
440    entity_id: EntityId<R>,
441
442    #[cfg_attr(
443        feature = "serialization",
444        serde(bound(
445            serialize = "StatusId<R>: Serialize",
446            deserialize = "StatusId<R>: Deserialize<'de>"
447        ))
448    )]
449    status_id: StatusId<R>,
450}
451
452impl<R: BattleRules> ClearStatus<R> {
453    /// Returns a trigger for this event.
454    pub fn trigger<'a, P: EventProcessor<R>>(
455        processor: &'a mut P,
456        entity_id: EntityId<R>,
457        status_id: StatusId<R>,
458    ) -> ClearStatusTrigger<'a, R, P> {
459        ClearStatusTrigger {
460            processor,
461            entity_id,
462            status_id,
463        }
464    }
465
466    /// Returns the id of the entity from whom the status will be cleared.
467    pub fn entity_id(&self) -> &EntityId<R> {
468        &self.entity_id
469    }
470
471    /// Returns the id of the status to be cleared.
472    pub fn status_id(&self) -> &StatusId<R> {
473        &self.status_id
474    }
475}
476
477impl<R: BattleRules> Debug for ClearStatus<R> {
478    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
479        write!(
480            f,
481            "ClearStatus {{ entity_id: {:?}, status_id: {:?} }}",
482            self.entity_id, self.status_id
483        )
484    }
485}
486
487impl<R: BattleRules> Clone for ClearStatus<R> {
488    fn clone(&self) -> Self {
489        Self {
490            entity_id: self.entity_id.clone(),
491            status_id: self.status_id.clone(),
492        }
493    }
494}
495
496impl<R: BattleRules + 'static> Event<R> for ClearStatus<R> {
497    fn verify(&self, battle: &Battle<R>) -> WeaselResult<(), R> {
498        let character = verify_get_character(battle.entities(), &self.entity_id)?;
499        // The character must be afflicted by the status.
500        if character.status(&self.status_id).is_none() {
501            Err(WeaselError::StatusNotPresent(
502                self.entity_id.clone(),
503                self.status_id.clone(),
504            ))
505        } else {
506            Ok(())
507        }
508    }
509
510    fn apply(&self, battle: &mut Battle<R>, event_queue: &mut Option<EventQueue<R>>) {
511        // Retrieve the character.
512        let character = battle
513            .state
514            .entities
515            .character(&self.entity_id)
516            .unwrap_or_else(|| {
517                panic!(
518                    "constraint violated: character {:?} not found",
519                    self.entity_id
520                )
521            });
522        // Delete the status' side effects.
523        let status = character.status(&self.status_id).unwrap_or_else(|| {
524            panic!(
525                "constraint violated: status {:?} not found in {:?}",
526                self.status_id, self.entity_id
527            )
528        });
529        battle.rules.fight_rules().delete_status(
530            &battle.state,
531            character,
532            status,
533            event_queue,
534            &mut battle.entropy,
535            &mut battle.metrics.write_handle(),
536        );
537        // Retrieve the character, this time through a mutable reference.
538        let character = battle
539            .state
540            .entities
541            .character_mut(&self.entity_id)
542            .unwrap_or_else(|| {
543                panic!(
544                    "constraint violated: character {:?} not found",
545                    self.entity_id
546                )
547            });
548        // Remove the status from the character.
549        character.remove_status(&self.status_id);
550    }
551
552    fn kind(&self) -> EventKind {
553        EventKind::ClearStatus
554    }
555
556    fn box_clone(&self) -> Box<dyn Event<R> + Send> {
557        Box::new(self.clone())
558    }
559
560    fn as_any(&self) -> &dyn Any {
561        self
562    }
563}
564
565/// Trigger to build and fire a `ClearStatus` event.
566pub struct ClearStatusTrigger<'a, R, P>
567where
568    R: BattleRules,
569    P: EventProcessor<R>,
570{
571    processor: &'a mut P,
572    entity_id: EntityId<R>,
573    status_id: StatusId<R>,
574}
575
576impl<'a, R, P> EventTrigger<'a, R, P> for ClearStatusTrigger<'a, R, P>
577where
578    R: BattleRules + 'static,
579    P: EventProcessor<R>,
580{
581    fn processor(&'a mut self) -> &'a mut P {
582        self.processor
583    }
584
585    /// Returns a `ClearStatus` event.
586    fn event(&self) -> Box<dyn Event<R> + Send> {
587        Box::new(ClearStatus {
588            entity_id: self.entity_id.clone(),
589            status_id: self.status_id.clone(),
590        })
591    }
592}
593
594/// An event to alter the statuses of a character.
595///
596/// # Examples
597/// ```
598/// use weasel::{
599///     battle_rules, rules::empty::*, AlterStatuses, Battle, BattleController, BattleRules,
600///     CreateCreature, CreateTeam, EntityId, EventKind, EventTrigger, Server,
601/// };
602///
603/// battle_rules! {}
604///
605/// let battle = Battle::builder(CustomRules::new()).build();
606/// let mut server = Server::builder(battle).build();
607///
608/// let team_id = 1;
609/// CreateTeam::trigger(&mut server, team_id).fire().unwrap();
610/// let creature_id = 1;
611/// let position = ();
612/// CreateCreature::trigger(&mut server, creature_id, team_id, position)
613///     .fire()
614///     .unwrap();
615///
616/// let alteration = ();
617/// AlterStatuses::trigger(&mut server, EntityId::Creature(creature_id), alteration)
618///     .fire()
619///     .unwrap();
620/// assert_eq!(
621///     server.battle().history().events().iter().last().unwrap().kind(),
622///     EventKind::AlterStatuses
623/// );
624/// ```
625#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
626pub struct AlterStatuses<R: BattleRules> {
627    #[cfg_attr(
628        feature = "serialization",
629        serde(bound(
630            serialize = "EntityId<R>: Serialize",
631            deserialize = "EntityId<R>: Deserialize<'de>"
632        ))
633    )]
634    id: EntityId<R>,
635
636    #[cfg_attr(
637        feature = "serialization",
638        serde(bound(
639            serialize = "StatusesAlteration<R>: Serialize",
640            deserialize = "StatusesAlteration<R>: Deserialize<'de>"
641        ))
642    )]
643    alteration: StatusesAlteration<R>,
644}
645
646impl<R: BattleRules> AlterStatuses<R> {
647    /// Returns a trigger for this event.
648    pub fn trigger<'a, P: EventProcessor<R>>(
649        processor: &'a mut P,
650        id: EntityId<R>,
651        alteration: StatusesAlteration<R>,
652    ) -> AlterStatusesTrigger<'a, R, P> {
653        AlterStatusesTrigger {
654            processor,
655            id,
656            alteration,
657        }
658    }
659
660    /// Returns the character's entity id.
661    pub fn id(&self) -> &EntityId<R> {
662        &self.id
663    }
664
665    /// Returns the definition of the changes to the character's statuses.
666    pub fn alteration(&self) -> &StatusesAlteration<R> {
667        &self.alteration
668    }
669}
670
671impl<R: BattleRules> Debug for AlterStatuses<R> {
672    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
673        write!(
674            f,
675            "AlterStatuses {{ id: {:?}, alteration: {:?} }}",
676            self.id, self.alteration
677        )
678    }
679}
680
681impl<R: BattleRules> Clone for AlterStatuses<R> {
682    fn clone(&self) -> Self {
683        Self {
684            id: self.id.clone(),
685            alteration: self.alteration.clone(),
686        }
687    }
688}
689
690impl<R: BattleRules + 'static> Event<R> for AlterStatuses<R> {
691    fn verify(&self, battle: &Battle<R>) -> WeaselResult<(), R> {
692        verify_get_character(battle.entities(), &self.id).map(|_| ())
693    }
694
695    fn apply(&self, battle: &mut Battle<R>, _: &mut Option<EventQueue<R>>) {
696        // Retrieve the character.
697        let character = battle
698            .state
699            .entities
700            .character_mut(&self.id)
701            .unwrap_or_else(|| panic!("constraint violated: character {:?} not found", self.id));
702        // Alter the character.
703        battle.rules.character_rules().alter_statuses(
704            character,
705            &self.alteration,
706            &mut battle.entropy,
707            &mut battle.metrics.write_handle(),
708        );
709    }
710
711    fn kind(&self) -> EventKind {
712        EventKind::AlterStatuses
713    }
714
715    fn box_clone(&self) -> Box<dyn Event<R> + Send> {
716        Box::new(self.clone())
717    }
718
719    fn as_any(&self) -> &dyn Any {
720        self
721    }
722}
723
724/// Trigger to build and fire an `AlterStatuses` event.
725pub struct AlterStatusesTrigger<'a, R, P>
726where
727    R: BattleRules,
728    P: EventProcessor<R>,
729{
730    processor: &'a mut P,
731    id: EntityId<R>,
732    alteration: StatusesAlteration<R>,
733}
734
735impl<'a, R, P> EventTrigger<'a, R, P> for AlterStatusesTrigger<'a, R, P>
736where
737    R: BattleRules + 'static,
738    P: EventProcessor<R>,
739{
740    fn processor(&'a mut self) -> &'a mut P {
741        self.processor
742    }
743
744    /// Returns an `AlterStatuses` event.
745    fn event(&self) -> Box<dyn Event<R> + Send> {
746        Box::new(AlterStatuses {
747            id: self.id.clone(),
748            alteration: self.alteration.clone(),
749        })
750    }
751}