veloren_common/comp/
aura.rs

1use crate::{
2    combat::GroupTarget,
3    comp::{
4        buff::{BuffCategory, BuffData, BuffKind, BuffSource},
5        tool::ToolKind,
6    },
7    resources::{Secs, Time},
8    uid::Uid,
9};
10use serde::{Deserialize, Serialize};
11use slotmap::{SlotMap, new_key_type};
12use specs::{Component, DerefFlaggedStorage, VecStorage};
13use std::collections::{HashMap, HashSet};
14
15new_key_type! { pub struct AuraKey; }
16
17/// AuraKind is what kind of effect an aura applies
18/// Currently only buffs are implemented
19#[derive(Clone, Debug, Serialize, Deserialize)]
20pub enum AuraKind {
21    /// The Buff kind is (surprise!) a buff :D
22    Buff {
23        kind: BuffKind,
24        data: BuffData,
25        category: BuffCategory,
26        source: BuffSource,
27    },
28    /// Enables free-for-all friendly-fire. Includes group members, and pets.
29    /// BattleMode checks still apply.
30    FriendlyFire,
31    /// Ignores the [`crate::resources::BattleMode`] of all entities
32    /// affected by this aura, only player entities will be affected by this
33    /// aura.
34    ForcePvP,
35    /* TODO: Implement other effects here. Things to think about
36     * are terrain/sprite effects, collision and physics, and
37     * environmental conditions like temperature and humidity
38     * Multiple auras can be given to an entity. */
39}
40
41/// Variants of [`AuraKind`] without data
42#[derive(Clone, Copy, Debug, Serialize, Deserialize, Hash, PartialEq, Eq, PartialOrd, Ord)]
43pub enum AuraKindVariant {
44    Buff,
45    FriendlyFire,
46    ForcePvP,
47}
48
49/// Aura
50/// Applies a buff to entities in the radius if meeting
51/// conditions set forth in the aura system.
52#[derive(Clone, Debug, Serialize, Deserialize)]
53pub struct Aura {
54    /// The kind of aura applied
55    pub aura_kind: AuraKind,
56    /// The radius of the aura
57    pub radius: f32,
58    // None corresponds to an indefinite aura
59    pub end_time: Option<Time>,
60    /* TODO: Add functionality for fading or a gradient */
61    /// Used to filter which entities this aura will apply to. For example,
62    /// globally neutral auras which affect all entities will have the type
63    /// `AuraTarget::All`. Whereas auras which only affect a player's party
64    /// members will have the type `AuraTarget::GroupOf`.
65    pub target: AuraTarget,
66    /// Contains data about the original state of the aura that does not change
67    /// over time
68    pub data: AuraData,
69    //Specifies if there should be a persistent visual effect during the aura
70    pub frontend_specifier: Option<Specifier>,
71}
72
73/// Information about whether aura addition or removal was requested.
74/// This to implement "on_add" and "on_remove" hooks for auras
75#[derive(Clone, Debug)]
76pub enum AuraChange {
77    /// Adds this aura
78    Add(Aura),
79    /// Removes auras of these indices
80    RemoveByKey(Vec<AuraKey>),
81    EnterAura(Uid, AuraKey, AuraKindVariant),
82    ExitAura(Uid, AuraKey, AuraKindVariant),
83}
84
85/// Used by the aura system to filter entities when applying an effect.
86#[derive(Copy, Clone, Debug, Deserialize, Serialize)]
87pub enum AuraTarget {
88    /// Targets the group of the entity specified by the `Uid`. This is useful
89    /// for auras which should only affect a player's party.
90    GroupOf(Uid),
91    /// Targets everyone not in the group of the entity specified by the `Uid`.
92    /// This is useful for auras which should only affect a player's
93    /// enemies.
94    NotGroupOf(Uid),
95    /// Targets all entities. This is for auras which are global or neutral.
96    All,
97}
98
99#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
100pub enum Specifier {
101    WardingAura,
102    HealingAura,
103    Frozen,
104    FieryAura,
105}
106
107impl From<(Option<GroupTarget>, Option<&Uid>)> for AuraTarget {
108    fn from((target, uid): (Option<GroupTarget>, Option<&Uid>)) -> Self {
109        match (target, uid) {
110            (Some(GroupTarget::InGroup), Some(uid)) => Self::GroupOf(*uid),
111            (Some(GroupTarget::OutOfGroup), Some(uid)) => Self::NotGroupOf(*uid),
112            (Some(GroupTarget::All), _) => Self::All,
113            _ => Self::All,
114        }
115    }
116}
117
118impl AsRef<AuraKindVariant> for AuraKind {
119    fn as_ref(&self) -> &AuraKindVariant {
120        match self {
121            AuraKind::Buff { .. } => &AuraKindVariant::Buff,
122            AuraKind::FriendlyFire => &AuraKindVariant::FriendlyFire,
123            AuraKind::ForcePvP => &AuraKindVariant::ForcePvP,
124        }
125    }
126}
127
128#[derive(Clone, Debug, Serialize, Deserialize)]
129pub struct AuraData {
130    pub duration: Option<Secs>,
131}
132
133impl AuraData {
134    #[must_use]
135    fn new(duration: Option<Secs>) -> Self { Self { duration } }
136}
137
138impl Aura {
139    /// Creates a new Aura to be assigned to an entity
140    pub fn new(
141        aura_kind: AuraKind,
142        radius: f32,
143        duration: Option<Secs>,
144        target: AuraTarget,
145        time: Time,
146        frontend_specifier: Option<Specifier>,
147    ) -> Self {
148        Self {
149            aura_kind,
150            radius,
151            end_time: duration.map(|dur| Time(time.0 + dur.0)),
152            target,
153            data: AuraData::new(duration),
154            frontend_specifier,
155        }
156    }
157}
158
159/// Component holding all auras emitted by an entity.
160#[derive(Clone, Debug, Serialize, Deserialize, Default)]
161pub struct Auras {
162    pub auras: SlotMap<AuraKey, Aura>,
163}
164
165impl Auras {
166    pub fn new(auras: Vec<Aura>) -> Self {
167        let mut auras_comp: SlotMap<AuraKey, Aura> = SlotMap::with_key();
168        for aura in auras {
169            auras_comp.insert(aura);
170        }
171        Self { auras: auras_comp }
172    }
173
174    pub fn insert(&mut self, aura: Aura) { self.auras.insert(aura); }
175
176    pub fn remove(&mut self, key: AuraKey) { self.auras.remove(key); }
177}
178
179#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
180#[serde(deny_unknown_fields)]
181pub struct AuraBuffConstructor {
182    pub kind: BuffKind,
183    pub strength: f32,
184    pub duration: Option<Secs>,
185    pub category: BuffCategory,
186}
187
188impl AuraBuffConstructor {
189    pub fn to_aura(
190        &self,
191        entity_info: (&Uid, Option<ToolKind>),
192        radius: f32,
193        duration: Option<Secs>,
194        target: AuraTarget,
195        time: Time,
196        frontend_specifier: Option<Specifier>,
197    ) -> Aura {
198        let aura_kind = AuraKind::Buff {
199            kind: self.kind,
200            data: BuffData::new(self.strength, self.duration),
201            category: self.category.clone(),
202            source: BuffSource::Character {
203                by: *entity_info.0,
204                tool_kind: entity_info.1,
205            },
206        };
207        Aura::new(
208            aura_kind,
209            radius,
210            duration,
211            target,
212            time,
213            frontend_specifier,
214        )
215    }
216}
217
218/// Auras affecting an entity
219#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
220pub struct EnteredAuras {
221    /// [`AuraKey`] is local to each [`Auras`] component, therefore we also
222    /// store the [`Uid`] of the aura caster
223    pub auras: HashMap<AuraKindVariant, HashSet<(Uid, AuraKey)>>,
224}
225
226impl EnteredAuras {
227    pub fn flatten(&self) -> impl Iterator<Item = (Uid, AuraKey)> + '_ {
228        self.auras.values().flat_map(|i| i.iter().copied())
229    }
230}
231
232impl Component for Auras {
233    type Storage = DerefFlaggedStorage<Self, VecStorage<Self>>;
234}
235
236impl Component for EnteredAuras {
237    type Storage = DerefFlaggedStorage<Self, VecStorage<Self>>;
238}