Skip to main content

elevator_core/
door.rs

1//! Door open/close finite-state machine.
2
3use serde::{Deserialize, Serialize};
4
5/// State machine for elevator doors.
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
7#[non_exhaustive]
8pub enum DoorState {
9    /// Doors are fully closed.
10    Closed,
11    /// Doors are in the process of opening.
12    Opening {
13        /// Ticks left in the opening transition.
14        ticks_remaining: u32,
15        /// How many ticks the door stays open once fully opened.
16        open_duration: u32,
17        /// How many ticks the closing transition takes.
18        close_duration: u32,
19    },
20    /// Doors are fully open and holding.
21    Open {
22        /// Ticks left before the doors begin closing.
23        ticks_remaining: u32,
24        /// How many ticks the closing transition takes.
25        close_duration: u32,
26    },
27    /// Doors are in the process of closing.
28    Closing {
29        /// Ticks left in the closing transition.
30        ticks_remaining: u32,
31    },
32}
33
34/// A manual door-control command submitted by game code.
35///
36/// Submitted via
37/// [`Simulation::request_door_open`](crate::sim::Simulation::request_door_open),
38/// [`Simulation::request_door_close`](crate::sim::Simulation::request_door_close),
39/// [`Simulation::hold_door_open`](crate::sim::Simulation::hold_door_open),
40/// and [`Simulation::cancel_door_hold`](crate::sim::Simulation::cancel_door_hold).
41/// Commands are queued on the target elevator and processed at the start of
42/// the door phase; those that are not yet valid stay queued until they are.
43#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
44#[non_exhaustive]
45pub enum DoorCommand {
46    /// Open the doors now (or on arrival at the next stop).
47    Open,
48    /// Close the doors now (or as soon as loading is done).
49    Close,
50    /// Extend the open dwell by `ticks`. Cumulative across calls.
51    HoldOpen {
52        /// Additional ticks to hold the doors open.
53        ticks: u32,
54    },
55    /// Cancel any pending hold extension.
56    CancelHold,
57}
58
59/// Transition emitted when the door state changes phase.
60#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
61#[non_exhaustive]
62pub enum DoorTransition {
63    /// No phase change occurred this tick.
64    None,
65    /// Doors just finished opening and are now fully open.
66    FinishedOpening,
67    /// Doors just finished holding open and are about to close.
68    FinishedOpen,
69    /// Doors just finished closing and are now fully closed.
70    FinishedClosing,
71}
72
73impl std::fmt::Display for DoorState {
74    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
75        match self {
76            Self::Closed => write!(f, "Closed"),
77            Self::Opening {
78                ticks_remaining, ..
79            } => write!(f, "Opening({ticks_remaining})"),
80            Self::Open {
81                ticks_remaining, ..
82            } => write!(f, "Open({ticks_remaining})"),
83            Self::Closing { ticks_remaining } => write!(f, "Closing({ticks_remaining})"),
84        }
85    }
86}
87
88impl DoorState {
89    /// Returns `true` if the doors are fully open.
90    #[must_use]
91    pub const fn is_open(&self) -> bool {
92        matches!(self, Self::Open { .. })
93    }
94
95    /// Returns `true` if the doors are fully closed.
96    #[must_use]
97    pub const fn is_closed(&self) -> bool {
98        matches!(self, Self::Closed)
99    }
100
101    /// Begin opening the door.
102    #[must_use]
103    pub const fn request_open(transition_ticks: u32, open_ticks: u32) -> Self {
104        Self::Opening {
105            ticks_remaining: transition_ticks,
106            open_duration: open_ticks,
107            close_duration: transition_ticks,
108        }
109    }
110
111    /// Advance the door state by one tick. Returns the transition that occurred.
112    pub const fn tick(&mut self) -> DoorTransition {
113        match self {
114            Self::Closed => DoorTransition::None,
115            Self::Opening {
116                ticks_remaining,
117                open_duration,
118                close_duration,
119            } => {
120                if *ticks_remaining <= 1 {
121                    let od = *open_duration;
122                    let cd = *close_duration;
123                    *self = Self::Open {
124                        ticks_remaining: od,
125                        close_duration: cd,
126                    };
127                    DoorTransition::FinishedOpening
128                } else {
129                    *ticks_remaining -= 1;
130                    DoorTransition::None
131                }
132            }
133            Self::Open {
134                ticks_remaining,
135                close_duration,
136            } => {
137                if *ticks_remaining <= 1 {
138                    let cd = *close_duration;
139                    *self = Self::Closing {
140                        ticks_remaining: cd,
141                    };
142                    DoorTransition::FinishedOpen
143                } else {
144                    *ticks_remaining -= 1;
145                    DoorTransition::None
146                }
147            }
148            Self::Closing { ticks_remaining } => {
149                if *ticks_remaining <= 1 {
150                    *self = Self::Closed;
151                    DoorTransition::FinishedClosing
152                } else {
153                    *ticks_remaining -= 1;
154                    DoorTransition::None
155                }
156            }
157        }
158    }
159}