Skip to main content

steel_core/entity/base/
relationships.rs

1use std::sync::Weak;
2
3use crate::entity::{RemovalReason, SharedEntity, WeakEntity};
4
5/// Non-physical lifecycle state shared by every entity.
6#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7pub(super) struct EntityLifecycleState {
8    pub(super) removal_reason: Option<RemovalReason>,
9    pub(super) pending_world_change: Option<PendingWorldChangeToken>,
10    pub(super) next_world_change_token: u64,
11}
12
13impl EntityLifecycleState {
14    pub(super) const fn new() -> Self {
15        Self {
16            removal_reason: None,
17            pending_world_change: None,
18            next_world_change_token: 1,
19        }
20    }
21
22    pub(super) fn next_world_change_token(&mut self) -> PendingWorldChangeToken {
23        let token = PendingWorldChangeToken(self.next_world_change_token);
24        self.next_world_change_token = self.next_world_change_token.wrapping_add(1).max(1);
25        token
26    }
27}
28
29/// Runtime token for an in-flight world change request.
30///
31/// The token is intentionally not persisted. It protects async preparation jobs
32/// from completing or clearing a newer transition started by the same entity.
33#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
34pub struct PendingWorldChangeToken(u64);
35
36impl PendingWorldChangeToken {
37    #[cfg(test)]
38    pub(crate) const fn for_test(value: u64) -> Self {
39        Self(value)
40    }
41}
42
43/// Vanilla passenger and vehicle relationship state.
44///
45/// Stored separately from movement state because riding relationships affect
46/// collision, tracking, saving, and future pathfinding without being part of
47/// the entity's physical pose/velocity snapshot.
48#[derive(Default)]
49pub(super) struct EntityRelationshipState {
50    pub(super) vehicle: Option<WeakEntity>,
51    pub(super) passengers: Vec<WeakEntity>,
52    pub(super) boarding_cooldown: i32,
53}
54
55impl EntityRelationshipState {
56    pub(super) fn vehicle(&mut self) -> Option<SharedEntity> {
57        let vehicle = self.vehicle.as_ref().and_then(Weak::upgrade);
58        if vehicle.is_none() {
59            self.vehicle = None;
60        }
61        vehicle
62    }
63
64    pub(super) fn passengers(&mut self) -> Vec<SharedEntity> {
65        let mut live_passengers = Vec::new();
66        self.passengers.retain(|passenger| {
67            if let Some(entity) = passenger.upgrade() {
68                live_passengers.push(entity);
69                true
70            } else {
71                false
72            }
73        });
74        live_passengers
75    }
76
77    pub(super) fn first_passenger(&mut self) -> Option<SharedEntity> {
78        self.passengers
79            .retain(|passenger| passenger.strong_count() > 0);
80        self.passengers.first().and_then(Weak::upgrade)
81    }
82
83    pub(super) fn has_passenger_id(&mut self, passenger_id: i32) -> bool {
84        self.passengers
85            .retain(|passenger| passenger.strong_count() > 0);
86        self.passengers.iter().any(|passenger| {
87            passenger
88                .upgrade()
89                .is_some_and(|entity| entity.id() == passenger_id)
90        })
91    }
92
93    pub(super) fn remove_passenger_id(&mut self, passenger_id: i32) -> bool {
94        let mut removed = false;
95        self.passengers.retain(|passenger| {
96            let Some(entity) = passenger.upgrade() else {
97                return false;
98            };
99            if entity.id() == passenger_id {
100                removed = true;
101                false
102            } else {
103                true
104            }
105        });
106        removed
107    }
108}