mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-11-25 11:44:25 +03:00
moving time traveler over to new stackable state model. and with that,
the stackable state model is good enough!
This commit is contained in:
parent
6905595a05
commit
8c67267d28
@ -20,8 +20,7 @@ pub struct SandboxMode {
|
||||
following: Option<TripID>,
|
||||
route_viewer: route_viewer::RouteViewer,
|
||||
show_activity: show_activity::ShowActivity,
|
||||
time_travel: time_travel::TimeTravel,
|
||||
// TODO Not while Spawning or TimeTraveling or ExploringRoute...
|
||||
pub time_travel: time_travel::InactiveTimeTravel,
|
||||
common: CommonState,
|
||||
menu: ModalMenu,
|
||||
}
|
||||
@ -33,7 +32,7 @@ impl SandboxMode {
|
||||
following: None,
|
||||
route_viewer: route_viewer::RouteViewer::Inactive,
|
||||
show_activity: show_activity::ShowActivity::Inactive,
|
||||
time_travel: time_travel::TimeTravel::new(),
|
||||
time_travel: time_travel::InactiveTimeTravel::new(),
|
||||
common: CommonState::new(),
|
||||
menu: ModalMenu::new(
|
||||
"Sandbox Mode",
|
||||
@ -148,9 +147,7 @@ impl State for SandboxMode {
|
||||
self.route_viewer.event(ctx, ui, &mut self.menu);
|
||||
self.show_activity.event(ctx, ui, &mut self.menu);
|
||||
if self.menu.action("start time traveling") {
|
||||
//self.state = State::TimeTraveling;
|
||||
//self.time_travel.start(ctx, ui);
|
||||
//return EventLoopMode::InputOnly;
|
||||
return self.time_travel.start(ctx, ui);
|
||||
}
|
||||
if self.menu.action("scoreboard") {
|
||||
return Transition::Push(Box::new(score::Scoreboard::new(ctx, ui)));
|
||||
@ -276,21 +273,6 @@ impl State for SandboxMode {
|
||||
self.show_activity.draw(g, ui);
|
||||
self.menu.draw(g);
|
||||
self.speed.draw(g);
|
||||
|
||||
/*match state.mode {
|
||||
Mode::Sandbox(ref mode) => match mode.state {
|
||||
State::TimeTraveling => {
|
||||
state.ui.draw(
|
||||
g,
|
||||
DrawOptions::new(),
|
||||
&mode.time_travel,
|
||||
&ShowEverything::new(),
|
||||
);
|
||||
mode.time_travel.draw(g);
|
||||
}
|
||||
},
|
||||
_ => unreachable!(),
|
||||
}*/
|
||||
}
|
||||
|
||||
fn on_suspend(&mut self, _: &mut UI) {
|
||||
|
@ -1,4 +1,7 @@
|
||||
use crate::ui::UI;
|
||||
use crate::game::{State, Transition};
|
||||
use crate::render::DrawOptions;
|
||||
use crate::sandbox::SandboxMode;
|
||||
use crate::ui::{ShowEverything, UI};
|
||||
use abstutil::MultiMap;
|
||||
use ezgui::{hotkey, EventCtx, GfxCtx, ItemSlider, Key, Text};
|
||||
use geom::Duration;
|
||||
@ -6,15 +9,12 @@ use map_model::{Map, Traversable};
|
||||
use sim::{CarID, DrawCarInput, DrawPedestrianInput, GetDrawAgents, PedestrianID};
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
pub enum TimeTravel {
|
||||
Active(ItemSlider<StateAtTime>),
|
||||
Inactive {
|
||||
should_record: bool,
|
||||
moments: Vec<(StateAtTime, Text)>,
|
||||
},
|
||||
pub struct InactiveTimeTravel {
|
||||
should_record: bool,
|
||||
moments: Vec<(StateAtTime, Text)>,
|
||||
}
|
||||
|
||||
pub struct StateAtTime {
|
||||
struct StateAtTime {
|
||||
time: Duration,
|
||||
cars: BTreeMap<CarID, DrawCarInput>,
|
||||
peds: BTreeMap<PedestrianID, DrawPedestrianInput>,
|
||||
@ -22,139 +22,114 @@ pub struct StateAtTime {
|
||||
peds_per_traversable: MultiMap<Traversable, PedestrianID>,
|
||||
}
|
||||
|
||||
impl TimeTravel {
|
||||
pub fn new() -> TimeTravel {
|
||||
TimeTravel::Inactive {
|
||||
impl InactiveTimeTravel {
|
||||
pub fn new() -> InactiveTimeTravel {
|
||||
InactiveTimeTravel {
|
||||
should_record: false,
|
||||
moments: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn start(&mut self, ctx: &mut EventCtx, ui: &UI) {
|
||||
pub fn start(&mut self, ctx: &mut EventCtx, ui: &UI) -> Transition {
|
||||
self.should_record = true;
|
||||
// In case we weren't already...
|
||||
match self {
|
||||
TimeTravel::Inactive {
|
||||
ref mut should_record,
|
||||
..
|
||||
} => {
|
||||
*should_record = true;
|
||||
}
|
||||
TimeTravel::Active(_) => unreachable!(),
|
||||
}
|
||||
self.record(ui);
|
||||
|
||||
// TODO More cleanly?
|
||||
let items = match self {
|
||||
TimeTravel::Inactive {
|
||||
ref mut moments, ..
|
||||
} => std::mem::replace(moments, Vec::new()),
|
||||
TimeTravel::Active(_) => unreachable!(),
|
||||
};
|
||||
*self = TimeTravel::Active(ItemSlider::new(
|
||||
items,
|
||||
"Time Traveler",
|
||||
"moment",
|
||||
vec![(hotkey(Key::Escape), "quit")],
|
||||
ctx,
|
||||
));
|
||||
// Temporarily move our state into the new one.
|
||||
let items = std::mem::replace(&mut self.moments, Vec::new());
|
||||
|
||||
return Transition::Push(Box::new(TimeTraveler {
|
||||
slider: ItemSlider::new(
|
||||
items,
|
||||
"Time Traveler",
|
||||
"moment",
|
||||
vec![(hotkey(Key::Escape), "quit")],
|
||||
ctx,
|
||||
),
|
||||
}));
|
||||
}
|
||||
|
||||
// TODO Now that we take big jumps forward in the source sim, the time traveler sees the same
|
||||
// granularity when replaying.
|
||||
pub fn record(&mut self, ui: &UI) {
|
||||
match self {
|
||||
TimeTravel::Inactive {
|
||||
ref should_record,
|
||||
ref mut moments,
|
||||
} => {
|
||||
if !*should_record {
|
||||
return;
|
||||
}
|
||||
|
||||
let map = &ui.primary.map;
|
||||
let sim = &ui.primary.sim;
|
||||
let now = sim.time();
|
||||
|
||||
if let Some((ref state, _)) = moments.last() {
|
||||
// Already have this
|
||||
if now == state.time {
|
||||
return;
|
||||
}
|
||||
// We just loaded a new savestate or reset or something. Clear out our memory.
|
||||
if now < state.time {
|
||||
moments.clear();
|
||||
}
|
||||
}
|
||||
|
||||
let mut state = StateAtTime {
|
||||
time: now,
|
||||
cars: BTreeMap::new(),
|
||||
peds: BTreeMap::new(),
|
||||
cars_per_traversable: MultiMap::new(),
|
||||
peds_per_traversable: MultiMap::new(),
|
||||
};
|
||||
for draw in sim.get_all_draw_cars(map).into_iter() {
|
||||
state.cars_per_traversable.insert(draw.on, draw.id);
|
||||
state.cars.insert(draw.id, draw);
|
||||
}
|
||||
for draw in sim.get_all_draw_peds(map).into_iter() {
|
||||
state.peds_per_traversable.insert(draw.on, draw.id);
|
||||
state.peds.insert(draw.id, draw);
|
||||
}
|
||||
let label = Text::from_line(state.time.to_string());
|
||||
moments.push((state, label));
|
||||
}
|
||||
TimeTravel::Active(_) => unreachable!(),
|
||||
if !self.should_record {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Returns true if done.
|
||||
pub fn event(&mut self, ctx: &mut EventCtx) -> bool {
|
||||
match self {
|
||||
TimeTravel::Inactive { .. } => unreachable!(),
|
||||
TimeTravel::Active(ref mut slider) => {
|
||||
slider.event(ctx);
|
||||
ctx.canvas.handle_event(ctx.input);
|
||||
let map = &ui.primary.map;
|
||||
let sim = &ui.primary.sim;
|
||||
let now = sim.time();
|
||||
|
||||
if slider.action("quit") {
|
||||
*self = TimeTravel::Inactive {
|
||||
should_record: true,
|
||||
moments: slider.consume_all_items(),
|
||||
};
|
||||
return true;
|
||||
}
|
||||
false
|
||||
if let Some((ref state, _)) = self.moments.last() {
|
||||
// Already have this
|
||||
if now == state.time {
|
||||
return;
|
||||
}
|
||||
// We just loaded a new savestate or reset or something. Clear out our memory.
|
||||
if now < state.time {
|
||||
self.moments.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn draw(&self, g: &mut GfxCtx) {
|
||||
match self {
|
||||
TimeTravel::Inactive { .. } => unreachable!(),
|
||||
TimeTravel::Active(ref slider) => {
|
||||
slider.draw(g);
|
||||
}
|
||||
let mut state = StateAtTime {
|
||||
time: now,
|
||||
cars: BTreeMap::new(),
|
||||
peds: BTreeMap::new(),
|
||||
cars_per_traversable: MultiMap::new(),
|
||||
peds_per_traversable: MultiMap::new(),
|
||||
};
|
||||
for draw in sim.get_all_draw_cars(map).into_iter() {
|
||||
state.cars_per_traversable.insert(draw.on, draw.id);
|
||||
state.cars.insert(draw.id, draw);
|
||||
}
|
||||
}
|
||||
|
||||
fn get_current_state(&self) -> &StateAtTime {
|
||||
match self {
|
||||
TimeTravel::Inactive { .. } => unreachable!(),
|
||||
TimeTravel::Active(ref slider) => slider.get().1,
|
||||
for draw in sim.get_all_draw_peds(map).into_iter() {
|
||||
state.peds_per_traversable.insert(draw.on, draw.id);
|
||||
state.peds.insert(draw.id, draw);
|
||||
}
|
||||
let label = Text::from_line(state.time.to_string());
|
||||
self.moments.push((state, label));
|
||||
}
|
||||
}
|
||||
|
||||
impl GetDrawAgents for TimeTravel {
|
||||
struct TimeTraveler {
|
||||
slider: ItemSlider<StateAtTime>,
|
||||
}
|
||||
|
||||
impl State for TimeTraveler {
|
||||
// Returns true if done.
|
||||
fn event(&mut self, ctx: &mut EventCtx, _: &mut UI) -> Transition {
|
||||
self.slider.event(ctx);
|
||||
ctx.canvas.handle_event(ctx.input);
|
||||
|
||||
if self.slider.action("quit") {
|
||||
let moments = self.slider.consume_all_items();
|
||||
return Transition::PopWithData(Box::new(|state| {
|
||||
let mut sandbox = state.downcast_mut::<SandboxMode>().unwrap();
|
||||
sandbox.time_travel.moments = moments;
|
||||
}));
|
||||
}
|
||||
Transition::Keep
|
||||
}
|
||||
|
||||
fn draw(&self, g: &mut GfxCtx, ui: &UI) {
|
||||
ui.draw(g, DrawOptions::new(), self, &ShowEverything::new());
|
||||
self.slider.draw(g);
|
||||
}
|
||||
}
|
||||
|
||||
impl TimeTraveler {
|
||||
fn get_current_state(&self) -> &StateAtTime {
|
||||
self.slider.get().1
|
||||
}
|
||||
}
|
||||
|
||||
impl GetDrawAgents for TimeTraveler {
|
||||
fn time(&self) -> Duration {
|
||||
self.get_current_state().time
|
||||
}
|
||||
|
||||
fn step_count(&self) -> usize {
|
||||
match self {
|
||||
TimeTravel::Inactive { .. } => unreachable!(),
|
||||
TimeTravel::Active(ref slider) => slider.get().0,
|
||||
}
|
||||
self.slider.get().0
|
||||
}
|
||||
|
||||
fn get_draw_car(&self, id: CarID, _map: &Map) -> Option<DrawCarInput> {
|
||||
|
Loading…
Reference in New Issue
Block a user