Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Riders

A rider is anything that rides an elevator. The core library is deliberately generic – a rider could be a person, a cargo crate, a robot, or a game character. Your game adds meaning through extensions and game logic; the simulation handles movement, queuing, and capacity.

Spawning riders

The simplest way to create a rider is spawn_rider, which takes an origin stop, a destination stop, and a weight:

#![allow(unused)]
fn main() {
use elevator_core::prelude::*;
fn run(sim: &mut Simulation) -> Result<(), SimError> {
let rider_id = sim.spawn_rider(StopId(0), StopId(3), 75.0)?;
let _ = rider_id;
Ok(())
}
}

This creates a rider at stop 0, heading to stop 3, weighing 75 units. The rider starts in the Waiting phase and a RiderSpawned event is emitted.

For more control, use the RiderBuilder fluent API:

#![allow(unused)]
fn main() {
use elevator_core::prelude::*;
fn run(sim: &mut Simulation) -> Result<(), SimError> {
let rider_id = sim.build_rider(StopId(0), StopId(3))?
    .weight(80.0)
    .patience(600)                 // abandon after 10 seconds at 60 tps
    .preferences(
        Preferences::default()
            .with_skip_full_elevator(true)
    )
    .spawn()?;
let _ = rider_id;
Ok(())
}
}

The builder lets you set patience, boarding preferences, access control, and other per-rider options in a single chain.

Rider phases

Each rider is in one phase at a time. The phases and their transitions are covered in detail in Rider Lifecycle. Here is the overview:

PhaseWhere is the rider?
WaitingAt a stop, in the queue
BoardingBeing loaded into an elevator (transient, one tick)
RidingInside an elevator
ExitingLeaving an elevator (transient, one tick)
WalkingTransferring between stops on a multi-leg route
ArrivedReached destination – your game decides next step
AbandonedGave up waiting – your game can settle or despawn
ResidentParked at a stop, not seeking an elevator

Each transition emits an event: RiderSpawned, RiderBoarded, RiderExited, RiderAbandoned, RiderSettled, RiderRerouted, RiderDespawned.

Rider data

Key accessors on the Rider component (all are getter methods):

  • weight – how much this rider contributes to elevator load
  • spawn_tick – when the rider was created
  • board_tick – when the rider boarded (if applicable)
  • current_stopOption<EntityId>, the stop the rider is currently at (or None if aboard an elevator)
  • phase – the current RiderPhase

Population tracking

The simulation maintains a reverse index (RiderIndex) that tracks riders at each stop, enabling O(1) population queries without scanning every entity:

#![allow(unused)]
fn main() {
use elevator_core::prelude::*;
fn run(sim: &Simulation, lobby_entity: EntityId, floor_10: EntityId, stop_entity: EntityId) {
// Who is waiting at the lobby?
let waiting: Vec<EntityId> = sim.waiting_at(lobby_entity).collect();

// How many residents live on floor 10?
let count = sim.residents_at(floor_10).count();

// Did anyone abandon at this stop?
let abandoned: Vec<EntityId> = sim.abandoned_at(stop_entity).collect();
let _ = (waiting, count, abandoned);
}
}

These three methods cover the main population categories:

MethodReturns riders in phase…
sim.waiting_at(stop)Waiting
sim.residents_at(stop)Resident
sim.abandoned_at(stop)Abandoned

Entity type checks

When you have an EntityId and need to know what it refers to, use the type-check helpers:

#![allow(unused)]
fn main() {
use elevator_core::prelude::*;
fn run(sim: &Simulation, id: EntityId) {
if sim.is_rider(id) {
    // handle rider
} else if sim.is_elevator(id) {
    // handle elevator
} else if sim.is_stop(id) {
    // handle stop
}
}
}

These are more readable than querying sim.world().elevator(id).is_some() and are the preferred pattern in game code.

Lifecycle methods

Three methods manage rider state transitions after arrival or abandonment:

MethodEffect
sim.settle_rider(id)Transitions an Arrived or Abandoned rider to Resident at their current stop
sim.reroute_rider(id, route)Sends a Resident rider back to Waiting with a new route
sim.despawn_rider(id)Removes the rider entity and updates all indexes

Always use sim.despawn_rider(id) instead of calling world.despawn() directly – it keeps the population index consistent and emits a RiderDespawned event.

#![allow(unused)]
fn main() {
use elevator_core::prelude::*;
fn run(sim: &mut Simulation, rider_id: RiderId, new_route: Route) -> Result<(), SimError> {
// A rider arrives at their destination. Settle them as a resident.
sim.settle_rider(rider_id)?;

// Later, they want to go back down. Reroute them.
// (`reroute_rider` takes an `EntityId`; the sibling calls take `RiderId`.)
sim.reroute_rider(rider_id.entity(), new_route)?;

// Or remove them entirely.
sim.despawn_rider(rider_id)?;
Ok(())
}
}

Next steps