veloren_common/comp/
aura.rs

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