Skip to main content

steel_core/entity/damage/history/
source.rs

1use std::sync::Arc;
2
3use glam::DVec3;
4use steel_registry::{REGISTRY, TaggedRegistryExt, damage_type::DamageType};
5use steel_utils::Identifier;
6
7use super::DamageSource;
8use crate::entity::{EntityGeneration, SharedEntity, WeakEntity};
9
10struct WeakSourceEntity {
11    entity: WeakEntity,
12    generation: EntityGeneration,
13}
14
15impl WeakSourceEntity {
16    fn new(entity: &SharedEntity) -> Self {
17        Self {
18            entity: Arc::downgrade(entity),
19            generation: entity.generation(),
20        }
21    }
22
23    fn resolve(&self) -> ResolvedSourceEntity {
24        ResolvedSourceEntity {
25            entity: self.entity.upgrade(),
26            generation: self.generation,
27        }
28    }
29}
30
31pub(super) struct WeakDamageSource {
32    damage_type: &'static DamageType,
33    causing_entity: Option<WeakSourceEntity>,
34    direct_entity: Option<WeakSourceEntity>,
35    source_position: Option<DVec3>,
36}
37
38impl WeakDamageSource {
39    pub(super) fn new(source: &DamageSource) -> Self {
40        Self {
41            damage_type: source.damage_type,
42            causing_entity: source.causing_entity().map(WeakSourceEntity::new),
43            direct_entity: source.direct_entity().map(WeakSourceEntity::new),
44            source_position: source.source_position_raw(),
45        }
46    }
47
48    pub(super) fn resolve(&self) -> RecentDamageSource {
49        RecentDamageSource {
50            damage_type: self.damage_type,
51            causing_entity: self.causing_entity.as_ref().map(WeakSourceEntity::resolve),
52            direct_entity: self.direct_entity.as_ref().map(WeakSourceEntity::resolve),
53            source_position: self.source_position,
54        }
55    }
56}
57
58struct ResolvedSourceEntity {
59    entity: Option<SharedEntity>,
60    generation: EntityGeneration,
61}
62
63/// A read of recent history, retaining the original entities that still exist.
64///
65/// Unlike vanilla, a removed or unmanaged victim's history does not keep source
66/// entities alive. Generation accessors distinguish a released entity from an
67/// originally absent one. Each entity resolves independently, so a released
68/// projectile does not hide its still-live shooter.
69pub struct RecentDamageSource {
70    /// The original damage type, even when its entities have been released.
71    pub damage_type: &'static DamageType,
72    causing_entity: Option<ResolvedSourceEntity>,
73    direct_entity: Option<ResolvedSourceEntity>,
74    source_position: Option<DVec3>,
75}
76
77impl RecentDamageSource {
78    /// The original causing entity, if it was still alive when history was read.
79    #[must_use]
80    pub fn causing_entity(&self) -> Option<&SharedEntity> {
81        self.causing_entity.as_ref()?.entity.as_ref()
82    }
83
84    /// The original direct entity, if it was still alive when history was read.
85    #[must_use]
86    pub fn direct_entity(&self) -> Option<&SharedEntity> {
87        self.direct_entity.as_ref()?.entity.as_ref()
88    }
89
90    /// Original allocation identity; `None` means the source never had a cause.
91    #[must_use]
92    pub fn causing_entity_generation(&self) -> Option<EntityGeneration> {
93        self.causing_entity.as_ref().map(|source| source.generation)
94    }
95
96    /// Original allocation identity; `None` means the source never had a direct entity.
97    #[must_use]
98    pub fn direct_entity_generation(&self) -> Option<EntityGeneration> {
99        self.direct_entity.as_ref().map(|source| source.generation)
100    }
101
102    /// Original directness, including after either entity has been released.
103    #[must_use]
104    pub fn is_direct(&self) -> bool {
105        self.causing_entity_generation() == self.direct_entity_generation()
106    }
107
108    /// Tests the original damage type's tag membership.
109    #[must_use]
110    pub fn is(&self, tag: &Identifier) -> bool {
111        REGISTRY.damage_types.is_in_tag(self.damage_type, tag)
112    }
113
114    /// Explicit position, or the retained direct entity's current position.
115    #[must_use]
116    pub fn source_position(&self) -> Option<DVec3> {
117        self.source_position
118            .or_else(|| self.direct_entity().map(|entity| entity.position()))
119    }
120
121    /// The original explicit source position, without substituting an entity position.
122    #[must_use]
123    pub const fn source_position_raw(&self) -> Option<DVec3> {
124        self.source_position
125    }
126}