use crate::components::ElevatorPhase;
use crate::entity::{ElevatorId, EntityId};
use crate::error::SimError;
use crate::events::Event;
use crate::stop::StopRef;
use crate::world::World;
impl super::Simulation {
#[must_use]
pub fn destination_queue(&self, elev: ElevatorId) -> Option<&[EntityId]> {
let elev = elev.entity();
self.world
.destination_queue(elev)
.map(crate::components::DestinationQueue::queue)
}
pub fn push_destination(
&mut self,
elev: ElevatorId,
stop: impl Into<StopRef>,
) -> Result<(), SimError> {
let elev = elev.entity();
let stop = self.resolve_stop(stop.into())?;
self.validate_push_targets(elev, stop)?;
let appended = self
.world
.destination_queue_mut(elev)
.is_some_and(|q| q.push_back(stop));
if appended {
self.events.emit(Event::DestinationQueued {
elevator: elev,
stop,
tick: self.tick,
});
}
Ok(())
}
pub fn push_destination_front(
&mut self,
elev: ElevatorId,
stop: impl Into<StopRef>,
) -> Result<(), SimError> {
let elev = elev.entity();
let stop = self.resolve_stop(stop.into())?;
self.validate_push_targets(elev, stop)?;
let inserted = self
.world
.destination_queue_mut(elev)
.is_some_and(|q| q.push_front(stop));
if inserted {
self.events.emit(Event::DestinationQueued {
elevator: elev,
stop,
tick: self.tick,
});
}
Ok(())
}
pub fn clear_destinations(&mut self, elev: ElevatorId) -> Result<(), SimError> {
let elev = elev.entity();
if self.world.elevator(elev).is_none() {
return Err(SimError::NotAnElevator(elev));
}
if let Some(q) = self.world.destination_queue_mut(elev) {
q.clear();
}
Ok(())
}
pub fn abort_movement(&mut self, elev: ElevatorId) -> Result<(), SimError> {
let eid = elev.entity();
let Some(car) = self.world.elevator(eid) else {
return Err(SimError::NotAnElevator(eid));
};
if !car.phase().is_moving() {
return Ok(());
}
let pos = self.world.position(eid).map_or(0.0, |p| p.value);
let vel = self.world.velocity(eid).map_or(0.0, |v| v.value);
let Some(brake_pos) = self.future_stop_position(eid) else {
return Ok(());
};
let Some(brake_stop) = brake_target_stop(&self.world, pos, vel, brake_pos) else {
return Ok(());
};
if let Some(car) = self.world.elevator_mut(eid) {
car.phase = ElevatorPhase::Repositioning(brake_stop);
car.target_stop = Some(brake_stop);
car.repositioning = true;
}
if let Some(q) = self.world.destination_queue_mut(eid) {
q.clear();
}
self.events.emit(Event::MovementAborted {
elevator: eid,
brake_target: brake_stop,
tick: self.tick,
});
Ok(())
}
fn validate_push_targets(&self, elev: EntityId, stop: EntityId) -> Result<(), SimError> {
if self.world.elevator(elev).is_none() {
return Err(SimError::NotAnElevator(elev));
}
if self.world.stop(stop).is_none() {
return Err(SimError::NotAStop(stop));
}
Ok(())
}
}
fn brake_target_stop(world: &World, pos: f64, vel: f64, brake_pos: f64) -> Option<EntityId> {
let dir = vel.signum();
if dir != 0.0 {
let ahead_of_brake = world
.iter_stops()
.filter(|(_, stop)| (stop.position() - brake_pos) * dir >= 0.0)
.min_by(|(_, a), (_, b)| {
(a.position() - pos)
.abs()
.total_cmp(&(b.position() - pos).abs())
})
.map(|(id, _)| id);
if ahead_of_brake.is_some() {
return ahead_of_brake;
}
let ahead_of_car = world
.iter_stops()
.filter(|(_, stop)| (stop.position() - pos) * dir >= 0.0)
.max_by(|(_, a), (_, b)| {
((a.position() - pos) * dir).total_cmp(&((b.position() - pos) * dir))
})
.map(|(id, _)| id);
if ahead_of_car.is_some() {
return ahead_of_car;
}
}
world.find_nearest_stop(brake_pos)
}