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