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}
70
71/// Information about whether aura addition or removal was requested.
72/// This to implement "on_add" and "on_remove" hooks for auras
73#[derive(Clone, Debug)]
74pub enum AuraChange {
75    /// Adds this aura
76    Add(Aura),
77    /// Removes auras of these indices
78    RemoveByKey(Vec<AuraKey>),
79    EnterAura(Uid, AuraKey, AuraKindVariant),
80    ExitAura(Uid, AuraKey, AuraKindVariant),
81}
82
83/// Used by the aura system to filter entities when applying an effect.
84#[derive(Copy, Clone, Debug, Deserialize, Serialize)]
85pub enum AuraTarget {
86    /// Targets the group of the entity specified by the `Uid`. This is useful
87    /// for auras which should only affect a player's party.
88    GroupOf(Uid),
89    /// Targets everyone not in the group of the entity specified by the `Uid`.
90    /// This is useful for auras which should only affect a player's
91    /// enemies.
92    NotGroupOf(Uid),
93    /// Targets all entities. This is for auras which are global or neutral.
94    All,
95}
96
97#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
98pub enum Specifier {
99    WardingAura,
100    HealingAura,
101    Frozen,
102    FieryAura,
103}
104
105impl From<(Option<GroupTarget>, Option<&Uid>)> for AuraTarget {
106    fn from((target, uid): (Option<GroupTarget>, Option<&Uid>)) -> Self {
107        match (target, uid) {
108            (Some(GroupTarget::InGroup), Some(uid)) => Self::GroupOf(*uid),
109            (Some(GroupTarget::OutOfGroup), Some(uid)) => Self::NotGroupOf(*uid),
110            (Some(GroupTarget::All), _) => Self::All,
111            _ => Self::All,
112        }
113    }
114}
115
116impl AsRef<AuraKindVariant> for AuraKind {
117    fn as_ref(&self) -> &AuraKindVariant {
118        match self {
119            AuraKind::Buff { .. } => &AuraKindVariant::Buff,
120            AuraKind::FriendlyFire => &AuraKindVariant::FriendlyFire,
121            AuraKind::ForcePvP => &AuraKindVariant::ForcePvP,
122        }
123    }
124}
125
126#[derive(Clone, Debug, Serialize, Deserialize)]
127pub struct AuraData {
128    pub duration: Option<Secs>,
129}
130
131impl AuraData {
132    #[must_use]
133    fn new(duration: Option<Secs>) -> Self { Self { duration } }
134}
135
136impl Aura {
137    /// Creates a new Aura to be assigned to an entity
138    pub fn new(
139        aura_kind: AuraKind,
140        radius: f32,
141        duration: Option<Secs>,
142        target: AuraTarget,
143        time: Time,
144    ) -> Self {
145        Self {
146            aura_kind,
147            radius,
148            end_time: duration.map(|dur| Time(time.0 + dur.0)),
149            target,
150            data: AuraData::new(duration),
151        }
152    }
153}
154
155/// Component holding all auras emitted by an entity.
156#[derive(Clone, Debug, Serialize, Deserialize, Default)]
157pub struct Auras {
158    pub auras: SlotMap<AuraKey, Aura>,
159}
160
161impl Auras {
162    pub fn new(auras: Vec<Aura>) -> Self {
163        let mut auras_comp: SlotMap<AuraKey, Aura> = SlotMap::with_key();
164        for aura in auras {
165            auras_comp.insert(aura);
166        }
167        Self { auras: auras_comp }
168    }
169
170    pub fn insert(&mut self, aura: Aura) { self.auras.insert(aura); }
171
172    pub fn remove(&mut self, key: AuraKey) { self.auras.remove(key); }
173}
174
175#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
176#[serde(deny_unknown_fields)]
177pub struct AuraBuffConstructor {
178    pub kind: BuffKind,
179    pub strength: f32,
180    pub duration: Option<Secs>,
181    pub category: BuffCategory,
182}
183
184impl AuraBuffConstructor {
185    pub fn to_aura(
186        &self,
187        entity_info: (&Uid, Option<ToolKind>),
188        radius: f32,
189        duration: Option<Secs>,
190        target: AuraTarget,
191        time: Time,
192    ) -> Aura {
193        let aura_kind = AuraKind::Buff {
194            kind: self.kind,
195            data: BuffData::new(self.strength, self.duration),
196            category: self.category.clone(),
197            source: BuffSource::Character {
198                by: *entity_info.0,
199                tool_kind: entity_info.1,
200            },
201        };
202        Aura::new(aura_kind, radius, duration, target, time)
203    }
204}
205
206/// Auras affecting an entity
207#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
208pub struct EnteredAuras {
209    /// [`AuraKey`] is local to each [`Auras`] component, therefore we also
210    /// store the [`Uid`] of the aura caster
211    pub auras: HashMap<AuraKindVariant, HashSet<(Uid, AuraKey)>>,
212}
213
214impl EnteredAuras {
215    pub fn flatten(&self) -> impl Iterator<Item = (Uid, AuraKey)> + '_ {
216        self.auras.values().flat_map(|i| i.iter().copied())
217    }
218}
219
220impl Component for Auras {
221    type Storage = DerefFlaggedStorage<Self, VecStorage<Self>>;
222}
223
224impl Component for EnteredAuras {
225    type Storage = DerefFlaggedStorage<Self, VecStorage<Self>>;
226}