use crate::components::ElevatorPhase;
use crate::door::DoorState;
use crate::entity::EntityId;
use crate::events::{Event, EventBus};
use crate::world::World;
use super::PhaseContext;
use super::dispatch::update_indicators;
fn indicators_for_travel(world: &World, target: EntityId, from_pos: f64) -> (bool, bool) {
match world.stop_position(target) {
Some(p) if p > from_pos => (true, false),
Some(p) if p < from_pos => (false, true),
_ => (true, true),
}
}
pub fn run(
world: &mut World,
events: &mut EventBus,
ctx: &PhaseContext,
elevator_ids: &[EntityId],
) {
for &eid in elevator_ids {
if world.is_disabled(eid) {
continue;
}
let Some(car) = world.elevator(eid) else {
continue;
};
let phase = car.phase;
let current_target = car.target_stop;
let is_repositioning = car.repositioning;
let Some(queue) = world.destination_queue(eid) else {
continue;
};
let front = queue.front();
if is_repositioning && front.is_none() {
continue;
}
match phase {
ElevatorPhase::Idle | ElevatorPhase::Stopped => {
let Some(next) = front else { continue };
let pos = world.position(eid).map_or(0.0, |p| p.value);
let at_stop = world.find_stop_at_position(pos);
if at_stop == Some(next) {
if let Some(q) = world.destination_queue_mut(eid) {
q.pop_front();
}
update_indicators(world, events, eid, true, true, ctx.tick);
events.emit(Event::ElevatorArrived {
elevator: eid,
at_stop: next,
tick: ctx.tick,
});
if let Some(car) = world.elevator_mut(eid) {
car.phase = ElevatorPhase::DoorOpening;
car.target_stop = Some(next);
car.door =
DoorState::request_open(car.door_transition_ticks, car.door_open_ticks);
}
} else {
let from_stop = at_stop;
let (new_up, new_down) = indicators_for_travel(world, next, pos);
if let Some(car) = world.elevator_mut(eid) {
car.phase = ElevatorPhase::MovingToStop(next);
car.target_stop = Some(next);
}
update_indicators(world, events, eid, new_up, new_down, ctx.tick);
if let Some(from) = from_stop {
events.emit(Event::ElevatorDeparted {
elevator: eid,
from_stop: from,
tick: ctx.tick,
});
}
}
}
ElevatorPhase::MovingToStop(t) | ElevatorPhase::Repositioning(t) => {
if front == Some(t) {
continue;
}
match front {
Some(new_target) => {
let pos = world.position(eid).map_or(0.0, |p| p.value);
let (new_up, new_down) = indicators_for_travel(world, new_target, pos);
if let Some(car) = world.elevator_mut(eid) {
car.phase = ElevatorPhase::MovingToStop(new_target);
car.target_stop = Some(new_target);
car.repositioning = false;
}
update_indicators(world, events, eid, new_up, new_down, ctx.tick);
}
None => {
let _ = current_target;
}
}
}
_ => {}
}
}
}