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::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            (Some(GroupTarget::All), _) => Self::All,
107            _ => Self::All,
108        }
109    }
110}
111
112impl AsRef<AuraKindVariant> for AuraKind {
113    fn as_ref(&self) -> &AuraKindVariant {
114        match self {
115            AuraKind::Buff { .. } => &AuraKindVariant::Buff,
116            AuraKind::FriendlyFire => &AuraKindVariant::FriendlyFire,
117            AuraKind::ForcePvP => &AuraKindVariant::ForcePvP,
118        }
119    }
120}
121
122#[derive(Clone, Debug, Serialize, Deserialize)]
123pub struct AuraData {
124    pub duration: Option<Secs>,
125}
126
127impl AuraData {
128    #[must_use]
129    fn new(duration: Option<Secs>) -> Self { Self { duration } }
130}
131
132impl Aura {
133    /// Creates a new Aura to be assigned to an entity
134    pub fn new(
135        aura_kind: AuraKind,
136        radius: f32,
137        duration: Option<Secs>,
138        target: AuraTarget,
139        time: Time,
140    ) -> Self {
141        Self {
142            aura_kind,
143            radius,
144            end_time: duration.map(|dur| Time(time.0 + dur.0)),
145            target,
146            data: AuraData::new(duration),
147        }
148    }
149}
150
151/// Component holding all auras emitted by an entity.
152#[derive(Clone, Debug, Serialize, Deserialize, Default)]
153pub struct Auras {
154    pub auras: SlotMap<AuraKey, Aura>,
155}
156
157impl Auras {
158    pub fn new(auras: Vec<Aura>) -> Self {
159        let mut auras_comp: SlotMap<AuraKey, Aura> = SlotMap::with_key();
160        for aura in auras {
161            auras_comp.insert(aura);
162        }
163        Self { auras: auras_comp }
164    }
165
166    pub fn insert(&mut self, aura: Aura) { self.auras.insert(aura); }
167
168    pub fn remove(&mut self, key: AuraKey) { self.auras.remove(key); }
169}
170
171#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
172#[serde(deny_unknown_fields)]
173pub struct AuraBuffConstructor {
174    pub kind: BuffKind,
175    pub strength: f32,
176    pub duration: Option<Secs>,
177    pub category: BuffCategory,
178}
179
180impl AuraBuffConstructor {
181    pub fn to_aura(
182        &self,
183        uid: &Uid,
184        radius: f32,
185        duration: Option<Secs>,
186        target: AuraTarget,
187        time: Time,
188    ) -> Aura {
189        let aura_kind = AuraKind::Buff {
190            kind: self.kind,
191            data: BuffData::new(self.strength, self.duration),
192            category: self.category.clone(),
193            source: BuffSource::Character { by: *uid },
194        };
195        Aura::new(aura_kind, radius, duration, target, time)
196    }
197}
198
199/// Auras affecting an entity
200#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
201pub struct EnteredAuras {
202    /// [`AuraKey`] is local to each [`Auras`] component, therefore we also
203    /// store the [`Uid`] of the aura caster
204    pub auras: HashMap<AuraKindVariant, HashSet<(Uid, AuraKey)>>,
205}
206
207impl EnteredAuras {
208    pub fn flatten(&self) -> impl Iterator<Item = (Uid, AuraKey)> + '_ {
209        self.auras.values().flat_map(|i| i.iter().copied())
210    }
211}
212
213impl Component for Auras {
214    type Storage = DerefFlaggedStorage<Self, VecStorage<Self>>;
215}
216
217impl Component for EnteredAuras {
218    type Storage = DerefFlaggedStorage<Self, VecStorage<Self>>;
219}